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

Create SnapResult class to hold various snapping results

This commit is contained in:
Dean Herbert 2020-05-20 18:19:21 +09:00
parent 3354d48a38
commit c46bfc2532
10 changed files with 70 additions and 61 deletions

View File

@ -1,7 +1,6 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
@ -45,12 +44,7 @@ namespace osu.Game.Rulesets.Mania.Edit
public int TotalColumns => Playfield.TotalColumns;
public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position)
{
throw new NotImplementedException();
}
public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
{
var hoc = Playfield.GetColumn(0).HitObjectContainer;
@ -69,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Edit
drawableRuleset.ScrollingInfo.TimeRange.Value,
hoc.DrawHeight);
return (targetPosition, targetTime);
return new SnapResult(targetPosition, targetTime);
}
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)

View File

@ -174,9 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private class SnapProvider : IPositionSnapProvider
{
public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0);
public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0);
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;

View File

@ -162,11 +162,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
if (ControlPoint == slider.Path.ControlPoints[0])
{
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
(Vector2 snappedPosition, double snappedTime) = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition) ?? (e.MousePosition, slider.StartTime);
Vector2 movementDelta = snappedPosition - slider.Position;
var result = snapProvider?.SnapScreenSpacePositionToValidTime(e.MousePosition);
Vector2 movementDelta = (result?.ScreenSpacePosition ?? e.MousePosition) - slider.Position;
slider.Position += movementDelta;
slider.StartTime = snappedTime;
slider.StartTime = result?.Time ?? slider.StartTime;
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)

View File

@ -153,9 +153,7 @@ namespace osu.Game.Tests.Visual.Editing
private class SnapProvider : IPositionSnapProvider
{
public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) => (position, 0);
public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => (screenSpacePosition, 0);
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public float GetBeatSnapDistanceAt(double referenceTime) => 10;

View File

@ -245,7 +245,8 @@ namespace osu.Game.Rulesets.Edit
{
EditorBeatmap.PlacementObject.Value = hitObject;
hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).time;
if (SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time is double time)
hitObject.StartTime = time;
}
public void EndPlacement(HitObject hitObject, bool commit)
@ -264,11 +265,13 @@ namespace osu.Game.Rulesets.Edit
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
public override (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) =>
distanceSnapGrid?.GetSnappedPosition(position) ?? (position, 0);
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
{
if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null);
public override (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
=> SnapPositionToValidTime(drawableRulesetWrapper.Playfield.ToLocalSpace(screenSpacePosition));
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time);
}
public override float GetBeatSnapDistanceAt(double referenceTime)
{
@ -326,9 +329,7 @@ namespace osu.Game.Rulesets.Edit
[CanBeNull]
protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable<HitObject> selectedHitObjects) => null;
public abstract (Vector2 position, double time) SnapPositionToValidTime(Vector2 position);
public abstract (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
public abstract float GetBeatSnapDistanceAt(double referenceTime);

View File

@ -8,13 +8,11 @@ namespace osu.Game.Rulesets.Edit
public interface IPositionSnapProvider
{
/// <summary>
/// Given a position (local to the provider), find a valid time snap
/// Given a position, find a valid time snap.
/// </summary>
/// <param name="position">The local position to be snapped.</param>
/// <param name="screenSpacePosition">The screen-space position to be snapped.</param>
/// <returns>The time and position post-snapping.</returns>
(Vector2 position, double time) SnapPositionToValidTime(Vector2 position);
(Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
/// <summary>
/// Retrieves the distance between two points within a timing point that are one beat length apart.
@ -55,4 +53,23 @@ namespace osu.Game.Rulesets.Edit
/// <returns>A value that represents <paramref name="distance"/> snapped to the closest beat of the timing point.</returns>
float GetSnappedDistanceFromDistance(double referenceTime, float distance);
}
public class SnapResult
{
/// <summary>
/// The screen space position, potentially altered for snapping.
/// </summary>
public Vector2 ScreenSpacePosition;
/// <summary>
/// The resultant time for snapping, if a value could be attained.
/// </summary>
public double? Time;
public SnapResult(Vector2 screenSpacePosition, double? time)
{
ScreenSpacePosition = screenSpacePosition;
Time = time;
}
}
}

View File

@ -405,16 +405,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
// Retrieve a snapped position.
(Vector2 snappedPosition, double snappedTime) = snapProvider.SnapScreenSpacePositionToValidTime(movePosition);
var result = snapProvider.SnapScreenSpacePositionToValidTime(movePosition);
// Move the hitobjects.
if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition))))
if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, result.ScreenSpacePosition)))
return true;
if (result.Time.HasValue)
{
// Apply the start time at the newly snapped-to position
double offset = snappedTime - draggedObject.StartTime;
double offset = result.Time.Value - draggedObject.StartTime;
foreach (HitObject obj in selectionHandler.SelectedHitObjects)
obj.StartTime += offset;
}
return true;
}

View File

@ -67,7 +67,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void updatePlacementPosition(Vector2 screenSpacePosition)
{
Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).position;
Vector2 snappedPlayfieldPosition = composer.SnapScreenSpacePositionToValidTime(screenSpacePosition).ScreenSpacePosition;
currentPlacement.UpdatePosition(ToScreenSpace(snappedPlayfieldPosition));
}

View File

@ -181,11 +181,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
[Resolved]
private IBeatSnapProvider beatSnapProvider { get; set; }
public (Vector2 position, double time) SnapPositionToValidTime(Vector2 position) =>
(position, beatSnapProvider.SnapTime(getTimeFromPosition(position)));
public (Vector2 position, double time) SnapScreenSpacePositionToValidTime(Vector2 position) =>
(position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position))));
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 position) =>
new SnapResult(position, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(position))));
private double getTimeFromPosition(Vector2 localPosition) =>
(localPosition.X / Content.DrawWidth) * track.Length;

View File

@ -275,8 +275,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
OnDragHandled?.Invoke(e);
var time = timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).time;
if (timeline.SnapScreenSpacePositionToValidTime(e.ScreenSpaceMousePosition).Time is double time)
{
switch (hitObject)
{
case IHasRepeats repeatHitObject:
@ -302,6 +302,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
beatmap.UpdateHitObject(hitObject);
}
}
protected override void OnDragEnd(DragEndEvent e)
{