1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 00:02:54 +08:00

Update IPositionalSnapProvider to take a HitObject as reference, rather than raw time

This allows fetching the correct `DifficultyControlPoint` from the
hitobject. Nothing more.
This commit is contained in:
Dean Herbert 2021-09-01 18:05:10 +09:00
parent a3d9ab1e2e
commit d4e5a612ea
13 changed files with 83 additions and 68 deletions

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
@ -101,27 +102,27 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
throw new System.NotImplementedException();
}
public override float GetBeatSnapDistanceAt(double referenceTime)
public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
throw new System.NotImplementedException();
}
public override float DurationToDistance(double referenceTime, double duration)
public override float DurationToDistance(HitObject referenceObject, double duration)
{
throw new System.NotImplementedException();
}
public override double DistanceToDuration(double referenceTime, float distance)
public override double DistanceToDuration(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}

View File

@ -11,6 +11,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Objects;
@ -179,15 +180,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length;
public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length;
public float DurationToDistance(double referenceTime, double duration) => (float)duration;
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
public double DistanceToDuration(double referenceTime, float distance) => distance;
public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
public double GetSnappedDurationFromDistance(double referenceTime, float distance) => 0;
public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0;
public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => 0;
public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0;
}
}
}

View File

@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void updateSlider()
{
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
bodyPiece.UpdateFrom(HitObject);
headCirclePiece.UpdateFrom(HitObject.HeadCircle);

View File

@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void updatePath()
{
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
editorBeatmap?.Update(HitObject);
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
{
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
: base(hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime)
: base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime)
{
Masking = true;
}

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
@ -198,19 +199,19 @@ namespace osu.Game.Tests.Editing
}
private void assertSnapDistance(float expectedDistance)
=> AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(0) == expectedDistance);
=> AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(new HitObject()) == expectedDistance);
private void assertDurationToDistance(double duration, float expectedDistance)
=> AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(0, duration) == expectedDistance);
=> AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(new HitObject(), duration) == expectedDistance);
private void assertDistanceToDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(0, distance) == expectedDuration);
=> AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(new HitObject(), distance) == expectedDuration);
private void assertSnappedDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(0, distance) == expectedDuration);
=> AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(new HitObject(), distance) == expectedDuration);
private void assertSnappedDistance(float distance, float expectedDistance)
=> AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(0, distance) == expectedDistance);
=> AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(new HitObject(), distance) == expectedDistance);
private class TestHitObjectComposer : OsuHitObjectComposer
{

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
@ -81,7 +82,7 @@ namespace osu.Game.Tests.Visual.Editing
public new float DistanceSpacing => base.DistanceSpacing;
public TestDistanceSnapGrid(double? endTime = null)
: base(grid_position, 0, endTime)
: base(new HitObject(), grid_position, 0, endTime)
{
}
@ -158,15 +159,15 @@ namespace osu.Game.Tests.Visual.Editing
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public float GetBeatSnapDistanceAt(double referenceTime) => 10;
public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10;
public float DurationToDistance(double referenceTime, double duration) => (float)duration;
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
public double DistanceToDuration(double referenceTime, float distance) => distance;
public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
public double GetSnappedDurationFromDistance(double referenceTime, float distance) => 0;
public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => 0;
public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => 0;
public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => 0;
}
}
}

View File

@ -13,7 +13,6 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
@ -389,41 +388,42 @@ namespace osu.Game.Rulesets.Edit
return new SnapResult(screenSpacePosition, targetTime, playfield);
}
public override float GetBeatSnapDistanceAt(double referenceTime)
public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime);
return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
}
public override float DurationToDistance(double referenceTime, double duration)
public override float DurationToDistance(HitObject referenceObject, double duration)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceTime));
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject));
}
public override double DistanceToDuration(double referenceTime, float distance)
public override double DistanceToDuration(HitObject referenceObject, float distance)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
return distance / GetBeatSnapDistanceAt(referenceTime) * beatLength;
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength;
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
=> BeatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime;
public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
=> BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime;
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
double actualDuration = referenceTime + DistanceToDuration(referenceTime, distance);
double startTime = referenceObject.StartTime;
double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, referenceTime);
double actualDuration = startTime + DistanceToDuration(referenceObject, distance);
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, startTime);
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(startTime);
// we don't want to exceed the actual duration and snap to a point in the future.
// as we are snapping to beat length via SnapTime (which will round-to-nearest), check for snapping in the forward direction and reverse it.
if (snappedEndTime > actualDuration + 1)
snappedEndTime -= beatLength;
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
return DurationToDistance(referenceObject, snappedEndTime - startTime);
}
#endregion
@ -466,15 +466,15 @@ namespace osu.Game.Rulesets.Edit
public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
public abstract float GetBeatSnapDistanceAt(double referenceTime);
public abstract float GetBeatSnapDistanceAt(HitObject referenceObject);
public abstract float DurationToDistance(double referenceTime, double duration);
public abstract float DurationToDistance(HitObject referenceObject, double duration);
public abstract double DistanceToDuration(double referenceTime, float distance);
public abstract double DistanceToDuration(HitObject referenceObject, float distance);
public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance);
public abstract double GetSnappedDurationFromDistance(HitObject referenceObject, float distance);
public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance);
public abstract float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance);
#endregion
}

