mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge pull request #25409 from Tom94/bspline-sliders
Add free-hand drawing of sliders to the editor
This commit is contained in:
commit
bf972a04ee
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.1111.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.1121.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -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.PERFECT_CURVE, 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.PERFECT_CURVE, 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.PERFECT_CURVE);
|
||||
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", () =>
|
||||
|
@ -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.PERFECT_CURVE : 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));
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(-100, -200)),
|
||||
new PathControlPoint(new Vector2(100, -200))
|
||||
}),
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(-100, 0)),
|
||||
new PathControlPoint(new Vector2(100, 20))
|
||||
};
|
||||
|
@ -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.PERFECT_CURVE);
|
||||
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.PERFECT_CURVE);
|
||||
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.PERFECT_CURVE);
|
||||
assertControlPointPathType(3, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -133,21 +133,45 @@ 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.PERFECT_CURVE);
|
||||
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);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCatmullAvailableIffSelectionContainsCatmull()
|
||||
{
|
||||
createVisualiser(true);
|
||||
|
||||
addControlPointStep(new Vector2(200), PathType.CATMULL);
|
||||
addControlPointStep(new Vector2(300));
|
||||
addControlPointStep(new Vector2(500, 300));
|
||||
addControlPointStep(new Vector2(700, 200));
|
||||
addControlPointStep(new Vector2(500, 100));
|
||||
|
||||
moveMouseToControlPoint(2);
|
||||
AddStep("select first and third control point", () =>
|
||||
{
|
||||
visualiser.Pieces[0].IsSelected.Value = true;
|
||||
visualiser.Pieces[2].IsSelected.Value = true;
|
||||
});
|
||||
addContextMenuItemStep("Catmull");
|
||||
|
||||
assertControlPointPathType(0, PathType.CATMULL);
|
||||
assertControlPointPathType(2, PathType.CATMULL);
|
||||
assertControlPointPathType(4, null);
|
||||
}
|
||||
|
||||
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser<Slider>(slider, allowSelection)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@ -158,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
private void addControlPointStep(Vector2 position, PathType? type)
|
||||
{
|
||||
AddStep($"add {type} control point at {position}", () =>
|
||||
AddStep($"add {type?.Type} control point at {position}", () =>
|
||||
{
|
||||
slider.Path.ControlPoints.Add(new PathControlPoint(position, type));
|
||||
});
|
||||
|
@ -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.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[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.PERFECT_CURVE);
|
||||
|
||||
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.PERFECT_CURVE);
|
||||
|
||||
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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[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.PERFECT_CURVE);
|
||||
|
||||
moveMouseToControlPoint(4);
|
||||
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
assertControlPointType(3, PathType.PerfectCurve);
|
||||
assertControlPointType(3, PathType.PERFECT_CURVE);
|
||||
|
||||
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)
|
||||
|
@ -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.PERFECT_CURVE),
|
||||
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))
|
||||
};
|
||||
|
@ -1,8 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -58,7 +57,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 +71,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -90,7 +89,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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -112,7 +111,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 +130,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 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertLength(100);
|
||||
}
|
||||
|
||||
@ -172,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -196,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(4);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -216,8 +215,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 +239,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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -269,25 +268,79 @@ 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.PERFECT_CURVE);
|
||||
assertControlPointType(2, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeginPlacementWithoutReleasingMouse()
|
||||
public void TestSliderDrawingDoesntActivateAfterNormalPlacement()
|
||||
{
|
||||
Vector2 startPoint = new Vector2(200);
|
||||
|
||||
addMovementStep(startPoint);
|
||||
addClickStep(MouseButton.Left);
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
if (i == 5)
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50));
|
||||
}
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
assertPlaced(false);
|
||||
|
||||
addClickStep(MouseButton.Right);
|
||||
assertPlaced(true);
|
||||
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderDrawingCurve()
|
||||
{
|
||||
Vector2 startPoint = new Vector2(200);
|
||||
|
||||
addMovementStep(startPoint);
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50));
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
assertPlaced(true);
|
||||
assertLength(760, tolerance: 10);
|
||||
assertControlPointCount(5);
|
||||
assertControlPointType(0, PathType.BSpline(3));
|
||||
assertControlPointType(1, null);
|
||||
assertControlPointType(2, null);
|
||||
assertControlPointType(3, null);
|
||||
assertControlPointType(4, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderDrawingLinear()
|
||||
{
|
||||
addMovementStep(new Vector2(200));
|
||||
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
addMovementStep(new Vector2(300, 200));
|
||||
addMovementStep(new Vector2(400, 200));
|
||||
addMovementStep(new Vector2(400, 300));
|
||||
addMovementStep(new Vector2(400));
|
||||
addMovementStep(new Vector2(300, 400));
|
||||
addMovementStep(new Vector2(200, 400));
|
||||
|
||||
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
addClickStep(MouseButton.Right);
|
||||
|
||||
assertPlaced(true);
|
||||
assertLength(200);
|
||||
assertControlPointCount(2);
|
||||
assertControlPointType(0, PathType.Linear);
|
||||
assertLength(600, tolerance: 10);
|
||||
assertControlPointCount(4);
|
||||
assertControlPointType(0, PathType.LINEAR);
|
||||
assertControlPointType(1, null);
|
||||
assertControlPointType(2, null);
|
||||
assertControlPointType(3, null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -306,7 +359,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -326,7 +379,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -347,7 +400,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -368,7 +421,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.Bezier);
|
||||
assertControlPointType(0, PathType.BEZIER);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -385,7 +438,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
assertPlaced(true);
|
||||
assertControlPointCount(3);
|
||||
assertControlPointType(0, PathType.PerfectCurve);
|
||||
assertControlPointType(0, PathType.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
||||
@ -397,16 +450,16 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
|
||||
private void assertPlaced(bool expected) => AddAssert($"slider {(expected ? "placed" : "not placed")}", () => (getSlider() != null) == expected);
|
||||
|
||||
private void assertLength(double expected) => AddAssert($"slider length is {expected}", () => Precision.AlmostEquals(expected, getSlider().Distance, 1));
|
||||
private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider()!.Distance, () => Is.EqualTo(expected).Within(tolerance));
|
||||
|
||||
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected);
|
||||
private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider()!.Path.ControlPoints.Count, () => Is.EqualTo(expected));
|
||||
|
||||
private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type);
|
||||
private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider()!.Path.ControlPoints[index].Type, () => Is.EqualTo(type));
|
||||
|
||||
private void assertControlPointPosition(int index, Vector2 position) =>
|
||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1));
|
||||
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider()!.Path.ControlPoints[index].Position, 1));
|
||||
|
||||
private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
|
||||
private Slider? getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
|
||||
|
||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject);
|
||||
protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint();
|
||||
|
@ -22,12 +22,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
private readonly PathControlPoint[][] paths =
|
||||
{
|
||||
createPathSegment(
|
||||
PathType.PerfectCurve,
|
||||
PathType.PERFECT_CURVE,
|
||||
new Vector2(200, -50),
|
||||
new Vector2(250, 0)
|
||||
),
|
||||
createPathSegment(
|
||||
PathType.Linear,
|
||||
PathType.LINEAR,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100, 100)
|
||||
)
|
||||
|
@ -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),
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||
new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE),
|
||||
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.PERFECT_CURVE);
|
||||
|
||||
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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[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.PERFECT_CURVE);
|
||||
|
||||
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.PERFECT_CURVE);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -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.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
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.PERFECT_CURVE),
|
||||
(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.PERFECT_CURVE),
|
||||
(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.PERFECT_CURVE),
|
||||
(new Vector2(150, 200), null),
|
||||
(new Vector2(300, 50), PathType.PerfectCurve),
|
||||
(new Vector2(300, 50), PathType.PERFECT_CURVE),
|
||||
(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.PERFECT_CURVE),
|
||||
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.PERFECT_CURVE),
|
||||
(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.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(150, 150)),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
|
||||
new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(400, 0)),
|
||||
new PathControlPoint(new Vector2(400, 150))
|
||||
})
|
||||
|
@ -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)),
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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.PERFECT_CURVE, 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.PERFECT_CURVE, 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.PERFECT_CURVE, 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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
@ -488,7 +488,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.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(slider_path_length, 0),
|
||||
|
@ -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.PERFECT_CURVE, 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.PERFECT_CURVE, 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.PERFECT_CURVE, new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(300, 200)
|
||||
|
@ -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),
|
||||
|
@ -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.PERFECT_CURVE)
|
||||
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)
|
||||
switch (pathType.Type)
|
||||
{
|
||||
case PathType.Catmull:
|
||||
case SplineType.Catmull:
|
||||
return colours.SeaFoam;
|
||||
|
||||
case PathType.Bezier:
|
||||
return colours.Pink;
|
||||
case SplineType.BSpline:
|
||||
if (!pathType.Degree.HasValue)
|
||||
return colours.PinkLighter;
|
||||
|
||||
case PathType.PerfectCurve:
|
||||
int idx = Math.Clamp(pathType.Degree.Value, 0, 3);
|
||||
return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx];
|
||||
|
||||
case SplineType.PerfectCurve:
|
||||
return colours.PurpleDark;
|
||||
|
||||
default:
|
||||
@ -275,6 +279,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
}
|
||||
}
|
||||
|
||||
public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty;
|
||||
public LocalisableString TooltipText => ControlPoint.Type?.Description ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -242,18 +242,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
|
||||
|
||||
switch (type)
|
||||
if (type?.Type == 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,13 @@ 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.PERFECT_CURVE));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER));
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3)));
|
||||
|
||||
if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull))
|
||||
curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL));
|
||||
|
||||
var menuItems = new List<MenuItem>
|
||||
{
|
||||
@ -405,7 +405,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
int totalCount = Pieces.Count(p => p.IsSelected.Value);
|
||||
int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type);
|
||||
|
||||
var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ =>
|
||||
var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ =>
|
||||
{
|
||||
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
|
||||
updatePathType(p, type);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
@ -10,6 +11,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -44,6 +46,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; }
|
||||
|
||||
private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder();
|
||||
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||
|
||||
public SliderPlacementBlueprint()
|
||||
@ -51,7 +58,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;
|
||||
}
|
||||
|
||||
@ -66,13 +73,28 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
controlPointVisualiser = new PathControlPointVisualiser<Slider>(HitObject, false)
|
||||
};
|
||||
|
||||
setState(SliderPlacementState.Initial);
|
||||
state = SliderPlacementState.Initial;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
inputManager = GetContainingInputManager();
|
||||
|
||||
if (freehandToolboxGroup != null)
|
||||
{
|
||||
freehandToolboxGroup.Tolerance.BindValueChanged(e =>
|
||||
{
|
||||
bSplineBuilder.Tolerance = e.NewValue;
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}, true);
|
||||
|
||||
freehandToolboxGroup.CornerThreshold.BindValueChanged(e =>
|
||||
{
|
||||
bSplineBuilder.CornerThreshold = e.NewValue;
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
@ -87,8 +109,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
case SliderPlacementState.Initial:
|
||||
BeginPlacement();
|
||||
|
||||
double? nearestSliderVelocity = (editorBeatmap.HitObjects
|
||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier;
|
||||
double? nearestSliderVelocity = (editorBeatmap
|
||||
.HitObjects
|
||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier;
|
||||
|
||||
HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1;
|
||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||
@ -98,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
ApplyDefaultsToHitObject();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
updateCursor();
|
||||
break;
|
||||
}
|
||||
@ -115,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
beginCurve();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
if (canPlaceNewControlPoint(out var lastPoint))
|
||||
{
|
||||
// Place a new point by detatching the current cursor.
|
||||
@ -128,7 +151,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;
|
||||
}
|
||||
@ -139,25 +162,50 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (e.Button != MouseButton.Left)
|
||||
return base.OnDragStart(e);
|
||||
|
||||
if (state != SliderPlacementState.ControlPoints)
|
||||
return base.OnDragStart(e);
|
||||
|
||||
// Only enter drawing mode if no additional control points have been placed.
|
||||
int controlPointCount = HitObject.Path.ControlPoints.Count;
|
||||
if (controlPointCount > 2 || (controlPointCount == 2 && HitObject.Path.ControlPoints.Last() != cursor))
|
||||
return base.OnDragStart(e);
|
||||
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position);
|
||||
state = SliderPlacementState.Drawing;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
base.OnDrag(e);
|
||||
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
{
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position);
|
||||
Scheduler.AddOnce(updateSliderPathFromBSplineBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
base.OnDragEnd(e);
|
||||
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
endCurve();
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (state == SliderPlacementState.Body && e.Button == MouseButton.Right)
|
||||
if (state == SliderPlacementState.ControlPoints && e.Button == MouseButton.Right)
|
||||
endCurve();
|
||||
base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
private void beginCurve()
|
||||
{
|
||||
BeginPlacement(commitStart: true);
|
||||
setState(SliderPlacementState.Body);
|
||||
}
|
||||
|
||||
private void endCurve()
|
||||
{
|
||||
updateSlider();
|
||||
EndPlacement(true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -167,21 +215,39 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
updatePathType();
|
||||
}
|
||||
|
||||
private void beginCurve()
|
||||
{
|
||||
BeginPlacement(commitStart: true);
|
||||
state = SliderPlacementState.ControlPoints;
|
||||
}
|
||||
|
||||
private void endCurve()
|
||||
{
|
||||
updateSlider();
|
||||
EndPlacement(true);
|
||||
}
|
||||
|
||||
private void updatePathType()
|
||||
{
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
{
|
||||
segmentStart.Type = PathType.BSpline(3);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentSegmentLength)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
segmentStart.Type = PathType.Linear;
|
||||
segmentStart.Type = PathType.LINEAR;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
segmentStart.Type = PathType.PerfectCurve;
|
||||
segmentStart.Type = PathType.PERFECT_CURVE;
|
||||
break;
|
||||
|
||||
default:
|
||||
segmentStart.Type = PathType.Bezier;
|
||||
segmentStart.Type = PathType.BEZIER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -195,13 +261,13 @@ 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();
|
||||
}
|
||||
|
||||
// Update the cursor position.
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.Body ? SnapType.GlobalGrids : SnapType.All);
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.ControlPoints ? SnapType.GlobalGrids : SnapType.All);
|
||||
cursor.Position = ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||
}
|
||||
else if (cursor != null)
|
||||
@ -210,7 +276,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();
|
||||
}
|
||||
@ -240,15 +306,55 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
tailCirclePiece.UpdateFrom(HitObject.TailCircle);
|
||||
}
|
||||
|
||||
private void setState(SliderPlacementState newState)
|
||||
private void updateSliderPathFromBSplineBuilder()
|
||||
{
|
||||
state = newState;
|
||||
IReadOnlyList<Vector2> builderPoints = bSplineBuilder.ControlPoints;
|
||||
|
||||
if (builderPoints.Count == 0)
|
||||
return;
|
||||
|
||||
int lastSegmentStart = 0;
|
||||
PathType? lastPathType = null;
|
||||
|
||||
HitObject.Path.ControlPoints.Clear();
|
||||
|
||||
// Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate.
|
||||
// Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started.
|
||||
for (int i = 0; i < builderPoints.Count; i++)
|
||||
{
|
||||
bool isLastPoint = i == builderPoints.Count - 1;
|
||||
bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2];
|
||||
|
||||
if (isNewSegment || isLastPoint)
|
||||
{
|
||||
int pointsInSegment = i - lastSegmentStart;
|
||||
|
||||
// Where possible, we can use the simpler LINEAR path type.
|
||||
PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3);
|
||||
|
||||
// Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined.
|
||||
if (lastPathType == pathType && lastPathType == PathType.LINEAR)
|
||||
pathType = null;
|
||||
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType));
|
||||
for (int j = lastSegmentStart + 1; j < i; j++)
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j]));
|
||||
|
||||
if (isLastPoint)
|
||||
HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i]));
|
||||
|
||||
// Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type.
|
||||
lastSegmentStart = (i += 2);
|
||||
if (pathType != null) lastPathType = pathType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum SliderPlacementState
|
||||
{
|
||||
Initial,
|
||||
Body,
|
||||
ControlPoints,
|
||||
Drawing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs
Normal file
100
osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs
Normal file
@ -0,0 +1,100 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class FreehandSliderToolboxGroup : EditorToolboxGroup
|
||||
{
|
||||
public FreehandSliderToolboxGroup()
|
||||
: base("slider")
|
||||
{
|
||||
}
|
||||
|
||||
public BindableFloat Tolerance { get; } = new BindableFloat(1.5f)
|
||||
{
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 3f,
|
||||
Precision = 0.01f
|
||||
};
|
||||
|
||||
public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f)
|
||||
{
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 1f,
|
||||
Precision = 0.01f
|
||||
};
|
||||
|
||||
// We map internal ranges to a more standard range of values for display to the user.
|
||||
private readonly BindableInt displayTolerance = new BindableInt(40)
|
||||
{
|
||||
MinValue = 5,
|
||||
MaxValue = 100
|
||||
};
|
||||
|
||||
private readonly BindableInt displayCornerThreshold = new BindableInt(40)
|
||||
{
|
||||
MinValue = 5,
|
||||
MaxValue = 100
|
||||
};
|
||||
|
||||
private ExpandableSlider<int> toleranceSlider = null!;
|
||||
private ExpandableSlider<int> cornerThresholdSlider = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
toleranceSlider = new ExpandableSlider<int>
|
||||
{
|
||||
Current = displayTolerance
|
||||
},
|
||||
cornerThresholdSlider = new ExpandableSlider<int>
|
||||
{
|
||||
Current = displayCornerThreshold
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
displayTolerance.BindValueChanged(tolerance =>
|
||||
{
|
||||
toleranceSlider.ContractedLabelText = $"C. P. S.: {tolerance.NewValue:N0}";
|
||||
toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {tolerance.NewValue:N0}";
|
||||
|
||||
Tolerance.Value = displayToInternalTolerance(tolerance.NewValue);
|
||||
}, true);
|
||||
|
||||
displayCornerThreshold.BindValueChanged(threshold =>
|
||||
{
|
||||
cornerThresholdSlider.ContractedLabelText = $"C. T.: {threshold.NewValue:N0}";
|
||||
cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {threshold.NewValue:N0}";
|
||||
|
||||
CornerThreshold.Value = displayToInternalCornerThreshold(threshold.NewValue);
|
||||
}, true);
|
||||
|
||||
Tolerance.BindValueChanged(tolerance =>
|
||||
displayTolerance.Value = internalToDisplayTolerance(tolerance.NewValue)
|
||||
);
|
||||
CornerThreshold.BindValueChanged(threshold =>
|
||||
displayCornerThreshold.Value = internalToDisplayCornerThreshold(threshold.NewValue)
|
||||
);
|
||||
|
||||
float displayToInternalTolerance(float v) => v / 33f;
|
||||
int internalToDisplayTolerance(float v) => (int)Math.Round(v * 33f);
|
||||
|
||||
float displayToInternalCornerThreshold(float v) => v / 100f;
|
||||
int internalToDisplayCornerThreshold(float v) => (int)Math.Round(v * 100f);
|
||||
}
|
||||
}
|
||||
}
|
@ -64,6 +64,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
[Cached(typeof(IDistanceSnapProvider))]
|
||||
protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider();
|
||||
|
||||
[Cached]
|
||||
protected readonly FreehandSliderToolboxGroup FreehandlSliderToolboxGroup = new FreehandSliderToolboxGroup();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -95,10 +98,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
// we may be entering the screen with a selection already active
|
||||
updateDistanceSnapGrid();
|
||||
|
||||
RightToolbox.Add(new TransformToolboxGroup
|
||||
{
|
||||
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler
|
||||
});
|
||||
RightToolbox.AddRange(new EditorToolboxGroup[]
|
||||
{
|
||||
new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, },
|
||||
FreehandlSliderToolboxGroup
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||
|
@ -321,7 +321,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.
|
||||
@ -351,7 +351,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));
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.PERFECT_CURVE));
|
||||
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.PERFECT_CURVE));
|
||||
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.PERFECT_CURVE);
|
||||
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.PERFECT_CURVE);
|
||||
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));
|
||||
|
@ -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.
|
||||
@ -113,6 +113,33 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(areComboColoursEqual(expected.skin.Configuration, actual.skin.Configuration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncodeBSplineCurveType()
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Slider
|
||||
{
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.BSpline(3)),
|
||||
new PathControlPoint(new Vector2(50)),
|
||||
new PathControlPoint(new Vector2(100), PathType.BSpline(3)),
|
||||
new PathControlPoint(new Vector2(150))
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
|
||||
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
|
||||
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(4));
|
||||
Assert.That(decodedSlider.Path.ControlPoints[0].Type, Is.EqualTo(PathType.BSpline(3)));
|
||||
Assert.That(decodedSlider.Path.ControlPoints[2].Type, Is.EqualTo(PathType.BSpline(3)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncodeMultiSegmentSliderWithFloatingPointError()
|
||||
{
|
||||
@ -125,10 +152,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))
|
||||
})
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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),
|
||||
|
@ -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 { Type = 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 { Type = 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.PERFECT_CURVE, 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.PERFECT_CURVE, 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.PERFECT_CURVE, 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.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -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) },
|
||||
},
|
||||
});
|
||||
|
@ -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 { Type = 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 { Type = 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.PERFECT_CURVE, 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.PERFECT_CURVE, 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)));
|
||||
|
||||
|
@ -437,7 +437,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.PERFECT_CURVE;
|
||||
|
||||
// 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.
|
||||
@ -453,21 +453,21 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
if (needsExplicitSegment)
|
||||
{
|
||||
switch (point.Type)
|
||||
switch (point.Type?.Type)
|
||||
{
|
||||
case PathType.Bezier:
|
||||
writer.Write("B|");
|
||||
case SplineType.BSpline:
|
||||
writer.Write(point.Type.Value.Degree > 0 ? $"B{point.Type.Value.Degree}|" : "B|");
|
||||
break;
|
||||
|
||||
case PathType.Catmull:
|
||||
case SplineType.Catmull:
|
||||
writer.Write("C|");
|
||||
break;
|
||||
|
||||
case PathType.PerfectCurve:
|
||||
case SplineType.PerfectCurve:
|
||||
writer.Write("P|");
|
||||
break;
|
||||
|
||||
case PathType.Linear:
|
||||
case SplineType.Linear:
|
||||
writer.Write("L|");
|
||||
break;
|
||||
}
|
||||
|
@ -78,14 +78,15 @@ 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;
|
||||
|
||||
if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue;
|
||||
if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1
|
||||
&& hasPath.Path.ControlPoints[0].Type!.Value.Degree == null) continue;
|
||||
|
||||
var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints);
|
||||
|
||||
|
@ -68,32 +68,35 @@ 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)
|
||||
switch (segmentType.Type)
|
||||
{
|
||||
case PathType.Catmull:
|
||||
case SplineType.Catmull:
|
||||
result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position);
|
||||
|
||||
break;
|
||||
|
||||
case PathType.Linear:
|
||||
case SplineType.Linear:
|
||||
result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position);
|
||||
|
||||
break;
|
||||
|
||||
case PathType.PerfectCurve:
|
||||
case SplineType.PerfectCurve:
|
||||
result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
case SplineType.BSpline:
|
||||
if (segmentType.Degree != null)
|
||||
throw new NotImplementedException("BSpline conversion of arbitrary degree is not implemented.");
|
||||
|
||||
foreach (Vector2 v in segmentVertices)
|
||||
{
|
||||
result.Add(v + position);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(segmentType.Type), segmentType.Type, "Unsupported segment type found when converting to legacy Bezier");
|
||||
}
|
||||
|
||||
// Start the new segment at the current vertex
|
||||
@ -104,7 +107,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,49 +127,56 @@ 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)
|
||||
switch (segmentType.Type)
|
||||
{
|
||||
case PathType.Catmull:
|
||||
case 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.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.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;
|
||||
|
||||
case SplineType.BSpline:
|
||||
var bSplineResult = segmentType.Degree == null
|
||||
? segmentVertices
|
||||
: PathApproximator.BSplineToBezier(segmentVertices, segmentType.Degree.Value);
|
||||
|
||||
for (int j = 0; j < bSplineResult.Length - 1; j++)
|
||||
{
|
||||
result.Add(new PathControlPoint(bSplineResult[j], j == 0 ? PathType.BEZIER : null));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
for (int j = 0; j < segmentVertices.Length - 1; j++)
|
||||
{
|
||||
result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? PathType.Bezier : null));
|
||||
}
|
||||
|
||||
break;
|
||||
throw new ArgumentOutOfRangeException(nameof(segmentType.Type), segmentType.Type, "Unsupported segment type found when converting to legacy Bezier");
|
||||
}
|
||||
|
||||
// Start the new segment at the current vertex
|
||||
|
@ -224,16 +224,19 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
default:
|
||||
case 'C':
|
||||
return PathType.Catmull;
|
||||
return PathType.CATMULL;
|
||||
|
||||
case 'B':
|
||||
return PathType.Bezier;
|
||||
if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0)
|
||||
return PathType.BSpline(degree);
|
||||
|
||||
return PathType.BEZIER;
|
||||
|
||||
case 'L':
|
||||
return PathType.Linear;
|
||||
return PathType.LINEAR;
|
||||
|
||||
case 'P':
|
||||
return PathType.PerfectCurve;
|
||||
return PathType.PERFECT_CURVE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,8 +273,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
while (++endIndex < pointSplit.Length)
|
||||
{
|
||||
// Keep incrementing endIndex while it's not the start of a new segment (indicated by having a type descriptor of length 1).
|
||||
if (pointSplit[endIndex].Length > 1)
|
||||
// Keep incrementing endIndex while it's not the start of a new segment (indicated by having an alpha character at position 0).
|
||||
if (!char.IsLetter(pointSplit[endIndex][0]))
|
||||
continue;
|
||||
|
||||
// Multi-segmented sliders DON'T contain the end point as part of the current segment as it's assumed to be the start of the next segment.
|
||||
@ -320,14 +323,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.PERFECT_CURVE)
|
||||
{
|
||||
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 +352,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.
|
||||
|
@ -260,7 +260,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,16 +288,16 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, PathType type)
|
||||
{
|
||||
switch (type)
|
||||
switch (type.Type)
|
||||
{
|
||||
case PathType.Linear:
|
||||
return PathApproximator.ApproximateLinear(subControlPoints);
|
||||
case SplineType.Linear:
|
||||
return PathApproximator.LinearToPiecewiseLinear(subControlPoints);
|
||||
|
||||
case PathType.PerfectCurve:
|
||||
case SplineType.PerfectCurve:
|
||||
if (subControlPoints.Length != 3)
|
||||
break;
|
||||
|
||||
List<Vector2> subPath = PathApproximator.ApproximateCircularArc(subControlPoints);
|
||||
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.
|
||||
if (subPath.Count == 0)
|
||||
@ -305,11 +305,11 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
return subPath;
|
||||
|
||||
case PathType.Catmull:
|
||||
return PathApproximator.ApproximateCatmull(subControlPoints);
|
||||
case SplineType.Catmull:
|
||||
return PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
||||
}
|
||||
|
||||
return PathApproximator.ApproximateBezier(subControlPoints);
|
||||
return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length);
|
||||
}
|
||||
|
||||
private void calculateLength()
|
||||
|
@ -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.PERFECT_CURVE && controlPoints[^2].Type is null && segmentEnds.Any())
|
||||
{
|
||||
double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0;
|
||||
double lastSegmentEnd = segmentEnds[^1];
|
||||
|
@ -1,13 +1,87 @@
|
||||
// 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 osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Types
|
||||
{
|
||||
public enum PathType
|
||||
public enum SplineType
|
||||
{
|
||||
Catmull,
|
||||
Bezier,
|
||||
BSpline,
|
||||
Linear,
|
||||
PerfectCurve
|
||||
}
|
||||
|
||||
public readonly struct PathType : IEquatable<PathType>, IHasDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the spline that should be used to interpret the control points of the path.
|
||||
/// </summary>
|
||||
public SplineType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The degree of a BSpline. Unused if <see cref="Type"/> 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)
|
||||
{
|
||||
Type = splineType;
|
||||
Degree = null;
|
||||
}
|
||||
|
||||
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 PERFECT_CURVE = new PathType(SplineType.PerfectCurve);
|
||||
|
||||
public static PathType BSpline(int degree)
|
||||
{
|
||||
if (degree <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(degree), "The degree of a B-Spline path must be greater than zero.");
|
||||
|
||||
return new PathType { Type = SplineType.BSpline, Degree = degree };
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case SplineType.Catmull:
|
||||
return "Catmull";
|
||||
|
||||
case SplineType.BSpline:
|
||||
return Degree == null ? "Bezier" : "B-spline";
|
||||
|
||||
case SplineType.Linear:
|
||||
return "Linear";
|
||||
|
||||
case SplineType.PerfectCurve:
|
||||
return "Perfect curve";
|
||||
|
||||
default:
|
||||
return Type.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(Type, Degree);
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is PathType pathType && Equals(pathType);
|
||||
|
||||
public bool Equals(PathType other)
|
||||
=> Type == other.Type && Degree == other.Degree;
|
||||
|
||||
public static bool operator ==(PathType a, PathType b) => a.Equals(b);
|
||||
public static bool operator !=(PathType a, PathType b) => !a.Equals(b);
|
||||
|
||||
public override string ToString() => Description;
|
||||
}
|
||||
}
|
||||
|
@ -269,13 +269,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)),
|
||||
});
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.1111.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.1121.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.1114.0" />
|
||||
<PackageReference Include="Sentry" Version="3.40.0" />
|
||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||
|
@ -23,6 +23,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.1111.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.1121.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user