Merge branch 'master' into sheared-search-textbox

This commit is contained in:
Dean Herbert 2022-05-03 14:12:59 +09:00 committed by GitHub
commit 4fb66a812f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 296 additions and 153 deletions

View File

@ -24,7 +24,7 @@ using osuTK;
namespace osu.Game.Rulesets.Catch.Edit
{
public class CatchHitObjectComposer : HitObjectComposer<CatchHitObject>
public class CatchHitObjectComposer : DistancedHitObjectComposer<CatchHitObject>
{
private const float distance_snap_radius = 50;
@ -42,6 +42,10 @@ namespace osu.Game.Rulesets.Catch.Edit
[BackgroundDependencyLoader]
private void load()
{
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
RightSideToolboxContainer.Alpha = 0;
DistanceSpacingMultiplier.Disabled = true;
LayerBelowRuleset.Add(new PlayfieldBorder
{
RelativeSizeAxes = Axes.Both,

View File

@ -13,7 +13,6 @@ 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;
@ -107,30 +106,5 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
{
throw new System.NotImplementedException();
}
public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
throw new System.NotImplementedException();
}
public override float DurationToDistance(HitObject referenceObject, double duration)
{
throw new System.NotImplementedException();
}
public override double DistanceToDuration(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
throw new System.NotImplementedException();
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -33,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
[Cached]
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
[Cached(typeof(IPositionSnapProvider))]
[Cached(typeof(IDistanceSnapProvider))]
private readonly SnapProvider snapProvider = new SnapProvider();
private TestOsuDistanceSnapGrid grid;
@ -179,13 +180,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
}
}
private class SnapProvider : IPositionSnapProvider
private class SnapProvider : IDistanceSnapProvider
{
public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length;
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public Action<List<PathControlPoint>> RemoveControlPointsRequested;
[Resolved(CanBeNull = true)]
private IPositionSnapProvider snapProvider { get; set; }
private IDistanceSnapProvider snapProvider { get; set; }
public PathControlPointVisualiser(Slider slider, bool allowSelection)
{

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private int currentSegmentLength;
[Resolved(CanBeNull = true)]
private HitObjectComposer composer { get; set; }
private IDistanceSnapProvider snapProvider { get; set; }
public SliderPlacementBlueprint()
: base(new Objects.Slider())
@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
private void updateSlider()
{
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
HitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
bodyPiece.UpdateFrom(HitObject);
headCirclePiece.UpdateFrom(HitObject.HeadCircle);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected PathControlPointVisualiser ControlPointVisualiser { get; private set; }
[Resolved(CanBeNull = true)]
private HitObjectComposer composer { get; set; }
private IDistanceSnapProvider snapProvider { get; set; }
[Resolved(CanBeNull = true)]
private IPlacementHandler placementHandler { get; set; }
@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
// Move the control points from the insertion index onwards to make room for the insertion
controlPoints.Insert(insertionIndex, pathControlPoint);
HitObject.SnapTo(composer);
HitObject.SnapTo(snapProvider);
return pathControlPoint;
}
@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
}
// Snap the slider to the current beat divisor before checking length validity.
HitObject.SnapTo(composer);
HitObject.SnapTo(snapProvider);
// If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted
if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength)

View File

@ -24,7 +24,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuHitObjectComposer : HitObjectComposer<OsuHitObject>
public class OsuHitObjectComposer : DistancedHitObjectComposer<OsuHitObject>
{
public OsuHitObjectComposer(Ruleset ruleset)
: base(ruleset)

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit
public class OsuSelectionHandler : EditorSelectionHandler
{
[Resolved(CanBeNull = true)]
private IPositionSnapProvider? positionSnapProvider { get; set; }
private IDistanceSnapProvider? snapProvider { get; set; }
/// <summary>
/// During a transform, the initial origin is stored so it can be used throughout the operation.
@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu.Edit
// Snap the slider's length to the current beat divisor
// to calculate the final resulting duration / bounding box before the final checks.
slider.SnapTo(positionSnapProvider);
slider.SnapTo(snapProvider);
//if sliderhead or sliderend end up outside playfield, revert scaling.
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Edit
point.Position = oldControlPoints.Dequeue();
// Snap the slider's length again to undo the potentially-invalid length applied by the previous snap.
slider.SnapTo(positionSnapProvider);
slider.SnapTo(snapProvider);
}
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
@ -69,7 +70,7 @@ namespace osu.Game.Tests.Editing
[TestCase(2)]
public void TestSliderMultiplier(float multiplier)
{
AddStep($"set multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier);
AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier);
assertSnapDistance(100 * multiplier);
}
@ -221,6 +222,8 @@ namespace osu.Game.Tests.Editing
{
public new EditorBeatmap EditorBeatmap => base.EditorBeatmap;
public new Bindable<double> DistanceSpacingMultiplier => base.DistanceSpacingMultiplier;
public TestHitObjectComposer()
: base(new OsuRuleset())
{

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
@ -25,7 +26,7 @@ namespace osu.Game.Tests.Visual.Editing
[Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap;
[Cached(typeof(IPositionSnapProvider))]
[Cached(typeof(IDistanceSnapProvider))]
private readonly SnapProvider snapProvider = new SnapProvider();
public TestSceneDistanceSnapGrid()
@ -159,13 +160,15 @@ namespace osu.Game.Tests.Visual.Editing
=> (Vector2.Zero, 0);
}
private class SnapProvider : IPositionSnapProvider
private class SnapProvider : IDistanceSnapProvider
{
public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10;
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;

View File

@ -21,6 +21,7 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.RadioButtons;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
@ -86,6 +87,23 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Tool changed", () => hitObjectComposer.ChildrenOfType<ComposeBlueprintContainer>().First().CurrentTool is HitCircleCompositionTool);
}
[Test]
public void TestDistanceSpacingHotkeys()
{
double originalSpacing = 0;
AddStep("retrieve original spacing", () => originalSpacing = editorBeatmap.BeatmapInfo.DistanceSpacing);
AddStep("hold ctrl", () => InputManager.PressKey(Key.LControl));
AddStep("hold alt", () => InputManager.PressKey(Key.LAlt));
AddStep("scroll mouse 5 steps", () => InputManager.ScrollVerticalBy(5));
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
AddStep("release alt", () => InputManager.ReleaseKey(Key.LAlt));
AddStep("release ctrl", () => InputManager.ReleaseKey(Key.LControl));
}
public class EditorBeatmapContainer : Container
{
private readonly IWorkingBeatmap working;

View File

@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private TestExpandingContainer container;
private SettingsToolboxGroup toolboxGroup;
private ExpandableSlider<float, SizeSlider> slider1;
private ExpandableSlider<float, SizeSlider<float>> slider1;
private ExpandableSlider<double> slider2;
[SetUp]
@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Width = 1,
Children = new Drawable[]
{
slider1 = new ExpandableSlider<float, SizeSlider>
slider1 = new ExpandableSlider<float, SizeSlider<float>>
{
Current = new BindableFloat
{

View File

@ -109,7 +109,7 @@ namespace osu.Game.Beatmaps
public bool SamplesMatchPlaybackRate { get; set; } = true;
public double DistanceSpacing { get; set; }
public double DistanceSpacing { get; set; } = 1.0;
public int BeatDivisor { get; set; }

View File

@ -70,6 +70,15 @@ namespace osu.Game.Graphics.UserInterface
set => slider.Current = value;
}
/// <summary>
/// A custom step value for each key press which actuates a change on this control.
/// </summary>
public float KeyboardStep
{
get => slider.KeyboardStep;
set => slider.KeyboardStep = value;
}
public BindableBool Expanded { get; } = new BindableBool();
public override bool HandlePositionalInput => true;

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
Children = new Drawable[]
{
new SettingsSlider<float, SizeSlider>
new SettingsSlider<float, SizeSlider<float>>
{
LabelText = SkinSettingsStrings.GameplayCursorSize,
Current = config.GetBindable<float>(OsuSetting.GameplayCursorSize),

View File

@ -1,6 +1,8 @@
// 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.Globalization;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
@ -9,8 +11,9 @@ namespace osu.Game.Overlays.Settings.Sections
/// <summary>
/// A slider intended to show a "size" multiplier number, where 1x is 1.0.
/// </summary>
internal class SizeSlider : OsuSliderBar<float>
public class SizeSlider<T> : OsuSliderBar<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible, IFormattable
{
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x");
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo);
}
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
LabelText = UserInterfaceStrings.CursorRotation,
Current = config.GetBindable<bool>(OsuSetting.CursorRotation)
},
new SettingsSlider<float, SizeSlider>
new SettingsSlider<float, SizeSlider<float>>
{
LabelText = UserInterfaceStrings.MenuCursorSize,
Current = config.GetBindable<float>(OsuSetting.MenuCursorSize),

View File

@ -0,0 +1,162 @@
// 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.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Rulesets.Edit
{
/// <summary>
/// Represents a <see cref="HitObjectComposer{TObject}"/> for rulesets with the concept of distances between objects.
/// </summary>
/// <typeparam name="TObject">The base type of supported objects.</typeparam>
[Cached(typeof(IDistanceSnapProvider))]
public abstract class DistancedHitObjectComposer<TObject> : HitObjectComposer<TObject>, IDistanceSnapProvider
where TObject : HitObject
{
protected Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
{
MinValue = 0.1,
MaxValue = 6.0,
Precision = 0.01,
};
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; }
private ExpandableSlider<double, SizeSlider<double>> distanceSpacingSlider;
private bool distanceSpacingScrollActive;
protected DistancedHitObjectComposer(Ruleset ruleset)
: base(ruleset)
{
}
[BackgroundDependencyLoader]
private void load()
{
AddInternal(RightSideToolboxContainer = new ExpandingToolboxContainer
{
Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Child = new EditorToolboxGroup("snapping")
{
Child = distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
{
Current = { BindTarget = DistanceSpacingMultiplier },
KeyboardStep = 0.1f,
}
}
});
}
protected override void LoadComplete()
{
base.LoadComplete();
if (!DistanceSpacingMultiplier.Disabled)
{
DistanceSpacingMultiplier.Value = EditorBeatmap.BeatmapInfo.DistanceSpacing;
DistanceSpacingMultiplier.BindValueChanged(v =>
{
distanceSpacingSlider.ContractedLabelText = $"D. S. ({v.NewValue:0.##x})";
distanceSpacingSlider.ExpandedLabelText = $"Distance Spacing ({v.NewValue:0.##x})";
EditorBeatmap.BeatmapInfo.DistanceSpacing = v.NewValue;
}, true);
}
}
protected override bool OnKeyDown(KeyDownEvent e)
{
if (!DistanceSpacingMultiplier.Disabled && e.ControlPressed && e.AltPressed && !e.Repeat)
{
RightSideToolboxContainer.Expanded.Value = true;
distanceSpacingScrollActive = true;
return true;
}
return base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyUpEvent e)
{
if (!DistanceSpacingMultiplier.Disabled && distanceSpacingScrollActive && (!e.AltPressed || !e.ControlPressed))
{
RightSideToolboxContainer.Expanded.Value = false;
distanceSpacingScrollActive = false;
}
}
protected override bool OnScroll(ScrollEvent e)
{
if (distanceSpacingScrollActive)
{
DistanceSpacingMultiplier.Value += e.ScrollDelta.Y * (e.IsPrecise ? 0.01f : 0.1f);
return true;
}
return base.OnScroll(e);
}
public virtual float GetBeatSnapDistanceAt(HitObject referenceObject)
{
return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
}
public virtual float DurationToDistance(HitObject referenceObject, double duration)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject));
}
public virtual double DistanceToDuration(HitObject referenceObject, float distance)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength;
}
public virtual double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
=> BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime;
public virtual float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
double startTime = referenceObject.StartTime;
double actualDuration = startTime + DistanceToDuration(referenceObject, distance);
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(referenceObject, snappedEndTime - startTime);
}
protected class ExpandingToolboxContainer : ExpandingContainer
{
protected override double HoverExpansionDelay => 250;
public ExpandingToolboxContainer()
: base(130, 250)
{
RelativeSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = 10 };
FillFlow.Spacing = new Vector2(10);
}
}
}
}

View File

@ -381,44 +381,6 @@ namespace osu.Game.Rulesets.Edit
return new SnapResult(screenSpacePosition, targetTime, playfield);
}
public override float GetBeatSnapDistanceAt(HitObject referenceObject)
{
return (float)(100 * EditorBeatmap.Difficulty.SliderMultiplier * referenceObject.DifficultyControlPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
}
public override float DurationToDistance(HitObject referenceObject, double duration)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return (float)(duration / beatLength * GetBeatSnapDistanceAt(referenceObject));
}
public override double DistanceToDuration(HitObject referenceObject, float distance)
{
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceObject.StartTime);
return distance / GetBeatSnapDistanceAt(referenceObject) * beatLength;
}
public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
=> BeatSnapProvider.SnapTime(referenceObject.StartTime + DistanceToDuration(referenceObject, distance), referenceObject.StartTime) - referenceObject.StartTime;
public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
{
double startTime = referenceObject.StartTime;
double actualDuration = startTime + DistanceToDuration(referenceObject, distance);
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(referenceObject, snappedEndTime - startTime);
}
#endregion
private class LeftToolboxFlow : ExpandingButtonContainer
@ -471,16 +433,6 @@ namespace osu.Game.Rulesets.Edit
public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
public abstract float GetBeatSnapDistanceAt(HitObject referenceObject);
public abstract float DurationToDistance(HitObject referenceObject, double duration);
public abstract double DistanceToDuration(HitObject referenceObject, float distance);
public abstract double GetSnappedDurationFromDistance(HitObject referenceObject, float distance);
public abstract float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance);
#endregion
}
}

