1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 00:47:24 +08:00

Migrate osu! rotation handling to SelectionRotationHandler

This commit is contained in:
Bartłomiej Dach 2023-07-23 19:18:38 +02:00
parent ba8ebefb50
commit ba904fd77b
No known key found for this signature in database
2 changed files with 101 additions and 27 deletions

View File

@ -28,11 +28,6 @@ namespace osu.Game.Rulesets.Osu.Edit
[Resolved(CanBeNull = true)]
private IDistanceSnapProvider? snapProvider { get; set; }
/// <summary>
/// During a transform, the initial origin is stored so it can be used throughout the operation.
/// </summary>
private Vector2? referenceOrigin;
/// <summary>
/// During a transform, the initial path types of a single selected slider are stored so they
/// can be maintained throughout the operation.
@ -54,7 +49,6 @@ namespace osu.Game.Rulesets.Osu.Edit
protected override void OnOperationEnded()
{
base.OnOperationEnded();
referenceOrigin = null;
referencePathTypes = null;
}
@ -170,28 +164,10 @@ namespace osu.Game.Rulesets.Osu.Edit
if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y;
}
public override bool HandleRotation(float delta)
public override SelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler(ChangeHandler)
{
var hitObjects = selectedMovableObjects;
Quad quad = GeometryUtils.GetSurroundingQuad(hitObjects);
referenceOrigin ??= quad.Centre;
foreach (var h in hitObjects)
{
h.Position = GeometryUtils.RotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
if (h is IHasPath path)
{
foreach (PathControlPoint cp in path.Path.ControlPoints)
cp.Position = GeometryUtils.RotatePointAroundOrigin(cp.Position, Vector2.Zero, delta);
}
}
// this isn't always the case but let's be lenient for now.
return true;
}
SelectedItems = { BindTarget = SelectedItems }
};
private void scaleSlider(Slider slider, Vector2 scale)
{

View File

@ -0,0 +1,98 @@
// 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.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuSelectionRotationHandler : SelectionRotationHandler
{
private readonly IEditorChangeHandler? changeHandler;
public BindableList<HitObject> SelectedItems { get; } = new BindableList<HitObject>();
public OsuSelectionRotationHandler(IEditorChangeHandler? changeHandler)
{
this.changeHandler = changeHandler;
SelectedItems.CollectionChanged += (_, __) => updateState();
}
private void updateState()
{
var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);
CanRotate.Value = quad.Width > 0 || quad.Height > 0;
}
private OsuHitObject[]? objectsInRotation;
private Vector2? defaultOrigin;
private Dictionary<OsuHitObject, Vector2>? originalPositions;
private Dictionary<IHasPath, Vector2[]>? originalPathControlPointPositions;
public override void Begin()
{
if (objectsInRotation != null)
throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");
changeHandler?.BeginChange();
objectsInRotation = selectedMovableObjects.ToArray();
defaultOrigin = GeometryUtils.GetSurroundingQuad(objectsInRotation).Centre;
originalPositions = objectsInRotation.ToDictionary(obj => obj, obj => obj.Position);
originalPathControlPointPositions = objectsInRotation.OfType<IHasPath>().ToDictionary(
obj => obj,
obj => obj.Path.ControlPoints.Select(point => point.Position).ToArray());
}
public override void Update(float rotation, Vector2? origin = null)
{
if (objectsInRotation == null)
throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");
Debug.Assert(originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null);
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
foreach (var ho in objectsInRotation)
{
ho.Position = GeometryUtils.RotatePointAroundOrigin(originalPositions[ho], actualOrigin, rotation);
if (ho is IHasPath withPath)
{
var originalPath = originalPathControlPointPositions[withPath];
for (int i = 0; i < withPath.Path.ControlPoints.Count; ++i)
withPath.Path.ControlPoints[i].Position = GeometryUtils.RotatePointAroundOrigin(originalPath[i], Vector2.Zero, rotation);
}
}
}
public override void Commit()
{
if (objectsInRotation == null)
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
changeHandler?.EndChange();
objectsInRotation = null;
originalPositions = null;
originalPathControlPointPositions = null;
defaultOrigin = null;
}
private IEnumerable<OsuHitObject> selectedMovableObjects => SelectedItems.Cast<OsuHitObject>()
.Where(h => h is not Spinner);
}
}