diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 56c3ba9315..ea16946dcb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -105,9 +105,7 @@ namespace osu.Game.Rulesets.Osu.Edit // is not looking to change the duration of the slider but expand the whole pattern. if (objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider) { - var originalInfo = objectsInScale[slider]; - Debug.Assert(originalInfo.PathControlPointPositions != null && originalInfo.PathControlPointTypes != null); - scaleSlider(slider, scale, originalInfo.PathControlPointPositions, originalInfo.PathControlPointTypes, axisRotation); + scaleSlider(slider, scale, actualOrigin, objectsInScale[slider], axisRotation); } else { @@ -159,21 +157,25 @@ namespace osu.Game.Rulesets.Osu.Edit return scale; } - private void scaleSlider(Slider slider, Vector2 scale, Vector2[] originalPathPositions, PathType?[] originalPathTypes, float axisRotation = 0) + private void scaleSlider(Slider slider, Vector2 scale, Vector2 origin, OriginalHitObjectState originalInfo, float axisRotation = 0) { + Debug.Assert(originalInfo.PathControlPointPositions != null && originalInfo.PathControlPointTypes != null); + scale = Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON)); // Maintain the path types in case they were defaulted to bezier at some point during scaling for (int i = 0; i < slider.Path.ControlPoints.Count; i++) { - slider.Path.ControlPoints[i].Position = GeometryUtils.GetScaledPosition(scale, Vector2.Zero, originalPathPositions[i], axisRotation); - slider.Path.ControlPoints[i].Type = originalPathTypes[i]; + slider.Path.ControlPoints[i].Position = GeometryUtils.GetScaledPosition(scale, Vector2.Zero, originalInfo.PathControlPointPositions[i], axisRotation); + slider.Path.ControlPoints[i].Type = originalInfo.PathControlPointTypes[i]; } // Snap the slider's length to the current beat divisor // to calculate the final resulting duration / bounding box before the final checks. slider.SnapTo(snapProvider); + slider.Position = GeometryUtils.GetScaledPosition(scale, origin, originalInfo.Position, axisRotation); + //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider }); (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); @@ -182,7 +184,9 @@ namespace osu.Game.Rulesets.Osu.Edit return; for (int i = 0; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position = originalPathPositions[i]; + slider.Path.ControlPoints[i].Position = originalInfo.PathControlPointPositions[i]; + + slider.Position = originalInfo.Position; // Snap the slider's length again to undo the potentially-invalid length applied by the previous snap. slider.SnapTo(snapProvider); diff --git a/osu.Game.Rulesets.Osu/Edit/PreciseScalePopover.cs b/osu.Game.Rulesets.Osu/Edit/PreciseScalePopover.cs index dff370d259..33b0c14185 100644 --- a/osu.Game.Rulesets.Osu/Edit/PreciseScalePopover.cs +++ b/osu.Game.Rulesets.Osu/Edit/PreciseScalePopover.cs @@ -10,7 +10,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.RadioButtons; using osuTK; @@ -35,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Edit private OsuCheckbox xCheckBox = null!; private OsuCheckbox yCheckBox = null!; + private BindableList selectedItems { get; } = new BindableList(); + public PreciseScalePopover(OsuSelectionScaleHandler scaleHandler, OsuGridToolboxGroup gridToolbox) { this.scaleHandler = scaleHandler; @@ -44,8 +49,10 @@ namespace osu.Game.Rulesets.Osu.Edit } [BackgroundDependencyLoader] - private void load() + private void load(EditorBeatmap editorBeatmap) { + selectedItems.BindTo(editorBeatmap.SelectedHitObjects); + Child = new FillFlowContainer { Width = 220, @@ -191,14 +198,26 @@ namespace osu.Game.Rulesets.Osu.Edit updateAxisCheckBoxesEnabled(); } - private Vector2? getOriginPosition(PreciseScaleInfo scale) => - scale.Origin switch + private Vector2? getOriginPosition(PreciseScaleInfo scale) + { + switch (scale.Origin) { - ScaleOrigin.GridCentre => gridToolbox.StartPosition.Value, - ScaleOrigin.PlayfieldCentre => OsuPlayfield.BASE_SIZE / 2, - ScaleOrigin.SelectionCentre => null, - _ => throw new ArgumentOutOfRangeException(nameof(scale)) - }; + case ScaleOrigin.GridCentre: + return gridToolbox.StartPosition.Value; + + case ScaleOrigin.PlayfieldCentre: + return OsuPlayfield.BASE_SIZE / 2; + + case ScaleOrigin.SelectionCentre: + if (selectedItems.Count == 1 && selectedItems.First() is Slider slider) + return slider.Position; + + return null; + + default: + throw new ArgumentOutOfRangeException(nameof(scale)); + } + } private Axes getAdjustAxis(PreciseScaleInfo scale) => scale.XAxis ? scale.YAxis ? Axes.Both : Axes.X : Axes.Y;