Implement proper rotation algorithm for skin editor

This commit is contained in:
Dean Herbert 2021-05-20 18:21:16 +09:00
parent a55879e511
commit 27e81d6504
3 changed files with 57 additions and 31 deletions

View File

@ -1,7 +1,6 @@
// 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 osu.Framework.Graphics;
@ -12,7 +11,7 @@
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
using Vector2 = osuTK.Vector2;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
@ -173,12 +172,12 @@ public override bool HandleRotation(float delta)
foreach (var h in hitObjects)
{
h.Position = rotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
h.Position = RotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
if (h is IHasPath path)
{
foreach (var point in path.Path.ControlPoints)
point.Position.Value = rotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
}
}
@ -324,28 +323,5 @@ private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType<OsuHitObject>()
.Where(h => !(h is Spinner))
.ToArray();
/// <summary>
/// Rotate a point around an arbitrary origin.
/// </summary>
/// <param name="point">The point.</param>
/// <param name="origin">The centre origin to rotate around.</param>
/// <param name="angle">The angle to rotate (in degrees).</param>
private static Vector2 rotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle)
{
angle = -angle;
point.X -= origin.X;
point.Y -= origin.Y;
Vector2 ret;
ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle));
ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle));
ret.X += origin.X;
ret.Y += origin.Y;
return ret;
}
}
}

View File

@ -14,6 +14,7 @@
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
@ -353,6 +354,29 @@ protected virtual IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumera
#region Helper Methods
/// <summary>
/// Rotate a point around an arbitrary origin.
/// </summary>
/// <param name="point">The point.</param>
/// <param name="origin">The centre origin to rotate around.</param>
/// <param name="angle">The angle to rotate (in degrees).</param>
protected static Vector2 RotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle)
{
angle = -angle;
point.X -= origin.X;
point.Y -= origin.Y;
Vector2 ret;
ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle));
ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle));
ret.X += origin.X;
ret.Y += origin.Y;
return ret;
}
/// <summary>
/// Given a flip direction, a surrounding quad for all selected objects, and a position,
/// will return the flipped position in screen space coordinates.

View File

@ -18,16 +18,42 @@ namespace osu.Game.Skinning.Editor
{
public class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
{
private Vector2? referenceOrigin;
[Resolved]
private SkinEditor skinEditor { get; set; }
protected override void OnOperationEnded()
{
base.OnOperationEnded();
referenceOrigin = null;
}
public override bool HandleRotation(float angle)
{
// TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection.
foreach (var c in SelectedBlueprints)
((Drawable)c.Item).Rotation += angle;
if (SelectedBlueprints.Count == 1)
{
// for single items, rotate around the origin rather than the selection centre.
((Drawable)SelectedBlueprints.First().Item).Rotation += angle;
}
else
{
var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b =>
b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray()));
return base.HandleRotation(angle);
referenceOrigin ??= selectionQuad.Centre;
foreach (var b in SelectedBlueprints)
{
var drawableItem = (Drawable)b.Item;
drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, referenceOrigin.Value, angle)) - drawableItem.AnchorPosition;
drawableItem.Rotation += angle;
}
}
// this isn't always the case but let's be lenient for now.
return true;
}
public override bool HandleScale(Vector2 scale, Anchor anchor)