mirror of
https://github.com/ppy/osu.git
synced 2024-09-21 18:47:27 +08:00
calculate max scale bounds for scale slider
This commit is contained in:
parent
88314dc584
commit
4eeebdf60c
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));
|
objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));
|
||||||
OriginalSurroundingQuad = objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider
|
OriginalSurroundingQuad = objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider
|
||||||
? GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position))
|
? GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => slider.Position + p.Position))
|
||||||
: GeometryUtils.GetSurroundingQuad(objectsInScale.Keys);
|
: GeometryUtils.GetSurroundingQuad(objectsInScale.Keys);
|
||||||
defaultOrigin = OriginalSurroundingQuad.Value.Centre;
|
defaultOrigin = OriginalSurroundingQuad.Value.Centre;
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
scale = getClampedScale(OriginalSurroundingQuad.Value, actualOrigin, scale);
|
scale = GetClampedScale(scale, actualOrigin);
|
||||||
|
|
||||||
foreach (var (ho, originalState) in objectsInScale)
|
foreach (var (ho, originalState) in objectsInScale)
|
||||||
{
|
{
|
||||||
@ -155,30 +155,33 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return (xInBounds, yInBounds);
|
return (xInBounds, yInBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public override Vector2 GetClampedScale(Vector2 scale, Vector2? origin = null)
|
||||||
/// Clamp scale for multi-object-scaling where selection does not exceed playfield bounds or flip.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="selectionQuad">The quad surrounding the hitobjects</param>
|
|
||||||
/// <param name="origin">The origin from which the scale operation is performed</param>
|
|
||||||
/// <param name="scale">The scale to be clamped</param>
|
|
||||||
/// <returns>The clamped scale vector</returns>
|
|
||||||
private Vector2 getClampedScale(Quad selectionQuad, Vector2 origin, Vector2 scale)
|
|
||||||
{
|
{
|
||||||
//todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead.
|
//todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead.
|
||||||
|
if (objectsInScale == null)
|
||||||
|
return scale;
|
||||||
|
|
||||||
var tl1 = Vector2.Divide(-origin, selectionQuad.TopLeft - origin);
|
Debug.Assert(defaultOrigin != null && OriginalSurroundingQuad != null);
|
||||||
var tl2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - origin, selectionQuad.TopLeft - origin);
|
|
||||||
var br1 = Vector2.Divide(-origin, selectionQuad.BottomRight - origin);
|
|
||||||
var br2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - origin, selectionQuad.BottomRight - origin);
|
|
||||||
|
|
||||||
if (!Precision.AlmostEquals(selectionQuad.TopLeft.X - origin.X, 0))
|
if (objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider)
|
||||||
scale.X = selectionQuad.TopLeft.X - origin.X < 0 ? MathHelper.Clamp(scale.X, tl2.X, tl1.X) : MathHelper.Clamp(scale.X, tl1.X, tl2.X);
|
origin = slider.Position;
|
||||||
if (!Precision.AlmostEquals(selectionQuad.TopLeft.Y - origin.Y, 0))
|
|
||||||
scale.Y = selectionQuad.TopLeft.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, tl2.Y, tl1.Y) : MathHelper.Clamp(scale.Y, tl1.Y, tl2.Y);
|
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
||||||
if (!Precision.AlmostEquals(selectionQuad.BottomRight.X - origin.X, 0))
|
var selectionQuad = OriginalSurroundingQuad.Value;
|
||||||
scale.X = selectionQuad.BottomRight.X - origin.X < 0 ? MathHelper.Clamp(scale.X, br2.X, br1.X) : MathHelper.Clamp(scale.X, br1.X, br2.X);
|
|
||||||
if (!Precision.AlmostEquals(selectionQuad.BottomRight.Y - origin.Y, 0))
|
var tl1 = Vector2.Divide(-actualOrigin, selectionQuad.TopLeft - actualOrigin);
|
||||||
scale.Y = selectionQuad.BottomRight.Y - origin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y);
|
var tl2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - actualOrigin, selectionQuad.TopLeft - actualOrigin);
|
||||||
|
var br1 = Vector2.Divide(-actualOrigin, selectionQuad.BottomRight - actualOrigin);
|
||||||
|
var br2 = Vector2.Divide(OsuPlayfield.BASE_SIZE - actualOrigin, selectionQuad.BottomRight - actualOrigin);
|
||||||
|
|
||||||
|
if (!Precision.AlmostEquals(selectionQuad.TopLeft.X - actualOrigin.X, 0))
|
||||||
|
scale.X = selectionQuad.TopLeft.X - actualOrigin.X < 0 ? MathHelper.Clamp(scale.X, tl2.X, tl1.X) : MathHelper.Clamp(scale.X, tl1.X, tl2.X);
|
||||||
|
if (!Precision.AlmostEquals(selectionQuad.TopLeft.Y - actualOrigin.Y, 0))
|
||||||
|
scale.Y = selectionQuad.TopLeft.Y - actualOrigin.Y < 0 ? MathHelper.Clamp(scale.Y, tl2.Y, tl1.Y) : MathHelper.Clamp(scale.Y, tl1.Y, tl2.Y);
|
||||||
|
if (!Precision.AlmostEquals(selectionQuad.BottomRight.X - actualOrigin.X, 0))
|
||||||
|
scale.X = selectionQuad.BottomRight.X - actualOrigin.X < 0 ? MathHelper.Clamp(scale.X, br2.X, br1.X) : MathHelper.Clamp(scale.X, br1.X, br2.X);
|
||||||
|
if (!Precision.AlmostEquals(selectionQuad.BottomRight.Y - actualOrigin.Y, 0))
|
||||||
|
scale.Y = selectionQuad.BottomRight.Y - actualOrigin.Y < 0 ? MathHelper.Clamp(scale.Y, br2.Y, br1.Y) : MathHelper.Clamp(scale.Y, br1.Y, br2.Y);
|
||||||
|
|
||||||
return Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON));
|
return Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -22,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
private readonly Bindable<PreciseScaleInfo> scaleInfo = new Bindable<PreciseScaleInfo>(new PreciseScaleInfo(1, ScaleOrigin.PlayfieldCentre, true, true));
|
private readonly Bindable<PreciseScaleInfo> scaleInfo = new Bindable<PreciseScaleInfo>(new PreciseScaleInfo(1, ScaleOrigin.PlayfieldCentre, true, true));
|
||||||
|
|
||||||
private SliderWithTextBoxInput<float> scaleInput = null!;
|
private SliderWithTextBoxInput<float> scaleInput = null!;
|
||||||
|
private BindableNumber<float> scaleInputBindable = null!;
|
||||||
private EditorRadioButtonCollection scaleOrigin = null!;
|
private EditorRadioButtonCollection scaleOrigin = null!;
|
||||||
|
|
||||||
private RadioButton selectionCentreButton = null!;
|
private RadioButton selectionCentreButton = null!;
|
||||||
@ -45,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
scaleInput = new SliderWithTextBoxInput<float>("Scale:")
|
scaleInput = new SliderWithTextBoxInput<float>("Scale:")
|
||||||
{
|
{
|
||||||
Current = new BindableNumber<float>
|
Current = scaleInputBindable = new BindableNumber<float>
|
||||||
{
|
{
|
||||||
MinValue = 0.5f,
|
MinValue = 0.5f,
|
||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
@ -61,10 +63,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
Items = new[]
|
Items = new[]
|
||||||
{
|
{
|
||||||
new RadioButton("Playfield centre",
|
new RadioButton("Playfield centre",
|
||||||
() => scaleInfo.Value = scaleInfo.Value with { Origin = ScaleOrigin.PlayfieldCentre },
|
() => setOrigin(ScaleOrigin.PlayfieldCentre),
|
||||||
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
|
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
|
||||||
selectionCentreButton = new RadioButton("Selection centre",
|
selectionCentreButton = new RadioButton("Selection centre",
|
||||||
() => scaleInfo.Value = scaleInfo.Value with { Origin = ScaleOrigin.SelectionCentre },
|
() => setOrigin(ScaleOrigin.SelectionCentre),
|
||||||
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
|
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,14 +98,39 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
scaleInfo.BindValueChanged(scale =>
|
scaleInfo.BindValueChanged(scale =>
|
||||||
{
|
{
|
||||||
var newScale = new Vector2(scale.NewValue.XAxis ? scale.NewValue.Scale : 1, scale.NewValue.YAxis ? scale.NewValue.Scale : 1);
|
var newScale = new Vector2(scale.NewValue.XAxis ? scale.NewValue.Scale : 1, scale.NewValue.YAxis ? scale.NewValue.Scale : 1);
|
||||||
scaleHandler.Update(newScale, scale.NewValue.Origin == ScaleOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null);
|
scaleHandler.Update(newScale, getOriginPosition(scale.NewValue));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMaxScale()
|
||||||
|
{
|
||||||
|
if (!scaleHandler.OriginalSurroundingQuad.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const float max_scale = 10;
|
||||||
|
var scale = scaleHandler.GetClampedScale(new Vector2(max_scale), getOriginPosition(scaleInfo.Value));
|
||||||
|
|
||||||
|
if (!scaleInfo.Value.XAxis)
|
||||||
|
scale.X = max_scale;
|
||||||
|
if (!scaleInfo.Value.YAxis)
|
||||||
|
scale.Y = max_scale;
|
||||||
|
|
||||||
|
scaleInputBindable.MaxValue = MathF.Max(1, MathF.Min(scale.X, scale.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOrigin(ScaleOrigin origin)
|
||||||
|
{
|
||||||
|
scaleInfo.Value = scaleInfo.Value with { Origin = origin };
|
||||||
|
updateMaxScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2? getOriginPosition(PreciseScaleInfo scale) => scale.Origin == ScaleOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null;
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
base.PopIn();
|
base.PopIn();
|
||||||
scaleHandler.Begin();
|
scaleHandler.Begin();
|
||||||
|
updateMaxScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
|
@ -34,6 +34,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
public Quad? OriginalSurroundingQuad { get; protected set; }
|
public Quad? OriginalSurroundingQuad { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clamp scale where selection does not exceed playfield bounds or flip.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="origin">The origin from which the scale operation is performed</param>
|
||||||
|
/// <param name="scale">The scale to be clamped</param>
|
||||||
|
/// <returns>The clamped scale vector</returns>
|
||||||
|
public virtual Vector2 GetClampedScale(Vector2 scale, Vector2? origin = null) => scale;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a single, instant, atomic scale operation.
|
/// Performs a single, instant, atomic scale operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user