diff --git a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
index e825df5a3f..5a4e76d586 100644
--- a/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
+++ b/osu.Game.Tests/Editor/TestSceneHitObjectComposerDistanceSnapping.cs
@@ -118,17 +118,19 @@ namespace osu.Game.Tests.Editor
[Test]
public void TestGetSnappedDurationFromDistance()
{
- assertSnappedDuration(50, 0);
+ assertSnappedDuration(0, 0);
+ assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000);
- assertSnappedDuration(150, 1000);
+ assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000);
- assertSnappedDuration(250, 2000);
+ assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
+ assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0);
- assertSnappedDuration(100, 0);
- assertSnappedDuration(150, 0);
+ assertSnappedDuration(100, 1000);
+ assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000);
@@ -139,8 +141,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDuration(50, 0);
- assertSnappedDuration(100, 0);
- assertSnappedDuration(150, 0);
+ assertSnappedDuration(100, 500);
+ assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000);
@@ -149,17 +151,17 @@ namespace osu.Game.Tests.Editor
[Test]
public void GetSnappedDistanceFromDistance()
{
- assertSnappedDistance(50, 0);
+ assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100);
- assertSnappedDistance(150, 100);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
- assertSnappedDistance(250, 200);
+ assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0);
- assertSnappedDistance(100, 0);
- assertSnappedDistance(150, 0);
+ assertSnappedDistance(100, 200);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
@@ -170,8 +172,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDistance(50, 0);
- assertSnappedDistance(100, 0);
- assertSnappedDistance(150, 0);
+ assertSnappedDistance(100, 200);
+ assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400);
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index c413d25f09..be73bc5db3 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -275,10 +275,10 @@ namespace osu.Game.Rulesets.Edit
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
- => beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance));
+ => beatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime;
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
- => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)));
+ => DurationToDistance(referenceTime, beatSnapProvider.SnapTime(DistanceToDuration(referenceTime, distance), referenceTime));
protected override void Dispose(bool isDisposing)
{
diff --git a/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs
index e1daafaebe..616f854cd7 100644
--- a/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs
+++ b/osu.Game/Rulesets/Edit/IBeatSnapProvider.cs
@@ -8,10 +8,10 @@ namespace osu.Game.Rulesets.Edit
///
/// Snaps a duration to the closest beat of a timing point applicable at the reference time.
///
- /// The time of the timing point which resides in.
- /// The duration to snap.
- /// A value that represents snapped to the closest beat of the timing point.
- double SnapTime(double referenceTime, double duration);
+ /// The time to snap.
+ /// An optional reference point to use for timing point lookup.
+ /// A value that represents snapped to the closest beat of the timing point.
+ double SnapTime(double time, double? referenceTime = null);
///
/// Get the most appropriate beat length at a given time.
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 5b2dd343e6..a33040f400 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -30,10 +30,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
ZoomDuration = 200;
ZoomEasing = Easing.OutQuint;
-
- Zoom = 60;
- MaxZoom = 240;
-
ScrollbarVisible = false;
}
@@ -64,9 +60,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track;
+
+ MinZoom = getZoomLevelForVisibleMilliseconds(10000);
+ MaxZoom = getZoomLevelForVisibleMilliseconds(500);
+ Zoom = getZoomLevelForVisibleMilliseconds(2000);
}, true);
}
+ private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(track.Length / milliseconds);
+
///
/// The timeline's scroll position in the last frame.
///
@@ -180,7 +182,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
{
var targetTime = (position.X / Content.DrawWidth) * track.Length;
- return (position, beatSnapProvider.SnapTime(targetTime, targetTime));
+ return (position, beatSnapProvider.SnapTime(targetTime));
}
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
index 9aa527667b..7ce8a751e0 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
@@ -36,12 +36,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
}
- private int minZoom = 1;
+ private float minZoom = 1;
///
/// The minimum zoom level allowed.
///
- public int MinZoom
+ public float MinZoom
{
get => minZoom;
set
@@ -56,12 +56,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
- private int maxZoom = 60;
+ private float maxZoom = 60;
///
/// The maximum zoom level allowed.
///
- public int MaxZoom
+ public float MaxZoom
{
get => maxZoom;
set
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index eae94a3c8e..8c7270d3a2 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -348,7 +348,7 @@ namespace osu.Game.Screens.Edit
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
}
- public double SnapTime(double referenceTime, double duration) => editorBeatmap.SnapTime(referenceTime, duration);
+ public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);
diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs
index 9c75d40bec..6edd62fa67 100644
--- a/osu.Game/Screens/Edit/EditorBeatmap.cs
+++ b/osu.Game/Screens/Edit/EditorBeatmap.cs
@@ -128,12 +128,12 @@ namespace osu.Game.Screens.Edit
return list.Count - 1;
}
- public double SnapTime(double referenceTime, double duration)
+ public double SnapTime(double time, double? referenceTime)
{
- double beatLength = GetBeatLengthAtTime(referenceTime);
+ var timingPoint = ControlPointInfo.TimingPointAt(referenceTime ?? time);
+ var beatLength = timingPoint.BeatLength / BeatDivisor;
- // A 1ms offset prevents rounding errors due to minute variations in duration
- return (int)((duration + 1) / beatLength) * beatLength;
+ return timingPoint.Time + (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero) * beatLength;
}
public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;