From 1776485b93eeb2ff0b7e5ccf1657aa5704dac997 Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 7 Dec 2022 20:20:11 +0000
Subject: [PATCH 1/4] Reflect nested objects vertically in the playfield
correctly
---
osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
index f565456911..5b327fc3b0 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
@@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Utils
if (osuObject is not Slider slider)
return;
- void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(nested.X, slider.Y - (nested.Y - slider.Y));
+ void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(nested.Position.X, OsuPlayfield.BASE_SIZE.Y - nested.Position.Y);
static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(point.Position.X, -point.Position.Y);
modifySlider(slider, flipNestedObject, flipControlPoint);
From 684b16cef5dc7338ef3333106e3433de8085153f Mon Sep 17 00:00:00 2001
From: apollo-dw <83023433+apollo-dw@users.noreply.github.com>
Date: Wed, 7 Dec 2022 21:09:53 +0000
Subject: [PATCH 2/4] Disambiguate object flipping and reflection methods
---
osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 2 +-
osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs | 8 ++---
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +-
.../Utils/OsuHitObjectGenerationUtils.cs | 32 +++++++++++--------
4 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
index 5430929143..19d4a1bf83 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
var osuObject = (OsuHitObject)hitObject;
- OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
+ OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
index 0a54d58718..6d01808fb5 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModMirror.cs
@@ -27,16 +27,16 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (Reflection.Value)
{
case MirrorType.Horizontal:
- OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
+ OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(osuObject);
break;
case MirrorType.Vertical:
- OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
+ OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
break;
case MirrorType.Both:
- OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
- OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
+ OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(osuObject);
+ OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
break;
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
index 58f5b2fa8d..307d731fd4 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Mods
if (positionInfos[i].HitObject is Slider slider && random.NextDouble() < 0.5)
{
- OsuHitObjectGenerationUtils.FlipSliderHorizontally(slider);
+ OsuHitObjectGenerationUtils.FlipSliderInPlaceHorizontally(slider);
}
if (i == 0)
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
index 5b327fc3b0..15bc03261f 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
@@ -112,21 +112,24 @@ namespace osu.Game.Rulesets.Osu.Utils
/// Reflects the position of the in the playfield horizontally.
///
/// The object to reflect.
- public static void ReflectHorizontally(OsuHitObject osuObject)
+ public static void ReflectHorizontallyAlongPlayfield(OsuHitObject osuObject)
{
osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y);
if (osuObject is not Slider slider)
return;
- FlipSliderHorizontally(slider);
+ void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - nested.Position.X, nested.Position.Y);
+ static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
+
+ modifySlider(slider, flipNestedObject, flipControlPoint);
}
///
/// Reflects the position of the in the playfield vertically.
///
/// The object to reflect.
- public static void ReflectVertically(OsuHitObject osuObject)
+ public static void ReflectVerticallyAlongPlayfield(OsuHitObject osuObject)
{
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
@@ -139,6 +142,18 @@ namespace osu.Game.Rulesets.Osu.Utils
modifySlider(slider, flipNestedObject, flipControlPoint);
}
+ ///
+ /// Flips the position of the around its start position horizontally.
+ ///
+ /// The slider to be flipped.
+ public static void FlipSliderInPlaceHorizontally(Slider slider)
+ {
+ void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(slider.X - (nested.X - slider.X), nested.Y);
+ static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
+
+ modifySlider(slider, flipNestedObject, flipControlPoint);
+ }
+
///
/// Rotate a slider about its start position by the specified angle.
///
@@ -152,17 +167,6 @@ namespace osu.Game.Rulesets.Osu.Utils
modifySlider(slider, rotateNestedObject, rotateControlPoint);
}
- ///
- /// Flips the slider about its start position horizontally.
- ///
- public static void FlipSliderHorizontally(Slider slider)
- {
- void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(slider.X - (nested.X - slider.X), nested.Y);
- static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
-
- modifySlider(slider, flipNestedObject, flipControlPoint);
- }
-
private static void modifySlider(Slider slider, Action modifyNestedObject, Action modifyControlPoint)
{
// No need to update the head and tail circles, since slider handles that when the new slider path is set
From 7676838cc04d162c37d87749d275b582cbed8e50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 7 Dec 2022 23:27:02 +0100
Subject: [PATCH 3/4] Apply "reflect" vernacular in nested methods
---
.../Utils/OsuHitObjectGenerationUtils.cs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
index 15bc03261f..aa4cd0af14 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
@@ -119,10 +119,10 @@ namespace osu.Game.Rulesets.Osu.Utils
if (osuObject is not Slider slider)
return;
- void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - nested.Position.X, nested.Position.Y);
- static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
+ void reflectNestedObject(OsuHitObject nested) => nested.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - nested.Position.X, nested.Position.Y);
+ static void reflectControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
- modifySlider(slider, flipNestedObject, flipControlPoint);
+ modifySlider(slider, reflectNestedObject, reflectControlPoint);
}
///
@@ -136,10 +136,10 @@ namespace osu.Game.Rulesets.Osu.Utils
if (osuObject is not Slider slider)
return;
- void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(nested.Position.X, OsuPlayfield.BASE_SIZE.Y - nested.Position.Y);
- static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(point.Position.X, -point.Position.Y);
+ void reflectNestedObject(OsuHitObject nested) => nested.Position = new Vector2(nested.Position.X, OsuPlayfield.BASE_SIZE.Y - nested.Position.Y);
+ static void reflectControlPoint(PathControlPoint point) => point.Position = new Vector2(point.Position.X, -point.Position.Y);
- modifySlider(slider, flipNestedObject, flipControlPoint);
+ modifySlider(slider, reflectNestedObject, reflectControlPoint);
}
///
From 5807d4c43dcc2dfd7f8b9b61e277b2dc35c862da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?=
Date: Wed, 7 Dec 2022 23:50:01 +0100
Subject: [PATCH 4/4] Add test coverage for slider transformations
---
.../OsuHitObjectGenerationUtilsTest.cs | 91 +++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100644 osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs
new file mode 100644
index 0000000000..daa914cac2
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs
@@ -0,0 +1,91 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+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.Objects;
+using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.Osu.Utils;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ [TestFixture]
+ public class OsuHitObjectGenerationUtilsTest
+ {
+ private static Slider createTestSlider()
+ {
+ var slider = new Slider
+ {
+ Position = new Vector2(128, 128),
+ Path = new SliderPath
+ {
+ 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)
+ }
+ },
+ RepeatCount = 1
+ };
+ slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ return slider;
+ }
+
+ [Test]
+ public void TestReflectSliderHorizontallyAlongPlayfield()
+ {
+ var slider = createTestSlider();
+
+ OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(slider);
+
+ Assert.That(slider.Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 128, 128)));
+ Assert.That(slider.NestedHitObjects.OfType().Single().Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 0, 128)));
+ Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
+ {
+ new Vector2(),
+ new Vector2(64, -128),
+ new Vector2(128, 0)
+ }));
+ }
+
+ [Test]
+ public void TestReflectSliderVerticallyAlongPlayfield()
+ {
+ var slider = createTestSlider();
+
+ OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(slider);
+
+ Assert.That(slider.Position, Is.EqualTo(new Vector2(128, OsuPlayfield.BASE_SIZE.Y - 128)));
+ Assert.That(slider.NestedHitObjects.OfType().Single().Position, Is.EqualTo(new Vector2(0, OsuPlayfield.BASE_SIZE.Y - 128)));
+ Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
+ {
+ new Vector2(),
+ new Vector2(-64, 128),
+ new Vector2(-128, 0)
+ }));
+ }
+
+ [Test]
+ public void TestFlipSliderInPlaceHorizontally()
+ {
+ var slider = createTestSlider();
+
+ OsuHitObjectGenerationUtils.FlipSliderInPlaceHorizontally(slider);
+
+ Assert.That(slider.Position, Is.EqualTo(new Vector2(128, 128)));
+ Assert.That(slider.NestedHitObjects.OfType().Single().Position, Is.EqualTo(new Vector2(256, 128)));
+ Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
+ {
+ new Vector2(),
+ new Vector2(64, -128),
+ new Vector2(128, 0)
+ }));
+ }
+ }
+}