From 779e57f0cab8a91c18e4c40c38acb70067ae2a47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 16:32:11 +0900 Subject: [PATCH 01/17] Change .idea ignore rules to not ignore run configurations --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8f011deabe..f95a04e517 100644 --- a/.gitignore +++ b/.gitignore @@ -252,7 +252,11 @@ paket-files/ .fake/ # JetBrains Rider -.idea/ +.idea/.idea.osu/.idea/*.xml +.idea/.idea.osu/.idea/codeStyles/*.xml +.idea/.idea.osu/.idea/dataSources/*.xml +.idea/.idea.osu/.idea/dictionaries/*.xml +.idea/.idea.osu/*.iml *.sln.iml # CodeRush From 5d51719572815210626eb87357eda6d82c2ccd80 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Nov 2018 16:21:32 +0900 Subject: [PATCH 02/17] Make selection blueprints handle position adjustments --- .../Blueprints/HoldNoteSelectionBlueprint.cs | 3 +-- .../Blueprints/ManiaSelectionBlueprint.cs | 21 ++++++++++++++++++ .../Edit/Blueprints/NoteSelectionBlueprint.cs | 3 +-- .../HitCircles/HitCircleSelectionBlueprint.cs | 3 +-- .../Edit/Blueprints/OsuSelectionBlueprint.cs | 22 +++++++++++++++++++ .../Sliders/SliderCircleSelectionBlueprint.cs | 10 +++++++-- .../Sliders/SliderSelectionBlueprint.cs | 3 +-- .../Spinners/SpinnerSelectionBlueprint.cs | 9 ++++++-- osu.Game/Rulesets/Edit/SelectionBlueprint.cs | 10 +++++---- .../Compose/Components/BlueprintContainer.cs | 3 +-- .../Edit/Compose/Components/SelectionBox.cs | 12 ++-------- 11 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs index 0ede2a7b9d..3da2538497 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs @@ -5,7 +5,6 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; @@ -15,7 +14,7 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class HoldNoteSelectionBlueprint : SelectionBlueprint + public class HoldNoteSelectionBlueprint : ManiaSelectionBlueprint { public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject; diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs new file mode 100644 index 0000000000..474b8c662e --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.Edit.Blueprints +{ + public class ManiaSelectionBlueprint : SelectionBlueprint + { + public ManiaSelectionBlueprint(DrawableHitObject hitObject) + : base(hitObject) + { + } + + public override void AdjustPosition(DragEvent dragEvent) + { + } + } +} diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs index 19726a368d..7c0337dc4e 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs @@ -3,13 +3,12 @@ using osu.Framework.Allocation; using osu.Game.Graphics; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Mania.Edit.Blueprints { - public class NoteSelectionBlueprint : SelectionBlueprint + public class NoteSelectionBlueprint : ManiaSelectionBlueprint { public NoteSelectionBlueprint(DrawableNote note) : base(note) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs index 9c4b804d77..a59dac1834 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs @@ -1,14 +1,13 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles { - public class HitCircleSelectionBlueprint : SelectionBlueprint + public class HitCircleSelectionBlueprint : OsuSelectionBlueprint { public HitCircleSelectionBlueprint(DrawableHitCircle hitCircle) : base(hitCircle) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs new file mode 100644 index 0000000000..8431d5d5d0 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Edit.Blueprints +{ + public class OsuSelectionBlueprint : SelectionBlueprint + { + protected OsuHitObject OsuObject => (OsuHitObject)HitObject.HitObject; + + public OsuSelectionBlueprint(DrawableHitObject hitObject) + : base(hitObject) + { + } + + public override void AdjustPosition(DragEvent dragEvent) => OsuObject.Position += dragEvent.Delta; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs index 99ebfecd93..4bac9d3556 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs @@ -1,18 +1,22 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Edit; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { - public class SliderCircleSelectionBlueprint : SelectionBlueprint + public class SliderCircleSelectionBlueprint : OsuSelectionBlueprint { + private readonly Slider slider; + public SliderCircleSelectionBlueprint(DrawableOsuHitObject hitObject, Slider slider, SliderPosition position) : base(hitObject) { + this.slider = slider; + InternalChild = new SliderCirclePiece(slider, position); Select(); @@ -20,5 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. public override bool HandlePositionalInput => false; + + public override void AdjustPosition(DragEvent dragEvent) => slider.Position += dragEvent.Delta; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 430ddbdc38..4810d76bf8 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -10,7 +9,7 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { - public class SliderSelectionBlueprint : SelectionBlueprint + public class SliderSelectionBlueprint : OsuSelectionBlueprint { private readonly SliderCircleSelectionBlueprint headBlueprint; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs index 869c4c2c79..9e9cc87c5e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Edit; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -9,7 +9,7 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners { - public class SpinnerSelectionBlueprint : SelectionBlueprint + public class SpinnerSelectionBlueprint : OsuSelectionBlueprint { private readonly SpinnerPiece piece; @@ -20,5 +20,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => piece.ReceivePositionalInputAt(screenSpacePos); + + public override void AdjustPosition(DragEvent dragEvent) + { + // Spinners don't support position adjustments + } } } diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs index 6e0d136e1f..db35d47b2b 100644 --- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs +++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Edit /// /// A blueprint placed above a adding editing functionality. /// - public class SelectionBlueprint : CompositeDrawable, IStateful + public abstract class SelectionBlueprint : CompositeDrawable, IStateful { /// /// Invoked when this has been selected. @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit /// /// Invoked when this has requested drag. /// - public event Action DragRequested; + public event Action DragRequested; /// /// The which this applies to. @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit public override bool HandlePositionalInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; - public SelectionBlueprint(DrawableHitObject hitObject) + protected SelectionBlueprint(DrawableHitObject hitObject) { HitObject = hitObject; @@ -130,10 +130,12 @@ namespace osu.Game.Rulesets.Edit protected override bool OnDrag(DragEvent e) { - DragRequested?.Invoke(this, e.Delta, e.CurrentState); + DragRequested?.Invoke(e); return true; } + public abstract void AdjustPosition(DragEvent dragEvent); + /// /// The screen-space point that causes this to be selected. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 45003545a3..acbfd1f1d6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -12,7 +12,6 @@ using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; namespace osu.Game.Screens.Edit.Compose.Components { @@ -165,7 +164,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionBox.HandleSelectionRequested(blueprint, state); - private void onDragRequested(SelectionBlueprint blueprint, Vector2 delta, InputState state) => selectionBox.HandleDrag(blueprint, delta, state); + private void onDragRequested(DragEvent dragEvent) => selectionBox.HandleDrag(dragEvent); private class SelectionBlueprintContainer : Container { diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index e2a70a9ee7..8732672723 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -12,7 +12,6 @@ using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Types; using OpenTK; using OpenTK.Input; @@ -60,19 +59,12 @@ namespace osu.Game.Screens.Edit.Compose.Components #region User Input Handling - public void HandleDrag(SelectionBlueprint m, Vector2 delta, InputState state) + public void HandleDrag(DragEvent dragEvent) { // Todo: Various forms of snapping foreach (var blueprint in selectedBlueprints) - { - switch (blueprint.HitObject.HitObject) - { - case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(delta); - break; - } - } + blueprint.AdjustPosition(dragEvent); } protected override bool OnKeyDown(KeyDownEvent e) From c6350c6efd20334056406fa2c5c5facb245a7c6b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Nov 2018 16:21:52 +0900 Subject: [PATCH 03/17] Remove IHasEditablePosition --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 5 +---- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++-- osu.Game.Rulesets.Osu/Objects/SliderCircle.cs | 10 ---------- osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 5 ----- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 6 ------ .../Rulesets/Edit/Types/IHasEditablePosition.cs | 13 ------------- 6 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 67396c7ae4..61d199a7dc 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -7,11 +7,10 @@ using osu.Game.Rulesets.Objects; using OpenTK; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Edit.Types; namespace osu.Game.Rulesets.Osu.Objects { - public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition + public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition { public const double OBJECT_RADIUS = 64; @@ -100,8 +99,6 @@ namespace osu.Game.Rulesets.Osu.Objects Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } - public virtual void OffsetPosition(Vector2 offset) => Position += offset; - protected override HitWindows CreateHitWindows() => new OsuHitWindows(); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 3680c38945..cff742ca29 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createSliderEnds() { - HeadCircle = new SliderCircle(this) + HeadCircle = new SliderCircle { StartTime = StartTime, Position = Position, @@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Objects ComboIndex = ComboIndex, }; - TailCircle = new SliderTailCircle(this) + TailCircle = new SliderTailCircle { StartTime = EndTime, Position = EndPosition, diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs index 1bdd16c9df..deda951378 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs @@ -1,19 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; - namespace osu.Game.Rulesets.Osu.Objects { public class SliderCircle : HitCircle { - private readonly Slider slider; - - public SliderCircle(Slider slider) - { - this.slider = slider; - } - - public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset); } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index 23616ea005..b567bd8423 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -8,11 +8,6 @@ namespace osu.Game.Rulesets.Osu.Objects { public class SliderTailCircle : SliderCircle { - public SliderTailCircle(Slider slider) - : base(slider) - { - } - public override Judgement CreateJudgement() => new OsuSliderTailJudgement(); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 1270685ab5..1c60fd4831 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -7,7 +7,6 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; -using OpenTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -32,10 +31,5 @@ namespace osu.Game.Rulesets.Osu.Objects } public override Judgement CreateJudgement() => new OsuJudgement(); - - public override void OffsetPosition(Vector2 offset) - { - // for now we don't want to allow spinners to be moved around. - } } } diff --git a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs b/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs deleted file mode 100644 index 7107b6c763..0000000000 --- a/osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Rulesets.Objects.Types; -using OpenTK; - -namespace osu.Game.Rulesets.Edit.Types -{ - public interface IHasEditablePosition : IHasPosition - { - void OffsetPosition(Vector2 offset); - } -} From 4783df1d4ba8ea8335f1f65934d685a71fe464d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 9 Nov 2018 11:36:06 +0900 Subject: [PATCH 04/17] Fix compose mode not working --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 485c1921cf..932cfe5789 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Edit layerBelowRuleset.Child = new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }; var layerAboveRuleset = CreateLayerContainer(); - layerAboveRuleset.Child = new BlueprintContainer(); + layerAboveRuleset.Child = blueprintContainer = new BlueprintContainer(); layerContainers.Add(layerBelowRuleset); layerContainers.Add(layerAboveRuleset); From 54ab256c8e01e369ae664408e29e25f5161e12e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Nov 2018 15:38:19 +0900 Subject: [PATCH 05/17] Instantiate a new path rather than setting properties on it # Conflicts: # osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs # osu.Game.Rulesets.Catch/Objects/JuiceStream.cs # osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs # osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/SliderPath.cs --- .../TestCaseAutoJuiceStream.cs | 7 +- .../Beatmaps/CatchBeatmapConverter.cs | 4 +- .../Objects/JuiceStream.cs | 27 ++-- osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs | 40 +++--- .../TestCaseSliderSelectionBlueprint.cs | 7 +- .../Beatmaps/OsuBeatmapConverter.cs | 4 +- .../Components/PathControlPointPiece.cs | 16 +-- .../Components/PathControlPointVisualiser.cs | 6 +- .../Sliders/Components/SliderCirclePiece.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 9 +- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/DrawableSliderHead.cs | 2 +- .../Objects/Drawables/DrawableSliderTail.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 30 ++--- .../Visual/TestCaseHitObjectComposer.cs | 6 +- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 +- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 7 +- .../Legacy/Mania/ConvertHitObjectParser.cs | 4 +- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 +- .../Legacy/Taiko/ConvertHitObjectParser.cs | 4 +- osu.Game/Rulesets/Objects/SliderPath.cs | 122 +++++++----------- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 10 -- 22 files changed, 117 insertions(+), 202 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index cac1356c81..bea64302c3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Play; using osu.Game.Tests.Visual; @@ -37,13 +38,11 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { X = 0.5f - width / 2, - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) - }, - PathType = PathType.Linear, - Distance = width * CatchPlayfield.BASE_WIDTH, + }), StartTime = i * 2000, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index fed65c42af..c3dc9499c2 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -34,9 +34,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { StartTime = obj.StartTime, Samples = obj.Samples, - ControlPoints = curveData.ControlPoints, - PathType = curveData.PathType, - Distance = curveData.Distance, + Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH, diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index f1e131932b..a4e04ae837 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -10,7 +10,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using OpenTK; namespace osu.Game.Rulesets.Catch.Objects { @@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (TickDistance == 0) return; - var length = Path.Distance; + var length = Path.GetDistance(); var tickDistance = Math.Min(TickDistance, length); var spanDuration = length / Velocity; @@ -132,34 +131,24 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime; - public double Distance + private SliderPath path; + + public SliderPath Path { - get { return Path.Distance; } - set { Path.Distance = value; } + get => path; + set => path = value; } - public SliderPath Path { get; } = new SliderPath(); - - public Vector2[] ControlPoints - { - get { return Path.ControlPoints; } - set { Path.ControlPoints = value; } - } + public double Distance => Path.GetDistance(); public List> NodeSamples { get; set; } = new List>(); - public PathType PathType - { - get { return Path.PathType; } - set { Path.PathType = value; } - } - public double? LegacyLastTickOffset { get; set; } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index 0bd6bb5abc..5b638782fb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -18,6 +18,7 @@ using System.Linq; using NUnit.Framework; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -108,13 +109,12 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(239, 176), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(154, 28), new Vector2(52, -34) - }, - Distance = 700, + }, 700), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats), StackHeight = 10 @@ -141,12 +141,11 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-(distance / 2), 0), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(distance, 0), - }, - Distance = distance, + }, distance), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats), StackHeight = stackHeight @@ -161,13 +160,12 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(200, 200), new Vector2(400, 0) - }, - Distance = 600, + }, 600), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -181,10 +179,9 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(150, 75), @@ -192,8 +189,7 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(300, -200), new Vector2(400, 0), new Vector2(430, 0) - }, - Distance = 793.4417, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -207,18 +203,16 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Bezier, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Bezier, new[] { Vector2.Zero, new Vector2(150, 75), new Vector2(200, 100), new Vector2(300, -200), new Vector2(430, 0) - }, - Distance = 480, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -232,10 +226,9 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(0, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(-200, 0), @@ -243,8 +236,7 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(0, -200), new Vector2(-200, -200), new Vector2(0, -200) - }, - Distance = 1000, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -264,15 +256,13 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-100, 0), - PathType = PathType.Catmull, - ControlPoints = new[] + Path = new SliderPath(PathType.Catmull, new[] { Vector2.Zero, new Vector2(50, -50), new Vector2(150, 50), new Vector2(200, 0) - }, - Distance = 300, + }), RepeatCount = repeats, NodeSamples = repeatSamples }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs index 78e3d76313..cacbcb2cd6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -35,14 +36,12 @@ namespace osu.Game.Rulesets.Osu.Tests var slider = new Slider { Position = new Vector2(256, 192), - ControlPoints = new[] + Path = new SliderPath(PathType.Bezier, new[] { Vector2.Zero, new Vector2(150, 150), new Vector2(300, 0) - }, - PathType = PathType.Bezier, - Distance = 350 + }) }; slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 }); diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 87c81cdd3b..4fc4f3edc3 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -35,9 +35,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { StartTime = original.StartTime, Samples = original.Samples, - ControlPoints = curveData.ControlPoints, - PathType = curveData.PathType, - Distance = curveData.Distance, + Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, 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 175e9d79f4..22ad911c21 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using OpenTK; @@ -55,16 +56,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.ControlPoints[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; path.ClearVertices(); - if (index != slider.ControlPoints.Length - 1) + if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.ControlPoints[index + 1] - slider.ControlPoints[index]); + path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnDrag(DragEvent e) { - var newControlPoints = slider.ControlPoints.ToArray(); + var newControlPoints = slider.Path.ControlPoints.ToArray(); if (index == 0) { @@ -96,8 +97,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (isSegmentSeparatorWithPrevious) newControlPoints[index - 1] = newControlPoints[index]; - slider.ControlPoints = newControlPoints; - slider.Path.Calculate(true); + slider.Path = new SliderPath(slider.Path.Type, newControlPoints); return true; } @@ -106,8 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.ControlPoints.Length - 1 && slider.ControlPoints[index + 1] == slider.ControlPoints[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.ControlPoints[index - 1] == slider.ControlPoints[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; } } 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 db8e879126..ab9d81574a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -19,15 +19,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChild = pieces = new Container { RelativeSizeAxes = Axes.Both }; - slider.ControlPointsChanged += _ => updatePathControlPoints(); + slider.PathChanged += _ => updatePathControlPoints(); updatePathControlPoints(); } private void updatePathControlPoints() { - while (slider.ControlPoints.Length > pieces.Count) + while (slider.Path.ControlPoints.Length > pieces.Count) pieces.Add(new PathControlPointPiece(slider, pieces.Count)); - while (slider.ControlPoints.Length < pieces.Count) + while (slider.Path.ControlPoints.Length < pieces.Count) pieces.Remove(pieces[pieces.Count - 1]); } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs index a91739737f..1ee765f5e0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components this.slider = slider; this.position = position; - slider.ControlPointsChanged += _ => UpdatePosition(); + slider.PathChanged += _ => UpdatePosition(); } protected override void UpdatePosition() diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index e01d71e1f8..223e4df844 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -32,12 +32,11 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var newControlPoints = new Vector2[slider.ControlPoints.Length]; - for (int i = 0; i < slider.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y); + var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; + for (int i = 0; i < slider.Path.ControlPoints.Length; i++) + newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); - slider.ControlPoints = newControlPoints; - slider.Path?.Calculate(); // Recalculate the slider curve + slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 514ae09064..a90182cecb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Ball.Scale = new Vector2(HitObject.Scale); }; - slider.ControlPointsChanged += _ => Body.Refresh(); + slider.PathChanged += _ => Body.Refresh(); } public override Color4 AccentColour diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 6a836679a2..b933364887 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.slider = slider; h.PositionChanged += _ => updatePosition(); - slider.ControlPointsChanged += _ => updatePosition(); + slider.PathChanged += _ => updatePosition(); updatePosition(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index cc88a6718b..6946a55d8e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true; hitCircle.PositionChanged += _ => updatePosition(); - slider.ControlPointsChanged += _ => updatePosition(); + slider.PathChanged += _ => updatePosition(); updatePosition(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index cff742ca29..07e526956a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -22,9 +22,9 @@ namespace osu.Game.Rulesets.Osu.Objects /// private const float base_scoring_distance = 100; - public event Action ControlPointsChanged; + public event Action PathChanged; - public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; public double Duration => EndTime - StartTime; public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); @@ -52,35 +52,23 @@ namespace osu.Game.Rulesets.Osu.Objects } } - public SliderPath Path { get; } = new SliderPath(); + private SliderPath path; - public Vector2[] ControlPoints + public SliderPath Path { - get => Path.ControlPoints; + get => path; set { - if (Path.ControlPoints == value) - return; - Path.ControlPoints = value; + path = value; - ControlPointsChanged?.Invoke(value); + PathChanged?.Invoke(value); if (TailCircle != null) TailCircle.Position = EndPosition; } } - public PathType PathType - { - get { return Path.PathType; } - set { Path.PathType = value; } - } - - public double Distance - { - get { return Path.Distance; } - set { Path.Distance = value; } - } + public double Distance => Path.GetDistance(); public override Vector2 Position { @@ -190,7 +178,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createTicks() { - var length = Path.Distance; + var length = Path.GetDistance(); var tickDistance = MathHelper.Clamp(TickDistance, 0, length); if (tickDistance == 0) return; diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 2629b29c6c..d894d2738e 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -11,6 +11,7 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; @@ -53,12 +54,11 @@ namespace osu.Game.Tests.Visual new Slider { Position = new Vector2(128, 256), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(216, 0), - }, - Distance = 216, + }), Scale = 0.5f, } }, diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c805c55ed1..b167812c1d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -50,9 +50,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch X = position.X, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index b3d9f3c40c..901cc1ba9f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -3,7 +3,6 @@ using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; -using OpenTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -20,11 +19,9 @@ namespace osu.Game.Rulesets.Objects.Legacy /// /// s don't need a curve since they're converted to ruleset-specific hitobjects. /// - public SliderPath Path { get; } = null; - public Vector2[] ControlPoints { get; set; } - public PathType PathType { get; set; } + public SliderPath Path { get; set; } - public double Distance { get; set; } + public double Distance => Path.GetDistance(); public List> NodeSamples { get; set; } public int RepeatCount { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 90b7f3d554..fa5e769d3c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -31,9 +31,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania return new ConvertSlider { X = position.X, - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index bb41a147b0..e21903dc6d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -51,9 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - ControlPoints = controlPoints, - Distance = Math.Max(0, length), - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, Math.Max(0, length)), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index ae913b3bef..8e1e01a9fd 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -27,9 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko { return new ConvertSlider { - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 423cd3b069..195e429f2b 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -10,22 +10,31 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public class SliderPath + public readonly struct SliderPath { - public double Distance; + public readonly Vector2[] ControlPoints; + public readonly PathType Type; + public readonly double? ExpectedDistance; - public Vector2[] ControlPoints = Array.Empty(); + public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) + { + ControlPoints = controlPoints; + Type = type; + ExpectedDistance = expectedDistance; - public PathType PathType = PathType.PerfectCurve; + calculatedPath = new List(); + cumulativeLength = new List(); - public Vector2 Offset; + calculatePath(); + calculateCumulativeLength(); + } - private readonly List calculatedPath = new List(); - private readonly List cumulativeLength = new List(); + private readonly List calculatedPath; + private readonly List cumulativeLength; private List calculateSubpath(ReadOnlySpan subControlPoints) { - switch (PathType) + switch (Type) { case PathType.Linear: return PathApproximator.ApproximateLinear(subControlPoints); @@ -77,49 +86,6 @@ namespace osu.Game.Rulesets.Objects } } - private void calculateCumulativeLengthAndTrimPath() - { - double l = 0; - - cumulativeLength.Clear(); - cumulativeLength.Add(l); - - for (int i = 0; i < calculatedPath.Count - 1; ++i) - { - Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; - double d = diff.Length; - - // Shorten slider paths that are too long compared to what's - // in the .osu file. - if (Distance - l < d) - { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d); - calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - - l = Distance; - cumulativeLength.Add(l); - break; - } - - l += d; - cumulativeLength.Add(l); - } - - // Lengthen slider paths that are too short compared to what's - // in the .osu file. - if (l < Distance && calculatedPath.Count > 1) - { - Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; - double d = diff.Length; - - if (d <= 0) - return; - - calculatedPath[calculatedPath.Count - 1] += diff * (float)((Distance - l) / d); - cumulativeLength[calculatedPath.Count - 1] = Distance; - } - } - private void calculateCumulativeLength() { double l = 0; @@ -132,21 +98,33 @@ namespace osu.Game.Rulesets.Objects Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; double d = diff.Length; + // Shorted slider paths that are too long compared to the expected distance + if (ExpectedDistance.HasValue && ExpectedDistance - l < d) + { + calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d); + calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); + + l = ExpectedDistance.Value; + cumulativeLength.Add(l); + break; + } + l += d; cumulativeLength.Add(l); } - Distance = l; - } + // Lengthen slider paths that are too short compared to the expected distance + if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1) + { + Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; + double d = diff.Length; - public void Calculate(bool updateDistance = false) - { - calculatePath(); + if (d <= 0) + return; - if (!updateDistance) - calculateCumulativeLengthAndTrimPath(); - else - calculateCumulativeLength(); + calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d); + cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value; + } } private int indexOfDistance(double d) @@ -159,7 +137,7 @@ namespace osu.Game.Rulesets.Objects private double progressToDistance(double progress) { - return MathHelper.Clamp(progress, 0, 1) * Distance; + return MathHelper.Clamp(progress, 0, 1) * GetDistance(); } private Vector2 interpolateVertices(int i, double d) @@ -169,7 +147,7 @@ namespace osu.Game.Rulesets.Objects if (i <= 0) return calculatedPath.First(); - else if (i >= calculatedPath.Count) + if (i >= calculatedPath.Count) return calculatedPath.Last(); Vector2 p0 = calculatedPath[i - 1]; @@ -186,6 +164,8 @@ namespace osu.Game.Rulesets.Objects return p0 + (p1 - p0) * (float)w; } + public double GetDistance() => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) /// to 1 (end of the slider) and stores the generated path in the given list. @@ -195,23 +175,22 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { - if (calculatedPath.Count == 0 && ControlPoints.Length > 0) - Calculate(); - double d0 = progressToDistance(p0); double d1 = progressToDistance(p1); path.Clear(); int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) + { + } - path.Add(interpolateVertices(i, d0) + Offset); + path.Add(interpolateVertices(i, d0)); for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i] + Offset); + path.Add(calculatedPath[i]); - path.Add(interpolateVertices(i, d1) + Offset); + path.Add(interpolateVertices(i, d1)); } /// @@ -222,11 +201,8 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { - if (calculatedPath.Count == 0 && ControlPoints.Length > 0) - Calculate(); - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d) + Offset; + return interpolateVertices(indexOfDistance(d), d); } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 2a0d495e94..a097b62851 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -14,16 +14,6 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// SliderPath Path { get; } - - /// - /// The control points that shape the curve. - /// - Vector2[] ControlPoints { get; } - - /// - /// The type of curve. - /// - PathType PathType { get; } } public static class HasCurveExtensions From 51e4feeda768ec0765e3bbbfefd21a434e9eff9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 13:55:14 +0900 Subject: [PATCH 06/17] Adjust to new path structure --- .../Sliders/Components/SliderBodyPiece.cs | 2 - .../Sliders/SliderPlacementBlueprint.cs | 40 ++----------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 6fc7d39e6c..06bc265258 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -45,8 +45,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - slider.Path.Calculate(); - var vertices = new List(); slider.Path.GetPathToProgress(vertices, 0, 1); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index add9cb69f3..d59cd35f19 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -1,16 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using OpenTK; @@ -119,12 +118,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - for (int i = 0; i < segments.Count; i++) - segments[i].Calculate(i == segments.Count - 1 ? (Vector2?)cursor : null); - - HitObject.ControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray(); - HitObject.PathType = HitObject.ControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear; - HitObject.Distance = segments.Sum(s => s.Distance); + var newControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray(); + HitObject.Path = new SliderPath(newControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, newControlPoints); } private void setState(PlacementState newState) @@ -140,41 +135,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private class Segment { - public float Distance { get; private set; } - public readonly List ControlPoints = new List(); public Segment(Vector2 offset) { ControlPoints.Add(offset); } - - public void Calculate(Vector2? cursor = null) - { - Span allControlPoints = stackalloc Vector2[ControlPoints.Count + (cursor.HasValue ? 1 : 0)]; - - for (int i = 0; i < ControlPoints.Count; i++) - allControlPoints[i] = ControlPoints[i]; - if (cursor.HasValue) - allControlPoints[allControlPoints.Length - 1] = cursor.Value; - - List result; - - switch (allControlPoints.Length) - { - case 1: - case 2: - result = PathApproximator.ApproximateLinear(allControlPoints); - break; - default: - result = PathApproximator.ApproximateBezier(allControlPoints); - break; - } - - Distance = 0; - for (int i = 0; i < result.Count - 1; i++) - Distance += Vector2.Distance(result[i], result[i + 1]); - } } } } From 3b88d94793feafa815abf8a3e7ca9d18d6e92294 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:03:54 +0900 Subject: [PATCH 07/17] Make SliderPath.ControlPoints read-only --- .../Sliders/Components/PathControlPointPiece.cs | 9 ++++----- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 6 +++--- 3 files changed, 8 insertions(+), 9 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 22ad911c21..d46fa46c22 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.Path.ControlPoints[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints.Span[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; @@ -65,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); + path.AddVertex(slider.Path.ControlPoints.Span[index + 1] - slider.Path.ControlPoints.Span[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -106,8 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints.Span[index + 1] == slider.Path.ControlPoints.Span[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints.Span[index - 1] == slider.Path.ControlPoints.Span[index]; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 223e4df844..b66b5d3d39 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Mods var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; for (int i = 0; i < slider.Path.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); + newControlPoints[i] = new Vector2(slider.Path.ControlPoints.Span[i].X, -slider.Path.ControlPoints.Span[i].Y); slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 195e429f2b..5ad1cec6b6 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Objects { public readonly struct SliderPath { - public readonly Vector2[] ControlPoints; + public readonly ReadOnlyMemory ControlPoints; public readonly PathType Type; public readonly double? ExpectedDistance; @@ -73,9 +73,9 @@ namespace osu.Game.Rulesets.Objects { end++; - if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) + if (i == ControlPoints.Length - 1 || ControlPoints.Span[i] == ControlPoints.Span[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.Span.Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) From 3aba462e524a4f769114f197bf7edac5f2cfb3c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:07:48 +0900 Subject: [PATCH 08/17] Make Path.Distance a property again --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 6 +++--- osu.Game.Rulesets.Osu/Objects/Slider.cs | 6 +++--- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index a4e04ae837..d8bd3e0edc 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (TickDistance == 0) return; - var length = Path.GetDistance(); + var length = Path.Distance; var tickDistance = Math.Min(TickDistance, length); var spanDuration = length / Velocity; @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects set => path = value; } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public List> NodeSamples { get; set; } = new List>(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 07e526956a..cf57f24b83 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects public event Action PathChanged; - public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double Duration => EndTime - StartTime; public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects } } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public override Vector2 Position { @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createTicks() { - var length = Path.GetDistance(); + var length = Path.Distance; var tickDistance = MathHelper.Clamp(TickDistance, 0, length); if (tickDistance == 0) return; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 901cc1ba9f..0512a97354 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// public SliderPath Path { get; set; } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public List> NodeSamples { get; set; } public int RepeatCount { get; set; } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 5ad1cec6b6..3f0f0518d6 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Objects private double progressToDistance(double progress) { - return MathHelper.Clamp(progress, 0, 1) * GetDistance(); + return MathHelper.Clamp(progress, 0, 1) * Distance; } private Vector2 interpolateVertices(int i, double d) @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Objects return p0 + (p1 - p0) * (float)w; } - public double GetDistance() => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) From 4eef1134a629db002036fdde63ac37f2e476513f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:08:36 +0900 Subject: [PATCH 09/17] Re-order file --- osu.Game/Rulesets/Objects/SliderPath.cs | 86 ++++++++++++------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 3f0f0518d6..b81ccbe1e1 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -16,6 +16,9 @@ namespace osu.Game.Rulesets.Objects public readonly PathType Type; public readonly double? ExpectedDistance; + private readonly List calculatedPath; + private readonly List cumulativeLength; + public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { ControlPoints = controlPoints; @@ -29,8 +32,46 @@ namespace osu.Game.Rulesets.Objects calculateCumulativeLength(); } - private readonly List calculatedPath; - private readonly List cumulativeLength; + public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + + /// + /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) + /// to 1 (end of the slider) and stores the generated path in the given list. + /// + /// The list to be filled with the computed path. + /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + public void GetPathToProgress(List path, double p0, double p1) + { + double d0 = progressToDistance(p0); + double d1 = progressToDistance(p1); + + path.Clear(); + + int i = 0; + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) + { + } + + path.Add(interpolateVertices(i, d0)); + + for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) + path.Add(calculatedPath[i]); + + path.Add(interpolateVertices(i, d1)); + } + + /// + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) + /// to 1 (end of the path). + /// + /// Ranges from 0 (beginning of the path) to 1 (end of the path). + /// + public Vector2 PositionAt(double progress) + { + double d = progressToDistance(progress); + return interpolateVertices(indexOfDistance(d), d); + } private List calculateSubpath(ReadOnlySpan subControlPoints) { @@ -163,46 +204,5 @@ namespace osu.Game.Rulesets.Objects double w = (d - d0) / (d1 - d0); return p0 + (p1 - p0) * (float)w; } - - public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; - - /// - /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) - /// to 1 (end of the slider) and stores the generated path in the given list. - /// - /// The list to be filled with the computed path. - /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - public void GetPathToProgress(List path, double p0, double p1) - { - double d0 = progressToDistance(p0); - double d1 = progressToDistance(p1); - - path.Clear(); - - int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) - { - } - - path.Add(interpolateVertices(i, d0)); - - for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i]); - - path.Add(interpolateVertices(i, d1)); - } - - /// - /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) - /// to 1 (end of the path). - /// - /// Ranges from 0 (beginning of the path) to 1 (end of the path). - /// - public Vector2 PositionAt(double progress) - { - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d); - } } } From 77d16aa968d2ea6de8194cd3756c49484cfac720 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:16:21 +0900 Subject: [PATCH 10/17] Add xmldocs --- osu.Game/Rulesets/Objects/SliderPath.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index b81ccbe1e1..66acbeed68 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -12,13 +12,33 @@ namespace osu.Game.Rulesets.Objects { public readonly struct SliderPath { + /// + /// The control points of the path. + /// public readonly ReadOnlyMemory ControlPoints; + + /// + /// The type of path. + /// public readonly PathType Type; + + /// + /// The user-set distance of the path. If non-null, will match this value, + /// and the path will be shortened/lengthened to match this length. + /// public readonly double? ExpectedDistance; private readonly List calculatedPath; private readonly List cumulativeLength; + /// + /// Creates a new . + /// + /// The type of path. + /// The control points of the path. + /// A user-set distance of the path that may be shorter or longer than the true distance between all + /// . The path will be shortened/lengthened to match this length. + /// If null, the path will use the true distance between all . public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { ControlPoints = controlPoints; @@ -32,6 +52,9 @@ namespace osu.Game.Rulesets.Objects calculateCumulativeLength(); } + /// + /// The distance of the path after lengthening/shortening to account for . + /// public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; /// From d594ce35304218ac8eb613159737a66e1955bc34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:20:38 +0900 Subject: [PATCH 11/17] Revert "Make SliderPath.ControlPoints read-only" This reverts commit 3b88d94793feafa815abf8a3e7ca9d18d6e92294. # Conflicts: # osu.Game/Rulesets/Objects/SliderPath.cs --- .../Sliders/Components/PathControlPointPiece.cs | 9 +++++---- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 6 +++--- 3 files changed, 9 insertions(+), 8 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 d46fa46c22..22ad911c21 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.Path.ControlPoints.Span[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; @@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints.Span[index + 1] - slider.Path.ControlPoints.Span[index]); + path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -105,8 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints.Span[index + 1] == slider.Path.ControlPoints.Span[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints.Span[index - 1] == slider.Path.ControlPoints.Span[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index b66b5d3d39..223e4df844 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Mods var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; for (int i = 0; i < slider.Path.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.Path.ControlPoints.Span[i].X, -slider.Path.ControlPoints.Span[i].Y); + newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 66acbeed68..a174280456 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects /// /// The control points of the path. /// - public readonly ReadOnlyMemory ControlPoints; + public readonly Vector2[] ControlPoints; /// /// The type of path. @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.Objects { end++; - if (i == ControlPoints.Length - 1 || ControlPoints.Span[i] == ControlPoints.Span[i + 1]) + if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.Span.Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) From 8ad9b6a02a268f72940259e1f271670d28516876 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:14 +0900 Subject: [PATCH 12/17] Safety for default(SliderPath) --- osu.Game/Rulesets/Objects/SliderPath.cs | 52 ++++++++++++++++++------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index a174280456..27f864e2aa 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -10,26 +10,28 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public readonly struct SliderPath + public struct SliderPath { /// /// The control points of the path. /// public readonly Vector2[] ControlPoints; - /// - /// The type of path. - /// - public readonly PathType Type; - /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. /// public readonly double? ExpectedDistance; - private readonly List calculatedPath; - private readonly List cumulativeLength; + /// + /// The type of path. + /// + public readonly PathType Type; + + private List calculatedPath; + private List cumulativeLength; + + private bool isInitialised; /// /// Creates a new . @@ -41,21 +43,26 @@ namespace osu.Game.Rulesets.Objects /// If null, the path will use the true distance between all . public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { + this = default; + ControlPoints = controlPoints; Type = type; ExpectedDistance = expectedDistance; - calculatedPath = new List(); - cumulativeLength = new List(); - - calculatePath(); - calculateCumulativeLength(); + ensureInitialised(); } /// /// The distance of the path after lengthening/shortening to account for . /// - public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + public double Distance + { + get + { + ensureInitialised(); + return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + } + } /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) @@ -66,6 +73,8 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { + ensureInitialised(); + double d0 = progressToDistance(p0); double d1 = progressToDistance(p1); @@ -92,10 +101,25 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { + ensureInitialised(); + double d = progressToDistance(progress); return interpolateVertices(indexOfDistance(d), d); } + private void ensureInitialised() + { + if (isInitialised) + return; + isInitialised = true; + + calculatedPath = new List(); + cumulativeLength = new List(); + + calculatePath(); + calculateCumulativeLength(); + } + private List calculateSubpath(ReadOnlySpan subControlPoints) { switch (Type) From 0e92b385f01500c85fb6b94ec60793bbb40bab1f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:33 +0900 Subject: [PATCH 13/17] Define default json deserialisation constructor --- osu.Game/Rulesets/Objects/SliderPath.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 27f864e2aa..c5d3a39ab1 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Types; using OpenTK; @@ -41,6 +42,7 @@ namespace osu.Game.Rulesets.Objects /// A user-set distance of the path that may be shorter or longer than the true distance between all /// . The path will be shortened/lengthened to match this length. /// If null, the path will use the true distance between all . + [JsonConstructor] public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { this = default; From 0220ed21b03530088525ce15cd0e56c6d6abb819 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:39 +0900 Subject: [PATCH 14/17] Ignore distance for json serialisation --- osu.Game/Rulesets/Objects/SliderPath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index c5d3a39ab1..2bb903155e 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Objects /// /// The distance of the path after lengthening/shortening to account for . /// + [JsonIgnore] public double Distance { get From f4fd6189f892bd25cc37af571dd7183fc2e667de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:53:30 +0900 Subject: [PATCH 15/17] Implement IEquatable --- osu.Game/Rulesets/Objects/SliderPath.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 2bb903155e..548e1680f7 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -11,7 +11,7 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public struct SliderPath + public struct SliderPath : IEquatable { /// /// The control points of the path. @@ -254,5 +254,21 @@ namespace osu.Game.Rulesets.Objects double w = (d - d0) / (d1 - d0); return p0 + (p1 - p0) * (float)w; } + + public bool Equals(SliderPath other) + { + if (ControlPoints == null && other.ControlPoints != null) + return false; + if (other.ControlPoints == null && ControlPoints != null) + return false; + + return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is SliderPath other && Equals(other); + } } } From 1101e161d9155a5e51da153bf9c15fb9e325be4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Nov 2018 17:01:50 +0900 Subject: [PATCH 16/17] Update framework and resources --- osu-resources | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-resources b/osu-resources index 9ee64e369f..651e598b01 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 9ee64e369fe6fdafc6aed40f5a35b5f01eb82c53 +Subproject commit 651e598b016b43e31ab1c1b29d5b30c92361b8d9 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9461ea504..9f7996a5fd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From f3ba4297018b97fb4eacd1b4d118a6aeb364e6a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 17:10:37 +0900 Subject: [PATCH 17/17] Make sure control points is internally initialised --- .../Components/PathControlPointPiece.cs | 1 - osu.Game/Rulesets/Objects/SliderPath.cs | 26 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 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 22ad911c21..7100d9443e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 548e1680f7..74a312698c 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Objects { public struct SliderPath : IEquatable { - /// - /// The control points of the path. - /// - public readonly Vector2[] ControlPoints; - /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. @@ -29,6 +24,9 @@ namespace osu.Game.Rulesets.Objects /// public readonly PathType Type; + [JsonProperty] + private Vector2[] controlPoints; + private List calculatedPath; private List cumulativeLength; @@ -46,14 +44,27 @@ namespace osu.Game.Rulesets.Objects public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { this = default; + this.controlPoints = controlPoints; - ControlPoints = controlPoints; Type = type; ExpectedDistance = expectedDistance; ensureInitialised(); } + /// + /// The control points of the path. + /// + [JsonIgnore] + public ReadOnlySpan ControlPoints + { + get + { + ensureInitialised(); + return controlPoints.AsSpan(); + } + } + /// /// The distance of the path after lengthening/shortening to account for . /// @@ -116,6 +127,7 @@ namespace osu.Game.Rulesets.Objects return; isInitialised = true; + controlPoints = controlPoints ?? Array.Empty(); calculatedPath = new List(); cumulativeLength = new List(); @@ -166,7 +178,7 @@ namespace osu.Game.Rulesets.Objects if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t)