1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 18:32:55 +08:00

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. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -12,7 +11,7 @@ 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 Vector2 = osuTK.Vector2; using osuTK;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
{ {
@ -173,12 +172,12 @@ namespace osu.Game.Rulesets.Osu.Edit
foreach (var h in hitObjects) 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) if (h is IHasPath path)
{ {
foreach (var point in path.Path.ControlPoints) 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 @@ namespace osu.Game.Rulesets.Osu.Edit
private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType<OsuHitObject>() private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType<OsuHitObject>()
.Where(h => !(h is Spinner)) .Where(h => !(h is Spinner))
.ToArray(); .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.Graphics.UserInterface;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
@ -353,6 +354,29 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region Helper Methods #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> /// <summary>
/// Given a flip direction, a surrounding quad for all selected objects, and a position, /// Given a flip direction, a surrounding quad for all selected objects, and a position,
/// will return the flipped position in screen space coordinates. /// 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> public class SkinSelectionHandler : SelectionHandler<ISkinnableDrawable>
{ {
private Vector2? referenceOrigin;
[Resolved] [Resolved]
private SkinEditor skinEditor { get; set; } private SkinEditor skinEditor { get; set; }
protected override void OnOperationEnded()
{
base.OnOperationEnded();
referenceOrigin = null;
}
public override bool HandleRotation(float angle) public override bool HandleRotation(float angle)
{ {
// TODO: this doesn't correctly account for origin/anchor specs being different in a multi-selection. if (SelectedBlueprints.Count == 1)
foreach (var c in SelectedBlueprints) {
((Drawable)c.Item).Rotation += angle; // 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) public override bool HandleScale(Vector2 scale, Anchor anchor)