Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add free-hand drawing of sliders to the editor #25409

Merged
merged 52 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
926636c
Generalize Bezier curves to BSplines of Nth degree
Tom94 Nov 8, 2023
3f85aa7
Add free-hand drawing of sliders to the editor
PM-CS Nov 11, 2023
54b8244
CI Fixup
PM-CS Nov 11, 2023
c24b354
Fix OnDragStart on macOS
Tom94 Nov 13, 2023
e6b4dfb
Fix doubled control point at beginning of drawn slider
Tom94 Nov 13, 2023
8ad8764
Update framework
peppy Nov 13, 2023
fa976a5
Fix code style/quality issues
PM-CS Nov 13, 2023
90ec689
Automatic red control point generation & corner threshold
Tom94 Nov 14, 2023
ceeaf5b
CI fixes and small tweaks
PM-CS Nov 15, 2023
5206429
Fix control point hover text and context menu
PM-CS Nov 15, 2023
360864f
Hide catmull curve type when possible
PM-CS Nov 15, 2023
b6e3e42
Merge branch 'master' into bspline-sliders
bdach Nov 20, 2023
33b592f
Update framework (again)
bdach Nov 20, 2023
577cb99
Move static instances / construction methods closer together
bdach Nov 20, 2023
25c1a90
Change switchexpr to standard switch statement
bdach Nov 20, 2023
7820c8c
Decrease redundancy of equality implementations
bdach Nov 20, 2023
518dcc5
Null-check `drawingSettingsProvider`
bdach Nov 20, 2023
f46945a
Avoid one unnecessary path update from B-spline builder
bdach Nov 20, 2023
831884a
Remove unused enum member
bdach Nov 20, 2023
affef85
Remove `ISliderDrawingSettingsProvider`
bdach Nov 20, 2023
5d1bac6
Remove `IToolboxAttachment` for now
bdach Nov 20, 2023
487326a
Remove pattern matching syntax usage in switch
bdach Nov 20, 2023
80a3225
Use static `BEZIER` instead of allocating new every time
bdach Nov 20, 2023
6d7d826
Fix incorrect legacy conversion when B-splines are used
bdach Nov 20, 2023
46d4587
Add test for slider drawing
bdach Nov 20, 2023
5f30266
Remove test terminally broken by introduction of slider drawing
bdach Nov 20, 2023
8e39dbb
Adjust casing of curve type menu items
bdach Nov 20, 2023
43dbd65
Show Catmull as a control point type option if selection already cont…
bdach Nov 20, 2023
4061417
Decrease default value for slider tolerance
bdach Nov 20, 2023
7633c92
Merge branch 'master' into bspline-sliders
peppy Nov 20, 2023
ded9981
Update framework
peppy Nov 20, 2023
492fd06
Remove unnecessary null override
peppy Nov 20, 2023
750bbc8
Simplify null checks
peppy Nov 20, 2023
6f5c468
Rename settings class
peppy Nov 20, 2023
638c8f1
Get rid of weird cruft and non-standard flow
peppy Nov 20, 2023
e9f371a
Refactor slider settings class
peppy Nov 21, 2023
3680024
Fix tolerance not being transferred to blueprint in all cases
peppy Nov 21, 2023
314a7bf
Simplify `AddOnce` call to avoid `self` argument
peppy Nov 21, 2023
0a5444d
Fix using the incorrect position for the first point
peppy Nov 21, 2023
e69e78a
Refactor b-spline path conversion code to better handle linear segments
peppy Nov 21, 2023
5175464
Update test coverage (and add test coverage of curve drawing)
peppy Nov 21, 2023
7bedbe4
Apply NRT to `SliderPlacementBlueprint` tests
peppy Nov 21, 2023
9c3f9db
Add failing test coverage of BSpline encoding parse failure
peppy Nov 21, 2023
3d094f8
Fix incorrect parsing of BSpline curve types
peppy Nov 21, 2023
ba6fbbe
Update framework
peppy Nov 21, 2023
92728ea
Simplify toolbox initialisation
peppy Nov 21, 2023
cf6f66b
Remove redundant `Clear()` call
peppy Nov 21, 2023
016de7b
Simplify drag handling code in `SliderPlacementBlueprint`
peppy Nov 21, 2023
a210469
Reorder methods
peppy Nov 21, 2023
1660eb3
Add failing test coverage of drag after point placement
peppy Nov 21, 2023
cc33e12
Fix dragging after one point already placed incorrectly entering draw…
peppy Nov 21, 2023
4b2d8aa
Add `ToString` on `PathType` for better test output
peppy Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void TestUpdateFromHitObject()

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),
Expand Down Expand Up @@ -190,16 +190,16 @@ public void TestDeleteVertex()
[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", () =>
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public void TestRandomConvertFromSliderPath(double velocity)
} 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)
Expand Down Expand Up @@ -215,7 +215,7 @@ public void TestRandomConvertToSliderPath()

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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void TestJuiceStream(bool shouldMiss)
var stream = new JuiceStream
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(100, 0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void TestModRelax() => CreateModTest(new ModTestData
{
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 })
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
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)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void TestJuiceStream()
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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void spawnJuiceStream(bool hit = false)
{
X = xCoords,
StartTime = playfieldTime + 1000,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(0, 200)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void TestJuiceStreamEndingCombo()
new JuiceStream
{
X = CatchPlayfield.CENTER_X,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(0, 100)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void InitializeFromHitObject(JuiceStream hitObject)
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.
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public void ConvertToSliderPath(SliderPath sliderPath, float sliderStartY, doubl

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void TestSliderInCenter()
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))
}),
}
Expand All @@ -128,7 +128,7 @@ public void TestSliderNearEdge()
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))
}),
}
Expand All @@ -149,7 +149,7 @@ public void TestSliderNearEdgeStackedOffscreen()
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
Expand All @@ -171,7 +171,7 @@ public void TestSliderOffscreenStart()
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)
}),
}
Expand All @@ -192,7 +192,7 @@ public void TestSliderOffscreenEnd()
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)
}),
}
Expand All @@ -214,7 +214,7 @@ public void TestSliderOffscreenPath()
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))
}),
Expand Down
10 changes: 5 additions & 5 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void TestSimpleMerge()
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());
Expand Down Expand Up @@ -73,11 +73,11 @@ public void TestMergeCircleSlider()

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);
Expand Down Expand Up @@ -172,7 +172,7 @@ public void TestNonMerge()
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);
Expand Down Expand Up @@ -227,7 +227,7 @@ public void TestSameStartTimeMerge()
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)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void TestSelectDoesNotModify()

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))
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void TestPerfectCurveTooManyPoints()
{
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));
Expand All @@ -63,17 +63,17 @@ public void TestPerfectCurveTooManyPoints()
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]
public void TestPerfectCurveLastThreePoints()
{
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));
Expand All @@ -83,8 +83,8 @@ public void TestPerfectCurveLastThreePoints()
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);
}

Expand All @@ -93,7 +93,7 @@ public void TestPerfectCurveLastTwoPoints()
{
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));
Expand All @@ -103,7 +103,7 @@ public void TestPerfectCurveLastTwoPoints()
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);
}

Expand All @@ -112,7 +112,7 @@ public void TestPerfectCurveTooManyPointsLinear()
{
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));
Expand All @@ -123,28 +123,28 @@ public void TestPerfectCurveTooManyPointsLinear()
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]
public void TestPerfectCurveChangeToBezier()
{
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);
}

Expand Down
Loading