From 27e81d6504aff75e4e0673496c878c8fb0698ac8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:21:16 +0900 Subject: [PATCH 1/3] Implement proper rotation algorithm for skin editor --- .../Edit/OsuSelectionHandler.cs | 30 ++-------------- .../Compose/Components/SelectionHandler.cs | 24 +++++++++++++ .../Skinning/Editor/SkinSelectionHandler.cs | 34 ++++++++++++++++--- 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 8cb86bc108..57d0cd859d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . 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; 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 @@ namespace osu.Game.Rulesets.Osu.Edit 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 @@ namespace osu.Game.Rulesets.Osu.Edit private OsuHitObject[] selectedMovableObjects => SelectedItems.OfType() .Where(h => !(h is Spinner)) .ToArray(); - - /// - /// Rotate a point around an arbitrary origin. - /// - /// The point. - /// The centre origin to rotate around. - /// The angle to rotate (in degrees). - 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; - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 26328b4dc7..8939be925a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface; 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 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Helper Methods + /// + /// Rotate a point around an arbitrary origin. + /// + /// The point. + /// The centre origin to rotate around. + /// The angle to rotate (in degrees). + 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; + } + /// /// Given a flip direction, a surrounding quad for all selected objects, and a position, /// will return the flipped position in screen space coordinates. diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 2f9611ba65..cc4c120e23 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -18,16 +18,42 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { + 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) From 95c78b918510e8c4ebbc68a95496e0956a676278 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 May 2021 18:24:25 +0900 Subject: [PATCH 2/3] Split out common selection quad logic --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index cc4c120e23..ec45830f94 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; @@ -38,8 +39,7 @@ namespace osu.Game.Skinning.Editor } else { - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + var selectionQuad = getSelectionQuad(); referenceOrigin ??= selectionQuad.Centre; @@ -63,8 +63,7 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); - var selectionQuad = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + var selectionQuad = getSelectionQuad(); // the selection quad is always upright, so use a rect to make mutating the values easier. var adjustedRect = selectionQuad.AABBFloat; @@ -220,6 +219,13 @@ namespace osu.Game.Skinning.Editor } } + /// + /// A screen-space quad surrounding all selected drawables, accounting for their full displayed size. + /// + /// + private Quad getSelectionQuad() => + GetSurroundingQuad(SelectedBlueprints.SelectMany(b => b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())); + private void applyAnchor(Anchor anchor) { foreach (var item in SelectedItems) From 7d88a19d7f67bc540646eed0992ba909fd51990b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 May 2021 21:03:40 +0900 Subject: [PATCH 3/3] Remove unnecessary field storage of origin reference --- osu.Game/Skinning/Editor/SkinSelectionHandler.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs index 7f04252c6b..7d9ed6b46c 100644 --- a/osu.Game/Skinning/Editor/SkinSelectionHandler.cs +++ b/osu.Game/Skinning/Editor/SkinSelectionHandler.cs @@ -20,17 +20,9 @@ namespace osu.Game.Skinning.Editor { public class SkinSelectionHandler : SelectionHandler { - private Vector2? referenceOrigin; - [Resolved] private SkinEditor skinEditor { get; set; } - protected override void OnOperationEnded() - { - base.OnOperationEnded(); - referenceOrigin = null; - } - public override bool HandleRotation(float angle) { if (SelectedBlueprints.Count == 1) @@ -42,13 +34,11 @@ namespace osu.Game.Skinning.Editor { var selectionQuad = getSelectionQuad(); - 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.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, selectionQuad.Centre, angle)) - drawableItem.AnchorPosition; drawableItem.Rotation += angle; } }