View File

@ -0,0 +1,58 @@
// 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.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Edit
{
public interface IDistanceSnapProvider : IPositionSnapProvider
{
/// <summary>
/// The spacing multiplier applied to beat snap distances.
/// </summary>
/// <seealso cref="BeatmapInfo.DistanceSpacing"/>
IBindable<double> DistanceSpacingMultiplier { get; }
/// <summary>
/// Retrieves the distance between two points within a timing point that are one beat length apart.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</param>
/// <returns>The distance between two points residing in the timing point that are one beat length apart.</returns>
float GetBeatSnapDistanceAt(HitObject referenceObject);
/// <summary>
/// Converts a duration to a distance.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, double duration);
/// <summary>
/// Converts a distance to a duration.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, float distance);
/// <summary>
/// Converts a distance to a snapped duration.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(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="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, float distance);
}
}

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 osu.Game.Rulesets.Objects;
using osuTK;
namespace osu.Game.Rulesets.Edit
@ -24,45 +23,5 @@ namespace osu.Game.Rulesets.Edit
/// <param name="screenSpacePosition">The screen-space position to be snapped.</param>
/// <returns>The position post-snapping. Time will always be null.</returns>
SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition);
/// <summary>
/// Retrieves the distance between two points within a timing point that are one beat length apart.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</param>
/// <returns>The distance between two points residing in the timing point that are one beat length apart.</returns>
float GetBeatSnapDistanceAt(HitObject referenceObject);
/// <summary>
/// Converts a duration to a distance.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, double duration);
/// <summary>
/// Converts a distance to a duration.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, float distance);
/// <summary>
/// Converts a distance to a snapped duration.
/// </summary>
/// <param name="referenceObject">An object to be used as a reference point for this operation.</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(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="referenceObject">An object to be used as a reference point for this operation.</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(HitObject referenceObject, float distance);
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Snaps the provided <paramref name="hitObject"/>'s duration using the <paramref name="snapProvider"/>.
/// </summary>
public static void SnapTo<THitObject>(this THitObject hitObject, IPositionSnapProvider? snapProvider)
public static void SnapTo<THitObject>(this THitObject hitObject, IDistanceSnapProvider? snapProvider)
where THitObject : HitObject, IHasPath
{
hitObject.Path.ExpectedDistance.Value = snapProvider?.GetSnappedDistanceFromDistance(hitObject, (float)hitObject.Path.CalculatedDistance) ?? hitObject.Path.CalculatedDistance;

View File

@ -3,6 +3,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -44,7 +45,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
protected OsuColour Colours { get; private set; }
[Resolved]
protected IPositionSnapProvider SnapProvider { get; private set; }
protected IDistanceSnapProvider SnapProvider { get; private set; }
[Resolved]
private EditorBeatmap beatmap { get; set; }
@ -52,6 +53,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
private IBindable<double> distanceSpacingMultiplier;
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
private readonly double? endTime;
@ -81,12 +84,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
base.LoadComplete();
beatDivisor.BindValueChanged(_ => updateSpacing(), true);
beatDivisor.BindValueChanged(_ => updateSpacing());
distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy();
distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true);
}
private void updateSpacing()
{
DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
DistanceSpacing = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value);
if (endTime == null)
MaxIntervals = int.MaxValue;

View File

@ -15,7 +15,6 @@ 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
@ -316,15 +315,5 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private double getTimeFromPosition(Vector2 localPosition) =>
(localPosition.X / Content.DrawWidth) * track.Length;
public float GetBeatSnapDistanceAt(HitObject referenceObject) => throw new NotImplementedException();
public float DurationToDistance(HitObject referenceObject, double duration) => throw new NotImplementedException();
public double DistanceToDuration(HitObject referenceObject, float distance) => throw new NotImplementedException();
public double GetSnappedDurationFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
public float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance) => throw new NotImplementedException();
}
}