1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 01:52:55 +08:00

Move common functionality out of OsuSelectionHandler and implement flip support

This commit is contained in:
Dean Herbert 2021-05-18 18:34:06 +09:00
parent c80e736712
commit d661e98fa6
3 changed files with 74 additions and 56 deletions

View File

@ -12,12 +12,23 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using Vector2 = osuTK.Vector2;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
{ {
public class OsuSelectionHandler : EditorSelectionHandler public class OsuSelectionHandler : EditorSelectionHandler
{ {
/// <summary>
/// During a transform, the initial origin is stored so it can be used throughout the operation.
/// </summary>
private Vector2? referenceOrigin;
/// <summary>
/// During a transform, the initial path types of a single selected slider are stored so they
/// can be maintained throughout the operation.
/// </summary>
private List<PathType?> referencePathTypes;
protected override void OnSelectionChanged() protected override void OnSelectionChanged()
{ {
base.OnSelectionChanged(); base.OnSelectionChanged();
@ -50,17 +61,6 @@ namespace osu.Game.Rulesets.Osu.Edit
return true; return true;
} }
/// <summary>
/// During a transform, the initial origin is stored so it can be used throughout the operation.
/// </summary>
private Vector2? referenceOrigin;
/// <summary>
/// During a transform, the initial path types of a single selected slider are stored so they
/// can be maintained throughout the operation.
/// </summary>
private List<PathType?> referencePathTypes;
public override bool HandleReverse() public override bool HandleReverse()
{ {
var hitObjects = EditorBeatmap.SelectedHitObjects; var hitObjects = EditorBeatmap.SelectedHitObjects;
@ -114,24 +114,10 @@ namespace osu.Game.Rulesets.Osu.Edit
var hitObjects = selectedMovableObjects; var hitObjects = selectedMovableObjects;
var selectedObjectsQuad = getSurroundingQuad(hitObjects); var selectedObjectsQuad = getSurroundingQuad(hitObjects);
var centre = selectedObjectsQuad.Centre;
foreach (var h in hitObjects) foreach (var h in hitObjects)
{ {
var pos = h.Position; h.Position = GetFlippedPosition(direction, selectedObjectsQuad, h.Position);
switch (direction)
{
case Direction.Horizontal:
pos.X = centre.X - (pos.X - centre.X);
break;
case Direction.Vertical:
pos.Y = centre.Y - (pos.Y - centre.Y);
break;
}
h.Position = pos;
if (h is Slider slider) if (h is Slider slider)
{ {
@ -204,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList(); referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList();
Quad sliderQuad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0. // Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size; scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
@ -333,7 +319,7 @@ namespace osu.Game.Rulesets.Osu.Edit
/// </summary> /// </summary>
/// <param name="hitObjects">The hit objects to calculate a quad for.</param> /// <param name="hitObjects">The hit objects to calculate a quad for.</param>
private Quad getSurroundingQuad(OsuHitObject[] hitObjects) => private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
getSurroundingQuad(hitObjects.SelectMany(h => GetSurroundingQuad(hitObjects.SelectMany(h =>
{ {
if (h is IHasPath path) if (h is IHasPath path)
{ {
@ -348,30 +334,6 @@ namespace osu.Game.Rulesets.Osu.Edit
return new[] { h.Position }; return new[] { h.Position };
})); }));
/// <summary>
/// Returns a gamefield-space quad surrounding the provided points.
/// </summary>
/// <param name="points">The points to calculate a quad for.</param>
private Quad getSurroundingQuad(IEnumerable<Vector2> points)
{
if (!EditorBeatmap.SelectedHitObjects.Any())
return new Quad();
Vector2 minPosition = new Vector2(float.MaxValue, float.MaxValue);
Vector2 maxPosition = new Vector2(float.MinValue, float.MinValue);
// Go through all hitobjects to make sure they would remain in the bounds of the editor after movement, before any movement is attempted
foreach (var p in points)
{
minPosition = Vector2.ComponentMin(minPosition, p);
maxPosition = Vector2.ComponentMax(maxPosition, p);
}
Vector2 size = maxPosition - minPosition;
return new Quad(minPosition.X, minPosition.Y, size.X, size.Y);
}
/// <summary> /// <summary>
/// All osu! hitobjects which can be moved/rotated/scaled. /// All osu! hitobjects which can be moved/rotated/scaled.
/// </summary> /// </summary>

View File

@ -350,5 +350,55 @@ namespace osu.Game.Screens.Edit.Compose.Components
=> Enumerable.Empty<MenuItem>(); => Enumerable.Empty<MenuItem>();
#endregion #endregion
#region Helper Methods
/// <summary>
/// Given a flip direction, a surrounding quad for all selected objects, and a position,
/// will return the flipped position in screen space coordinates.
/// </summary>
protected static Vector2 GetFlippedPosition(Direction direction, Quad quad, Vector2 position)
{
var centre = quad.Centre;
switch (direction)
{
case Direction.Horizontal:
position.X = centre.X - (position.X - centre.X);
break;
case Direction.Vertical:
position.Y = centre.Y - (position.Y - centre.Y);
break;
}
return position;
}
/// <summary>
/// Returns a quad surrounding the provided points.
/// </summary>
/// <param name="points">The points to calculate a quad for.</param>
protected static Quad GetSurroundingQuad(IEnumerable<Vector2> points)
{
if (!points.Any())
return new Quad();
Vector2 minPosition = new Vector2(float.MaxValue, float.MaxValue);
Vector2 maxPosition = new Vector2(float.MinValue, float.MinValue);
// Go through all hitobjects to make sure they would remain in the bounds of the editor after movement, before any movement is attempted
foreach (var p in points)
{
minPosition = Vector2.ComponentMin(minPosition, p);
maxPosition = Vector2.ComponentMax(maxPosition, p);
}
Vector2 size = maxPosition - minPosition;
return new Quad(minPosition.X, minPosition.Y, size.X, size.Y);
}
#endregion
} }
} }

View File

@ -43,10 +43,16 @@ namespace osu.Game.Skinning.Editor
public override bool HandleFlip(Direction direction) public override bool HandleFlip(Direction direction)
{ {
// TODO: this is temporary as well. var selectionQuad = GetSurroundingQuad(SelectedBlueprints.Select(b => b.ScreenSpaceSelectionPoint));
foreach (var c in SelectedBlueprints)
foreach (var b in SelectedBlueprints)
{ {
((Drawable)c.Item).Scale *= new Vector2( var drawableItem = (Drawable)b.Item;
drawableItem.Position =
drawableItem.Parent.ToLocalSpace(GetFlippedPosition(direction, selectionQuad, b.ScreenSpaceSelectionPoint)) - drawableItem.AnchorPosition;
drawableItem.Scale *= new Vector2(
direction == Direction.Horizontal ? -1 : 1, direction == Direction.Horizontal ? -1 : 1,
direction == Direction.Vertical ? -1 : 1 direction == Direction.Vertical ? -1 : 1
); );