mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 18:53:21 +08:00
Merge pull request #12866 from peppy/refactor-selection-scale-logic
Add proper scaling support to skin editor
This commit is contained in:
commit
485728ca4e
@ -225,26 +225,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
||||||
{
|
{
|
||||||
scale = getClampedScale(hitObjects, reference, scale);
|
scale = getClampedScale(hitObjects, reference, scale);
|
||||||
|
|
||||||
// move the selection before scaling if dragging from top or left anchors.
|
|
||||||
float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0;
|
|
||||||
float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0;
|
|
||||||
|
|
||||||
Quad selectionQuad = getSurroundingQuad(hitObjects);
|
Quad selectionQuad = getSurroundingQuad(hitObjects);
|
||||||
|
|
||||||
foreach (var h in hitObjects)
|
foreach (var h in hitObjects)
|
||||||
{
|
h.Position = GetScaledPosition(reference, scale, selectionQuad, h.Position);
|
||||||
var newPosition = h.Position;
|
|
||||||
|
|
||||||
// guard against no-ops and NaN.
|
|
||||||
if (scale.X != 0 && selectionQuad.Width > 0)
|
|
||||||
newPosition.X = selectionQuad.TopLeft.X + xOffset + (h.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X);
|
|
||||||
|
|
||||||
if (scale.Y != 0 && selectionQuad.Height > 0)
|
|
||||||
newPosition.Y = selectionQuad.TopLeft.Y + yOffset + (h.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y);
|
|
||||||
|
|
||||||
h.Position = newPosition;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private (bool X, bool Y) isQuadInBounds(Quad quad)
|
private (bool X, bool Y) isQuadInBounds(Quad quad)
|
||||||
|
@ -375,6 +375,26 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a scale vector, a surrounding quad for all selected objects, and a position,
|
||||||
|
/// will return the scaled position in screen space coordinates.
|
||||||
|
/// </summary>
|
||||||
|
protected static Vector2 GetScaledPosition(Anchor reference, Vector2 scale, Quad selectionQuad, Vector2 position)
|
||||||
|
{
|
||||||
|
// adjust the direction of scale depending on which side the user is dragging.
|
||||||
|
float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0;
|
||||||
|
float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0;
|
||||||
|
|
||||||
|
// guard against no-ops and NaN.
|
||||||
|
if (scale.X != 0 && selectionQuad.Width > 0)
|
||||||
|
position.X = selectionQuad.TopLeft.X + xOffset + (position.X - selectionQuad.TopLeft.X) / selectionQuad.Width * (selectionQuad.Width + scale.X);
|
||||||
|
|
||||||
|
if (scale.Y != 0 && selectionQuad.Height > 0)
|
||||||
|
position.Y = selectionQuad.TopLeft.Y + yOffset + (position.Y - selectionQuad.TopLeft.Y) / selectionQuad.Height * (selectionQuad.Height + scale.Y);
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a quad surrounding the provided points.
|
/// Returns a quad surrounding the provided points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -32,11 +33,72 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
public override bool HandleScale(Vector2 scale, Anchor anchor)
|
public override bool HandleScale(Vector2 scale, Anchor anchor)
|
||||||
{
|
{
|
||||||
|
// convert scale to screen space
|
||||||
|
scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero);
|
||||||
|
|
||||||
adjustScaleFromAnchor(ref scale, anchor);
|
adjustScaleFromAnchor(ref scale, anchor);
|
||||||
|
|
||||||
foreach (var c in SelectedBlueprints)
|
// the selection quad is always upright, so use an AABB rect to make mutating the values easier.
|
||||||
// TODO: this is temporary and will be fixed with a separate refactor of selection transform logic.
|
var selectionRect = GetSurroundingQuad(SelectedBlueprints.SelectMany(b =>
|
||||||
((Drawable)c.Item).Scale += scale * 0.02f;
|
b.Item.ScreenSpaceDrawQuad.GetVertices().ToArray())).AABBFloat;
|
||||||
|
|
||||||
|
// copy to mutate, as we will need to compare to the original later on.
|
||||||
|
var adjustedRect = selectionRect;
|
||||||
|
|
||||||
|
// first, remove any scale axis we are not interested in.
|
||||||
|
if (anchor.HasFlagFast(Anchor.x1)) scale.X = 0;
|
||||||
|
if (anchor.HasFlagFast(Anchor.y1)) scale.Y = 0;
|
||||||
|
|
||||||
|
bool shouldAspectLock =
|
||||||
|
// for now aspect lock scale adjustments that occur at corners..
|
||||||
|
(!anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1))
|
||||||
|
// ..or if any of the selection have been rotated.
|
||||||
|
// this is to avoid requiring skew logic (which would likely not be the user's expected transform anyway).
|
||||||
|
|| SelectedBlueprints.Any(b => !Precision.AlmostEquals(((Drawable)b.Item).Rotation, 0));
|
||||||
|
|
||||||
|
if (shouldAspectLock)
|
||||||
|
{
|
||||||
|
if (anchor.HasFlagFast(Anchor.x1))
|
||||||
|
// if dragging from the horizontal centre, only a vertical component is available.
|
||||||
|
scale.X = scale.Y / selectionRect.Height * selectionRect.Width;
|
||||||
|
else
|
||||||
|
// in all other cases (arbitrarily) use the horizontal component for aspect lock.
|
||||||
|
scale.Y = scale.X / selectionRect.Width * selectionRect.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anchor.HasFlagFast(Anchor.x0)) adjustedRect.X -= scale.X;
|
||||||
|
if (anchor.HasFlagFast(Anchor.y0)) adjustedRect.Y -= scale.Y;
|
||||||
|
|
||||||
|
adjustedRect.Width += scale.X;
|
||||||
|
adjustedRect.Height += scale.Y;
|
||||||
|
|
||||||
|
// scale adjust applied to each individual item should match that of the quad itself.
|
||||||
|
var scaledDelta = new Vector2(
|
||||||
|
adjustedRect.Width / selectionRect.Width,
|
||||||
|
adjustedRect.Height / selectionRect.Height
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var b in SelectedBlueprints)
|
||||||
|
{
|
||||||
|
var drawableItem = (Drawable)b.Item;
|
||||||
|
|
||||||
|
// each drawable's relative position should be maintained in the scaled quad.
|
||||||
|
var screenPosition = b.ScreenSpaceSelectionPoint;
|
||||||
|
|
||||||
|
var relativePositionInOriginal =
|
||||||
|
new Vector2(
|
||||||
|
(screenPosition.X - selectionRect.TopLeft.X) / selectionRect.Width,
|
||||||
|
(screenPosition.Y - selectionRect.TopLeft.Y) / selectionRect.Height
|
||||||
|
);
|
||||||
|
|
||||||
|
var newPositionInAdjusted = new Vector2(
|
||||||
|
adjustedRect.TopLeft.X + adjustedRect.Width * relativePositionInOriginal.X,
|
||||||
|
adjustedRect.TopLeft.Y + adjustedRect.Height * relativePositionInOriginal.Y
|
||||||
|
);
|
||||||
|
|
||||||
|
drawableItem.Position = drawableItem.Parent.ToLocalSpace(newPositionInAdjusted) - drawableItem.AnchorPosition;
|
||||||
|
drawableItem.Scale *= scaledDelta;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -158,13 +220,6 @@ namespace osu.Game.Skinning.Editor
|
|||||||
// reverse the scale direction if dragging from top or left.
|
// reverse the scale direction if dragging from top or left.
|
||||||
if ((reference & Anchor.x0) > 0) scale.X = -scale.X;
|
if ((reference & Anchor.x0) > 0) scale.X = -scale.X;
|
||||||
if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y;
|
if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y;
|
||||||
|
|
||||||
// for now aspect lock scale adjustments that occur at corners.
|
|
||||||
if (!reference.HasFlagFast(Anchor.x1) && !reference.HasFlagFast(Anchor.y1))
|
|
||||||
{
|
|
||||||
// TODO: temporary implementation - only dragging the corner handles across the X axis changes size.
|
|
||||||
scale.Y = scale.X;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user