From b0f5ace0e88b07ee34bbb2ed2020fa0e7ce64b33 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 29 Oct 2018 14:07:06 +0900 Subject: [PATCH] Implement slider control point visualisation --- .../TestCaseSliderSelectionMask.cs | 13 +++ .../Components/ControlPointPiece.cs | 90 +++++++++++++++++++ .../Components/ControlPointVisualiser.cs | 34 +++++++ .../Masks/SliderMasks/SliderSelectionMask.cs | 1 + osu.Game.Rulesets.Osu/Objects/Slider.cs | 13 ++- 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointPiece.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointVisualiser.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs index 5e68d5cdc9..a73cb69536 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs @@ -1,11 +1,14 @@ // 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 osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks; +using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; @@ -15,6 +18,16 @@ namespace osu.Game.Rulesets.Osu.Tests { public class TestCaseSliderSelectionMask : HitObjectSelectionMaskTestCase { + public override IReadOnlyList RequiredTypes => new Type[] + { + typeof(SliderSelectionMask), + typeof(SliderCircleSelectionMask), + typeof(SliderBodyPiece), + typeof(SliderCircle), + typeof(ControlPointVisualiser), + typeof(ControlPointPiece) + }; + private readonly DrawableSlider drawableObject; public TestCaseSliderSelectionMask() diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointPiece.cs new file mode 100644 index 0000000000..5a3c5d5aaf --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointPiece.cs @@ -0,0 +1,90 @@ +// 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; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components +{ + public class ControlPointPiece : CompositeDrawable + { + private readonly Slider slider; + private readonly int index; + + private readonly Path path; + private readonly CircularContainer marker; + + [Resolved] + private OsuColour colours { get; set; } + + public ControlPointPiece(Slider slider, int index) + { + this.slider = slider; + this.index = index; + + Origin = Anchor.Centre; + Size = new Vector2(10); + + InternalChildren = new Drawable[] + { + path = new SmoothPath + { + BypassAutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + PathWidth = 1 + }, + marker = new CircularContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = new Box { RelativeSizeAxes = Axes.Both } + } + }; + } + + protected override void Update() + { + base.Update(); + + Position = slider.StackedPosition + slider.ControlPoints[index]; + + marker.Colour = segmentSeparator ? colours.Red : colours.Yellow; + + path.ClearVertices(); + + if (index != slider.ControlPoints.Length - 1) + { + path.AddVertex(Vector2.Zero); + path.AddVertex(slider.ControlPoints[index + 1] - slider.ControlPoints[index]); + } + + path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); + } + + protected override bool OnDragStart(DragStartEvent e) => true; + + protected override bool OnDrag(DragEvent e) + { + var newControlPoints = slider.ControlPoints.ToArray(); + newControlPoints[index] += e.Delta; + + slider.ControlPoints = newControlPoints; + + return true; + } + + protected override bool OnDragEnd(DragEndEvent e) => true; + + private bool segmentSeparator => index != 0 && index != slider.ControlPoints.Length - 1 + && slider.ControlPoints[index - 1] != slider.ControlPoints[index] + && slider.ControlPoints[index + 1] != slider.ControlPoints[index]; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointVisualiser.cs new file mode 100644 index 0000000000..d8031c4f5b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/ControlPointVisualiser.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components +{ + public class ControlPointVisualiser : CompositeDrawable + { + private readonly Slider slider; + + private readonly Container pieces; + + public ControlPointVisualiser(Slider slider) + { + this.slider = slider; + + InternalChild = pieces = new Container { RelativeSizeAxes = Axes.Both }; + + slider.ControlPointsChanged += _ => updateControlPoints(); + updateControlPoints(); + } + + private void updateControlPoints() + { + while (slider.ControlPoints.Length > pieces.Count) + pieces.Add(new ControlPointPiece(slider, pieces.Count)); + while (slider.ControlPoints.Length < pieces.Count) + pieces.Remove(pieces[pieces.Count - 1]); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs index a411064f68..8478374a5f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks new SliderBodyPiece(sliderObject), headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start), new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End), + new ControlPointVisualiser(sliderObject), }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index a6f5bdb24e..10ffe82579 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects /// private const float base_scoring_distance = 100; + public event Action ControlPointsChanged; + public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; @@ -54,8 +56,15 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2[] ControlPoints { - get { return Curve.ControlPoints; } - set { Curve.ControlPoints = value; } + get => Curve.ControlPoints; + set + { + if (Curve.ControlPoints == value) + return; + Curve.ControlPoints = value; + + ControlPointsChanged?.Invoke(value); + } } public CurveType CurveType