2021-03-24 12:06:04 +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.
2022-06-17 15:37:17 +08:00
#nullable disable
2021-12-21 04:51:56 +08:00
using System.Linq ;
2021-12-21 05:09:01 +08:00
using Humanizer ;
2021-03-24 12:06:04 +08:00
using NUnit.Framework ;
2021-12-21 04:51:56 +08:00
using osu.Framework.Testing ;
2021-03-24 12:06:04 +08:00
using osu.Framework.Utils ;
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.HitCircles.Components ;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders ;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components ;
using osu.Game.Rulesets.Osu.Objects ;
using osu.Game.Rulesets.Osu.Objects.Drawables ;
using osu.Game.Tests.Visual ;
using osuTK ;
using osuTK.Input ;
namespace osu.Game.Rulesets.Osu.Tests.Editor
{
public partial class TestSceneSliderControlPointPiece : SelectionBlueprintTestScene
{
private Slider slider ;
private DrawableSlider drawableObject ;
[SetUp]
public void Setup ( ) = > Schedule ( ( ) = >
{
Clear ( ) ;
slider = new Slider
{
Position = new Vector2 ( 256 , 192 ) ,
Path = new SliderPath ( new [ ]
{
2023-11-13 15:24:09 +08:00
new PathControlPoint ( Vector2 . Zero , PathType . PERFECT_CURVE ) ,
2021-03-24 12:06:04 +08:00
new PathControlPoint ( new Vector2 ( 150 , 150 ) ) ,
2023-11-13 15:24:09 +08:00
new PathControlPoint ( new Vector2 ( 300 , 0 ) , PathType . PERFECT_CURVE ) ,
2021-03-24 12:06:04 +08:00
new PathControlPoint ( new Vector2 ( 400 , 0 ) ) ,
new PathControlPoint ( new Vector2 ( 400 , 150 ) )
} )
} ;
slider . ApplyDefaults ( new ControlPointInfo ( ) , new BeatmapDifficulty { CircleSize = 2 } ) ;
Add ( drawableObject = new DrawableSlider ( slider ) ) ;
2021-05-13 18:53:32 +08:00
AddBlueprint ( new TestSliderBlueprint ( slider ) , drawableObject ) ;
2021-03-24 12:06:04 +08:00
} ) ;
2021-12-21 05:09:01 +08:00
[Test]
public void TestSelection ( )
{
moveMouseToControlPoint ( 0 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertSelectionCount ( 1 ) ;
assertSelected ( 0 ) ;
2021-12-22 15:40:17 +08:00
AddStep ( "click right mouse" , ( ) = > InputManager . Click ( MouseButton . Right ) ) ;
assertSelectionCount ( 1 ) ;
assertSelected ( 0 ) ;
2021-12-21 05:09:01 +08:00
moveMouseToControlPoint ( 3 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertSelectionCount ( 1 ) ;
assertSelected ( 3 ) ;
AddStep ( "press control" , ( ) = > InputManager . PressKey ( Key . ControlLeft ) ) ;
moveMouseToControlPoint ( 2 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertSelectionCount ( 2 ) ;
assertSelected ( 2 ) ;
assertSelected ( 3 ) ;
moveMouseToControlPoint ( 0 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertSelectionCount ( 3 ) ;
assertSelected ( 0 ) ;
assertSelected ( 2 ) ;
assertSelected ( 3 ) ;
2021-12-22 15:40:17 +08:00
AddStep ( "click right mouse" , ( ) = > InputManager . Click ( MouseButton . Right ) ) ;
assertSelectionCount ( 3 ) ;
assertSelected ( 0 ) ;
assertSelected ( 2 ) ;
assertSelected ( 3 ) ;
2021-12-21 05:09:01 +08:00
AddStep ( "release control" , ( ) = > InputManager . ReleaseKey ( Key . ControlLeft ) ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
assertSelectionCount ( 1 ) ;
assertSelected ( 0 ) ;
2021-12-23 16:06:03 +08:00
moveMouseToRelativePosition ( new Vector2 ( 350 , 0 ) ) ;
2021-12-22 16:32:38 +08:00
AddStep ( "ctrl+click to create new point" , ( ) = >
{
InputManager . PressKey ( Key . ControlLeft ) ;
2021-12-22 17:57:39 +08:00
InputManager . PressButton ( MouseButton . Left ) ;
2021-12-22 16:32:38 +08:00
} ) ;
assertSelectionCount ( 1 ) ;
2021-12-22 17:57:39 +08:00
assertSelected ( 3 ) ;
AddStep ( "release ctrl+click" , ( ) = >
{
InputManager . ReleaseButton ( MouseButton . Left ) ;
InputManager . ReleaseKey ( Key . ControlLeft ) ;
} ) ;
assertSelectionCount ( 1 ) ;
2021-12-22 16:32:38 +08:00
assertSelected ( 3 ) ;
2021-12-23 16:06:03 +08:00
}
2021-12-22 16:32:38 +08:00
2021-12-23 16:06:03 +08:00
[Test]
public void TestNewControlPointCreation ( )
{
moveMouseToRelativePosition ( new Vector2 ( 350 , 0 ) ) ;
AddStep ( "ctrl+click to create new point" , ( ) = >
{
InputManager . PressKey ( Key . ControlLeft ) ;
InputManager . PressButton ( MouseButton . Left ) ;
} ) ;
AddAssert ( "slider has 6 control points" , ( ) = > slider . Path . ControlPoints . Count = = 6 ) ;
AddStep ( "release ctrl+click" , ( ) = >
{
InputManager . ReleaseButton ( MouseButton . Left ) ;
InputManager . ReleaseKey ( Key . ControlLeft ) ;
} ) ;
2021-12-21 05:09:01 +08:00
2021-12-23 16:06:03 +08:00
// ensure that the next drag doesn't attempt to move the placement that just finished.
moveMouseToRelativePosition ( new Vector2 ( 0 , 50 ) ) ;
AddStep ( "press left mouse" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
moveMouseToRelativePosition ( new Vector2 ( 0 , 100 ) ) ;
AddStep ( "release left mouse" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 3 , new Vector2 ( 350 , 0 ) ) ;
moveMouseToRelativePosition ( new Vector2 ( 400 , 75 ) ) ;
AddStep ( "ctrl+click to create new point" , ( ) = >
{
InputManager . PressKey ( Key . ControlLeft ) ;
InputManager . PressButton ( MouseButton . Left ) ;
} ) ;
AddAssert ( "slider has 7 control points" , ( ) = > slider . Path . ControlPoints . Count = = 7 ) ;
moveMouseToRelativePosition ( new Vector2 ( 350 , 75 ) ) ;
AddStep ( "release ctrl+click" , ( ) = >
{
InputManager . ReleaseButton ( MouseButton . Left ) ;
InputManager . ReleaseKey ( Key . ControlLeft ) ;
} ) ;
assertControlPointPosition ( 5 , new Vector2 ( 350 , 75 ) ) ;
// ensure that the next drag doesn't attempt to move the placement that just finished.
moveMouseToRelativePosition ( new Vector2 ( 0 , 50 ) ) ;
AddStep ( "press left mouse" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
moveMouseToRelativePosition ( new Vector2 ( 0 , 100 ) ) ;
AddStep ( "release left mouse" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 5 , new Vector2 ( 350 , 75 ) ) ;
2021-12-21 05:09:01 +08:00
}
2021-12-23 16:06:03 +08:00
private void assertSelectionCount ( int count ) = >
2022-11-03 19:25:23 +08:00
AddAssert ( $"{count} control point pieces selected" , ( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Count ( piece = > piece . IsSelected . Value ) = = count ) ;
2021-12-23 16:06:03 +08:00
private void assertSelected ( int index ) = >
AddAssert ( $"{(index + 1).ToOrdinalWords()} control point piece selected" ,
2022-11-03 19:25:23 +08:00
( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Single ( piece = > piece . ControlPoint = = slider . Path . ControlPoints [ index ] ) . IsSelected . Value ) ;
2021-12-23 16:06:03 +08:00
private void moveMouseToRelativePosition ( Vector2 relativePosition ) = >
AddStep ( $"move mouse to {relativePosition}" , ( ) = >
{
Vector2 position = slider . Position + relativePosition ;
2023-10-17 16:40:44 +08:00
InputManager . MoveMouseTo ( drawableObject . Parent ! . ToScreenSpace ( position ) ) ;
2021-12-23 16:06:03 +08:00
} ) ;
2021-03-24 12:06:04 +08:00
[Test]
public void TestDragControlPoint ( )
{
moveMouseToControlPoint ( 1 ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
addMovementStep ( new Vector2 ( 150 , 50 ) ) ;
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 1 , new Vector2 ( 150 , 50 ) ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 0 , PathType . PERFECT_CURVE ) ;
2021-03-24 12:06:04 +08:00
}
2021-12-21 04:01:11 +08:00
[Test]
public void TestDragMultipleControlPoints ( )
{
moveMouseToControlPoint ( 2 ) ;
2021-12-21 04:29:57 +08:00
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
2021-12-21 04:01:11 +08:00
AddStep ( "hold control" , ( ) = > InputManager . PressKey ( Key . LControl ) ) ;
moveMouseToControlPoint ( 3 ) ;
2021-12-21 04:29:57 +08:00
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
2021-12-21 04:01:11 +08:00
moveMouseToControlPoint ( 4 ) ;
2021-12-21 04:29:57 +08:00
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
2021-12-21 04:01:11 +08:00
moveMouseToControlPoint ( 2 ) ;
2021-12-21 04:29:57 +08:00
AddStep ( "hold left mouse" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
2021-12-21 04:51:56 +08:00
2022-11-03 19:25:23 +08:00
AddAssert ( "three control point pieces selected" , ( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Count ( piece = > piece . IsSelected . Value ) = = 3 ) ;
2021-12-21 04:51:56 +08:00
2021-12-21 04:01:11 +08:00
addMovementStep ( new Vector2 ( 450 , 50 ) ) ;
2021-12-21 04:29:57 +08:00
AddStep ( "release left mouse" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
2021-12-21 04:01:11 +08:00
2022-11-03 19:25:23 +08:00
AddAssert ( "three control point pieces selected" , ( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Count ( piece = > piece . IsSelected . Value ) = = 3 ) ;
2021-12-21 04:51:56 +08:00
2021-12-21 04:01:11 +08:00
assertControlPointPosition ( 2 , new Vector2 ( 450 , 50 ) ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 2 , PathType . PERFECT_CURVE ) ;
2021-12-21 04:01:11 +08:00
assertControlPointPosition ( 3 , new Vector2 ( 550 , 50 ) ) ;
assertControlPointPosition ( 4 , new Vector2 ( 550 , 200 ) ) ;
AddStep ( "release control" , ( ) = > InputManager . ReleaseKey ( Key . LControl ) ) ;
}
2021-12-21 19:34:55 +08:00
[Test]
public void TestDragMultipleControlPointsIncludingHead ( )
{
moveMouseToControlPoint ( 0 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
AddStep ( "hold control" , ( ) = > InputManager . PressKey ( Key . LControl ) ) ;
moveMouseToControlPoint ( 3 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
moveMouseToControlPoint ( 4 ) ;
AddStep ( "click left mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
moveMouseToControlPoint ( 3 ) ;
AddStep ( "hold left mouse" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
2022-11-03 19:25:23 +08:00
AddAssert ( "three control point pieces selected" , ( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Count ( piece = > piece . IsSelected . Value ) = = 3 ) ;
2021-12-21 19:34:55 +08:00
addMovementStep ( new Vector2 ( 550 , 50 ) ) ;
AddStep ( "release left mouse" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
2022-11-03 19:25:23 +08:00
AddAssert ( "three control point pieces selected" , ( ) = > this . ChildrenOfType < PathControlPointPiece < Slider > > ( ) . Count ( piece = > piece . IsSelected . Value ) = = 3 ) ;
2021-12-21 19:34:55 +08:00
// note: if the head is part of the selection being moved, the entire slider is moved.
// the unselected nodes will therefore change position relative to the slider head.
AddAssert ( "slider moved" , ( ) = > Precision . AlmostEquals ( slider . Position , new Vector2 ( 256 , 192 ) + new Vector2 ( 150 , 50 ) ) ) ;
assertControlPointPosition ( 0 , Vector2 . Zero ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 0 , PathType . PERFECT_CURVE ) ;
2021-12-21 19:34:55 +08:00
assertControlPointPosition ( 1 , new Vector2 ( 0 , 100 ) ) ;
assertControlPointPosition ( 2 , new Vector2 ( 150 , - 50 ) ) ;
assertControlPointPosition ( 3 , new Vector2 ( 400 , 0 ) ) ;
assertControlPointPosition ( 4 , new Vector2 ( 400 , 150 ) ) ;
AddStep ( "release control" , ( ) = > InputManager . ReleaseKey ( Key . LControl ) ) ;
}
2021-03-24 12:06:04 +08:00
[Test]
public void TestDragControlPointAlmostLinearlyExterior ( )
{
moveMouseToControlPoint ( 1 ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
addMovementStep ( new Vector2 ( 400 , 0.01f ) ) ;
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 1 , new Vector2 ( 400 , 0.01f ) ) ;
2023-11-08 18:43:54 +08:00
assertControlPointType ( 0 , PathType . BEZIER ) ;
2021-03-24 12:06:04 +08:00
}
[Test]
public void TestDragControlPointPathRecovery ( )
{
moveMouseToControlPoint ( 1 ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
2021-04-01 02:08:39 +08:00
addMovementStep ( new Vector2 ( 400 , 0.01f ) ) ;
2023-11-08 18:43:54 +08:00
assertControlPointType ( 0 , PathType . BEZIER ) ;
2021-03-24 12:06:04 +08:00
addMovementStep ( new Vector2 ( 150 , 50 ) ) ;
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 1 , new Vector2 ( 150 , 50 ) ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 0 , PathType . PERFECT_CURVE ) ;
2021-03-24 12:06:04 +08:00
}
[Test]
public void TestDragControlPointPathRecoveryOtherSegment ( )
{
moveMouseToControlPoint ( 4 ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
2021-04-01 02:08:39 +08:00
addMovementStep ( new Vector2 ( 350 , 0.01f ) ) ;
2023-11-08 18:43:54 +08:00
assertControlPointType ( 2 , PathType . BEZIER ) ;
2021-03-24 12:06:04 +08:00
2021-03-25 00:24:05 +08:00
addMovementStep ( new Vector2 ( 150 , 150 ) ) ;
2021-03-24 12:06:04 +08:00
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
2021-03-25 00:24:05 +08:00
assertControlPointPosition ( 4 , new Vector2 ( 150 , 150 ) ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 2 , PathType . PERFECT_CURVE ) ;
2021-03-24 12:06:04 +08:00
}
2021-04-07 23:18:55 +08:00
[Test]
public void TestDragControlPointPathAfterChangingType ( )
{
2023-11-08 18:43:54 +08:00
AddStep ( "change type to bezier" , ( ) = > slider . Path . ControlPoints [ 2 ] . Type = PathType . BEZIER ) ;
2021-04-07 23:18:55 +08:00
AddStep ( "add point" , ( ) = > slider . Path . ControlPoints . Add ( new PathControlPoint ( new Vector2 ( 500 , 10 ) ) ) ) ;
2023-11-13 15:24:09 +08:00
AddStep ( "change type to perfect" , ( ) = > slider . Path . ControlPoints [ 3 ] . Type = PathType . PERFECT_CURVE ) ;
2021-04-07 23:18:55 +08:00
moveMouseToControlPoint ( 4 ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
2023-11-13 15:24:09 +08:00
assertControlPointType ( 3 , PathType . PERFECT_CURVE ) ;
2021-04-07 23:18:55 +08:00
addMovementStep ( new Vector2 ( 350 , 0.01f ) ) ;
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
assertControlPointPosition ( 4 , new Vector2 ( 350 , 0.01f ) ) ;
2023-11-08 18:43:54 +08:00
assertControlPointType ( 3 , PathType . BEZIER ) ;
2021-04-07 23:18:55 +08:00
}
2021-03-24 12:06:04 +08:00
private void addMovementStep ( Vector2 relativePosition )
{
AddStep ( $"move mouse to {relativePosition}" , ( ) = >
{
Vector2 position = slider . Position + relativePosition ;
2023-10-17 16:40:44 +08:00
InputManager . MoveMouseTo ( drawableObject . Parent ! . ToScreenSpace ( position ) ) ;
2021-03-24 12:06:04 +08:00
} ) ;
}
private void moveMouseToControlPoint ( int index )
{
AddStep ( $"move mouse to control point {index}" , ( ) = >
{
2021-08-26 12:47:10 +08:00
Vector2 position = slider . Position + slider . Path . ControlPoints [ index ] . Position ;
2023-10-17 16:40:44 +08:00
InputManager . MoveMouseTo ( drawableObject . Parent ! . ToScreenSpace ( position ) ) ;
2021-03-24 12:06:04 +08:00
} ) ;
}
2021-08-26 12:47:10 +08:00
private void assertControlPointType ( int index , PathType type ) = > AddAssert ( $"control point {index} is {type}" , ( ) = > slider . Path . ControlPoints [ index ] . Type = = type ) ;
2021-03-24 12:06:04 +08:00
private void assertControlPointPosition ( int index , Vector2 position ) = >
2021-08-26 12:47:10 +08:00
AddAssert ( $"control point {index} at {position}" , ( ) = > Precision . AlmostEquals ( position , slider . Path . ControlPoints [ index ] . Position , 1 ) ) ;
2021-03-24 12:06:04 +08:00
private partial class TestSliderBlueprint : SliderSelectionBlueprint
{
public new SliderBodyPiece BodyPiece = > base . BodyPiece ;
2021-05-18 13:19:11 +08:00
public new TestSliderCircleOverlay HeadOverlay = > ( TestSliderCircleOverlay ) base . HeadOverlay ;
public new TestSliderCircleOverlay TailOverlay = > ( TestSliderCircleOverlay ) base . TailOverlay ;
2022-11-03 19:25:23 +08:00
public new PathControlPointVisualiser < Slider > ControlPointVisualiser = > base . ControlPointVisualiser ;
2021-03-24 12:06:04 +08:00
2021-05-13 18:53:32 +08:00
public TestSliderBlueprint ( Slider slider )
2021-03-24 12:06:04 +08:00
: base ( slider )
{
}
2021-05-18 13:19:11 +08:00
protected override SliderCircleOverlay CreateCircleOverlay ( Slider slider , SliderPosition position ) = > new TestSliderCircleOverlay ( slider , position ) ;
2021-03-24 12:06:04 +08:00
}
2021-05-18 13:19:11 +08:00
private partial class TestSliderCircleOverlay : SliderCircleOverlay
2021-03-24 12:06:04 +08:00
{
public new HitCirclePiece CirclePiece = > base . CirclePiece ;
2021-05-18 13:19:11 +08:00
public TestSliderCircleOverlay ( Slider slider , SliderPosition position )
2021-03-24 12:06:04 +08:00
: base ( slider , position )
{
}
}
}
}