From 725edfcbf35bdbdca7f1e277722ed7d749e5f8f2 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 09:05:35 +0200
Subject: [PATCH 1/9] Add path type menu change method
---
.../Components/PathControlPointVisualiser.cs | 14 +++++++++++++-
1 file changed, 13 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 ce5dc4855e..ff2e4ea152 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -153,6 +153,18 @@ private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
}
}
+ ///
+ /// Attempts to set the given control point piece to the given path type.
+ /// If that would fail, try to change the path such that it instead succeeds
+ /// in a UX-friendly way.
+ ///
+ /// The control point piece that we want to change the path type of.
+ /// The path type we want to assign to the given control point piece.
+ private void updatePathType(PathControlPointPiece piece, PathType? type)
+ {
+ piece.ControlPoint.Type.Value = type;
+ }
+
[Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; }
@@ -218,7 +230,7 @@ private MenuItem createMenuItemForPathType(PathType? type)
var item = new PathTypeMenuItem(type, () =>
{
foreach (var p in Pieces.Where(p => p.IsSelected.Value))
- p.ControlPoint.Type.Value = type;
+ updatePathType(p, type);
});
if (countOfState == totalCount)
From 0341023d13cf7926a04d67852ec995874e10e79f Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 09:06:28 +0200
Subject: [PATCH 2/9] Improve UX of selecting PerfectCurve
---
.../Components/PathControlPointVisualiser.cs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
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 ff2e4ea152..0b163cbc44 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -162,6 +162,22 @@ private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
/// The path type we want to assign to the given control point piece.
private void updatePathType(PathControlPointPiece piece, PathType? type)
{
+ int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
+
+ switch (type)
+ {
+ case PathType.PerfectCurve:
+ if (piece.PointsInSegment.Count > 3)
+ {
+ // 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 bezier segment.
+ piece.PointsInSegment[indexInSegment + 2].Type.Value = PathType.Bezier;
+ }
+
+ break;
+ }
+
piece.ControlPoint.Type.Value = type;
}
From be4520fe33ff6ec67367ef207a59a00fa47b8d61 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 11:46:00 +0200
Subject: [PATCH 3/9] Fix index out of range possibility
---
.../Components/PathControlPointVisualiser.cs | 14 +++++++-------
1 file changed, 7 insertions(+), 7 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 0b163cbc44..1dfbf97682 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -167,13 +167,13 @@ private void updatePathType(PathControlPointPiece piece, PathType? type)
switch (type)
{
case PathType.PerfectCurve:
- if (piece.PointsInSegment.Count > 3)
- {
- // 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 bezier segment.
- piece.PointsInSegment[indexInSegment + 2].Type.Value = PathType.Bezier;
- }
+ // 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 bezier segment.
+ int thirdPointIndex = indexInSegment + 2;
+
+ if (piece.PointsInSegment.Count > thirdPointIndex + 1)
+ piece.PointsInSegment[thirdPointIndex].Type.Value = PathType.Bezier;
break;
}
From 4110d1675d001919dca028a51f5e92b19eb5d611 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 11:46:52 +0200
Subject: [PATCH 4/9] Add path type menu test cases
---
.../TestScenePathControlPointVisualiser.cs | 101 +++++++++++++++++-
1 file changed, 99 insertions(+), 2 deletions(-)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
index 738a21b17e..3e2d1cff0e 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
@@ -4,9 +4,11 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Visual;
@@ -14,7 +16,7 @@
namespace osu.Game.Rulesets.Osu.Tests.Editor
{
- public class TestScenePathControlPointVisualiser : OsuTestScene
+ public class TestScenePathControlPointVisualiser : OsuManualInputManagerTestScene
{
private Slider slider;
private PathControlPointVisualiser visualiser;
@@ -43,12 +45,107 @@ public void TestAddOverlappingControlPoints()
});
}
+ [Test]
+ public void TestPerfectCurveTooManyPoints()
+ {
+ createVisualiser(true);
+
+ addControlPointStep(new Vector2(200), PathType.Bezier);
+ addControlPointStep(new Vector2(300));
+ addControlPointStep(new Vector2(500, 300));
+ addControlPointStep(new Vector2(700, 200));
+ addControlPointStep(new Vector2(500, 100));
+
+ // Must be both hovering and selecting the control point for the context menu to work.
+ moveMouseToControlPoint(1);
+ AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
+ addContextMenuItemStep("Perfect curve");
+
+ assertControlPointPathType(0, PathType.Bezier);
+ assertControlPointPathType(1, PathType.PerfectCurve);
+ AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
+ }
+
+ [Test]
+ public void TestPerfectCurveLastThreePoints()
+ {
+ createVisualiser(true);
+
+ addControlPointStep(new Vector2(200), PathType.Bezier);
+ addControlPointStep(new Vector2(300));
+ addControlPointStep(new Vector2(500, 300));
+ addControlPointStep(new Vector2(700, 200));
+ addControlPointStep(new Vector2(500, 100));
+
+ moveMouseToControlPoint(2);
+ AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true);
+ addContextMenuItemStep("Perfect curve");
+
+ assertControlPointPathType(0, PathType.Bezier);
+ assertControlPointPathType(2, PathType.PerfectCurve);
+ assertControlPointPathType(4, null);
+ }
+
+ [Test]
+ public void TestPerfectCurveLastTwoPoints()
+ {
+ createVisualiser(true);
+
+ addControlPointStep(new Vector2(200), PathType.Bezier);
+ addControlPointStep(new Vector2(300));
+ addControlPointStep(new Vector2(500, 300));
+ addControlPointStep(new Vector2(700, 200));
+ addControlPointStep(new Vector2(500, 100));
+
+ moveMouseToControlPoint(3);
+ AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
+ addContextMenuItemStep("Perfect curve");
+
+ assertControlPointPathType(0, PathType.Bezier);
+ AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
+ }
+
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
});
- private void addControlPointStep(Vector2 position) => AddStep($"add control point {position}", () => slider.Path.ControlPoints.Add(new PathControlPoint(position)));
+ private void addControlPointStep(Vector2 position) => addControlPointStep(position, null);
+
+ private void addControlPointStep(Vector2 position, PathType? type)
+ {
+ AddStep($"add {type} control point at {position}", () =>
+ {
+ slider.Path.ControlPoints.Add(new PathControlPoint(position, type));
+ });
+ }
+
+ private void moveMouseToControlPoint(int index)
+ {
+ AddStep($"move mouse to control point {index}", () =>
+ {
+ Vector2 position = slider.Path.ControlPoints[index].Position.Value;
+ InputManager.MoveMouseTo(visualiser.Pieces[0].Parent.ToScreenSpace(position));
+ });
+ }
+
+ private void assertControlPointPathType(int controlPointIndex, PathType? type)
+ {
+ AddAssert($"point {controlPointIndex} is {type}", () =>
+ {
+ return slider.Path.ControlPoints[controlPointIndex].Type.Value == type;
+ });
+ }
+
+ private void addContextMenuItemStep(string contextMenuText)
+ {
+ AddStep($"click context menu item \"{contextMenuText}\"", () =>
+ {
+ MenuItem item = visualiser.ContextMenuItems[1].Items.FirstOrDefault(menuItem => menuItem.Text.Value == contextMenuText);
+
+ item?.Action?.Value();
+ });
+ }
}
}
From 7d2b54ca42b5d98ffaf69b5bbaba2cbf5f8059df Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 12:32:45 +0200
Subject: [PATCH 5/9] Add change to Bezier test
---
.../TestScenePathControlPointVisualiser.cs | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
index 3e2d1cff0e..1d5a175a26 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
@@ -105,6 +105,26 @@ public void TestPerfectCurveLastTwoPoints()
AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
}
+ [Test]
+ public void TestPerfectCurveChangeToBezier()
+ {
+ createVisualiser(true);
+
+ 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(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(3, null);
+ }
+
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
{
Anchor = Anchor.Centre,
From 9a675a22195fe0d24576dbffec32285282f3209c Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 12:33:43 +0200
Subject: [PATCH 6/9] Correct 4+ point perfect curves to Bezier
---
.../Blueprints/Sliders/Components/PathControlPointPiece.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 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 20d4fe4ea9..6b78cff33e 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -213,10 +213,13 @@ private void updatePathType()
if (ControlPoint.Type.Value != PathType.PerfectCurve)
return;
- ReadOnlySpan points = PointsInSegment.Select(p => p.Position.Value).ToArray();
- if (points.Length != 3)
+ if (PointsInSegment.Count > 3)
+ ControlPoint.Type.Value = PathType.Bezier;
+
+ if (PointsInSegment.Count != 3)
return;
+ ReadOnlySpan points = PointsInSegment.Select(p => p.Position.Value).ToArray();
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
ControlPoint.Type.Value = PathType.Bezier;
From 2d9448456671c8b7e1966341af66338f45923f85 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Thu, 8 Apr 2021 12:49:46 +0200
Subject: [PATCH 7/9] Use lambda expression
Apparently CI dislikes this not being a lambda.
---
.../Editor/TestScenePathControlPointVisualiser.cs | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
index 1d5a175a26..440ab7c889 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
@@ -152,10 +152,7 @@ private void moveMouseToControlPoint(int index)
private void assertControlPointPathType(int controlPointIndex, PathType? type)
{
- AddAssert($"point {controlPointIndex} is {type}", () =>
- {
- return slider.Path.ControlPoints[controlPointIndex].Type.Value == type;
- });
+ AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type);
}
private void addContextMenuItemStep(string contextMenuText)
From 0af6d77192e3af39430098a2dc35c26cdde55d69 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Fri, 9 Apr 2021 11:03:38 +0200
Subject: [PATCH 8/9] Test for path type transfer
---
.../TestScenePathControlPointVisualiser.cs | 23 ++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
index 440ab7c889..35b79aa8ac 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs
@@ -63,7 +63,7 @@ public void TestPerfectCurveTooManyPoints()
assertControlPointPathType(0, PathType.Bezier);
assertControlPointPathType(1, PathType.PerfectCurve);
- AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
+ assertControlPointPathType(3, PathType.Bezier);
}
[Test]
@@ -105,6 +105,27 @@ public void TestPerfectCurveLastTwoPoints()
AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
}
+ [Test]
+ public void TestPerfectCurveTooManyPointsLinear()
+ {
+ createVisualiser(true);
+
+ addControlPointStep(new Vector2(200), PathType.Linear);
+ addControlPointStep(new Vector2(300));
+ addControlPointStep(new Vector2(500, 300));
+ addControlPointStep(new Vector2(700, 200));
+ addControlPointStep(new Vector2(500, 100));
+
+ // Must be both hovering and selecting the control point for the context menu to work.
+ moveMouseToControlPoint(1);
+ 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);
+ }
+
[Test]
public void TestPerfectCurveChangeToBezier()
{
From f64b2095bf6c06de31b2cc0ca7d9b888dc101347 Mon Sep 17 00:00:00 2001
From: Naxess <30292137+Naxesss@users.noreply.github.com>
Date: Fri, 9 Apr 2021 11:04:00 +0200
Subject: [PATCH 9/9] Carry over the previous path type
---
.../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 1dfbf97682..44c3056910 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -169,11 +169,11 @@ private void updatePathType(PathControlPointPiece piece, PathType? type)
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 bezier segment.
+ // and one segment of the previous type.
int thirdPointIndex = indexInSegment + 2;
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
- piece.PointsInSegment[thirdPointIndex].Type.Value = PathType.Bezier;
+ piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value;
break;
}