2019-10-25 09:19:26 +00:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2024-02-26 10:59:57 +00:00
using System.Linq ;
2019-10-25 09:19:26 +00:00
using NUnit.Framework ;
2020-01-01 15:41:06 +00:00
using osu.Framework.Allocation ;
2020-02-05 08:32:33 +00:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2023-08-19 13:08:30 +00:00
using osu.Framework.Graphics.Cursor ;
2019-10-25 09:19:26 +00:00
using osu.Framework.Testing ;
2022-11-01 06:21:17 +00:00
using osu.Framework.Utils ;
2019-10-25 09:19:26 +00:00
using osu.Game.Beatmaps.ControlPoints ;
2020-01-23 05:39:56 +00:00
using osu.Game.Rulesets.Edit ;
2021-09-01 09:05:10 +00:00
using osu.Game.Rulesets.Objects ;
2019-10-25 09:19:26 +00:00
using osu.Game.Rulesets.Osu ;
2020-01-01 15:41:06 +00:00
using osu.Game.Rulesets.Osu.Beatmaps ;
2019-10-25 09:19:26 +00:00
using osu.Game.Rulesets.Osu.Edit ;
2023-04-26 16:17:02 +00:00
using osu.Game.Rulesets.Osu.Objects ;
2019-10-25 09:19:26 +00:00
using osu.Game.Screens.Edit ;
using osu.Game.Tests.Visual ;
2024-02-26 10:59:57 +00:00
using osuTK ;
2019-10-25 09:19:26 +00:00
2020-04-23 08:07:55 +00:00
namespace osu.Game.Tests.Editing
2019-10-25 09:19:26 +00:00
{
[HeadlessTest]
public partial class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene
{
2022-08-17 06:40:01 +00:00
private TestHitObjectComposer composer = null ! ;
2019-10-25 09:19:26 +00:00
2020-01-01 15:41:06 +00:00
[Cached(typeof(EditorBeatmap))]
2020-01-23 05:39:56 +00:00
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap ;
2023-08-19 13:08:30 +00:00
protected override Container < Drawable > Content { get ; } = new PopoverContainer { RelativeSizeAxes = Axes . Both } ;
2020-02-05 08:32:33 +00:00
2020-01-23 05:39:56 +00:00
public TestSceneHitObjectComposerDistanceSnapping ( )
{
2020-02-05 08:32:33 +00:00
base . Content . Add ( new Container
{
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
2022-01-12 14:17:35 +00:00
editorBeatmap = new EditorBeatmap ( new OsuBeatmap
{
2022-08-17 06:40:01 +00:00
BeatmapInfo = { Ruleset = new OsuRuleset ( ) . RulesetInfo } ,
2022-01-12 14:17:35 +00:00
} ) ,
2022-08-17 06:40:01 +00:00
Content
2020-02-05 08:32:33 +00:00
} ,
} ) ;
2020-01-23 05:39:56 +00:00
}
2020-01-01 15:41:06 +00:00
2019-10-25 09:19:26 +00:00
[SetUp]
public void Setup ( ) = > Schedule ( ( ) = >
{
2024-08-04 15:45:42 +00:00
Child = composer = new TestHitObjectComposer ( ) ;
2019-10-25 09:19:26 +00:00
BeatDivisor . Value = 1 ;
2021-10-02 03:34:29 +00:00
composer . EditorBeatmap . Difficulty . SliderMultiplier = 1 ;
2019-10-28 02:52:04 +00:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 1000 } ) ;
2019-10-25 09:19:26 +00:00
} ) ;
[TestCase(1)]
[TestCase(2)]
public void TestSliderMultiplier ( float multiplier )
{
2022-04-28 03:46:20 +00:00
AddStep ( $"set slider multiplier = {multiplier}" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = multiplier ) ;
2019-10-25 09:19:26 +00:00
2022-11-01 06:13:05 +00:00
assertSnapDistance ( 100 * multiplier , null , true ) ;
2019-10-25 09:19:26 +00:00
}
[TestCase(1)]
[TestCase(2)]
2022-10-21 13:58:10 +00:00
public void TestSpeedMultiplierDoesNotChangeDistanceSnap ( float multiplier )
2019-10-25 09:19:26 +00:00
{
2023-04-26 16:17:02 +00:00
assertSnapDistance ( 100 , new Slider
2019-10-25 09:19:26 +00:00
{
2023-09-06 09:59:15 +00:00
SliderVelocityMultiplier = multiplier
2022-11-01 06:13:05 +00:00
} , false ) ;
}
[TestCase(1)]
[TestCase(2)]
public void TestSpeedMultiplierDoesChangeDistanceSnap ( float multiplier )
{
2023-04-26 16:17:02 +00:00
assertSnapDistance ( 100 * multiplier , new Slider
2022-11-01 06:13:05 +00:00
{
2023-09-06 09:59:15 +00:00
SliderVelocityMultiplier = multiplier
2022-11-01 06:13:05 +00:00
} , true ) ;
2019-10-25 09:19:26 +00:00
}
[TestCase(1)]
[TestCase(2)]
public void TestBeatDivisor ( int divisor )
{
AddStep ( $"set divisor = {divisor}" , ( ) = > BeatDivisor . Value = divisor ) ;
2022-11-01 06:13:05 +00:00
assertSnapDistance ( 100f / divisor , null , true ) ;
2019-10-25 09:19:26 +00:00
}
2022-11-01 06:21:17 +00:00
/// <summary>
/// The basic distance-duration functions should always include slider velocity of the reference object.
/// </summary>
[Test]
public void TestConversionsWithSliderVelocity ( )
{
const float base_distance = 100 ;
const float slider_velocity = 1.2f ;
2023-04-26 16:17:02 +00:00
var referenceObject = new Slider
2022-11-01 06:21:17 +00:00
{
2023-09-06 09:59:15 +00:00
SliderVelocityMultiplier = slider_velocity
2022-11-01 06:21:17 +00:00
} ;
assertSnapDistance ( base_distance * slider_velocity , referenceObject , true ) ;
assertSnappedDistance ( base_distance * slider_velocity + 10 , base_distance * slider_velocity , referenceObject ) ;
assertSnappedDuration ( base_distance * slider_velocity + 10 , 1000 , referenceObject ) ;
2022-11-01 08:14:30 +00:00
assertDistanceToDuration ( base_distance * slider_velocity , 1000 , referenceObject ) ;
assertDurationToDistance ( 1000 , base_distance * slider_velocity , referenceObject ) ;
2022-11-01 06:21:17 +00:00
}
2019-10-25 09:19:26 +00:00
[Test]
public void TestConvertDurationToDistance ( )
{
assertDurationToDistance ( 500 , 50 ) ;
assertDurationToDistance ( 1000 , 100 ) ;
2021-10-02 03:34:29 +00:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 09:19:26 +00:00
assertDurationToDistance ( 500 , 100 ) ;
assertDurationToDistance ( 1000 , 200 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 02:52:04 +00:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 09:19:26 +00:00
} ) ;
assertDurationToDistance ( 500 , 200 ) ;
assertDurationToDistance ( 1000 , 400 ) ;
}
[Test]
public void TestConvertDistanceToDuration ( )
{
assertDistanceToDuration ( 50 , 500 ) ;
assertDistanceToDuration ( 100 , 1000 ) ;
2021-10-02 03:34:29 +00:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 09:19:26 +00:00
assertDistanceToDuration ( 100 , 500 ) ;
assertDistanceToDuration ( 200 , 1000 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 02:52:04 +00:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 09:19:26 +00:00
} ) ;
assertDistanceToDuration ( 200 , 500 ) ;
assertDistanceToDuration ( 400 , 1000 ) ;
}
[Test]
public void TestGetSnappedDurationFromDistance ( )
{
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 0 , 0 ) ;
assertSnappedDuration ( 50 , 1000 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDuration ( 100 , 1000 ) ;
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 150 , 2000 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDuration ( 200 , 2000 ) ;
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 250 , 3000 ) ;
2019-10-25 09:19:26 +00:00
2021-10-02 03:34:29 +00:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 09:19:26 +00:00
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 0 , 0 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDuration ( 50 , 0 ) ;
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 100 , 1000 ) ;
assertSnappedDuration ( 150 , 1000 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDuration ( 200 , 1000 ) ;
assertSnappedDuration ( 250 , 1000 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 02:52:04 +00:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 09:19:26 +00:00
} ) ;
assertSnappedDuration ( 50 , 0 ) ;
2020-01-28 04:42:22 +00:00
assertSnappedDuration ( 100 , 500 ) ;
assertSnappedDuration ( 150 , 500 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDuration ( 200 , 500 ) ;
assertSnappedDuration ( 250 , 500 ) ;
assertSnappedDuration ( 400 , 1000 ) ;
}
[Test]
public void GetSnappedDistanceFromDistance ( )
{
2020-08-25 09:56:15 +00:00
assertSnappedDistance ( 50 , 0 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDistance ( 100 , 100 ) ;
2020-08-25 09:56:15 +00:00
assertSnappedDistance ( 150 , 100 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDistance ( 200 , 200 ) ;
2020-08-25 09:56:15 +00:00
assertSnappedDistance ( 250 , 200 ) ;
2019-10-25 09:19:26 +00:00
2021-10-02 03:34:29 +00:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDistance ( 50 , 0 ) ;
2020-08-25 09:56:15 +00:00
assertSnappedDistance ( 100 , 0 ) ;
assertSnappedDistance ( 150 , 0 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDistance ( 200 , 200 ) ;
assertSnappedDistance ( 250 , 200 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 02:52:04 +00:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 09:19:26 +00:00
} ) ;
assertSnappedDistance ( 50 , 0 ) ;
2020-08-25 09:56:15 +00:00
assertSnappedDistance ( 100 , 0 ) ;
assertSnappedDistance ( 150 , 0 ) ;
2019-10-25 09:19:26 +00:00
assertSnappedDistance ( 200 , 200 ) ;
assertSnappedDistance ( 250 , 200 ) ;
assertSnappedDistance ( 400 , 400 ) ;
}
2024-02-26 10:59:57 +00:00
[Test]
public void TestUseCurrentSnap ( )
{
AddStep ( "add objects to beatmap" , ( ) = >
{
editorBeatmap . Add ( new HitCircle { StartTime = 1000 } ) ;
editorBeatmap . Add ( new HitCircle { Position = new Vector2 ( 100 ) , StartTime = 2000 } ) ;
} ) ;
AddStep ( "hover use current snap button" , ( ) = > InputManager . MoveMouseTo ( composer . ChildrenOfType < ExpandableButton > ( ) . Single ( ) ) ) ;
AddUntilStep ( "use current snap expanded" , ( ) = > composer . ChildrenOfType < ExpandableButton > ( ) . Single ( ) . Expanded . Value , ( ) = > Is . True ) ;
AddStep ( "seek before first object" , ( ) = > EditorClock . Seek ( 0 ) ) ;
AddUntilStep ( "use current snap not available" , ( ) = > composer . ChildrenOfType < ExpandableButton > ( ) . Single ( ) . Enabled . Value , ( ) = > Is . False ) ;
AddStep ( "seek to between objects" , ( ) = > EditorClock . Seek ( 1500 ) ) ;
AddUntilStep ( "use current snap available" , ( ) = > composer . ChildrenOfType < ExpandableButton > ( ) . Single ( ) . Enabled . Value , ( ) = > Is . True ) ;
AddStep ( "seek after last object" , ( ) = > EditorClock . Seek ( 2500 ) ) ;
AddUntilStep ( "use current snap not available" , ( ) = > composer . ChildrenOfType < ExpandableButton > ( ) . Single ( ) . Enabled . Value , ( ) = > Is . False ) ;
}
2022-11-01 06:21:17 +00:00
private void assertSnapDistance ( float expectedDistance , HitObject ? referenceObject , bool includeSliderVelocity )
2023-10-19 09:20:10 +00:00
= > AddAssert ( $"distance is {expectedDistance}" , ( ) = > composer . DistanceSnapProvider . GetBeatSnapDistanceAt ( referenceObject ? ? new HitObject ( ) , includeSliderVelocity ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 09:19:26 +00:00
2022-11-01 06:21:17 +00:00
private void assertDurationToDistance ( double duration , float expectedDistance , HitObject ? referenceObject = null )
2023-10-19 09:20:10 +00:00
= > AddAssert ( $"duration = {duration} -> distance = {expectedDistance}" , ( ) = > composer . DistanceSnapProvider . DurationToDistance ( referenceObject ? ? new HitObject ( ) , duration ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 09:19:26 +00:00
2022-11-01 06:21:17 +00:00
private void assertDistanceToDuration ( float distance , double expectedDuration , HitObject ? referenceObject = null )
2023-10-19 09:20:10 +00:00
= > AddAssert ( $"distance = {distance} -> duration = {expectedDuration}" , ( ) = > composer . DistanceSnapProvider . DistanceToDuration ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDuration ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 09:19:26 +00:00
2022-11-01 06:21:17 +00:00
private void assertSnappedDuration ( float distance , double expectedDuration , HitObject ? referenceObject = null )
2023-10-19 09:20:10 +00:00
= > AddAssert ( $"distance = {distance} -> duration = {expectedDuration} (snapped)" , ( ) = > composer . DistanceSnapProvider . FindSnappedDuration ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDuration ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 09:19:26 +00:00
2022-11-01 06:21:17 +00:00
private void assertSnappedDistance ( float distance , float expectedDistance , HitObject ? referenceObject = null )
2023-10-19 09:20:10 +00:00
= > AddAssert ( $"distance = {distance} -> distance = {expectedDistance} (snapped)" , ( ) = > composer . DistanceSnapProvider . FindSnappedDistance ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 09:19:26 +00:00
private partial class TestHitObjectComposer : OsuHitObjectComposer
{
2019-12-27 10:39:30 +00:00
public new EditorBeatmap EditorBeatmap = > base . EditorBeatmap ;
2019-10-25 09:19:26 +00:00
2023-10-19 09:20:10 +00:00
public new IDistanceSnapProvider DistanceSnapProvider = > base . DistanceSnapProvider ;
2022-04-28 03:46:20 +00:00
2019-10-25 09:19:26 +00:00
public TestHitObjectComposer ( )
: base ( new OsuRuleset ( ) )
{
}
}
}
}