View File

@ -1,6 +1,7 @@
// 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 osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Rulesets.Edit
@ -27,41 +28,41 @@ namespace osu.Game.Rulesets.Edit
/// <summary>
/// Retrieves the distance between two points within a timing point that are one beat length apart.
/// </summary>
/// <param name="referenceTime">The time of the timing point.</param>
/// <param name="referenceObject"></param>
/// <returns>The distance between two points residing in the timing point that are one beat length apart.</returns>
float GetBeatSnapDistanceAt(double referenceTime);
float GetBeatSnapDistanceAt(HitObject referenceObject);
/// <summary>
/// Converts a duration to a distance.
/// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="duration"/> resides in.</param>
/// <param name="referenceObject"></param>
/// <param name="duration">The duration to convert.</param>
/// <returns>A value that represents <paramref name="duration"/> as a distance in the timing point.</returns>
float DurationToDistance(double referenceTime, double duration);
float DurationToDistance(HitObject referenceObject, double duration);
/// <summary>
/// Converts a distance to a duration.
/// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="distance"/> resides in.</param>
/// <param name="referenceObject"></param>
/// <param name="distance">The distance to convert.</param>
/// <returns>A value that represents <paramref name="distance"/> as a duration in the timing point.</returns>
double DistanceToDuration(double referenceTime, float distance);
double DistanceToDuration(HitObject referenceObject, float distance);
/// <summary>
/// Converts a distance to a snapped duration.
/// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="distance"/> resides in.</param>
/// <param name="referenceObject"></param>
/// <param name="distance">The distance to convert.</param>
/// <returns>A value that represents <paramref name="distance"/> as a duration snapped to the closest beat of the timing point.</returns>
double GetSnappedDurationFromDistance(double referenceTime, float distance);
double GetSnappedDurationFromDistance(HitObject referenceObject, float distance);
/// <summary>
/// Converts an unsnapped distance to a snapped distance.
/// The returned distance will always be floored (as to never exceed the provided <paramref name="distance"/>.
/// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="distance"/> resides in.</param>
/// <param name="referenceObject"></param>
/// <param name="distance">The distance to convert.</param>
/// <returns>A value that represents <paramref name="distance"/> snapped to the closest beat of the timing point.</returns>
float GetSnappedDistanceFromDistance(double referenceTime, float distance);
float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance);
}
}

View File

@ -142,7 +142,9 @@ namespace osu.Game.Rulesets.UI.Scrolling
var lastDifficultyPoint = new DifficultyControlPoint();
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
// TODO: fix
//allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
// Generate the timing points, making non-timing changes use the previous timing change and vice-versa
var timingChanges = allPoints.Select(c =>

View File

@ -5,14 +5,15 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public abstract class CircularDistanceSnapGrid : DistanceSnapGrid
{
protected CircularDistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null)
: base(startPosition, startTime, endTime)
protected CircularDistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
: base(referenceObject, startPosition, startTime, endTime)
{
}
@ -79,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
Vector2 normalisedDirection = direction * new Vector2(1f / distance);
Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius;
return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(StartTime, (snappedPosition - StartPosition).Length));
return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(ReferenceObject, (snappedPosition - StartPosition).Length));
}
}
}

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Layout;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
@ -54,15 +55,20 @@ namespace osu.Game.Screens.Edit.Compose.Components
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
private readonly double? endTime;
protected readonly HitObject ReferenceObject;
/// <summary>
/// Creates a new <see cref="DistanceSnapGrid"/>.
/// </summary>
/// <param name="referenceObject">A reference object to gather relevant difficulty values from.</param>
/// <param name="startPosition">The position at which the grid should start. The first tick is located one distance spacing length away from this point.</param>
/// <param name="startTime">The snapping time at <see cref="StartPosition"/>.</param>
/// <param name="endTime">The time at which the snapping grid should end. If null, the grid will continue until the bounds of the screen are exceeded.</param>
protected DistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null)
protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
{
ReferenceObject = referenceObject;
this.endTime = endTime;
StartPosition = startPosition;
StartTime = startTime;
@ -80,7 +86,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void updateSpacing()
{
DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(StartTime);
DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
if (endTime == null)
MaxIntervals = int.MaxValue;
@ -88,7 +94,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
// +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors
double maxDuration = endTime.Value - StartTime + 1;
MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(StartTime, DistanceSpacing));
MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceSpacing));
}
gridCache.Invalidate();

View File

@ -15,6 +15,7 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
@ -298,14 +299,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private double getTimeFromPosition(Vector2 localPosition) =>
(localPosition.X / Content.DrawWidth) * track.Length;
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException();
public float DurationToDistance(double referenceTime, double duration) => throw new NotImplementedException();
public float DurationToDistance(HitObject referenceObject, double duration) => throw new NotImplementedException();
public double DistanceToDuration(double referenceTime, float distance) => throw new NotImplementedException();
public double DistanceToDuration(HitObject referenceObject, float distance) => throw new NotImplementedException();
public double GetSnappedDurationFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
}
}