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 fcf1c44e69..7d9ed6b46c 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.Framework.Utils; using osu.Game.Extensions; @@ -24,11 +25,26 @@ namespace osu.Game.Skinning.Editor 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 = getSelectionQuad(); - return base.HandleRotation(angle); + foreach (var b in SelectedBlueprints) + { + var drawableItem = (Drawable)b.Item; + + drawableItem.Position = drawableItem.Parent.ToLocalSpace(RotatePointAroundOrigin(b.ScreenSpaceSelectionPoint, selectionQuad.Centre, 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) @@ -39,8 +55,7 @@ namespace osu.Game.Skinning.Editor adjustScaleFromAnchor(ref scale, anchor); // the selection quad is always upright, so use an AABB rect to make mutating the values easier. - var selectionRect = GetSurroundingQuad(SelectedBlueprints.SelectMany(b => - b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())).AABBFloat; + var selectionRect = getSelectionQuad().AABBFloat; // copy to mutate, as we will need to compare to the original later on. var adjustedRect = selectionRect; @@ -199,6 +214,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)