From 926636cc035a17543d987ba9a9ba9c47fa5e7f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Wed, 8 Nov 2023 19:43:54 +0900 Subject: [PATCH 01/50] Generalize Bezier curves to BSplines of Nth degree --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 8 +- .../JuiceStreamPathTest.cs | 4 +- .../Mods/TestSceneCatchModPerfect.cs | 2 +- .../Mods/TestSceneCatchModRelax.cs | 2 +- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 2 +- .../TestSceneJuiceStream.cs | 2 +- .../Blueprints/Components/EditablePath.cs | 2 +- .../Objects/JuiceStreamPath.cs | 2 +- .../Checks/CheckOffscreenObjectsTest.cs | 12 +-- .../Editor/TestSceneObjectMerging.cs | 10 +-- .../TestSceneOsuEditorSelectInvalidPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 36 ++++----- .../TestSceneSliderControlPointPiece.cs | 28 +++---- .../Editor/TestSceneSliderLengthValidity.cs | 8 +- .../TestSceneSliderPlacementBlueprint.cs | 42 +++++------ .../Editor/TestSceneSliderReversal.cs | 4 +- .../TestSceneSliderSelectionBlueprint.cs | 2 +- .../Editor/TestSceneSliderSnapping.cs | 10 +-- .../Editor/TestSceneSliderSplitting.cs | 28 +++---- .../Editor/TestSliderScaling.cs | 2 +- .../Mods/TestSceneOsuModHidden.cs | 10 +-- .../Mods/TestSceneOsuModPerfect.cs | 2 +- .../OsuHitObjectGenerationUtilsTest.cs | 6 +- .../TestSceneHitCircleLateFade.cs | 2 +- .../TestSceneLegacyHitPolicy.cs | 18 ++--- .../TestSceneSlider.cs | 14 ++-- .../TestSceneSliderApplication.cs | 6 +- .../TestSceneSliderFollowCircleInput.cs | 2 +- .../TestSceneSliderInput.cs | 8 +- .../TestSceneSliderSnaking.cs | 6 +- .../TestSceneStartTimeOrderedHitPolicy.cs | 10 +-- .../Components/PathControlPointPiece.cs | 20 +++-- .../Components/PathControlPointVisualiser.cs | 26 +++---- .../Sliders/SliderPlacementBlueprint.cs | 14 ++-- .../Edit/OsuSelectionHandler.cs | 4 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Formats/LegacyBeatmapDecoderTest.cs | 36 ++++----- .../Formats/LegacyBeatmapEncoderTest.cs | 6 +- .../Editing/LegacyEditorBeatmapPatcherTest.cs | 4 +- .../Editing/TestSceneEditorClipboard.cs | 2 +- .../Editing/TestSceneHitObjectComposer.cs | 2 +- .../Gameplay/TestSceneBezierConverter.cs | 40 +++++----- .../TestSceneGameplaySampleTriggerSource.cs | 2 +- .../Visual/Gameplay/TestSceneSliderPath.cs | 73 +++++++++++-------- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 15 ++-- osu.Game/Database/LegacyBeatmapExporter.cs | 4 +- osu.Game/Rulesets/Objects/BezierConverter.cs | 32 ++++---- .../Objects/Legacy/ConvertHitObjectParser.cs | 20 ++--- osu.Game/Rulesets/Objects/SliderPath.cs | 13 ++-- .../Rulesets/Objects/SliderPathExtensions.cs | 6 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 50 ++++++++++++- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 10 +-- 54 files changed, 372 insertions(+), 305 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 05d7a38a95..16b51d414a 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddStep("update hit object path", () => { - hitObject.Path = new SliderPath(PathType.PerfectCurve, new[] + hitObject.Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(100, 100), @@ -190,16 +190,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVertexResampling() { - addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[] + addBlueprintStep(100, 100, new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(100, 100), new Vector2(50, 200), }), 0.5); AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); - AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); addAddVertexSteps(150, 150); - AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear); + AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR); } private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 95b4fdc07e..82f24633b5 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests } while (rng.Next(2) != 0); int length = sliderPath.ControlPoints.Count - start + 1; - sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECTCURVE : PathType.BEZIER; } while (rng.Next(3) != 0); if (rng.Next(5) == 0) @@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.Tests foreach (var point in sliderPath.ControlPoints) { - Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(point.Type, Is.EqualTo(PathType.LINEAR).Or.Null); Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); } diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs index 71df523951..45e7d7aa28 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs @@ -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), diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index 5835ccaf78..a161615579 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -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 }) } } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 3261fb656e..202f010680 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -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) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index a44575a46e..419a846ec3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -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 } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 11d6419507..9c5cd68201 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -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) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index c31a7ca99f..9a923adaab 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -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) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index df76bf0a8c..86f92d16ca 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -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. diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index 0633151ddd..57acf7cee2 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -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; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index a72aaa966c..8612a8eb57 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = new Vector2(420, 240), Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(-100, 0)) }), } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), } @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), StackHeight = 5 @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = new Vector2(0, 0), Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(playfield_centre) }), } @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(-playfield_centre) }), } @@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Path = new SliderPath(new[] { // Circular arc shoots over the top of the screen. - new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(-100, -200)), new PathControlPoint(new Vector2(100, -200)) }), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs index 8d8386cae1..3d35ab79f7 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs @@ -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))); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs index 37a109de18..7ea4d40b90 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(-100, 0)), new PathControlPoint(new Vector2(100, 20)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index c267cd1f63..16800997f4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(1, PathType.PerfectCurve); - assertControlPointPathType(3, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(3, PathType.BEZIER); } [Test] @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -83,8 +83,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(2, PathType.PerfectCurve); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(2, PathType.PERFECTCURVE); assertControlPointPathType(4, null); } @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null); } @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Linear); + addControlPointStep(new Vector2(200), PathType.LINEAR); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Linear); - assertControlPointPathType(1, PathType.PerfectCurve); - assertControlPointPathType(3, PathType.Linear); + assertControlPointPathType(0, PathType.LINEAR); + assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(3, PathType.LINEAR); } [Test] @@ -133,18 +133,18 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); - addControlPointStep(new Vector2(300), PathType.PerfectCurve); + addControlPointStep(new Vector2(200), PathType.BEZIER); + addControlPointStep(new Vector2(300), PathType.PERFECTCURVE); addControlPointStep(new Vector2(500, 300)); - addControlPointStep(new Vector2(700, 200), PathType.Bezier); + addControlPointStep(new Vector2(700, 200), PathType.BEZIER); addControlPointStep(new Vector2(500, 100)); moveMouseToControlPoint(3); AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true); addContextMenuItemStep("Inherit"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(1, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(1, PathType.BEZIER); assertControlPointPathType(3, null); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs index 408205d6b2..1d8d2cf01a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(256, 192), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("three control point pieces selected", () => this.ChildrenOfType>().Count(piece => piece.IsSelected.Value) == 3); assertControlPointPosition(2, new Vector2(450, 50)); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(2, PathType.PERFECTCURVE); assertControlPointPosition(3, new Vector2(550, 50)); @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50))); assertControlPointPosition(0, Vector2.Zero); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); assertControlPointPosition(1, new Vector2(0, 100)); @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(400, 0.01f)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -282,13 +282,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); addMovementStep(new Vector2(400, 0.01f)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); addMovementStep(new Vector2(150, 50)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -298,32 +298,32 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); addMovementStep(new Vector2(350, 0.01f)); - assertControlPointType(2, PathType.Bezier); + assertControlPointType(2, PathType.BEZIER); addMovementStep(new Vector2(150, 150)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(150, 150)); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(2, PathType.PERFECTCURVE); } [Test] public void TestDragControlPointPathAfterChangingType() { - AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier); + AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.BEZIER); AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10)))); - AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve); + AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECTCURVE); moveMouseToControlPoint(4); AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); - assertControlPointType(3, PathType.PerfectCurve); + assertControlPointType(3, PathType.PERFECTCURVE); addMovementStep(new Vector2(350, 0.01f)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(350, 0.01f)); - assertControlPointType(3, PathType.Bezier); + assertControlPointType(3, PathType.BEZIER); } private void addMovementStep(Vector2 relativePosition) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs index 77e828e80a..38ebeb7e8f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(100, 0)), }; @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(100, 0)), }; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(100, 0)), new PathControlPoint(new Vector2(0, 10)) }; @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(0, 50)), new PathControlPoint(new Vector2(0, 100)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 7d29670daa..4b120c1a3f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(200); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(4); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100, 100)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -131,8 +131,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.LINEAR); } [Test] @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); assertLength(100); } @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(4); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -216,8 +216,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.LINEAR); } [Test] @@ -240,8 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(4); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.PerfectCurve); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.PERFECTCURVE); } [Test] @@ -269,8 +269,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(2, new Vector2(100)); assertControlPointPosition(3, new Vector2(200, 100)); assertControlPointPosition(4, new Vector2(200)); - assertControlPointType(0, PathType.PerfectCurve); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(2, PathType.PERFECTCURVE); } [Test] @@ -287,7 +287,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(200); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -306,7 +306,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -326,7 +326,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -368,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs index 9c5eb83e3c..0ddfc40946 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs @@ -22,12 +22,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private readonly PathControlPoint[][] paths = { createPathSegment( - PathType.PerfectCurve, + PathType.PERFECTCURVE, new Vector2(200, -50), new Vector2(250, 0) ), createPathSegment( - PathType.Linear, + PathType.LINEAR, new Vector2(100, 0), new Vector2(100, 100) ) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs index 413a3c3dfd..d4d99e1019 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 0ae14bdde8..c984d9168e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { ControlPoints = { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(136, 205)), new PathControlPoint(new Vector2(-4, 226)) } @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("rotate 90 degrees ccw", () => @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.HandleRotation(-90); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); } [Test] @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("flip slider horizontally", () => @@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.OnPressed(new KeyBindingPressEvent(InputManager.CurrentState, GlobalAction.EditorFlipVertically)); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); } [Test] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index ad37258c9b..cded9165f4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -73,20 +73,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 2 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], slider.StartTime, endTime + split_gap, - (new Vector2(300, 50), PathType.PerfectCurve), + (new Vector2(300, 50), PathType.PERFECTCURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); AddStep("undo", () => Editor.Undo()); AddAssert("original slider restored", () => EditorBeatmap.HitObjects.Count == 1 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, endTime, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), - (new Vector2(300, 50), PathType.PerfectCurve), + (new Vector2(300, 50), PathType.PERFECTCURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); @@ -104,11 +104,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.Bezier), + new PathControlPoint(new Vector2(300, 0), PathType.BEZIER), new PathControlPoint(new Vector2(400, 0)), - new PathControlPoint(new Vector2(400, 150), PathType.Catmull), + new PathControlPoint(new Vector2(400, 150), PathType.CATMULL), new PathControlPoint(new Vector2(300, 200)), new PathControlPoint(new Vector2(400, 250)) }) @@ -139,15 +139,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 3 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], EditorBeatmap.HitObjects[0].GetEndTime() + split_gap, slider.StartTime - split_gap, - (new Vector2(300, 50), PathType.Bezier), + (new Vector2(300, 50), PathType.BEZIER), (new Vector2(400, 50), null), (new Vector2(400, 200), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[2], EditorBeatmap.HitObjects[1].GetEndTime() + split_gap, endTime + split_gap * 2, - (new Vector2(400, 200), PathType.Catmull), + (new Vector2(400, 200), PathType.CATMULL), (new Vector2(300, 250), null), (new Vector2(400, 300), null) )); @@ -165,9 +165,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs index 64d23090d0..021fdba225 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs @@ -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)), }; diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 3f84ac6935..58bdd805c1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -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 { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index f0496efc19..26c4133bc4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -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); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs index daa914cac2..d78c32aa6a 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs @@ -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 diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs index 483155e646..7824f26251 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index fa6aa580a3..e460da9bd5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index b805e7ed63..60003e7950 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(239, 176), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(154, 28), @@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Tests SliderVelocityMultiplier = speedMultiplier, StartTime = Time.Current + time_offset, Position = new Vector2(0, -(distance / 2)), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(0, distance), @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(max_length / 2, max_length / 2), @@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(max_length * 0.375f, max_length * 0.18f), @@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.Bezier, new[] + Path = new SliderPath(PathType.BEZIER, new[] { Vector2.Zero, new Vector2(max_length * 0.375f, max_length * 0.18f), @@ -338,7 +338,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(0, 0), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(-max_length / 2, 0), @@ -365,7 +365,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 4, 0), - Path = new SliderPath(PathType.Catmull, new[] + Path = new SliderPath(PathType.CATMULL, new[] { Vector2.Zero, new Vector2(max_length * 0.125f, max_length * 0.125f), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index 88b70a8836..f41dd913ab 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs index d4bb789a12..fc9bb16cb7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index f718a5069f..08836ef819 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, RepeatCount = repeatCount, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(sliderLength, 0), @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, RepeatCount = repeatCount, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(sliderLength, 0), @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(slider_path_length * 10, 0), @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 0.1f, - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(slider_path_length, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index 13166c2b6b..ebc5143aed 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 3000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 13000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 23000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs index 3475680c71..895e9bbdee 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 12e5ca0236..9658e5f6c3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -221,11 +221,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type != PathType.PerfectCurve) + if (ControlPoint.Type != PathType.PERFECTCURVE) return; if (PointsInSegment.Count > 3) - ControlPoint.Type = PathType.Bezier; + ControlPoint.Type = PathType.BEZIER; if (PointsInSegment.Count != 3) return; @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components ReadOnlySpan 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; } /// @@ -256,18 +256,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private Color4 getColourFromNodeType() { - if (!(ControlPoint.Type is PathType pathType)) + if (ControlPoint.Type is not PathType pathType) return colours.Yellow; switch (pathType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: return colours.SeaFoam; - case PathType.Bezier: - return colours.Pink; + case { SplineType: SplineType.BSpline, Degree: null }: + return colours.PinkLighter; - case PathType.PerfectCurve: + case { SplineType: SplineType.BSpline, Degree: >= 1 }: + int idx = Math.Clamp(pathType.Degree.Value, 0, 3); + return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; + + case { SplineType: SplineType.PerfectCurve }: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index f891d23bbd..b5c9016538 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,18 +242,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - switch (type) + if (type.HasValue && type.Value.SplineType == SplineType.PerfectCurve) { - case PathType.PerfectCurve: - // Can't always create a circular arc out of 4 or more points, - // so we split the segment into one 3-point circular arc segment - // and one segment of the previous type. - int thirdPointIndex = indexInSegment + 2; + // Can't always create a circular arc out of 4 or more points, + // so we split the segment into one 3-point circular arc segment + // and one segment of the previous type. + int thirdPointIndex = indexInSegment + 2; - if (piece.PointsInSegment.Count > thirdPointIndex + 1) - piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; - - break; + if (piece.PointsInSegment.Count > thirdPointIndex + 1) + piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; } hitObject.Path.ExpectedDistance.Value = null; @@ -370,10 +367,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(null)); // todo: hide/disable items which aren't valid for selected points - curveTypeItems.Add(createMenuItemForPathType(PathType.Linear)); - curveTypeItems.Add(createMenuItemForPathType(PathType.PerfectCurve)); - curveTypeItems.Add(createMenuItemForPathType(PathType.Bezier)); - curveTypeItems.Add(createMenuItemForPathType(PathType.Catmull)); + curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR)); + curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECTCURVE)); + curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); + curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); + curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9b6adc04cf..8f0a2ee781 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { RelativeSizeAxes = Axes.Both; - HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear)); + HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.LINEAR)); currentSegmentLength = 1; } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Debug.Assert(lastPoint != null); segmentStart = lastPoint; - segmentStart.Type = PathType.Linear; + segmentStart.Type = PathType.LINEAR; currentSegmentLength = 1; } @@ -173,15 +173,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { case 1: case 2: - segmentStart.Type = PathType.Linear; + segmentStart.Type = PathType.LINEAR; break; case 3: - segmentStart.Type = PathType.PerfectCurve; + segmentStart.Type = PathType.PERFECTCURVE; break; default: - segmentStart.Type = PathType.Bezier; + segmentStart.Type = PathType.BEZIER; break; } } @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero }); - // The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier). + // The path type should be adjusted in the progression of updatePathType() (LINEAR -> PC -> BEZIER). currentSegmentLength++; updatePathType(); } @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HitObject.Path.ControlPoints.Remove(cursor); cursor = null; - // The path type should be adjusted in the reverse progression of updatePathType() (Bezier -> PC -> Linear). + // The path type should be adjusted in the reverse progression of updatePathType() (BEZIER -> PC -> LINEAR). currentSegmentLength--; updatePathType(); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index e81941d254..b972f09136 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -320,7 +320,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (mergedHitObject.Path.ControlPoints.Count == 0) { - mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.Linear)); + mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.LINEAR)); } // Merge all the selected hit objects into one slider path. @@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Edit // Turn the last control point into a linear type if this is the first merging circle in a sequence, so the subsequent control points can be inherited path type. if (!lastCircle) { - mergedHitObject.Path.ControlPoints.Last().Type = PathType.Linear; + mergedHitObject.Path.ControlPoints.Last().Type = PathType.LINEAR; } mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(selectedMergeableObject.Position - mergedHitObject.Position)); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5f47d486e6..2a76782a08 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -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 } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 66151a51e6..18c21046fb 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -663,7 +663,7 @@ namespace osu.Game.Tests.Beatmaps.Formats assertObjectHasBanks(hitObjects[9], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL); } - void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null) + static void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null) { Assert.AreEqual(normalBank, hitObject.Samples[0].Bank); @@ -808,14 +808,14 @@ namespace osu.Game.Tests.Beatmaps.Formats var first = ((IHasPath)decoded.HitObjects[0]).Path; Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null)); // ReSharper disable once HeuristicUnreachableCode // weird one, see https://youtrack.jetbrains.com/issue/RIDER-70159. Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); - Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15))); Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null)); Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132))); @@ -827,7 +827,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var second = ((IHasPath)decoded.HitObjects[1]).Path; Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); @@ -837,14 +837,14 @@ namespace osu.Game.Tests.Beatmaps.Formats var third = ((IHasPath)decoded.HitObjects[2]).Path; Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192))); Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192))); Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0))); - Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192))); Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192))); @@ -856,7 +856,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var fourth = ((IHasPath)decoded.HitObjects[3]).Path; Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); @@ -870,7 +870,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var fifth = ((IHasPath)decoded.HitObjects[4]).Path; Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); @@ -881,7 +881,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null)); Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4))); - Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5))); Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null)); @@ -889,12 +889,12 @@ namespace osu.Game.Tests.Beatmaps.Formats var sixth = ((IHasPath)decoded.HitObjects[5]).Path; Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[0].Type == PathType.BEZIER); Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); Assert.That(sixth.ControlPoints[1].Type == null); Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[2].Type == PathType.BEZIER); Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); Assert.That(sixth.ControlPoints[3].Type == null); Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); @@ -904,12 +904,12 @@ namespace osu.Game.Tests.Beatmaps.Formats var seventh = ((IHasPath)decoded.HitObjects[6]).Path; Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECTCURVE); Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); Assert.That(seventh.ControlPoints[1].Type == null); Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECTCURVE); Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); Assert.That(seventh.ControlPoints[3].Type == null); Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); @@ -1016,7 +1016,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(6)); - Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.CATMULL)); } } @@ -1032,9 +1032,9 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(4)); - Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); - Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.Catmull)); - Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL)); + Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.CATMULL)); + Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.CATMULL)); Assert.That(controlPoints[3].Type, Is.Null); } } @@ -1051,7 +1051,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(4)); - Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL)); Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero)); Assert.That(controlPoints[1].Type, Is.Null); Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero)); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 5d9049ead7..db50273f27 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.Formats compareBeatmaps(decoded, decodedAfterEncode); - ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo) + static ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo) { // emulate non-legacy control points by cloning the non-legacy portion. // the assertion is that the encoder can recreate this losslessly from hitobject data. @@ -125,10 +125,10 @@ namespace osu.Game.Tests.Beatmaps.Formats Position = new Vector2(0.6f), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.Bezier), + new PathControlPoint(Vector2.Zero, PathType.BEZIER), new PathControlPoint(new Vector2(0.5f)), new PathControlPoint(new Vector2(0.51f)), // This is actually on the same position as the previous one in legacy beatmaps (truncated to int). - new PathControlPoint(new Vector2(1f), PathType.Bezier), + new PathControlPoint(new Vector2(1f), PathType.BEZIER), new PathControlPoint(new Vector2(2f)) }) }, diff --git a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs index 5af0366e6e..21d8a165ff 100644 --- a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs @@ -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) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs index c4c05278b5..a766b253aa 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs @@ -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) } } }; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index ed3bffe5c2..f392841ac7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -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), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index a40eab5948..5eb82ccbdc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -114,23 +114,25 @@ namespace osu.Game.Tests.Visual.Gameplay { } - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestSingleSegment(PathType type) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestSingleSegment(SplineType splineType, int? degree) + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestMultipleSegment(PathType type) + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestMultipleSegment(SplineType splineType, int? degree) { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } @@ -139,9 +141,9 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100, 0))); - path.ControlPoints.AddRange(createSegment(PathType.Bezier, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100))); - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.BEZIER, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); }); } @@ -157,7 +159,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); }); } @@ -170,11 +172,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 0f16d3f394..3cbd5eefac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -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) }, }, }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 635d9f9604..e4d99f6741 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -52,59 +52,68 @@ namespace osu.Game.Tests.Visual.Gameplay { } - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestSingleSegment(PathType type) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestSingleSegment(SplineType splineType, int? degree) + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment( + new PathType { SplineType = splineType, Degree = degree }, + Vector2.Zero, + new Vector2(0, 100), + new Vector2(100), + new Vector2(0, 200), + new Vector2(200) + ))); - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestMultipleSegment(PathType type) + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestMultipleSegment(SplineType splineType, int? degree) { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } [Test] public void TestAddControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100)))); AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) })); } [Test] public void TestInsertControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100)))); AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) })); } [Test] public void TestRemoveControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); } [Test] public void TestChangePathType() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.BEZIER); } [Test] public void TestAddSegmentByChangingType() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); - AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); + AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.BEZIER); } [Test] @@ -112,8 +121,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type = PathType.Bezier; + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type = PathType.BEZIER; }); AddStep("change second point type to null", () => path.ControlPoints[1].Type = null); @@ -124,8 +133,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type = PathType.Bezier; + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type = PathType.BEZIER; }); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); @@ -140,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); @@ -153,35 +162,35 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLengthenLastSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300); } [Test] public void TestShortenLastSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150); } [Test] public void TestShortenFirstSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50); } [Test] public void TestShortenToZeroLength() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0); } [Test] public void TestShortenToNegativeLength() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10); } @@ -197,7 +206,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; double[] distances = { 100d, 200d, 300d }; - AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear)))); + AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.LINEAR)))); AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 300))); AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1))); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 4f8e935ee4..7029f61459 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -437,7 +438,7 @@ namespace osu.Game.Beatmaps.Formats // Explicit segments have a new format in which the type is injected into the middle of the control point string. // To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point. // One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments - bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve; + bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECTCURVE; // Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable. // Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder. @@ -455,19 +456,23 @@ namespace osu.Game.Beatmaps.Formats { switch (point.Type) { - case PathType.Bezier: + case { SplineType: SplineType.BSpline, Degree: > 0 }: + writer.Write($"B{point.Type.Value.Degree}|"); + break; + + case { SplineType: SplineType.BSpline, Degree: <= 0 }: writer.Write("B|"); break; - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: writer.Write("C|"); break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: writer.Write("P|"); break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: writer.Write("L|"); break; } diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index ece705f685..9ca12a79dd 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -78,10 +78,10 @@ namespace osu.Game.Database // wherein the last control point of an otherwise-single-segment slider path has a different type than previous, // which would lead to sliders being mangled when exported back to stable. // normally, that would be handled by the `BezierConverter.ConvertToModernBezier()` call below, - // which outputs a slider path containing only Bezier control points, + // which outputs a slider path containing only BEZIER control points, // but a non-inherited last control point is (rightly) not considered to be starting a new segment, // therefore it would fail to clear the `CountSegments() <= 1` check. - // by clearing explicitly we both fix the issue and avoid unnecessary conversions to Bezier. + // by clearing explicitly we both fix the issue and avoid unnecessary conversions to BEZIER. if (hasPath.Path.ControlPoints.Count > 1) hasPath.Path.ControlPoints[^1].Type = null; diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 0c878fa1fd..74fbe7d8f9 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -68,26 +68,26 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = controlPoints[start].Type ?? PathType.Linear; + var segmentType = controlPoints[start].Type ?? PathType.LINEAR; switch (segmentType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); - break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); - break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position)); - break; default: + if (segmentType.Degree != null) + throw new NotImplementedException("BSpline conversion of arbitrary degree is not implemented."); + foreach (Vector2 v in segmentVertices) { result.Add(v + position); @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects } /// - /// 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. /// /// The control points of the path. /// The list of bezier control points. @@ -124,38 +124,38 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = controlPoints[start].Type ?? PathType.Linear; + var segmentType = controlPoints[start].Type ?? PathType.LINEAR; switch (segmentType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) { - result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) { - result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) { - result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.BEZIER : null)); } break; @@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Objects default: for (int j = 0; j < segmentVertices.Length - 1; j++) { - result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null)); } break; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d20f2d31bb..30f4c092d9 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -224,16 +224,18 @@ namespace osu.Game.Rulesets.Objects.Legacy { default: case 'C': - return PathType.Catmull; + return new PathType(SplineType.Catmull); case 'B': - return PathType.Bezier; + if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) + return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType(SplineType.BSpline); case 'L': - return PathType.Linear; + return new PathType(SplineType.Linear); case 'P': - return PathType.PerfectCurve; + return new PathType(SplineType.PerfectCurve); } } @@ -320,14 +322,14 @@ namespace osu.Game.Rulesets.Objects.Legacy readPoint(endPoint, offset, out vertices[^1]); // Edge-case rules (to match stable). - if (type == PathType.PerfectCurve) + if (type == PathType.PERFECTCURVE) { if (vertices.Length != 3) - type = PathType.Bezier; + type = PathType.BEZIER; else if (isLinear(vertices)) { // osu-stable special-cased colinear perfect curves to a linear path - type = PathType.Linear; + type = PathType.LINEAR; } } @@ -349,10 +351,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; - // Legacy Catmull sliders don't support multiple segments, so adjacent Catmull segments should be treated as a single one. + // Legacy CATMULL sliders don't support multiple segments, so adjacent CATMULL segments should be treated as a single one. // Importantly, this is not applied to the first control point, which may duplicate the slider path's position // resulting in a duplicate (0,0) control point in the resultant list. - if (type == PathType.Catmull && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION) + if (type == PathType.CATMULL && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION) continue; // The last control point of each segment is not allowed to start a new implicit segment. diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 0ac057578b..4c24c111be 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using Microsoft.Toolkit.HighPerformance; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -260,7 +261,7 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type ?? PathType.Linear; + var segmentType = ControlPoints[start].Type ?? PathType.LINEAR; // No need to calculate path when there is only 1 vertex if (segmentVertices.Length == 1) @@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Objects private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) { - switch (type) + switch (type.SplineType) { - case PathType.Linear: + case SplineType.Linear: return PathApproximator.ApproximateLinear(subControlPoints); - case PathType.PerfectCurve: + case SplineType.PerfectCurve: if (subControlPoints.Length != 3) break; @@ -305,11 +306,11 @@ namespace osu.Game.Rulesets.Objects return subPath; - case PathType.Catmull: + case SplineType.Catmull: return PathApproximator.ApproximateCatmull(subControlPoints); } - return PathApproximator.ApproximateBezier(subControlPoints); + return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length); } private void calculateLength() diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 6c88f01249..d7e5e4574d 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -29,11 +29,11 @@ namespace osu.Game.Rulesets.Objects { var controlPoints = sliderPath.ControlPoints; - var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.Linear && p.Type is null).ToList(); + var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.LINEAR && p.Type is null).ToList(); // Inherited points after a linear point, as well as the first control point if it inherited, // should be treated as linear points, so their types are temporarily changed to linear. - inheritedLinearPoints.ForEach(p => p.Type = PathType.Linear); + inheritedLinearPoints.ForEach(p => p.Type = PathType.LINEAR); double[] segmentEnds = sliderPath.GetSegmentEnds().ToArray(); @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects inheritedLinearPoints.ForEach(p => p.Type = null); // Recalculate middle perfect curve control points at the end of the slider path. - if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PerfectCurve && controlPoints[^2].Type is null && segmentEnds.Any()) + if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECTCURVE && controlPoints[^2].Type is null && segmentEnds.Any()) { double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0; double lastSegmentEnd = segmentEnds[^1]; diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 923ce9eba4..41472fd8b5 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -1,13 +1,59 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Diagnostics; + namespace osu.Game.Rulesets.Objects.Types { - public enum PathType + public enum SplineType { Catmull, - Bezier, + BSpline, Linear, PerfectCurve } + + public struct PathType + { + public static readonly PathType CATMULL = new PathType(SplineType.Catmull); + public static readonly PathType BEZIER = new PathType(SplineType.BSpline); + public static readonly PathType LINEAR = new PathType(SplineType.Linear); + public static readonly PathType PERFECTCURVE = new PathType(SplineType.PerfectCurve); + + /// + /// The type of the spline that should be used to interpret the control points of the path. + /// + public SplineType SplineType { get; init; } + + /// + /// The degree of a BSpline. Unused if is not . + /// Null means the degree is equal to the number of control points, 1 means linear, 2 means quadratic, etc. + /// + public int? Degree { get; init; } + + public PathType(SplineType splineType) + { + SplineType = splineType; + Degree = null; + } + + public override int GetHashCode() + => HashCode.Combine(SplineType, Degree); + + public override bool Equals(object? obj) + => obj is PathType pathType && this == pathType; + + public static bool operator ==(PathType a, PathType b) + => a.SplineType == b.SplineType && a.Degree == b.Degree; + + public static bool operator !=(PathType a, PathType b) + => a.SplineType != b.SplineType || a.Degree != b.Degree; + + public static PathType BSpline(int degree) + { + Debug.Assert(degree > 0); + return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + } + } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 793d43f7ef..a114529bf9 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -246,13 +246,13 @@ namespace osu.Game.Screens.Play.HUD barPath = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.Bezier), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), + new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.BEZIER), new PathControlPoint(new Vector2(curveStart, 0)), - new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.Linear), - new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.Bezier), + new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.LINEAR), + new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.BEZIER), new PathControlPoint(new Vector2(curveEnd, BarHeight.Value)), - new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.Linear), + new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.LINEAR), new PathControlPoint(new Vector2(barLength, BarHeight.Value)), }); From 3f85aa79c5b4402b714da14295b6b796ffa8d6e6 Mon Sep 17 00:00:00 2001 From: cs Date: Sat, 11 Nov 2023 10:45:22 +0100 Subject: [PATCH 02/50] Add free-hand drawing of sliders to the editor --- .../Sliders/SliderPlacementBlueprint.cs | 86 +++++++++++++++++-- .../Edit/ISliderDrawingSettingsProvider.cs | 12 +++ .../Edit/OsuHitObjectComposer.cs | 8 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 68 +++++++++++++++ osu.Game/Rulesets/Objects/SliderPath.cs | 8 +- 5 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 8f0a2ee781..a5c6ae9465 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -10,6 +10,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 +45,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private IDistanceSnapProvider distanceSnapProvider { get; set; } + [Resolved(CanBeNull = true)] + private ISliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + + private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); + protected override bool IsValidForPlacement => HitObject.Path.HasValidLength; public SliderPlacementBlueprint() @@ -73,6 +79,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { base.LoadComplete(); inputManager = GetContainingInputManager(); + + drawingSettingsProvider.Tolerance.BindValueChanged(e => + { + if (bSplineBuilder.Tolerance != e.NewValue) + bSplineBuilder.Tolerance = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); } [Resolved] @@ -98,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders ApplyDefaultsToHitObject(); break; - case SliderPlacementState.Body: + case SliderPlacementState.ControlPoints: updateCursor(); break; } @@ -115,7 +128,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. @@ -139,9 +152,62 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button == MouseButton.Left) + { + switch (state) + { + case SliderPlacementState.Initial: + return true; + + case SliderPlacementState.ControlPoints: + if (HitObject.Path.ControlPoints.Count < 3) + { + var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); + if (lastCp != cursor) + return false; + + bSplineBuilder.Clear(); + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + setState(SliderPlacementState.Drawing); + return true; + } + return false; + } + } + return base.OnDragStart(e); + } + + protected override void OnDrag(DragEvent e) + { + base.OnDrag(e); + + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + updateSliderPathFromBSplineBuilder(); + } + + private void updateSliderPathFromBSplineBuilder() + { + Scheduler.AddOnce(static self => + { + var cps = self.bSplineBuilder.GetControlPoints(); + self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); + self.HitObject.Path.ControlPoints.AddRange(cps.Select(v => new PathControlPoint(v))); + }, this); + } + + 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); } @@ -149,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void beginCurve() { BeginPlacement(commitStart: true); - setState(SliderPlacementState.Body); + setState(SliderPlacementState.ControlPoints); } private void endCurve() @@ -169,6 +235,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updatePathType() { + if (state == SliderPlacementState.Drawing) + { + segmentStart.Type = PathType.BSpline(3); + return; + } + switch (currentSegmentLength) { case 1: @@ -201,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // 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) @@ -248,7 +320,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private enum SliderPlacementState { Initial, - Body, + ControlPoints, + Drawing, + DrawingFinalization } } } diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs new file mode 100644 index 0000000000..1138588259 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public interface ISliderDrawingSettingsProvider + { + BindableFloat Tolerance { get; } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0f8c960b65..d958b558cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -63,6 +63,9 @@ namespace osu.Game.Rulesets.Osu.Edit [Cached(typeof(IDistanceSnapProvider))] protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); + [Cached(typeof(ISliderDrawingSettingsProvider))] + protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); + [BackgroundDependencyLoader] private void load() { @@ -96,8 +99,11 @@ namespace osu.Game.Rulesets.Osu.Edit RightToolbox.Add(new TransformToolboxGroup { - RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler + RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); + + AddInternal(SliderDrawingSettingsProvider); + SliderDrawingSettingsProvider.AttachToToolbox(RightToolbox); } protected override ComposeBlueprintContainer CreateBlueprintContainer() diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs new file mode 100644 index 0000000000..ba2c39e1b5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider + { + public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) + { + MinValue = 0.05f, + MaxValue = 1f, + Precision = 0.01f + }; + + private BindableInt sliderTolerance = new BindableInt(10) + { + MinValue = 5, + MaxValue = 100 + }; + + private ExpandableSlider toleranceSlider = null!; + + private EditorToolboxGroup? toolboxGroup; + + public OsuSliderDrawingSettingsProvider() + { + sliderTolerance.BindValueChanged(v => + { + float newValue = v.NewValue / 100f; + if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + Tolerance.Value = newValue; + }); + Tolerance.BindValueChanged(v => + { + int newValue = (int)Math.Round(v.NewValue * 100f); + if (sliderTolerance.Value != newValue) + sliderTolerance.Value = newValue; + }); + } + + public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) + { + toolboxContainer.Add(toolboxGroup = new EditorToolboxGroup("drawing") + { + Children = new Drawable[] + { + toleranceSlider = new ExpandableSlider + { + Current = sliderTolerance + } + } + }); + + sliderTolerance.BindValueChanged(e => + { + toleranceSlider.ContractedLabelText = $"Tolerance: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Tolerance: {e.NewValue:N0}"; + }, true); + } + } +} diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 4c24c111be..75f1ab868d 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -292,13 +292,13 @@ namespace osu.Game.Rulesets.Objects switch (type.SplineType) { case SplineType.Linear: - return PathApproximator.ApproximateLinear(subControlPoints); + return PathApproximator.LinearToPiecewiseLinear(subControlPoints); case SplineType.PerfectCurve: if (subControlPoints.Length != 3) break; - List subPath = PathApproximator.ApproximateCircularArc(subControlPoints); + List 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) @@ -307,10 +307,10 @@ namespace osu.Game.Rulesets.Objects return subPath; case SplineType.Catmull: - return PathApproximator.ApproximateCatmull(subControlPoints); + return PathApproximator.CatmullToPiecewiseLinear(subControlPoints); } - return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length); + return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length); } private void calculateLength() From 54b8244a18ad3e74d40962be20cab996ba63e90d Mon Sep 17 00:00:00 2001 From: cs Date: Sat, 11 Nov 2023 15:02:06 +0100 Subject: [PATCH 03/50] CI Fixup --- .../Sliders/Components/PathControlPointPiece.cs | 8 ++++---- .../Components/PathControlPointVisualiser.cs | 2 +- .../Sliders/SliderPlacementBlueprint.cs | 2 ++ .../Edit/OsuSliderDrawingSettingsProvider.cs | 10 ++++------ .../Visual/Gameplay/TestSceneBezierConverter.cs | 4 ++-- .../Visual/Gameplay/TestSceneSliderPath.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 11 +++++------ osu.Game/Rulesets/Objects/BezierConverter.cs | 12 ++++++------ .../Objects/Legacy/ConvertHitObjectParser.cs | 3 ++- osu.Game/Rulesets/Objects/SliderPath.cs | 3 +-- osu.Game/Rulesets/Objects/Types/PathType.cs | 16 ++++++++-------- 11 files changed, 37 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 9658e5f6c3..53228cff82 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -261,17 +261,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components switch (pathType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: return colours.SeaFoam; - case { SplineType: SplineType.BSpline, Degree: null }: + case { Type: SplineType.BSpline, Degree: null }: return colours.PinkLighter; - case { SplineType: SplineType.BSpline, Degree: >= 1 }: + case { Type: SplineType.BSpline, Degree: >= 1 }: int idx = Math.Clamp(pathType.Degree.Value, 0, 3); return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index b5c9016538..4e85835652 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - if (type.HasValue && type.Value.SplineType == SplineType.PerfectCurve) + if (type.HasValue && type.Value.Type == SplineType.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 diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index a5c6ae9465..20f11c3585 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -173,9 +173,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders setState(SliderPlacementState.Drawing); return true; } + return false; } } + return base.OnDragStart(e); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index ba2c39e1b5..ae772f53fc 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private BindableInt sliderTolerance = new BindableInt(10) + private readonly BindableInt sliderTolerance = new BindableInt(10) { MinValue = 5, MaxValue = 100 @@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; - private EditorToolboxGroup? toolboxGroup; - public OsuSliderDrawingSettingsProvider() { sliderTolerance.BindValueChanged(v => @@ -47,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Edit public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) { - toolboxContainer.Add(toolboxGroup = new EditorToolboxGroup("drawing") + toolboxContainer.Add(new EditorToolboxGroup("drawing") { Children = new Drawable[] { @@ -60,8 +58,8 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(e => { - toleranceSlider.ContractedLabelText = $"Tolerance: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Tolerance: {e.NewValue:N0}"; + toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; }, true); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index 5eb82ccbdc..e2333011c7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(SplineType.Catmull, null)] [TestCase(SplineType.PerfectCurve, null)] public void TestSingleSegment(SplineType splineType, int? degree) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); [TestCase(SplineType.Linear, null)] [TestCase(SplineType.BSpline, null)] @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index e4d99f6741..d44af45fe4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(SplineType.PerfectCurve, null)] public void TestSingleSegment(SplineType splineType, int? degree) => AddStep("create path", () => path.ControlPoints.AddRange(createSegment( - new PathType { SplineType = splineType, Degree = degree }, + new PathType { Type = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100), @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7029f61459..ff446206ac 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -456,23 +455,23 @@ namespace osu.Game.Beatmaps.Formats { switch (point.Type) { - case { SplineType: SplineType.BSpline, Degree: > 0 }: + case { Type: SplineType.BSpline, Degree: > 0 }: writer.Write($"B{point.Type.Value.Degree}|"); break; - case { SplineType: SplineType.BSpline, Degree: <= 0 }: + case { Type: SplineType.BSpline, Degree: <= 0 }: writer.Write("B|"); break; - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: writer.Write("C|"); break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: writer.Write("P|"); break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: writer.Write("L|"); break; } diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 74fbe7d8f9..ed86fc10e0 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -72,15 +72,15 @@ namespace osu.Game.Rulesets.Objects switch (segmentType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position)); break; @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Objects switch (segmentType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Objects break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Objects break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 30f4c092d9..6a13a897c4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -228,7 +228,8 @@ namespace osu.Game.Rulesets.Objects.Legacy case 'B': if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) - return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType { Type = SplineType.BSpline, Degree = degree }; + return new PathType(SplineType.BSpline); case 'L': diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 75f1ab868d..e9a192669f 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; -using Microsoft.Toolkit.HighPerformance; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -289,7 +288,7 @@ namespace osu.Game.Rulesets.Objects private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) { - switch (type.SplineType) + switch (type.Type) { case SplineType.Linear: return PathApproximator.LinearToPiecewiseLinear(subControlPoints); diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 41472fd8b5..a6e8e173d4 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public struct PathType + public readonly struct PathType { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); @@ -24,36 +24,36 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The type of the spline that should be used to interpret the control points of the path. /// - public SplineType SplineType { get; init; } + public SplineType Type { get; init; } /// - /// The degree of a BSpline. Unused if is not . + /// The degree of a BSpline. Unused if is not . /// Null means the degree is equal to the number of control points, 1 means linear, 2 means quadratic, etc. /// public int? Degree { get; init; } public PathType(SplineType splineType) { - SplineType = splineType; + Type = splineType; Degree = null; } public override int GetHashCode() - => HashCode.Combine(SplineType, Degree); + => HashCode.Combine(Type, Degree); public override bool Equals(object? obj) => obj is PathType pathType && this == pathType; public static bool operator ==(PathType a, PathType b) - => a.SplineType == b.SplineType && a.Degree == b.Degree; + => a.Type == b.Type && a.Degree == b.Degree; public static bool operator !=(PathType a, PathType b) - => a.SplineType != b.SplineType || a.Degree != b.Degree; + => a.Type != b.Type || a.Degree != b.Degree; public static PathType BSpline(int degree) { Debug.Assert(degree > 0); - return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType { Type = SplineType.BSpline, Degree = degree }; } } } From c24b3543ab271536b2b98c3c4777a174bc0621c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Mon, 13 Nov 2023 12:47:12 +0900 Subject: [PATCH 04/50] Fix OnDragStart on macOS --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 20f11c3585..fe01b1b6d2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (HitObject.Path.ControlPoints.Count < 3) { var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); - if (lastCp != cursor) + if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) return false; bSplineBuilder.Clear(); From e6b4dfba369ee790ee0cee427aa702a935ce341b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Mon, 13 Nov 2023 12:49:59 +0900 Subject: [PATCH 05/50] Fix doubled control point at beginning of drawn slider --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index fe01b1b6d2..9ac28fe82a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { var cps = self.bSplineBuilder.GetControlPoints(); self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); - self.HitObject.Path.ControlPoints.AddRange(cps.Select(v => new PathControlPoint(v))); + self.HitObject.Path.ControlPoints.AddRange(cps.Skip(1).Select(v => new PathControlPoint(v))); }, this); } From 8ad8764947835e9ce4070338b6e1ea81016df2b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Nov 2023 14:06:18 +0900 Subject: [PATCH 06/50] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2870696c03..1f6a65c450 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index f1159f58b9..70525a5c59 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From fa976a5aa0eeb94dccbcd58b00d51a1c9d7e52df Mon Sep 17 00:00:00 2001 From: cs Date: Mon, 13 Nov 2023 08:24:09 +0100 Subject: [PATCH 07/50] Fix code style/quality issues --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 6 +++--- .../JuiceStreamPathTest.cs | 2 +- .../Checks/CheckOffscreenObjectsTest.cs | 2 +- .../TestSceneOsuEditorSelectInvalidPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 8 ++++---- .../TestSceneSliderControlPointPiece.cs | 18 ++++++++--------- .../Editor/TestSceneSliderLengthValidity.cs | 2 +- .../TestSceneSliderPlacementBlueprint.cs | 16 +++++++-------- .../Editor/TestSceneSliderReversal.cs | 2 +- .../Editor/TestSceneSliderSnapping.cs | 10 +++++----- .../Editor/TestSceneSliderSplitting.cs | 20 +++++++++---------- .../TestSceneSlider.cs | 6 +++--- .../TestSceneSliderInput.cs | 2 +- .../TestSceneSliderSnaking.cs | 6 +++--- .../Components/PathControlPointPiece.cs | 14 ++++++------- .../Components/PathControlPointVisualiser.cs | 2 +- .../Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 8 +++++--- .../Formats/LegacyBeatmapDecoderTest.cs | 8 ++++---- .../Gameplay/TestSceneBezierConverter.cs | 8 ++++---- .../Visual/Gameplay/TestSceneSliderPath.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 18 +++++++---------- .../Edit/ComposerDistanceSnapProvider.cs | 2 +- osu.Game/Rulesets/Edit/IToolboxAttachment.cs | 10 ++++++++++ osu.Game/Rulesets/Objects/BezierConverter.cs | 8 ++++---- .../Objects/Legacy/ConvertHitObjectParser.cs | 10 +++++----- .../Rulesets/Objects/SliderPathExtensions.cs | 2 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 12 +++++++---- osu.Game/osu.Game.csproj | 2 +- 29 files changed, 112 insertions(+), 100 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/IToolboxAttachment.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 16b51d414a..c96f32d87c 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -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,14 +190,14 @@ 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); } diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 82f24633b5..9fb55fc057 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -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) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 8612a8eb57..5db6dc6cdd 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -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)) }), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs index 7ea4d40b90..43dae38004 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -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)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 16800997f4..811ecf53e9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.BEZIER); - assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(1, PathType.PERFECT_CURVE); assertControlPointPathType(3, PathType.BEZIER); } @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.BEZIER); - assertControlPointPathType(2, PathType.PERFECTCURVE); + assertControlPointPathType(2, PathType.PERFECT_CURVE); assertControlPointPathType(4, null); } @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.LINEAR); - assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(1, PathType.PERFECT_CURVE); assertControlPointPathType(3, PathType.LINEAR); } @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor createVisualiser(true); addControlPointStep(new Vector2(200), PathType.BEZIER); - addControlPointStep(new Vector2(300), PathType.PERFECTCURVE); + addControlPointStep(new Vector2(300), PathType.PERFECT_CURVE); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200), PathType.BEZIER); addControlPointStep(new Vector2(500, 100)); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs index 1d8d2cf01a..99ced30ffe 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs @@ -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>().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)); @@ -288,7 +288,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] @@ -304,7 +304,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(150, 150)); - assertControlPointType(2, PathType.PERFECTCURVE); + assertControlPointType(2, PathType.PERFECT_CURVE); } [Test] @@ -312,12 +312,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { 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)); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs index 38ebeb7e8f..931c8c9e63 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs @@ -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)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 4b120c1a3f..ecfc8105f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -241,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); assertControlPointType(0, PathType.LINEAR); - assertControlPointType(1, PathType.PERFECTCURVE); + assertControlPointType(1, PathType.PERFECT_CURVE); } [Test] @@ -269,8 +269,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(2, new Vector2(100)); assertControlPointPosition(3, new Vector2(200, 100)); assertControlPointPosition(4, new Vector2(200)); - assertControlPointType(0, PathType.PERFECTCURVE); - assertControlPointType(2, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); + assertControlPointType(2, PathType.PERFECT_CURVE); } [Test] @@ -326,7 +326,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -385,7 +385,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))); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs index 0ddfc40946..a44c16a2e0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs @@ -22,7 +22,7 @@ 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) ), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index c984d9168e..541fefb3a6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -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(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] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index cded9165f4..6c7733e68a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -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,7 +104,7 @@ 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(400, 0)), @@ -139,7 +139,7 @@ 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, @@ -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)) }) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index 60003e7950..4600db8174 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -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), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 08836ef819..0f7dd8b7bc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 0.1f, - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(slider_path_length, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index ebc5143aed..912b2b0626 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -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) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 53228cff82..ac9048d5c7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type != PathType.PERFECTCURVE) + if (ControlPoint.Type != PathType.PERFECT_CURVE) return; if (PointsInSegment.Count > 3) @@ -259,19 +259,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (ControlPoint.Type is not PathType pathType) return colours.Yellow; - switch (pathType) + switch (pathType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: return colours.SeaFoam; - case { Type: SplineType.BSpline, Degree: null }: - return colours.PinkLighter; + case SplineType.BSpline: + if (!pathType.Degree.HasValue) + return colours.PinkLighter; - case { Type: SplineType.BSpline, Degree: >= 1 }: int idx = Math.Clamp(pathType.Degree.Value, 0, 3); return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 4e85835652..5ab050ed48 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -368,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // 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.PERFECT_CURVE)); curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9ac28fe82a..c722f0fdbc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case 3: - segmentStart.Type = PathType.PERFECTCURVE; + segmentStart.Type = PathType.PERFECT_CURVE; break; default: diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index ae772f53fc..643732d90a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider + public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment { public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) { @@ -27,12 +27,14 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; - public OsuSliderDrawingSettingsProvider() + protected override void LoadComplete() { + base.LoadComplete(); + sliderTolerance.BindValueChanged(v => { float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, Tolerance.Value)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 18c21046fb..34b96bbd3f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -808,7 +808,7 @@ 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)); @@ -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))); @@ -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))); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index e2333011c7..27497f77be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.Gameplay { 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.PERFECT_CURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); }); } @@ -159,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))); }); } @@ -172,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; } }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index d44af45fe4..44a2e5fb9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -149,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; } }); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index ff446206ac..b375a6f7ff 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -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,25 +453,21 @@ namespace osu.Game.Beatmaps.Formats if (needsExplicitSegment) { - switch (point.Type) + switch (point.Type?.Type) { - case { Type: SplineType.BSpline, Degree: > 0 }: - writer.Write($"B{point.Type.Value.Degree}|"); + case SplineType.BSpline: + writer.Write(point.Type.Value.Degree > 0 ? $"B{point.Type.Value.Degree}|" : "B|"); break; - case { Type: SplineType.BSpline, Degree: <= 0 }: - writer.Write("B|"); - break; - - case { Type: SplineType.Catmull }: + case SplineType.Catmull: writer.Write("C|"); break; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: writer.Write("P|"); break; - case { Type: SplineType.Linear }: + case SplineType.Linear: writer.Write("L|"); break; } diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index ddf539771d..68411d2b01 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -29,7 +29,7 @@ using osu.Game.Screens.Edit.Components.TernaryButtons; namespace osu.Game.Rulesets.Edit { - public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler + public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler, IToolboxAttachment { private const float adjust_step = 0.1f; diff --git a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs new file mode 100644 index 0000000000..7d7c5980b2 --- /dev/null +++ b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit +{ + public interface IToolboxAttachment + { + void AttachToToolbox(ExpandingToolboxContainer toolbox); + } +} diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index ed86fc10e0..5dc0839d37 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -126,9 +126,9 @@ namespace osu.Game.Rulesets.Objects var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = controlPoints[start].Type ?? PathType.LINEAR; - switch (segmentType) + switch (segmentType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Objects break; - case { Type: SplineType.Linear }: + case SplineType.Linear: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Objects break; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 6a13a897c4..92a92dca8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -224,19 +224,19 @@ namespace osu.Game.Rulesets.Objects.Legacy { default: case 'C': - return new PathType(SplineType.Catmull); + return PathType.CATMULL; case 'B': if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) - return new PathType { Type = SplineType.BSpline, Degree = degree }; + return PathType.BSpline(degree); return new PathType(SplineType.BSpline); case 'L': - return new PathType(SplineType.Linear); + return PathType.LINEAR; case 'P': - return new PathType(SplineType.PerfectCurve); + return PathType.PERFECT_CURVE; } } @@ -323,7 +323,7 @@ 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; diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index d7e5e4574d..29b34ae4f0 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -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]; diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index a6e8e173d4..4fb48bb8b4 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; namespace osu.Game.Rulesets.Objects.Types { @@ -14,12 +13,12 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public readonly struct PathType + public readonly struct PathType : IEquatable { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); public static readonly PathType LINEAR = new PathType(SplineType.Linear); - public static readonly PathType PERFECTCURVE = new PathType(SplineType.PerfectCurve); + public static readonly PathType PERFECT_CURVE = new PathType(SplineType.PerfectCurve); /// /// The type of the spline that should be used to interpret the control points of the path. @@ -52,8 +51,13 @@ namespace osu.Game.Rulesets.Objects.Types public static PathType BSpline(int degree) { - Debug.Assert(degree > 0); + 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 bool Equals(PathType other) + => Type == other.Type && Degree == other.Degree; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fc10f90e8f..8d42cd22e3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 90ec6895d100dea67e282c75a57779ffa87d0f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Tue, 14 Nov 2023 16:52:45 +0900 Subject: [PATCH 08/50] Automatic red control point generation & corner threshold --- .../Sliders/SliderPlacementBlueprint.cs | 28 +++++++++-- .../Edit/ISliderDrawingSettingsProvider.cs | 1 + .../Edit/OsuSliderDrawingSettingsProvider.cs | 46 +++++++++++++++++-- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c722f0fdbc..69e2a40689 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -86,6 +86,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }, true); + + drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + { + if (bSplineBuilder.CornerThreshold != e.NewValue) + bSplineBuilder.CornerThreshold = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); } [Resolved] @@ -100,8 +107,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); @@ -193,9 +201,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Scheduler.AddOnce(static self => { - var cps = self.bSplineBuilder.GetControlPoints(); - self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); - self.HitObject.Path.ControlPoints.AddRange(cps.Skip(1).Select(v => new PathControlPoint(v))); + var cps = self.bSplineBuilder.ControlPoints; + var sliderCps = self.HitObject.Path.ControlPoints; + sliderCps.RemoveRange(1, sliderCps.Count - 1); + + // Add the control points from the BSpline builder while converting control points that repeat + // three or more times to a single PathControlPoint with linear type. + for (int i = 1; i < cps.Count; i++) + { + bool isSharp = i < cps.Count - 2 && cps[i] == cps[i + 1] && cps[i] == cps[i + 2]; + sliderCps.Add(new PathControlPoint(cps[i], isSharp ? PathType.BSpline(3) : null)); + if (isSharp) + i += 2; + } }, this); } diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs index 1138588259..31ed98e1dd 100644 --- a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Osu.Edit public interface ISliderDrawingSettingsProvider { BindableFloat Tolerance { get; } + BindableFloat CornerThreshold { get; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 643732d90a..1fe1326f38 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -12,20 +12,34 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment { - public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) + public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) + { + MinValue = 0.05f, + MaxValue = 3f, + Precision = 0.01f + }; + + private readonly BindableInt sliderTolerance = new BindableInt(50) + { + MinValue = 5, + MaxValue = 100 + }; + + public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f) { MinValue = 0.05f, MaxValue = 1f, Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(10) + private readonly BindableInt sliderCornerThreshold = new BindableInt(40) { MinValue = 5, MaxValue = 100 }; private ExpandableSlider toleranceSlider = null!; + private ExpandableSlider cornerThresholdSlider = null!; protected override void LoadComplete() { @@ -33,16 +47,28 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(v => { - float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value)) + float newValue = v.NewValue / 33f; + if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => { - int newValue = (int)Math.Round(v.NewValue * 100f); + int newValue = (int)Math.Round(v.NewValue * 33f); if (sliderTolerance.Value != newValue) sliderTolerance.Value = newValue; }); + sliderCornerThreshold.BindValueChanged(v => + { + float newValue = v.NewValue / 100f; + if (!Precision.AlmostEquals(newValue, CornerThreshold.Value, 1e-7f)) + CornerThreshold.Value = newValue; + }); + CornerThreshold.BindValueChanged(v => + { + int newValue = (int)Math.Round(v.NewValue * 100f); + if (sliderCornerThreshold.Value != newValue) + sliderCornerThreshold.Value = newValue; + }); } public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) @@ -54,6 +80,10 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider = new ExpandableSlider { Current = sliderTolerance + }, + cornerThresholdSlider = new ExpandableSlider + { + Current = sliderCornerThreshold } } }); @@ -63,6 +93,12 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; }, true); + + sliderCornerThreshold.BindValueChanged(e => + { + cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; + }, true); } } } From ceeaf5b67c527c787b8d34eb3594207ed9ac14d7 Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:09:33 +0100 Subject: [PATCH 09/50] CI fixes and small tweaks --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 4 ++-- .../Edit/OsuSliderDrawingSettingsProvider.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 69e2a40689..df7d2c716b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -108,8 +108,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BeginPlacement(); double? nearestSliderVelocity = (editorBeatmap - .HitObjects - .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; + .HitObjects + .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1; HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 1fe1326f38..4326b2e943 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(v => { float newValue = v.NewValue / 33f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, Tolerance.Value)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit sliderCornerThreshold.BindValueChanged(v => { float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, CornerThreshold.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, CornerThreshold.Value)) CornerThreshold.Value = newValue; }); CornerThreshold.BindValueChanged(v => @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) { - toolboxContainer.Add(new EditorToolboxGroup("drawing") + toolboxContainer.Add(new EditorToolboxGroup("slider") { Children = new Drawable[] { From 520642975bda8de23c51e2b350c637a93115b08c Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:45:09 +0100 Subject: [PATCH 10/50] Fix control point hover text and context menu --- .../Sliders/Components/PathControlPointPiece.cs | 2 +- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index ac9048d5c7..03792d8520 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -279,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; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 5ab050ed48..faae966d02 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -403,7 +403,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 == null ? "Inherit" : type!.Value.Description, MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 4fb48bb8b4..e4249154e5 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.Objects.Types { @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public readonly struct PathType : IEquatable + public readonly struct PathType : IEquatable, IHasDescription { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); @@ -31,6 +32,15 @@ namespace osu.Game.Rulesets.Objects.Types /// public int? Degree { get; init; } + public string Description => Type switch + { + SplineType.Catmull => "Catmull", + SplineType.BSpline => Degree == null ? "Bezier" : "B-Spline", + SplineType.Linear => "Linear", + SplineType.PerfectCurve => "Perfect Curve", + _ => Type.ToString() + }; + public PathType(SplineType splineType) { Type = splineType; From 360864fd7b6dac30bda9a2cf4092a316b280427d Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:45:28 +0100 Subject: [PATCH 11/50] Hide catmull curve type when possible --- .../Sliders/Components/PathControlPointVisualiser.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index faae966d02..95c72a0a62 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -371,7 +371,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE)); curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); - curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); + + var hoveredPiece = Pieces.FirstOrDefault(p => p.IsHovered); + + if (hoveredPiece?.ControlPoint.Type == PathType.CATMULL) + curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List { From 33b592f1c723fb4824f2084337de9dbb940d2b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:04:30 +0900 Subject: [PATCH 12/50] Update framework (again) --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1f6a65c450..dd5a8996fb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 70525a5c59..9a0832b4e7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 577cb9994ce54202bf2c07548389f0e6b701fa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:24:32 +0900 Subject: [PATCH 13/50] Move static instances / construction methods closer together --- osu.Game/Rulesets/Objects/Types/PathType.cs | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index e4249154e5..0e3adb4473 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -16,11 +16,6 @@ namespace osu.Game.Rulesets.Objects.Types public readonly struct PathType : IEquatable, IHasDescription { - 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); - /// /// The type of the spline that should be used to interpret the control points of the path. /// @@ -32,6 +27,25 @@ namespace osu.Game.Rulesets.Objects.Types /// 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 => Type switch { SplineType.Catmull => "Catmull", @@ -41,12 +55,6 @@ namespace osu.Game.Rulesets.Objects.Types _ => Type.ToString() }; - public PathType(SplineType splineType) - { - Type = splineType; - Degree = null; - } - public override int GetHashCode() => HashCode.Combine(Type, Degree); @@ -59,14 +67,6 @@ namespace osu.Game.Rulesets.Objects.Types public static bool operator !=(PathType a, PathType b) => a.Type != b.Type || a.Degree != b.Degree; - 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 bool Equals(PathType other) => Type == other.Type && Degree == other.Degree; } From 25c1a900473de216ba5c07b306b4e49316ec7828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:25:44 +0900 Subject: [PATCH 14/50] Change switchexpr to standard switch statement --- osu.Game/Rulesets/Objects/Types/PathType.cs | 29 ++++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 0e3adb4473..95ddcb8b05 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -46,14 +46,29 @@ namespace osu.Game.Rulesets.Objects.Types return new PathType { Type = SplineType.BSpline, Degree = degree }; } - public string Description => Type switch + public string Description { - SplineType.Catmull => "Catmull", - SplineType.BSpline => Degree == null ? "Bezier" : "B-Spline", - SplineType.Linear => "Linear", - SplineType.PerfectCurve => "Perfect Curve", - _ => Type.ToString() - }; + 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); From 7820c8ce4d558381f80444fcf3fb38035382a852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:28:20 +0900 Subject: [PATCH 15/50] Decrease redundancy of equality implementations --- osu.Game/Rulesets/Objects/Types/PathType.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 95ddcb8b05..37c1d0ab50 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -74,15 +74,12 @@ namespace osu.Game.Rulesets.Objects.Types => HashCode.Combine(Type, Degree); public override bool Equals(object? obj) - => obj is PathType pathType && this == pathType; - - public static bool operator ==(PathType a, PathType b) - => a.Type == b.Type && a.Degree == b.Degree; - - public static bool operator !=(PathType a, PathType b) - => a.Type != b.Type || a.Degree != b.Degree; + => 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); } } From 518dcc567b14cdf8ade2d449b20cdcbcfcf45e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:41:22 +0900 Subject: [PATCH 16/50] Null-check `drawingSettingsProvider` As it's annotated as an optional dependency. --- .../Sliders/SliderPlacementBlueprint.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index df7d2c716b..9b24415604 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -80,19 +80,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - drawingSettingsProvider.Tolerance.BindValueChanged(e => + if (drawingSettingsProvider != null) { - if (bSplineBuilder.Tolerance != e.NewValue) - bSplineBuilder.Tolerance = e.NewValue; - updateSliderPathFromBSplineBuilder(); - }, true); + drawingSettingsProvider.Tolerance.BindValueChanged(e => + { + if (bSplineBuilder.Tolerance != e.NewValue) + bSplineBuilder.Tolerance = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); - drawingSettingsProvider.CornerThreshold.BindValueChanged(e => - { - if (bSplineBuilder.CornerThreshold != e.NewValue) - bSplineBuilder.CornerThreshold = e.NewValue; - updateSliderPathFromBSplineBuilder(); - }, true); + drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + { + if (bSplineBuilder.CornerThreshold != e.NewValue) + bSplineBuilder.CornerThreshold = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); + } } [Resolved] From f46945a29439716ef10e5e82277cf5e338044094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:42:16 +0900 Subject: [PATCH 17/50] Avoid one unnecessary path update from B-spline builder --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9b24415604..50b4377ccd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); - }, true); + }); drawingSettingsProvider.CornerThreshold.BindValueChanged(e => { From 831884a64b74113f76bc59eed0466f232b71f7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:00:12 +0900 Subject: [PATCH 18/50] Remove unused enum member --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 50b4377ccd..50abcf1233 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -344,8 +344,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Initial, ControlPoints, - Drawing, - DrawingFinalization + Drawing } } } From affef85f2521246412949531e291fdbed4cf1272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:02:51 +0900 Subject: [PATCH 19/50] Remove `ISliderDrawingSettingsProvider` Seems like excessive abstraction. --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/ISliderDrawingSettingsProvider.cs | 13 ------------- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- 4 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 50abcf1233..fb2c1d5149 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private ISliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + private OsuSliderDrawingSettingsProvider drawingSettingsProvider { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs deleted file mode 100644 index 31ed98e1dd..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public interface ISliderDrawingSettingsProvider - { - BindableFloat Tolerance { get; } - BindableFloat CornerThreshold { get; } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index d958b558cf..0dc0d6fd31 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Edit [Cached(typeof(IDistanceSnapProvider))] protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); - [Cached(typeof(ISliderDrawingSettingsProvider))] + [Cached] protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 4326b2e943..0126b366d5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment + public partial class OsuSliderDrawingSettingsProvider : Drawable, IToolboxAttachment { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { From 5d1bac6d7a55a980e608fbf6bfa8744ceb30b18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:17:43 +0900 Subject: [PATCH 20/50] Remove `IToolboxAttachment` for now The interface doesn't really do anything useful right now because it enforces a common contract, but all usages of the contract go through the concrete implementation, and it inflates the already-huge diff. --- .../Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs | 2 +- osu.Game/Rulesets/Edit/IToolboxAttachment.cs | 10 ---------- 3 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/IToolboxAttachment.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 0126b366d5..e44f0265c8 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, IToolboxAttachment + public partial class OsuSliderDrawingSettingsProvider : Drawable { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index 68411d2b01..ddf539771d 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -29,7 +29,7 @@ using osu.Game.Screens.Edit.Components.TernaryButtons; namespace osu.Game.Rulesets.Edit { - public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler, IToolboxAttachment + public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler { private const float adjust_step = 0.1f; diff --git a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs deleted file mode 100644 index 7d7c5980b2..0000000000 --- a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Edit -{ - public interface IToolboxAttachment - { - void AttachToToolbox(ExpandingToolboxContainer toolbox); - } -} From 487326a4c786aa89b597ea191ec83c701386736d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:22:36 +0900 Subject: [PATCH 21/50] Remove pattern matching syntax usage in switch Also throw on unknown types. --- osu.Game/Rulesets/Objects/BezierConverter.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 5dc0839d37..4a68161899 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -70,21 +70,21 @@ namespace osu.Game.Rulesets.Objects var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = controlPoints[start].Type ?? PathType.LINEAR; - switch (segmentType) + switch (segmentType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { Type: SplineType.Linear }: + case SplineType.Linear: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { Type: SplineType.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."); @@ -94,6 +94,9 @@ namespace osu.Game.Rulesets.Objects } 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 @@ -160,13 +163,16 @@ namespace osu.Game.Rulesets.Objects break; - default: + case SplineType.BSpline: for (int j = 0; j < segmentVertices.Length - 1; j++) { result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null)); } 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 From 80a3225bb28f315aeceda4dc5e10c2222faa0e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:35:07 +0900 Subject: [PATCH 22/50] Use static `BEZIER` instead of allocating new every time --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 92a92dca8f..411a9b0d63 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) return PathType.BSpline(degree); - return new PathType(SplineType.BSpline); + return PathType.BEZIER; case 'L': return PathType.LINEAR; From 6d7d826b8b76f775bf59d600d493912a92950540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:08:58 +0900 Subject: [PATCH 23/50] Fix incorrect legacy conversion when B-splines are used --- osu.Game/Database/LegacyBeatmapExporter.cs | 3 ++- osu.Game/Rulesets/Objects/BezierConverter.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 9ca12a79dd..69120ea885 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -85,7 +85,8 @@ namespace osu.Game.Database 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); diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 4a68161899..638975630e 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -164,9 +164,13 @@ namespace osu.Game.Rulesets.Objects break; case SplineType.BSpline: - for (int j = 0; j < segmentVertices.Length - 1; j++) + 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(segmentVertices[j], j == 0 ? segmentType : null)); + result.Add(new PathControlPoint(bSplineResult[j], j == 0 ? PathType.BEZIER : null)); } break; From 46d4587c97fc8a6d7b3a2ea0e98366fe149f2ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:34:01 +0900 Subject: [PATCH 24/50] Add test for slider drawing --- .../TestSceneSliderPlacementBlueprint.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index ecfc8105f1..d1c94c9c9c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -290,6 +290,27 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(0, PathType.LINEAR); } + [Test] + public void TestSliderDrawing() + { + 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)); + + assertPlaced(true); + assertLength(600, tolerance: 10); + assertControlPointCount(4); + assertControlPointType(0, PathType.BSpline(3)); + } + [Test] public void TestPlacePerfectCurveSegmentAlmostLinearlyExterior() { @@ -397,7 +418,7 @@ 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}", () => Precision.AlmostEquals(expected, getSlider().Distance, tolerance)); private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); From 5f302662be30eff9eb12318c40651f1f776fb09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:34:23 +0900 Subject: [PATCH 25/50] Remove test terminally broken by introduction of slider drawing --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index d1c94c9c9c..c7a21ba689 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -273,23 +273,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(2, PathType.PERFECT_CURVE); } - [Test] - public void TestBeginPlacementWithoutReleasingMouse() - { - addMovementStep(new Vector2(200)); - AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); - - addMovementStep(new Vector2(400, 200)); - AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); - - addClickStep(MouseButton.Right); - - assertPlaced(true); - assertLength(200); - assertControlPointCount(2); - assertControlPointType(0, PathType.LINEAR); - } - [Test] public void TestSliderDrawing() { From 8e39dbbff1898830ab70c5b418ec31b661fede42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:41:26 +0900 Subject: [PATCH 26/50] Adjust casing of curve type menu items The "Perfect curve" one in particular... fixes test failures, as some tests were relying on this particular casing. But the new version feels more correct anyway, so it's whatever. --- osu.Game/Rulesets/Objects/Types/PathType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 37c1d0ab50..f84d43e3e7 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Objects.Types return "Catmull"; case SplineType.BSpline: - return Degree == null ? "Bezier" : "B-Spline"; + return Degree == null ? "Bezier" : "B-spline"; case SplineType.Linear: return "Linear"; case SplineType.PerfectCurve: - return "Perfect Curve"; + return "Perfect curve"; default: return Type.ToString(); From 43dbd65708de80e4e671e042e76496ffb7526d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:53:25 +0900 Subject: [PATCH 27/50] Show Catmull as a control point type option if selection already contains it --- .../TestScenePathControlPointVisualiser.cs | 26 ++++++++++++++++++- .../Components/PathControlPointVisualiser.cs | 4 +-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 811ecf53e9..8234381283 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -148,6 +148,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor 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, 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)); }); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 95c72a0a62..751ca7e753 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -372,9 +372,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); - var hoveredPiece = Pieces.FirstOrDefault(p => p.IsHovered); - - if (hoveredPiece?.ControlPoint.Type == PathType.CATMULL) + if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull)) curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List From 4061417ac8cff9242cf5582fdb8b173f7095059e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:54:43 +0900 Subject: [PATCH 28/50] Decrease default value for slider tolerance Highly subjective change, but at 50 the drawing just did not feel responsive enough to input. --- osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index e44f0265c8..7e72eee510 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(50) + private readonly BindableInt sliderTolerance = new BindableInt(40) { MinValue = 5, MaxValue = 100 From ded9981d07b8bc54fd43a53a76f6335aab5d636f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Nov 2023 17:55:01 +0900 Subject: [PATCH 29/50] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index dd5a8996fb..8175869405 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 9a0832b4e7..a5425ba4c7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 492fd06c624b2d8d8ff65464798bcde72c4fd51c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Nov 2023 19:21:23 +0900 Subject: [PATCH 30/50] Remove unnecessary null override --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 751ca7e753..3128f46357 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -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!.Value.Description, MenuItemType.Standard, _ => + var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.Value.Description, MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); From 750bbc8a19cfdb0f1c3133e9bd86e02bacb51f0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:17:25 +0900 Subject: [PATCH 31/50] Simplify null checks --- .../Sliders/Components/PathControlPointVisualiser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 3128f46357..ef8a033014 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - if (type.HasValue && type.Value.Type == SplineType.PerfectCurve) + if (type?.Type == SplineType.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 @@ -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.Value.Description, MenuItemType.Standard, _ => + var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); From 6f5c468a83cd0341616d01d10ef5345568a7d943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:21:44 +0900 Subject: [PATCH 32/50] Rename settings class --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 8 ++++---- ...ovider.cs => FreehandSliderSettingsProvider.cs} | 14 +++++++------- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{OsuSliderDrawingSettingsProvider.cs => FreehandSliderSettingsProvider.cs} (98%) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index fb2c1d5149..df8417d8ff 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private OsuSliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + private FreehandSliderSettingsProvider freehandSettingsProvider { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); @@ -80,16 +80,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - if (drawingSettingsProvider != null) + if (freehandSettingsProvider != null) { - drawingSettingsProvider.Tolerance.BindValueChanged(e => + freehandSettingsProvider.Tolerance.BindValueChanged(e => { if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }); - drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + freehandSettingsProvider.CornerThreshold.BindValueChanged(e => { if (bSplineBuilder.CornerThreshold != e.NewValue) bSplineBuilder.CornerThreshold = e.NewValue; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs similarity index 98% rename from osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs rename to osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs index 7e72eee510..9ad2b5d0f5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable + public partial class FreehandSliderSettingsProvider : Drawable { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { @@ -19,12 +19,6 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(40) - { - MinValue = 5, - MaxValue = 100 - }; - public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f) { MinValue = 0.05f, @@ -32,6 +26,12 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; + private readonly BindableInt sliderTolerance = new BindableInt(40) + { + MinValue = 5, + MaxValue = 100 + }; + private readonly BindableInt sliderCornerThreshold = new BindableInt(40) { MinValue = 5, diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0dc0d6fd31..7bf1a12149 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); + protected readonly FreehandSliderSettingsProvider FreehandSliderSettingsProvider = new FreehandSliderSettingsProvider(); [BackgroundDependencyLoader] private void load() @@ -102,8 +102,8 @@ namespace osu.Game.Rulesets.Osu.Edit RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); - AddInternal(SliderDrawingSettingsProvider); - SliderDrawingSettingsProvider.AttachToToolbox(RightToolbox); + AddInternal(FreehandSliderSettingsProvider); + FreehandSliderSettingsProvider.AttachToToolbox(RightToolbox); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From 638c8f1adc2e135aff574e135c515adfbf384423 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:25:23 +0900 Subject: [PATCH 33/50] Get rid of weird cruft and non-standard flow --- .../Edit/FreehandSliderSettingsProvider.cs | 66 ++++++++++--------- .../Edit/OsuHitObjectComposer.cs | 5 +- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs index 9ad2b5d0f5..f4ee130938 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs @@ -2,6 +2,7 @@ // 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.Framework.Utils; @@ -10,8 +11,13 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class FreehandSliderSettingsProvider : Drawable + public partial class FreehandSliderSettingsProvider : EditorToolboxGroup { + public FreehandSliderSettingsProvider() + : base("slider") + { + } + public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { MinValue = 0.05f, @@ -41,6 +47,34 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; private ExpandableSlider cornerThresholdSlider = null!; + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + toleranceSlider = new ExpandableSlider + { + Current = sliderTolerance + }, + cornerThresholdSlider = new ExpandableSlider + { + Current = sliderCornerThreshold + } + }; + + sliderTolerance.BindValueChanged(e => + { + toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; + }, true); + + sliderCornerThreshold.BindValueChanged(e => + { + cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; + }, true); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -70,35 +104,5 @@ namespace osu.Game.Rulesets.Osu.Edit sliderCornerThreshold.Value = newValue; }); } - - public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) - { - toolboxContainer.Add(new EditorToolboxGroup("slider") - { - Children = new Drawable[] - { - toleranceSlider = new ExpandableSlider - { - Current = sliderTolerance - }, - cornerThresholdSlider = new ExpandableSlider - { - Current = sliderCornerThreshold - } - } - }); - - sliderTolerance.BindValueChanged(e => - { - toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; - }, true); - - sliderCornerThreshold.BindValueChanged(e => - { - cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; - cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; - }, true); - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 7bf1a12149..06584ef17a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly FreehandSliderSettingsProvider FreehandSliderSettingsProvider = new FreehandSliderSettingsProvider(); + protected readonly FreehandSliderSettingsProvider FreehandlSliderToolboxGroup = new FreehandSliderSettingsProvider(); [BackgroundDependencyLoader] private void load() @@ -102,8 +102,7 @@ namespace osu.Game.Rulesets.Osu.Edit RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); - AddInternal(FreehandSliderSettingsProvider); - FreehandSliderSettingsProvider.AttachToToolbox(RightToolbox); + RightToolbox.Add(FreehandlSliderToolboxGroup); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From e9f371a5811b72763a50fc1e5fffba5ef95e081f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 09:59:49 +0900 Subject: [PATCH 34/50] Refactor slider settings class --- .../Sliders/SliderPlacementBlueprint.cs | 8 +- ...vider.cs => FreehandSliderToolboxGroup.cs} | 74 +++++++++---------- .../Edit/OsuHitObjectComposer.cs | 2 +- 3 files changed, 38 insertions(+), 46 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{FreehandSliderSettingsProvider.cs => FreehandSliderToolboxGroup.cs} (53%) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index df8417d8ff..f8b7642643 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private FreehandSliderSettingsProvider freehandSettingsProvider { get; set; } + private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); @@ -80,16 +80,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - if (freehandSettingsProvider != null) + if (freehandToolboxGroup != null) { - freehandSettingsProvider.Tolerance.BindValueChanged(e => + freehandToolboxGroup.Tolerance.BindValueChanged(e => { if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }); - freehandSettingsProvider.CornerThreshold.BindValueChanged(e => + freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { if (bSplineBuilder.CornerThreshold != e.NewValue) bSplineBuilder.CornerThreshold = e.NewValue; diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs similarity index 53% rename from osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs rename to osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index f4ee130938..1974415d30 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -5,15 +5,14 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class FreehandSliderSettingsProvider : EditorToolboxGroup + public partial class FreehandSliderToolboxGroup : EditorToolboxGroup { - public FreehandSliderSettingsProvider() + public FreehandSliderToolboxGroup() : base("slider") { } @@ -32,13 +31,14 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(40) + // 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 sliderCornerThreshold = new BindableInt(40) + private readonly BindableInt displayCornerThreshold = new BindableInt(40) { MinValue = 5, MaxValue = 100 @@ -54,55 +54,47 @@ namespace osu.Game.Rulesets.Osu.Edit { toleranceSlider = new ExpandableSlider { - Current = sliderTolerance + Current = displayTolerance }, cornerThresholdSlider = new ExpandableSlider { - Current = sliderCornerThreshold + Current = displayCornerThreshold } }; - - sliderTolerance.BindValueChanged(e => - { - toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; - }, true); - - sliderCornerThreshold.BindValueChanged(e => - { - cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; - cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; - }, true); } protected override void LoadComplete() { base.LoadComplete(); - sliderTolerance.BindValueChanged(v => + displayTolerance.BindValueChanged(tolerance => { - float newValue = v.NewValue / 33f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value)) - Tolerance.Value = newValue; - }); - Tolerance.BindValueChanged(v => + 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 => { - int newValue = (int)Math.Round(v.NewValue * 33f); - if (sliderTolerance.Value != newValue) - sliderTolerance.Value = newValue; - }); - sliderCornerThreshold.BindValueChanged(v => - { - float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, CornerThreshold.Value)) - CornerThreshold.Value = newValue; - }); - CornerThreshold.BindValueChanged(v => - { - int newValue = (int)Math.Round(v.NewValue * 100f); - if (sliderCornerThreshold.Value != newValue) - sliderCornerThreshold.Value = newValue; - }); + 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); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 06584ef17a..061f72d72f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly FreehandSliderSettingsProvider FreehandlSliderToolboxGroup = new FreehandSliderSettingsProvider(); + protected readonly FreehandSliderToolboxGroup FreehandlSliderToolboxGroup = new FreehandSliderToolboxGroup(); [BackgroundDependencyLoader] private void load() From 3680024e3102d5dcca617371105d4c3bf3787945 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 11:15:00 +0900 Subject: [PATCH 35/50] Fix tolerance not being transferred to blueprint in all cases --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index f8b7642643..5f072eb171 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); - }); + }, true); freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { From 314a7bf6f1058195ecf5995fc18a5e5bc1eb94fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 11:33:05 +0900 Subject: [PATCH 36/50] Simplify `AddOnce` call to avoid `self` argument --- .../Sliders/SliderPlacementBlueprint.cs | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 5f072eb171..32fb7ad351 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -3,10 +3,12 @@ #nullable disable +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -84,16 +86,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { freehandToolboxGroup.Tolerance.BindValueChanged(e => { - if (bSplineBuilder.Tolerance != e.NewValue) - bSplineBuilder.Tolerance = e.NewValue; - updateSliderPathFromBSplineBuilder(); + bSplineBuilder.Tolerance = e.NewValue; + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); }, true); freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { - if (bSplineBuilder.CornerThreshold != e.NewValue) - bSplineBuilder.CornerThreshold = e.NewValue; - updateSliderPathFromBSplineBuilder(); + bSplineBuilder.CornerThreshold = e.NewValue; + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); }, true); } } @@ -197,27 +197,24 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnDrag(e); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); - updateSliderPathFromBSplineBuilder(); + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); } private void updateSliderPathFromBSplineBuilder() { - Scheduler.AddOnce(static self => - { - var cps = self.bSplineBuilder.ControlPoints; - var sliderCps = self.HitObject.Path.ControlPoints; - sliderCps.RemoveRange(1, sliderCps.Count - 1); + IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; + BindableList sliderPoints = HitObject.Path.ControlPoints; - // Add the control points from the BSpline builder while converting control points that repeat - // three or more times to a single PathControlPoint with linear type. - for (int i = 1; i < cps.Count; i++) - { - bool isSharp = i < cps.Count - 2 && cps[i] == cps[i + 1] && cps[i] == cps[i + 2]; - sliderCps.Add(new PathControlPoint(cps[i], isSharp ? PathType.BSpline(3) : null)); - if (isSharp) - i += 2; - } - }, this); + sliderPoints.RemoveRange(1, sliderPoints.Count - 1); + + // Add the control points from the BSpline builder while converting control points that repeat + // three or more times to a single PathControlPoint with linear type. + for (int i = 1; i < builderPoints.Count; i++) + { + bool isSharp = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + sliderPoints.Add(new PathControlPoint(builderPoints[i], isSharp ? PathType.BSpline(3) : null)); + if (isSharp) i += 2; + } } protected override void OnDragEnd(DragEndEvent e) From 0a5444d091de73c2cc9fe8360146248d1b06b39e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:05:31 +0900 Subject: [PATCH 37/50] Fix using the incorrect position for the first point --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 32fb7ad351..68f5b2d70f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return false; bSplineBuilder.Clear(); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); setState(SliderPlacementState.Drawing); return true; } From e69e78ad4123a4d89915aa44286a8e637e49429d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:10:44 +0900 Subject: [PATCH 38/50] Refactor b-spline path conversion code to better handle linear segments --- .../Sliders/SliderPlacementBlueprint.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 68f5b2d70f..6e9cc26af4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -203,17 +202,44 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSliderPathFromBSplineBuilder() { IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; - BindableList sliderPoints = HitObject.Path.ControlPoints; - sliderPoints.RemoveRange(1, sliderPoints.Count - 1); + if (builderPoints.Count == 0) + return; - // Add the control points from the BSpline builder while converting control points that repeat - // three or more times to a single PathControlPoint with linear type. - for (int i = 1; i < builderPoints.Count; i++) + 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 isSharp = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; - sliderPoints.Add(new PathControlPoint(builderPoints[i], isSharp ? PathType.BSpline(3) : null)); - if (isSharp) i += 2; + 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; + } } } From 5175464c18dafc3931b723ae06dbb4375c7c0a77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:35:04 +0900 Subject: [PATCH 39/50] Update test coverage (and add test coverage of curve drawing) --- .../TestSceneSliderPlacementBlueprint.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index c7a21ba689..8498263138 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Rulesets.Edit; @@ -274,7 +275,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor } [Test] - public void TestSliderDrawing() + 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)); @@ -291,7 +315,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(600, tolerance: 10); assertControlPointCount(4); - assertControlPointType(0, PathType.BSpline(3)); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, null); + assertControlPointType(2, null); + assertControlPointType(3, null); } [Test] @@ -401,11 +428,11 @@ 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, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => Precision.AlmostEquals(expected, getSlider().Distance, tolerance)); + 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)); From 7bedbe42642384a20b681220b507fcc737325c1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:36:06 +0900 Subject: [PATCH 40/50] Apply NRT to `SliderPlacementBlueprint` tests --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 8498263138..75778b3a7e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . 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; @@ -428,16 +426,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, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider().Distance, () => Is.EqualTo(expected).Within(tolerance)); + 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, () => Is.EqualTo(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?.ToString() ?? "inherit"}", () => getSlider().Path.ControlPoints[index].Type, () => Is.EqualTo(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(); From 9c3f9db31838295cccc0ea4f771933dbb4fa2dc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:02:08 +0900 Subject: [PATCH 41/50] Add failing test coverage of BSpline encoding parse failure --- .../Formats/LegacyBeatmapEncoderTest.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 0dd277dc89..e847b61fbe 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -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() { From 3d094f84add9f669b2bf0e1bd41bd83da4fd01fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:02:15 +0900 Subject: [PATCH 42/50] Fix incorrect parsing of BSpline curve types --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 411a9b0d63..f9e32fe26f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -273,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. From ba6fbbe43c122413c4b4b81eb9450e77f3361f8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:03:45 +0900 Subject: [PATCH 43/50] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8175869405..aa993485f3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index a5425ba4c7..b43cb1b3f1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 92728ea56460dfe0c2135d9165ee8d35e49fe8ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:10:48 +0900 Subject: [PATCH 44/50] Simplify toolbox initialisation --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 061f72d72f..65acdb61ae 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -97,12 +97,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.Add(FreehandlSliderToolboxGroup); + RightToolbox.AddRange(new EditorToolboxGroup[] + { + new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }, + FreehandlSliderToolboxGroup + } + ); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From cf6f66b84f729a71e04a34b7d9acf9091dfbd7cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:37:06 +0900 Subject: [PATCH 45/50] Remove redundant `Clear()` call --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 6e9cc26af4..32397330c6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -178,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) return false; - bSplineBuilder.Clear(); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); setState(SliderPlacementState.Drawing); return true; From 016de7be6a9c0a5709e966b6f2bb45349d8f38e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:51:09 +0900 Subject: [PATCH 46/50] Simplify drag handling code in `SliderPlacementBlueprint` --- .../Sliders/SliderPlacementBlueprint.cs | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 32397330c6..bb4558171f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders controlPointVisualiser = new PathControlPointVisualiser(HitObject, false) }; - setState(SliderPlacementState.Initial); + state = SliderPlacementState.Initial; } protected override void LoadComplete() @@ -164,38 +164,30 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDragStart(DragStartEvent e) { - if (e.Button == MouseButton.Left) - { - switch (state) - { - case SliderPlacementState.Initial: - return true; + if (e.Button != MouseButton.Left) + return base.OnDragStart(e); - case SliderPlacementState.ControlPoints: - if (HitObject.Path.ControlPoints.Count < 3) - { - var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); - if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) - return false; + if (state != SliderPlacementState.ControlPoints) + return base.OnDragStart(e); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); - setState(SliderPlacementState.Drawing); - return true; - } + // Only enter drawing mode if no additional control points have been placed. + if (HitObject.Path.ControlPoints.Count > 2) + return base.OnDragStart(e); - return false; - } - } - - 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); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); - Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); + if (state == SliderPlacementState.Drawing) + { + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); + } } private void updateSliderPathFromBSplineBuilder() @@ -260,7 +252,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void beginCurve() { BeginPlacement(commitStart: true); - setState(SliderPlacementState.ControlPoints); + state = SliderPlacementState.ControlPoints; } private void endCurve() @@ -357,11 +349,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders tailCirclePiece.UpdateFrom(HitObject.TailCircle); } - private void setState(SliderPlacementState newState) - { - state = newState; - } - private enum SliderPlacementState { Initial, From a210469956ca340bb302f76679c49142e2e0ae69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:52:21 +0900 Subject: [PATCH 47/50] Reorder methods --- .../Sliders/SliderPlacementBlueprint.cs | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index bb4558171f..397d6ffb91 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -190,50 +190,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } } - private void updateSliderPathFromBSplineBuilder() - { - IReadOnlyList 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; - } - } - } - protected override void OnDragEnd(DragEndEvent e) { base.OnDragEnd(e); @@ -249,6 +205,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnMouseUp(e); } + protected override void Update() + { + base.Update(); + updateSlider(); + + // Maintain the path type in case it got defaulted to bezier at some point during the drag. + updatePathType(); + } + private void beginCurve() { BeginPlacement(commitStart: true); @@ -261,15 +226,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders EndPlacement(true); } - protected override void Update() - { - base.Update(); - updateSlider(); - - // Maintain the path type in case it got defaulted to bezier at some point during the drag. - updatePathType(); - } - private void updatePathType() { if (state == SliderPlacementState.Drawing) @@ -349,6 +305,50 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders tailCirclePiece.UpdateFrom(HitObject.TailCircle); } + private void updateSliderPathFromBSplineBuilder() + { + IReadOnlyList 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, From 1660eb3c15b1075e93a64a2938e8e86451f5df84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:31:24 +0900 Subject: [PATCH 48/50] Add failing test coverage of drag after point placement --- .../TestSceneSliderPlacementBlueprint.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 75778b3a7e..7ac34bc6c8 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -272,6 +272,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(2, PathType.PERFECT_CURVE); } + [Test] + 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() { From cc33e121258adb0d607fba5c6bf9897e1979eac5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:16:40 +0900 Subject: [PATCH 49/50] Fix dragging after one point already placed incorrectly entering drawing mode --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 397d6ffb91..63370350ed 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -171,7 +171,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return base.OnDragStart(e); // Only enter drawing mode if no additional control points have been placed. - if (HitObject.Path.ControlPoints.Count > 2) + 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); From 4b2d8aa6a647b28c13f63dc3047cca04b8b8c398 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:31:39 +0900 Subject: [PATCH 50/50] Add `ToString` on `PathType` for better test output --- osu.Game/Rulesets/Objects/Types/PathType.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index f84d43e3e7..23f1ccf0bc 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -81,5 +81,7 @@ namespace osu.Game.Rulesets.Objects.Types 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; } }