mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 00:40:09 +09:00
Fix connections hidden due to overlapping controlpoints
This commit is contained in:
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestScenePathControlPointVisualiser : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(StringHumanizeExtensions),
|
||||||
|
typeof(PathControlPointPiece),
|
||||||
|
typeof(PathControlPointConnectionPiece)
|
||||||
|
};
|
||||||
|
|
||||||
|
private Slider slider;
|
||||||
|
private PathControlPointVisualiser visualiser;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
slider = new Slider();
|
||||||
|
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddOverlappingControlPoints()
|
||||||
|
{
|
||||||
|
createVisualiser(true);
|
||||||
|
|
||||||
|
addControlPointStep(new Vector2(200));
|
||||||
|
addControlPointStep(new Vector2(300));
|
||||||
|
addControlPointStep(new Vector2(300));
|
||||||
|
addControlPointStep(new Vector2(500, 300));
|
||||||
|
|
||||||
|
AddAssert("last connection displayed", () =>
|
||||||
|
{
|
||||||
|
var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300));
|
||||||
|
return lastConnection.DrawWidth > 50;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
});
|
||||||
|
|
||||||
|
private void addControlPointStep(Vector2 position) => AddStep($"add control point {position}", () => slider.Path.ControlPoints.Add(new PathControlPoint(position)));
|
||||||
|
}
|
||||||
|
}
|
@ -16,22 +16,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PathControlPointConnectionPiece : CompositeDrawable
|
public class PathControlPointConnectionPiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
public PathControlPoint ControlPoint;
|
public readonly PathControlPoint ControlPoint;
|
||||||
|
|
||||||
private readonly Path path;
|
private readonly Path path;
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
private readonly int controlPointIndex;
|
||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> sliderPosition;
|
||||||
private IBindable<int> pathVersion;
|
private IBindable<int> pathVersion;
|
||||||
|
|
||||||
public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
|
public PathControlPointConnectionPiece(Slider slider, int controlPointIndex)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
ControlPoint = controlPoint;
|
this.controlPointIndex = controlPointIndex;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
ControlPoint = slider.Path.ControlPoints[controlPointIndex];
|
||||||
|
|
||||||
InternalChild = path = new SmoothPath
|
InternalChild = path = new SmoothPath
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -61,13 +64,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
path.ClearVertices();
|
path.ClearVertices();
|
||||||
|
|
||||||
int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
|
int nextIndex = controlPointIndex + 1;
|
||||||
|
if (nextIndex == 0 || nextIndex == slider.Path.ControlPoints.Count)
|
||||||
if (index == 0 || index == slider.Path.ControlPoints.Count)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
path.AddVertex(Vector2.Zero);
|
path.AddVertex(Vector2.Zero);
|
||||||
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
|
path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value);
|
||||||
|
|
||||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -24,17 +25,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||||
{
|
{
|
||||||
internal readonly Container<PathControlPointPiece> Pieces;
|
internal readonly Container<PathControlPointPiece> Pieces;
|
||||||
|
internal readonly Container<PathControlPointConnectionPiece> Connections;
|
||||||
|
|
||||||
private readonly Container<PathControlPointConnectionPiece> connections;
|
private readonly IBindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
|
||||||
private readonly bool allowSelection;
|
private readonly bool allowSelection;
|
||||||
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
private IBindableList<PathControlPoint> controlPoints;
|
|
||||||
|
|
||||||
public Action<List<PathControlPoint>> RemoveControlPointsRequested;
|
public Action<List<PathControlPoint>> RemoveControlPointsRequested;
|
||||||
|
|
||||||
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
public PathControlPointVisualiser(Slider slider, bool allowSelection)
|
||||||
@ -46,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
|
Connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
|
||||||
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
|
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -57,33 +55,38 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
inputManager = GetContainingInputManager();
|
inputManager = GetContainingInputManager();
|
||||||
|
|
||||||
controlPoints = slider.Path.ControlPoints.GetBoundCopy();
|
controlPoints.CollectionChanged += onControlPointsChanged;
|
||||||
controlPoints.ItemsAdded += addControlPoints;
|
controlPoints.BindTo(slider.Path.ControlPoints);
|
||||||
controlPoints.ItemsRemoved += removeControlPoints;
|
|
||||||
|
|
||||||
addControlPoints(controlPoints);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addControlPoints(IEnumerable<PathControlPoint> controlPoints)
|
private void onControlPointsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
foreach (var point in controlPoints)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
case NotifyCollectionChangedAction.Add:
|
||||||
{
|
for (int i = 0; i < e.NewItems.Count; i++)
|
||||||
if (allowSelection)
|
{
|
||||||
d.RequestSelection = selectPiece;
|
var point = (PathControlPoint)e.NewItems[i];
|
||||||
}));
|
|
||||||
|
|
||||||
connections.Add(new PathControlPointConnectionPiece(slider, point));
|
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
||||||
}
|
{
|
||||||
}
|
if (allowSelection)
|
||||||
|
d.RequestSelection = selectPiece;
|
||||||
|
}));
|
||||||
|
|
||||||
private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints)
|
Connections.Add(new PathControlPointConnectionPiece(slider, e.NewStartingIndex + i));
|
||||||
{
|
}
|
||||||
foreach (var point in controlPoints)
|
|
||||||
{
|
break;
|
||||||
Pieces.RemoveAll(p => p.ControlPoint == point);
|
|
||||||
connections.RemoveAll(c => c.ControlPoint == point);
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
foreach (var point in e.OldItems.Cast<PathControlPoint>())
|
||||||
|
{
|
||||||
|
Pieces.RemoveAll(p => p.ControlPoint == point);
|
||||||
|
Connections.RemoveAll(c => c.ControlPoint == point);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user