2019-10-25 17:19:26 +08: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.
using NUnit.Framework ;
2020-01-01 23:41:06 +08:00
using osu.Framework.Allocation ;
2022-04-28 11:46:20 +08:00
using osu.Framework.Bindables ;
2020-02-05 16:32:33 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2019-10-25 17:19:26 +08:00
using osu.Framework.Testing ;
2022-11-01 14:21:17 +08:00
using osu.Framework.Utils ;
2019-10-25 17:19:26 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2020-01-23 13:39:56 +08:00
using osu.Game.Rulesets.Edit ;
2021-09-01 17:05:10 +08:00
using osu.Game.Rulesets.Objects ;
2019-10-25 17:19:26 +08:00
using osu.Game.Rulesets.Osu ;
2020-01-01 23:41:06 +08:00
using osu.Game.Rulesets.Osu.Beatmaps ;
2019-10-25 17:19:26 +08:00
using osu.Game.Rulesets.Osu.Edit ;
using osu.Game.Screens.Edit ;
using osu.Game.Tests.Visual ;
2020-04-23 16:07:55 +08:00
namespace osu.Game.Tests.Editing
2019-10-25 17:19:26 +08:00
{
[HeadlessTest]
public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene
{
2022-08-17 14:40:01 +08:00
private TestHitObjectComposer composer = null ! ;
2019-10-25 17:19:26 +08:00
2020-01-01 23:41:06 +08:00
[Cached(typeof(EditorBeatmap))]
2020-01-23 13:39:56 +08:00
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap ;
2022-08-17 14:40:01 +08:00
protected override Container < Drawable > Content { get ; } = new Container { RelativeSizeAxes = Axes . Both } ;
2020-02-05 16:32:33 +08:00
2020-01-23 13:39:56 +08:00
public TestSceneHitObjectComposerDistanceSnapping ( )
{
2020-02-05 16:32:33 +08:00
base . Content . Add ( new Container
{
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
2022-01-12 22:17:35 +08:00
editorBeatmap = new EditorBeatmap ( new OsuBeatmap
{
2022-08-17 14:40:01 +08:00
BeatmapInfo = { Ruleset = new OsuRuleset ( ) . RulesetInfo } ,
2022-01-12 22:17:35 +08:00
} ) ,
2022-08-17 14:40:01 +08:00
Content
2020-02-05 16:32:33 +08:00
} ,
} ) ;
2020-01-23 13:39:56 +08:00
}
2020-01-01 23:41:06 +08:00
2019-10-25 17:19:26 +08:00
[SetUp]
public void Setup ( ) = > Schedule ( ( ) = >
{
2020-02-05 16:32:33 +08:00
Children = new Drawable [ ]
{
composer = new TestHitObjectComposer ( )
} ;
2019-10-25 17:19:26 +08:00
BeatDivisor . Value = 1 ;
2021-10-02 11:34:29 +08:00
composer . EditorBeatmap . Difficulty . SliderMultiplier = 1 ;
2019-10-28 10:52:04 +08:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 1000 } ) ;
2019-10-25 17:19:26 +08:00
} ) ;
[TestCase(1)]
[TestCase(2)]
public void TestSliderMultiplier ( float multiplier )
{
2022-04-28 11:46:20 +08:00
AddStep ( $"set slider multiplier = {multiplier}" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = multiplier ) ;
2019-10-25 17:19:26 +08:00
2022-11-01 14:13:05 +08:00
assertSnapDistance ( 100 * multiplier , null , true ) ;
2019-10-25 17:19:26 +08:00
}
[TestCase(1)]
[TestCase(2)]
2022-10-21 21:58:10 +08:00
public void TestSpeedMultiplierDoesNotChangeDistanceSnap ( float multiplier )
2019-10-25 17:19:26 +08:00
{
2022-10-21 21:58:10 +08:00
assertSnapDistance ( 100 , new HitObject
2019-10-25 17:19:26 +08:00
{
2021-09-02 18:42:41 +08:00
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = multiplier
}
2022-11-01 14:13:05 +08:00
} , false ) ;
}
[TestCase(1)]
[TestCase(2)]
public void TestSpeedMultiplierDoesChangeDistanceSnap ( float multiplier )
{
assertSnapDistance ( 100 * multiplier , new HitObject
{
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = multiplier
}
} , true ) ;
2019-10-25 17:19:26 +08:00
}
[TestCase(1)]
[TestCase(2)]
public void TestBeatDivisor ( int divisor )
{
AddStep ( $"set divisor = {divisor}" , ( ) = > BeatDivisor . Value = divisor ) ;
2022-11-01 14:13:05 +08:00
assertSnapDistance ( 100f / divisor , null , true ) ;
2019-10-25 17:19:26 +08:00
}
2022-11-01 14:21:17 +08: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 ;
var referenceObject = new HitObject
{
DifficultyControlPoint = new DifficultyControlPoint
{
SliderVelocity = slider_velocity
}
} ;
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 16:14:30 +08:00
assertDistanceToDuration ( base_distance * slider_velocity , 1000 , referenceObject ) ;
assertDurationToDistance ( 1000 , base_distance * slider_velocity , referenceObject ) ;
2022-11-01 14:21:17 +08:00
}
2019-10-25 17:19:26 +08:00
[Test]
public void TestConvertDurationToDistance ( )
{
assertDurationToDistance ( 500 , 50 ) ;
assertDurationToDistance ( 1000 , 100 ) ;
2021-10-02 11:34:29 +08:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 17:19:26 +08:00
assertDurationToDistance ( 500 , 100 ) ;
assertDurationToDistance ( 1000 , 200 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 10:52:04 +08:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 17:19:26 +08:00
} ) ;
assertDurationToDistance ( 500 , 200 ) ;
assertDurationToDistance ( 1000 , 400 ) ;
}
[Test]
public void TestConvertDistanceToDuration ( )
{
assertDistanceToDuration ( 50 , 500 ) ;
assertDistanceToDuration ( 100 , 1000 ) ;
2021-10-02 11:34:29 +08:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 17:19:26 +08:00
assertDistanceToDuration ( 100 , 500 ) ;
assertDistanceToDuration ( 200 , 1000 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 10:52:04 +08:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 17:19:26 +08:00
} ) ;
assertDistanceToDuration ( 200 , 500 ) ;
assertDistanceToDuration ( 400 , 1000 ) ;
}
[Test]
public void TestGetSnappedDurationFromDistance ( )
{
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 0 , 0 ) ;
assertSnappedDuration ( 50 , 1000 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDuration ( 100 , 1000 ) ;
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 150 , 2000 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDuration ( 200 , 2000 ) ;
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 250 , 3000 ) ;
2019-10-25 17:19:26 +08:00
2021-10-02 11:34:29 +08:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 17:19:26 +08:00
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 0 , 0 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDuration ( 50 , 0 ) ;
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 100 , 1000 ) ;
assertSnappedDuration ( 150 , 1000 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDuration ( 200 , 1000 ) ;
assertSnappedDuration ( 250 , 1000 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 10:52:04 +08:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 17:19:26 +08:00
} ) ;
assertSnappedDuration ( 50 , 0 ) ;
2020-01-28 12:42:22 +08:00
assertSnappedDuration ( 100 , 500 ) ;
assertSnappedDuration ( 150 , 500 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDuration ( 200 , 500 ) ;
assertSnappedDuration ( 250 , 500 ) ;
assertSnappedDuration ( 400 , 1000 ) ;
}
[Test]
public void GetSnappedDistanceFromDistance ( )
{
2020-08-25 17:56:15 +08:00
assertSnappedDistance ( 50 , 0 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDistance ( 100 , 100 ) ;
2020-08-25 17:56:15 +08:00
assertSnappedDistance ( 150 , 100 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDistance ( 200 , 200 ) ;
2020-08-25 17:56:15 +08:00
assertSnappedDistance ( 250 , 200 ) ;
2019-10-25 17:19:26 +08:00
2021-10-02 11:34:29 +08:00
AddStep ( "set slider multiplier = 2" , ( ) = > composer . EditorBeatmap . Difficulty . SliderMultiplier = 2 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDistance ( 50 , 0 ) ;
2020-08-25 17:56:15 +08:00
assertSnappedDistance ( 100 , 0 ) ;
assertSnappedDistance ( 150 , 0 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDistance ( 200 , 200 ) ;
assertSnappedDistance ( 250 , 200 ) ;
AddStep ( "set beat length = 500" , ( ) = >
{
2019-10-28 10:52:04 +08:00
composer . EditorBeatmap . ControlPointInfo . Clear ( ) ;
composer . EditorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 500 } ) ;
2019-10-25 17:19:26 +08:00
} ) ;
assertSnappedDistance ( 50 , 0 ) ;
2020-08-25 17:56:15 +08:00
assertSnappedDistance ( 100 , 0 ) ;
assertSnappedDistance ( 150 , 0 ) ;
2019-10-25 17:19:26 +08:00
assertSnappedDistance ( 200 , 200 ) ;
assertSnappedDistance ( 250 , 200 ) ;
assertSnappedDistance ( 400 , 400 ) ;
}
2022-11-01 14:21:17 +08:00
private void assertSnapDistance ( float expectedDistance , HitObject ? referenceObject , bool includeSliderVelocity )
= > AddAssert ( $"distance is {expectedDistance}" , ( ) = > composer . GetBeatSnapDistanceAt ( referenceObject ? ? new HitObject ( ) , includeSliderVelocity ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 17:19:26 +08:00
2022-11-01 14:21:17 +08:00
private void assertDurationToDistance ( double duration , float expectedDistance , HitObject ? referenceObject = null )
= > AddAssert ( $"duration = {duration} -> distance = {expectedDistance}" , ( ) = > composer . DurationToDistance ( referenceObject ? ? new HitObject ( ) , duration ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 17:19:26 +08:00
2022-11-01 14:21:17 +08:00
private void assertDistanceToDuration ( float distance , double expectedDuration , HitObject ? referenceObject = null )
= > AddAssert ( $"distance = {distance} -> duration = {expectedDuration}" , ( ) = > composer . DistanceToDuration ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDuration ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 17:19:26 +08:00
2022-11-01 14:21:17 +08:00
private void assertSnappedDuration ( float distance , double expectedDuration , HitObject ? referenceObject = null )
= > AddAssert ( $"distance = {distance} -> duration = {expectedDuration} (snapped)" , ( ) = > composer . FindSnappedDuration ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDuration ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 17:19:26 +08:00
2022-11-01 14:21:17 +08:00
private void assertSnappedDistance ( float distance , float expectedDistance , HitObject ? referenceObject = null )
= > AddAssert ( $"distance = {distance} -> distance = {expectedDistance} (snapped)" , ( ) = > composer . FindSnappedDistance ( referenceObject ? ? new HitObject ( ) , distance ) , ( ) = > Is . EqualTo ( expectedDistance ) . Within ( Precision . FLOAT_EPSILON ) ) ;
2019-10-25 17:19:26 +08:00
private class TestHitObjectComposer : OsuHitObjectComposer
{
2019-12-27 18:39:30 +08:00
public new EditorBeatmap EditorBeatmap = > base . EditorBeatmap ;
2019-10-25 17:19:26 +08:00
2022-04-29 13:02:07 +08:00
public new Bindable < double > DistanceSpacingMultiplier = > base . DistanceSpacingMultiplier ;
2022-04-28 11:46:20 +08:00
2019-10-25 17:19:26 +08:00
public TestHitObjectComposer ( )
: base ( new OsuRuleset ( ) )
{
}
}
}
}