From 4c9dcdf156afc9ab1b45c05db1cbe949311a3b37 Mon Sep 17 00:00:00 2001 From: gotopie Date: Fri, 2 Nov 2018 19:04:30 -0400 Subject: [PATCH 01/14] hide seekbar when no song is playing --- osu.Game/Overlays/MusicController.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index b32fd265cb..77a3ae88a4 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -245,10 +245,10 @@ namespace osu.Game.Overlays { base.Update(); - if (current?.TrackLoaded ?? false) - { - var track = current.Track; + var track = (current?.TrackLoaded ?? false) ? current.Track : null; + if (track != null && !track.IsDummyDevice) + { progressBar.EndTime = track.Length; progressBar.CurrentTime = track.CurrentTime; @@ -258,7 +258,11 @@ namespace osu.Game.Overlays next(); } else + { + progressBar.CurrentTime = 0; + progressBar.EndTime = 1; playButton.Icon = FontAwesome.fa_play_circle_o; + } } private void play() From 8583fd1380639944316773b602446a5402eaa626 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 17:24:10 +0900 Subject: [PATCH 02/14] Fix testcase never working --- osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index b254325472..eb322df185 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -114,11 +114,11 @@ namespace osu.Game.Tests.Visual private class TestPlayfield : ScrollingPlayfield { - public new readonly ScrollingDirection Direction; + public new ScrollingDirection Direction => base.Direction; public TestPlayfield(ScrollingDirection direction) { - Direction = direction; + base.Direction.Value = direction; Padding = new MarginPadding(2); From 0bdeebbce2ad84685ca32f4c265e5a55de682db1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 17:31:43 +0900 Subject: [PATCH 03/14] Expose basic values from ISpeedChangeVisualiser --- .../Scrolling/ScrollingHitObjectContainer.cs | 17 ++++- .../ConstantSpeedChangeVisualiser.cs | 42 +++++++----- .../Visualisers/ISpeedChangeVisualiser.cs | 19 ++++-- .../OverlappingSpeedChangeVisualiser.cs | 66 +++++++++--------- .../SequentialSpeedChangeVisualiser.cs | 67 ++++++++++++------- 5 files changed, 125 insertions(+), 86 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 7307fc0ead..52b4072523 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -95,9 +95,22 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Update(); + speedChangeVisualiser.TimeRange = TimeRange.Value; + + switch (Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + speedChangeVisualiser.ScrollLength = DrawSize.Y; + break; + default: + speedChangeVisualiser.ScrollLength = DrawSize.X; + break; + } + if (!initialStateCache.IsValid) { - speedChangeVisualiser.ComputeInitialStates(Objects, Direction, TimeRange, DrawSize); + speedChangeVisualiser.ComputeInitialStates(Objects, Direction); initialStateCache.Validate(); } } @@ -107,7 +120,7 @@ namespace osu.Game.Rulesets.UI.Scrolling base.UpdateAfterChildrenLife(); // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions - speedChangeVisualiser.UpdatePositions(AliveObjects, Direction, Time.Current, TimeRange, DrawSize); + speedChangeVisualiser.UpdatePositions(AliveObjects, Direction, Time.Current); } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs index 9e910d6b11..f4417e393a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs @@ -4,64 +4,74 @@ using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; -using OpenTK; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser { - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + public double TimeRange { get; set; } + + public float ScrollLength { get; set; } + + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) { foreach (var obj in hitObjects) { - obj.LifetimeStart = obj.HitObject.StartTime - timeRange; + obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); if (obj.HitObject is IHasEndTime endTime) { - var hitObjectLength = (endTime.EndTime - obj.HitObject.StartTime) / timeRange; - switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(hitObjectLength * length.Y); + obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(hitObjectLength * length.X); + obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; } } - ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + ComputeInitialStates(obj.NestedHitObjects, direction); // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); } } - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) { foreach (var obj in hitObjects) { - var position = (obj.HitObject.StartTime - currentTime) / timeRange; - switch (direction) { case ScrollingDirection.Up: - obj.Y = (float)(position * length.Y); + obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Down: - obj.Y = (float)(-position * length.Y); + obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Left: - obj.X = (float)(position * length.X); + obj.X = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Right: - obj.X = (float)(-position * length.X); + obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); break; } } } + + public double GetDisplayStartTime(double startTime) => startTime - TimeRange; + + public float GetLength(double startTime, double endTime) + { + // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. + // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. + return -PositionAt(endTime, startTime); + } + + public float PositionAt(double currentTime, double startTime) => (float)((startTime - currentTime) / TimeRange * ScrollLength); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 097e28b2dc..b7d611df50 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -3,21 +3,22 @@ using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public interface ISpeedChangeVisualiser { + double TimeRange { get; set; } + + float ScrollLength { get; set; } + /// /// Computes the states of s that remain constant while scrolling, such as lifetime and spatial length. /// This is invoked once whenever or changes. /// /// The s whose states should be computed. /// The scrolling direction. - /// The duration required to scroll through one length of the screen before any speed adjustments. - /// The length of the screen that is scrolled through. - void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length); + void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction); /// /// Updates the positions of s, depending on the current time. This is invoked once per frame. @@ -25,8 +26,12 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers /// The s whose positions should be computed. /// The scrolling direction. /// The current time. - /// The duration required to scroll through one length of the screen before any speed adjustments. - /// The length of the screen that is scrolled through. - void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length); + void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime); + + double GetDisplayStartTime(double startTime); + + float GetLength(double startTime, double endTime); + + float PositionAt(double currentTime, double startTime); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index d2b79e2fa7..f6fbe9063f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -6,12 +6,15 @@ using osu.Framework.Lists; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; -using OpenTK; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser { + public double TimeRange { get; set; } + + public float ScrollLength { get; set; } + private readonly SortedList controlPoints; public OverlappingSpeedChangeVisualiser(SortedList controlPoints) @@ -19,79 +22,72 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers this.controlPoints = controlPoints; } - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) { foreach (var obj in hitObjects) { - // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases - double visibleDuration = timeRange / controlPointAt(obj.HitObject.StartTime).Multiplier; - - obj.LifetimeStart = obj.HitObject.StartTime - visibleDuration; + obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); if (obj.HitObject is IHasEndTime endTime) { - // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. - // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. - var hitObjectLength = -hitObjectPositionAt(obj, endTime.EndTime, timeRange); - switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(hitObjectLength * length.Y); + obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(hitObjectLength * length.X); + obj.Width = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; } } - ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + ComputeInitialStates(obj.NestedHitObjects, direction); // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); } } - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) { foreach (var obj in hitObjects) { - var position = hitObjectPositionAt(obj, currentTime, timeRange); - switch (direction) { case ScrollingDirection.Up: - obj.Y = (float)(position * length.Y); + obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Down: - obj.Y = (float)(-position * length.Y); + obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Left: - obj.X = (float)(position * length.X); + obj.X = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Right: - obj.X = (float)(-position * length.X); + obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); break; } } } - /// - /// Computes the position of a at a point in time. - /// - /// At t < startTime, position > 0.
- /// At t = startTime, position = 0.
- /// At t > startTime, position < 0. - ///
- ///
- /// The . - /// The time to find the position of at. - /// The amount of time visualised by the scrolling area. - /// The position of in the scrolling area at time = . - private double hitObjectPositionAt(DrawableHitObject obj, double time, double timeRange) - => (obj.HitObject.StartTime - time) / timeRange * controlPointAt(obj.HitObject.StartTime).Multiplier; + public double GetDisplayStartTime(double startTime) + { + // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases + double visibleDuration = TimeRange / controlPointAt(startTime).Multiplier; + return startTime - visibleDuration; + } + + public float GetLength(double startTime, double endTime) + { + // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. + // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. + return -PositionAt(endTime, startTime); + } + + public float PositionAt(double currentTime, double startTime) + => (float)((startTime - currentTime) / TimeRange * controlPointAt(startTime).Multiplier * ScrollLength); private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 25dea8dfbf..7b2471e674 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -6,13 +6,16 @@ using System.Collections.Generic; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; -using OpenTK; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser { - private readonly Dictionary hitObjectPositions = new Dictionary(); + public double TimeRange { get; set; } + + public float ScrollLength { get; set; } + + private readonly Dictionary positionCache = new Dictionary(); private readonly IReadOnlyList controlPoints; @@ -21,66 +24,78 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers this.controlPoints = controlPoints; } - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) { foreach (var obj in hitObjects) { - // To reduce iterations when updating hitobject positions later on, their initial positions are cached - var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange); - - // Todo: This is approximate and will be incorrect in the case of extreme speed changes - obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000; + obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); if (obj.HitObject is IHasEndTime endTime) { - var hitObjectLength = positionAt(endTime.EndTime, timeRange) - startPosition; - switch (direction) { case ScrollingDirection.Up: case ScrollingDirection.Down: - obj.Height = (float)(hitObjectLength * length.Y); + obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - obj.Width = (float)(hitObjectLength * length.X); + obj.Width = GetLength(obj.HitObject.StartTime, endTime.EndTime); break; } } - ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + ComputeInitialStates(obj.NestedHitObjects, direction); // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); } } - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) { - var timelinePosition = positionAt(currentTime, timeRange); - foreach (var obj in hitObjects) { - var finalPosition = hitObjectPositions[obj] - timelinePosition; - switch (direction) { case ScrollingDirection.Up: - obj.Y = (float)(finalPosition * length.Y); + obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Down: - obj.Y = (float)(-finalPosition * length.Y); + obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Left: - obj.X = (float)(finalPosition * length.X); + obj.X = PositionAt(currentTime, obj.HitObject.StartTime); break; case ScrollingDirection.Right: - obj.X = (float)(-finalPosition * length.X); + obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); break; } } } + public double GetDisplayStartTime(double startTime) => startTime - TimeRange - 1000; + + public float GetLength(double startTime, double endTime) + { + var objectLength = relativePositionAtCached(endTime) - relativePositionAtCached(startTime); + return (float)(objectLength * ScrollLength); + } + + public float PositionAt(double currentTime, double startTime) + { + // Caching is not used here as currentTime is unlikely to have been previously cached + double timelinePosition = relativePositionAt(currentTime); + return (float)((relativePositionAtCached(startTime) - timelinePosition) * ScrollLength); + } + + private double relativePositionAtCached(double time) + { + if (!positionCache.TryGetValue(time, out double existing)) + positionCache[time] = existing = relativePositionAt(time); + return existing; + } + /// /// Finds the position which corresponds to a point in time. /// This is a non-linear operation that depends on all the control points up to and including the one active at the time value. @@ -88,10 +103,10 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers /// The time to find the position at. /// The amount of time visualised by the scrolling area. /// A positive value indicating the position at . - private double positionAt(double time, double timeRange) + private double relativePositionAt(double time) { if (controlPoints.Count == 0) - return time / timeRange; + return time / TimeRange; double length = 0; @@ -115,7 +130,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers var durationInCurrent = Math.Min(currentDuration, time - current.StartTime); // Figure out how much of the time range the duration represents, and adjust it by the speed multiplier - length += durationInCurrent / timeRange * current.Multiplier; + length += durationInCurrent / TimeRange * current.Multiplier; } return length; From 589c3a47e2e4cd7432f1dbd5c50725dc0c493ecf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 18:00:55 +0900 Subject: [PATCH 04/14] Remove state computation + updates from ISpeedChangeVisualiser --- .../Scrolling/ScrollingHitObjectContainer.cs | 101 ++++++++++++++---- .../ConstantSpeedChangeVisualiser.cs | 65 ++--------- .../Visualisers/ISpeedChangeVisualiser.cs | 23 ---- .../OverlappingSpeedChangeVisualiser.cs | 71 ++---------- .../SequentialSpeedChangeVisualiser.cs | 78 +++----------- 5 files changed, 110 insertions(+), 228 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 52b4072523..31b9c22a2e 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Game.Configuration; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.UI.Scrolling.Visualisers; @@ -29,30 +30,19 @@ namespace osu.Game.Rulesets.UI.Scrolling protected readonly SortedList ControlPoints = new SortedList(); public readonly Bindable Direction = new Bindable(); + private readonly SpeedChangeVisualisationMethod visualisationMethod; private Cached initialStateCache = new Cached(); - - private readonly ISpeedChangeVisualiser speedChangeVisualiser; + private ISpeedChangeVisualiser visualiser; public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod) { + this.visualisationMethod = visualisationMethod; + RelativeSizeAxes = Axes.Both; TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate(); - - switch (visualisationMethod) - { - case SpeedChangeVisualisationMethod.Sequential: - speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints); - break; - case SpeedChangeVisualisationMethod.Overlapping: - speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); - break; - case SpeedChangeVisualisationMethod.Constant: - speedChangeVisualiser = new ConstantSpeedChangeVisualiser(); - break; - } } public override void Add(DrawableHitObject hitObject) @@ -95,23 +85,68 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.Update(); - speedChangeVisualiser.TimeRange = TimeRange.Value; + if (!initialStateCache.IsValid) + { + visualiser = createVisualiser(); + + foreach (var obj in Objects) + computeInitialStateRecursive(obj); + initialStateCache.Validate(); + } + } + + private ISpeedChangeVisualiser createVisualiser() + { + float scrollLength; switch (Direction.Value) { case ScrollingDirection.Up: case ScrollingDirection.Down: - speedChangeVisualiser.ScrollLength = DrawSize.Y; + scrollLength = DrawSize.Y; break; default: - speedChangeVisualiser.ScrollLength = DrawSize.X; + scrollLength = DrawSize.X; break; } - if (!initialStateCache.IsValid) + switch (visualisationMethod) { - speedChangeVisualiser.ComputeInitialStates(Objects, Direction); - initialStateCache.Validate(); + default: + case SpeedChangeVisualisationMethod.Constant: + return new ConstantSpeedChangeVisualiser(TimeRange, scrollLength); + case SpeedChangeVisualisationMethod.Overlapping: + return new OverlappingSpeedChangeVisualiser(ControlPoints, TimeRange, scrollLength); + case SpeedChangeVisualisationMethod.Sequential: + return new SequentialSpeedChangeVisualiser(ControlPoints, TimeRange, scrollLength); + } + } + + private void computeInitialStateRecursive(DrawableHitObject hitObject) + { + hitObject.LifetimeStart = visualiser.GetDisplayStartTime(hitObject.HitObject.StartTime); + + if (hitObject.HitObject is IHasEndTime endTime) + { + switch (Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime); + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime); + break; + } + } + + foreach (var obj in hitObject.NestedHitObjects) + { + computeInitialStateRecursive(obj); + + // Nested hitobjects don't need to scroll, but they do need accurate positions + updatePosition(obj, hitObject.HitObject.StartTime); } } @@ -119,8 +154,28 @@ namespace osu.Game.Rulesets.UI.Scrolling { base.UpdateAfterChildrenLife(); - // We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions - speedChangeVisualiser.UpdatePositions(AliveObjects, Direction, Time.Current); + // We need to calculate hitobject positions as soon as possible after lifetimes so that hitobjects get the final say in their positions + foreach (var obj in AliveObjects) + updatePosition(obj, Time.Current); + } + + private void updatePosition(DrawableHitObject hitObject, double currentTime) + { + switch (Direction.Value) + { + case ScrollingDirection.Up: + hitObject.Y = visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + break; + case ScrollingDirection.Down: + hitObject.Y = -visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + break; + case ScrollingDirection.Left: + hitObject.X = visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + break; + case ScrollingDirection.Right: + hitObject.X = -visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + break; + } } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs index f4417e393a..1d3020ec8b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs @@ -1,69 +1,20 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; - namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser + public readonly struct ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser { - public double TimeRange { get; set; } + private readonly double timeRange; + private readonly float scrollLength; - public float ScrollLength { get; set; } - - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) + public ConstantSpeedChangeVisualiser(double timeRange, float scrollLength) { - foreach (var obj in hitObjects) - { - obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); - - if (obj.HitObject is IHasEndTime endTime) - { - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - } - } - - ComputeInitialStates(obj.NestedHitObjects, direction); - - // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); - } + this.timeRange = timeRange; + this.scrollLength = scrollLength; } - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) - { - foreach (var obj in hitObjects) - { - switch (direction) - { - case ScrollingDirection.Up: - obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Down: - obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Left: - obj.X = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Right: - obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - } - } - } - - public double GetDisplayStartTime(double startTime) => startTime - TimeRange; + public double GetDisplayStartTime(double startTime) => startTime - timeRange; public float GetLength(double startTime, double endTime) { @@ -72,6 +23,6 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers return -PositionAt(endTime, startTime); } - public float PositionAt(double currentTime, double startTime) => (float)((startTime - currentTime) / TimeRange * ScrollLength); + public float PositionAt(double currentTime, double startTime) => (float)((startTime - currentTime) / timeRange * scrollLength); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index b7d611df50..478c10c6ce 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -1,33 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; - namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public interface ISpeedChangeVisualiser { - double TimeRange { get; set; } - - float ScrollLength { get; set; } - - /// - /// Computes the states of s that remain constant while scrolling, such as lifetime and spatial length. - /// This is invoked once whenever or changes. - /// - /// The s whose states should be computed. - /// The scrolling direction. - void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction); - - /// - /// Updates the positions of s, depending on the current time. This is invoked once per frame. - /// - /// The s whose positions should be computed. - /// The scrolling direction. - /// The current time. - void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime); - double GetDisplayStartTime(double startTime); float GetLength(double startTime, double endTime); diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index f6fbe9063f..646ea0c280 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -1,81 +1,32 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Lists; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser + public readonly struct OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser { - public double TimeRange { get; set; } - - public float ScrollLength { get; set; } + private readonly MultiplierControlPoint searchPoint; private readonly SortedList controlPoints; + private readonly double timeRange; + private readonly float scrollLength; - public OverlappingSpeedChangeVisualiser(SortedList controlPoints) + public OverlappingSpeedChangeVisualiser(SortedList controlPoints, double timeRange, float scrollLength) { this.controlPoints = controlPoints; - } + this.timeRange = timeRange; + this.scrollLength = scrollLength; - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) - { - foreach (var obj in hitObjects) - { - obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); - - if (obj.HitObject is IHasEndTime endTime) - { - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - obj.Width = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - } - } - - ComputeInitialStates(obj.NestedHitObjects, direction); - - // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); - } - } - - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) - { - foreach (var obj in hitObjects) - { - switch (direction) - { - case ScrollingDirection.Up: - obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Down: - obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Left: - obj.X = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Right: - obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - } - } + searchPoint = new MultiplierControlPoint(); } public double GetDisplayStartTime(double startTime) { // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases - double visibleDuration = TimeRange / controlPointAt(startTime).Multiplier; + double visibleDuration = timeRange / controlPointAt(startTime).Multiplier; return startTime - visibleDuration; } @@ -87,9 +38,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers } public float PositionAt(double currentTime, double startTime) - => (float)((startTime - currentTime) / TimeRange * controlPointAt(startTime).Multiplier * ScrollLength); - - private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint(); + => (float)((startTime - currentTime) / timeRange * controlPointAt(startTime).Multiplier * scrollLength); /// /// Finds the which affects the speed of hitobjects at a specific time. diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 7b2471e674..9e8099fdb5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -3,90 +3,40 @@ using System; using System.Collections.Generic; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser + public readonly struct SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser { - public double TimeRange { get; set; } - - public float ScrollLength { get; set; } - - private readonly Dictionary positionCache = new Dictionary(); + private readonly Dictionary positionCache; private readonly IReadOnlyList controlPoints; + private readonly double timeRange; + private readonly float scrollLength; - public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints) + public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints, double timeRange, float scrollLength) { this.controlPoints = controlPoints; + this.timeRange = timeRange; + this.scrollLength = scrollLength; + + positionCache = new Dictionary(); } - public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction) - { - foreach (var obj in hitObjects) - { - obj.LifetimeStart = GetDisplayStartTime(obj.HitObject.StartTime); - - if (obj.HitObject is IHasEndTime endTime) - { - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - obj.Height = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - obj.Width = GetLength(obj.HitObject.StartTime, endTime.EndTime); - break; - } - } - - ComputeInitialStates(obj.NestedHitObjects, direction); - - // Nested hitobjects don't need to scroll, but they do need accurate positions - UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime); - } - } - - public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime) - { - foreach (var obj in hitObjects) - { - switch (direction) - { - case ScrollingDirection.Up: - obj.Y = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Down: - obj.Y = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Left: - obj.X = PositionAt(currentTime, obj.HitObject.StartTime); - break; - case ScrollingDirection.Right: - obj.X = -PositionAt(currentTime, obj.HitObject.StartTime); - break; - } - } - } - - public double GetDisplayStartTime(double startTime) => startTime - TimeRange - 1000; + public double GetDisplayStartTime(double startTime) => startTime - timeRange - 1000; public float GetLength(double startTime, double endTime) { var objectLength = relativePositionAtCached(endTime) - relativePositionAtCached(startTime); - return (float)(objectLength * ScrollLength); + return (float)(objectLength * scrollLength); } public float PositionAt(double currentTime, double startTime) { // Caching is not used here as currentTime is unlikely to have been previously cached double timelinePosition = relativePositionAt(currentTime); - return (float)((relativePositionAtCached(startTime) - timelinePosition) * ScrollLength); + return (float)((relativePositionAtCached(startTime) - timelinePosition) * scrollLength); } private double relativePositionAtCached(double time) @@ -106,7 +56,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers private double relativePositionAt(double time) { if (controlPoints.Count == 0) - return time / TimeRange; + return time / timeRange; double length = 0; @@ -130,7 +80,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers var durationInCurrent = Math.Min(currentDuration, time - current.StartTime); // Figure out how much of the time range the duration represents, and adjust it by the speed multiplier - length += durationInCurrent / TimeRange * current.Multiplier; + length += durationInCurrent / timeRange * current.Multiplier; } return length; From 76ea314c273718621020d4f5b07dcd3c83376d9a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 18:04:13 +0900 Subject: [PATCH 05/14] Reorder params --- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 8 ++++---- .../Visualisers/ConstantSpeedChangeVisualiser.cs | 6 +++--- .../Scrolling/Visualisers/ISpeedChangeVisualiser.cs | 4 ++-- .../Visualisers/OverlappingSpeedChangeVisualiser.cs | 12 ++++++------ .../Visualisers/SequentialSpeedChangeVisualiser.cs | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 31b9c22a2e..641c8066a5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -164,16 +164,16 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (Direction.Value) { case ScrollingDirection.Up: - hitObject.Y = visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + hitObject.Y = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); break; case ScrollingDirection.Down: - hitObject.Y = -visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + hitObject.Y = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); break; case ScrollingDirection.Left: - hitObject.X = visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + hitObject.X = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); break; case ScrollingDirection.Right: - hitObject.X = -visualiser.PositionAt(currentTime, hitObject.HitObject.StartTime); + hitObject.X = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); break; } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs index 1d3020ec8b..7c8f9018a9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs @@ -14,15 +14,15 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers this.scrollLength = scrollLength; } - public double GetDisplayStartTime(double startTime) => startTime - timeRange; + public double GetDisplayStartTime(double time) => time - timeRange; public float GetLength(double startTime, double endTime) { // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. - return -PositionAt(endTime, startTime); + return -PositionAt(startTime, endTime); } - public float PositionAt(double currentTime, double startTime) => (float)((startTime - currentTime) / timeRange * scrollLength); + public float PositionAt(double time, double currentTime) => (float)((time - currentTime) / timeRange * scrollLength); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 478c10c6ce..5e719b4f2a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -5,10 +5,10 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public interface ISpeedChangeVisualiser { - double GetDisplayStartTime(double startTime); + double GetDisplayStartTime(double time); float GetLength(double startTime, double endTime); - float PositionAt(double currentTime, double startTime); + float PositionAt(double time, double currentTime); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 646ea0c280..1fbada4f4d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -23,22 +23,22 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers searchPoint = new MultiplierControlPoint(); } - public double GetDisplayStartTime(double startTime) + public double GetDisplayStartTime(double time) { // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases - double visibleDuration = timeRange / controlPointAt(startTime).Multiplier; - return startTime - visibleDuration; + double visibleDuration = timeRange / controlPointAt(time).Multiplier; + return time - visibleDuration; } public float GetLength(double startTime, double endTime) { // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. - return -PositionAt(endTime, startTime); + return -PositionAt(startTime, endTime); } - public float PositionAt(double currentTime, double startTime) - => (float)((startTime - currentTime) / timeRange * controlPointAt(startTime).Multiplier * scrollLength); + public float PositionAt(double time, double currentTime) + => (float)((time - currentTime) / timeRange * controlPointAt(time).Multiplier * scrollLength); /// /// Finds the which affects the speed of hitobjects at a specific time. diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 9e8099fdb5..1fa87a2f6b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers positionCache = new Dictionary(); } - public double GetDisplayStartTime(double startTime) => startTime - timeRange - 1000; + public double GetDisplayStartTime(double time) => time - timeRange - 1000; public float GetLength(double startTime, double endTime) { @@ -32,11 +32,11 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers return (float)(objectLength * scrollLength); } - public float PositionAt(double currentTime, double startTime) + public float PositionAt(double time, double currentTime) { // Caching is not used here as currentTime is unlikely to have been previously cached double timelinePosition = relativePositionAt(currentTime); - return (float)((relativePositionAtCached(startTime) - timelinePosition) * scrollLength); + return (float)((relativePositionAtCached(time) - timelinePosition) * scrollLength); } private double relativePositionAtCached(double time) From f41bfd14ca890730c71b646ccda47222213c85b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 18:14:11 +0900 Subject: [PATCH 06/14] Add some xmldocs --- .../Visualisers/ISpeedChangeVisualiser.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index 5e719b4f2a..b4435f558d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -5,10 +5,30 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { public interface ISpeedChangeVisualiser { + /// + /// Given a point in time, computes the time at which the point enters the visible time range of this . + /// + /// + /// E.g. For a constant visible time range of 5000ms, the time at which t=7000ms enters the visible time range is 2000ms. + /// + /// The time value. + /// The time at which enters the visible time range of this . double GetDisplayStartTime(double time); + /// + /// Computes the spatial length within a start and end time. + /// + /// The start time. + /// The end time. + /// The absolute spatial length. float GetLength(double startTime, double endTime); + /// + /// Given the current time, computes the spatial position of a point in time. + /// + /// The time to compute the spatial position of. + /// The current time. + /// The absolute spatial position. float PositionAt(double time, double currentTime); } } From 195f82fa966a0c07ff781dd8725faf8802b0f97b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 18:33:24 +0900 Subject: [PATCH 07/14] Give visualiser methods range+length params again --- .../Scrolling/ScrollingHitObjectContainer.cs | 75 +++++++++---------- .../ConstantSpeedChangeVisualiser.cs | 24 +++--- .../Visualisers/ISpeedChangeVisualiser.cs | 24 ++++-- .../OverlappingSpeedChangeVisualiser.cs | 20 ++--- .../SequentialSpeedChangeVisualiser.cs | 28 ++++--- 5 files changed, 86 insertions(+), 85 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 641c8066a5..3844a5903c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -30,19 +30,30 @@ namespace osu.Game.Rulesets.UI.Scrolling protected readonly SortedList ControlPoints = new SortedList(); public readonly Bindable Direction = new Bindable(); - private readonly SpeedChangeVisualisationMethod visualisationMethod; + + private readonly ISpeedChangeVisualiser visualiser; private Cached initialStateCache = new Cached(); - private ISpeedChangeVisualiser visualiser; public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod) { - this.visualisationMethod = visualisationMethod; - RelativeSizeAxes = Axes.Both; TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate(); + + switch (visualisationMethod) + { + case SpeedChangeVisualisationMethod.Sequential: + visualiser = new SequentialSpeedChangeVisualiser(ControlPoints); + break; + case SpeedChangeVisualisationMethod.Overlapping: + visualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); + break; + case SpeedChangeVisualisationMethod.Constant: + visualiser = new ConstantSpeedChangeVisualiser(); + break; + } } public override void Add(DrawableHitObject hitObject) @@ -81,13 +92,26 @@ namespace osu.Game.Rulesets.UI.Scrolling return base.Invalidate(invalidation, source, shallPropagate); } + private float scrollLength; + protected override void Update() { base.Update(); if (!initialStateCache.IsValid) { - visualiser = createVisualiser(); + switch (Direction.Value) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + scrollLength = DrawSize.Y; + break; + default: + scrollLength = DrawSize.X; + break; + } + + visualiser.Reset(); foreach (var obj in Objects) computeInitialStateRecursive(obj); @@ -95,36 +119,9 @@ namespace osu.Game.Rulesets.UI.Scrolling } } - private ISpeedChangeVisualiser createVisualiser() - { - float scrollLength; - - switch (Direction.Value) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - scrollLength = DrawSize.Y; - break; - default: - scrollLength = DrawSize.X; - break; - } - - switch (visualisationMethod) - { - default: - case SpeedChangeVisualisationMethod.Constant: - return new ConstantSpeedChangeVisualiser(TimeRange, scrollLength); - case SpeedChangeVisualisationMethod.Overlapping: - return new OverlappingSpeedChangeVisualiser(ControlPoints, TimeRange, scrollLength); - case SpeedChangeVisualisationMethod.Sequential: - return new SequentialSpeedChangeVisualiser(ControlPoints, TimeRange, scrollLength); - } - } - private void computeInitialStateRecursive(DrawableHitObject hitObject) { - hitObject.LifetimeStart = visualiser.GetDisplayStartTime(hitObject.HitObject.StartTime); + hitObject.LifetimeStart = visualiser.GetDisplayStartTime(hitObject.HitObject.StartTime, TimeRange); if (hitObject.HitObject is IHasEndTime endTime) { @@ -132,11 +129,11 @@ namespace osu.Game.Rulesets.UI.Scrolling { case ScrollingDirection.Up: case ScrollingDirection.Down: - hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime); + hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime); + hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); break; } } @@ -164,16 +161,16 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (Direction.Value) { case ScrollingDirection.Up: - hitObject.Y = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); + hitObject.Y = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Down: - hitObject.Y = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); + hitObject.Y = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Left: - hitObject.X = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); + hitObject.X = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Right: - hitObject.X = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime); + hitObject.X = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs index 7c8f9018a9..09710392c9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs @@ -3,26 +3,22 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public readonly struct ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser + public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser { - private readonly double timeRange; - private readonly float scrollLength; + public double GetDisplayStartTime(double time, double timeRange) => time - timeRange; - public ConstantSpeedChangeVisualiser(double timeRange, float scrollLength) - { - this.timeRange = timeRange; - this.scrollLength = scrollLength; - } - - public double GetDisplayStartTime(double time) => time - timeRange; - - public float GetLength(double startTime, double endTime) + public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) { // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. - return -PositionAt(startTime, endTime); + return -PositionAt(startTime, endTime, timeRange, scrollLength); } - public float PositionAt(double time, double currentTime) => (float)((time - currentTime) / timeRange * scrollLength); + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) + => (float)((time - currentTime) / timeRange * scrollLength); + + public void Reset() + { + } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs index b4435f558d..f950e7f375 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs @@ -6,29 +6,39 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers public interface ISpeedChangeVisualiser { /// - /// Given a point in time, computes the time at which the point enters the visible time range of this . + /// Given a point in time, computes the time at which it enters the time range. /// /// - /// E.g. For a constant visible time range of 5000ms, the time at which t=7000ms enters the visible time range is 2000ms. + /// E.g. For a constant time range of 5000ms, the time at which t=7000ms enters the time range is 2000ms. /// - /// The time value. - /// The time at which enters the visible time range of this . - double GetDisplayStartTime(double time); + /// The point in time. + /// The amount of visible time. + /// The time at which enters . + double GetDisplayStartTime(double time, double timeRange); /// /// Computes the spatial length within a start and end time. /// /// The start time. /// The end time. + /// The amount of visible time. + /// The absolute spatial length through . /// The absolute spatial length. - float GetLength(double startTime, double endTime); + float GetLength(double startTime, double endTime, double timeRange, float scrollLength); /// /// Given the current time, computes the spatial position of a point in time. /// /// The time to compute the spatial position of. /// The current time. + /// The amount of visible time. + /// The absolute spatial length through . /// The absolute spatial position. - float PositionAt(double time, double currentTime); + float PositionAt(double time, double currentTime, double timeRange, float scrollLength); + + /// + /// Resets this to a default state. + /// + void Reset(); } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs index 1fbada4f4d..8b0eacc26b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs @@ -6,40 +6,40 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public readonly struct OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser + public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser { private readonly MultiplierControlPoint searchPoint; private readonly SortedList controlPoints; - private readonly double timeRange; - private readonly float scrollLength; - public OverlappingSpeedChangeVisualiser(SortedList controlPoints, double timeRange, float scrollLength) + public OverlappingSpeedChangeVisualiser(SortedList controlPoints) { this.controlPoints = controlPoints; - this.timeRange = timeRange; - this.scrollLength = scrollLength; searchPoint = new MultiplierControlPoint(); } - public double GetDisplayStartTime(double time) + public double GetDisplayStartTime(double time, double timeRange) { // The total amount of time that the hitobject will remain visible within the timeRange, which decreases as the speed multiplier increases double visibleDuration = timeRange / controlPointAt(time).Multiplier; return time - visibleDuration; } - public float GetLength(double startTime, double endTime) + public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) { // At the hitobject's end time, the hitobject will be positioned such that its end rests at the origin. // This results in a negative-position value, and the absolute of it indicates the length of the hitobject. - return -PositionAt(startTime, endTime); + return -PositionAt(startTime, endTime, timeRange, scrollLength); } - public float PositionAt(double time, double currentTime) + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) => (float)((time - currentTime) / timeRange * controlPointAt(time).Multiplier * scrollLength); + public void Reset() + { + } + /// /// Finds the which affects the speed of hitobjects at a specific time. /// diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs index 1fa87a2f6b..bc63299bc8 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs @@ -7,45 +7,43 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public readonly struct SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser + public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser { private readonly Dictionary positionCache; private readonly IReadOnlyList controlPoints; - private readonly double timeRange; - private readonly float scrollLength; - public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints, double timeRange, float scrollLength) + public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints) { this.controlPoints = controlPoints; - this.timeRange = timeRange; - this.scrollLength = scrollLength; positionCache = new Dictionary(); } - public double GetDisplayStartTime(double time) => time - timeRange - 1000; + public double GetDisplayStartTime(double time, double timeRange) => time - timeRange - 1000; - public float GetLength(double startTime, double endTime) + public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) { - var objectLength = relativePositionAtCached(endTime) - relativePositionAtCached(startTime); + var objectLength = relativePositionAtCached(endTime, timeRange) - relativePositionAtCached(startTime, timeRange); return (float)(objectLength * scrollLength); } - public float PositionAt(double time, double currentTime) + public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) { // Caching is not used here as currentTime is unlikely to have been previously cached - double timelinePosition = relativePositionAt(currentTime); - return (float)((relativePositionAtCached(time) - timelinePosition) * scrollLength); + double timelinePosition = relativePositionAt(currentTime, timeRange); + return (float)((relativePositionAtCached(time, timeRange) - timelinePosition) * scrollLength); } - private double relativePositionAtCached(double time) + private double relativePositionAtCached(double time, double timeRange) { if (!positionCache.TryGetValue(time, out double existing)) - positionCache[time] = existing = relativePositionAt(time); + positionCache[time] = existing = relativePositionAt(time, timeRange); return existing; } + public void Reset() => positionCache.Clear(); + /// /// Finds the position which corresponds to a point in time. /// This is a non-linear operation that depends on all the control points up to and including the one active at the time value. @@ -53,7 +51,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers /// The time to find the position at. /// The amount of time visualised by the scrolling area. /// A positive value indicating the position at . - private double relativePositionAt(double time) + private double relativePositionAt(double time, double timeRange) { if (controlPoints.Count == 0) return time / timeRange; From 2f87f267a3b6be261aaf143305135911d224158e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Nov 2018 19:45:32 +0900 Subject: [PATCH 08/14] Fix height being set instead of width --- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 3844a5903c..cc5d7e7751 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.UI.Scrolling break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); + hitObject.Width = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); break; } } From f66a9f4f1e384c96879e2ef764f62daec8ab3325 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Nov 2018 19:51:34 +0900 Subject: [PATCH 09/14] Rename IScrollChangeVisualiser -> IScrollAlgorithm --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- ...ualisationMethod.cs => ScrollAlgorithm.cs} | 2 +- .../Scrolling/ScrollingHitObjectContainer.cs | 34 +++++++++---------- .../UI/Scrolling/ScrollingPlayfield.cs | 4 +-- ...sualiser.cs => ConstantScrollAlgorithm.cs} | 2 +- ...hangeVisualiser.cs => IScrollAlgorithm.cs} | 4 +-- ...liser.cs => OverlappingScrollAlgorithm.cs} | 4 +-- ...aliser.cs => SequentialScrollAlgorithm.cs} | 4 +-- 9 files changed, 29 insertions(+), 29 deletions(-) rename osu.Game/Configuration/{SpeedChangeVisualisationMethod.cs => ScrollAlgorithm.cs} (89%) rename osu.Game/Rulesets/UI/Scrolling/Visualisers/{ConstantSpeedChangeVisualiser.cs => ConstantScrollAlgorithm.cs} (93%) rename osu.Game/Rulesets/UI/Scrolling/Visualisers/{ISpeedChangeVisualiser.cs => IScrollAlgorithm.cs} (94%) rename osu.Game/Rulesets/UI/Scrolling/Visualisers/{OverlappingSpeedChangeVisualiser.cs => OverlappingScrollAlgorithm.cs} (93%) rename osu.Game/Rulesets/UI/Scrolling/Visualisers/{SequentialSpeedChangeVisualiser.cs => SequentialScrollAlgorithm.cs} (95%) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 925e7aaac9..160d784f5f 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant; + protected override ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Constant; public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) { diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 40ed659bd6..eab2965160 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping; + protected override ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Overlapping; private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; diff --git a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs b/osu.Game/Configuration/ScrollAlgorithm.cs similarity index 89% rename from osu.Game/Configuration/SpeedChangeVisualisationMethod.cs rename to osu.Game/Configuration/ScrollAlgorithm.cs index 39c6e5649c..be302d38f6 100644 --- a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs +++ b/osu.Game/Configuration/ScrollAlgorithm.cs @@ -5,7 +5,7 @@ using System.ComponentModel; namespace osu.Game.Configuration { - public enum SpeedChangeVisualisationMethod + public enum ScrollAlgorithm { [Description("Sequential")] Sequential, diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index cc5d7e7751..78032ddba9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -31,27 +31,27 @@ namespace osu.Game.Rulesets.UI.Scrolling public readonly Bindable Direction = new Bindable(); - private readonly ISpeedChangeVisualiser visualiser; + private readonly IScrollAlgorithm algorithm; private Cached initialStateCache = new Cached(); - public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod) + public ScrollingHitObjectContainer(ScrollAlgorithm scrollAlgorithm) { RelativeSizeAxes = Axes.Both; TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate(); - switch (visualisationMethod) + switch (scrollAlgorithm) { - case SpeedChangeVisualisationMethod.Sequential: - visualiser = new SequentialSpeedChangeVisualiser(ControlPoints); + case ScrollAlgorithm.Sequential: + algorithm = new SequentialScrollAlgorithm(ControlPoints); break; - case SpeedChangeVisualisationMethod.Overlapping: - visualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); + case ScrollAlgorithm.Overlapping: + algorithm = new OverlappingScrollAlgorithm(ControlPoints); break; - case SpeedChangeVisualisationMethod.Constant: - visualiser = new ConstantSpeedChangeVisualiser(); + case ScrollAlgorithm.Constant: + algorithm = new ConstantScrollAlgorithm(); break; } } @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.UI.Scrolling break; } - visualiser.Reset(); + algorithm.Reset(); foreach (var obj in Objects) computeInitialStateRecursive(obj); @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void computeInitialStateRecursive(DrawableHitObject hitObject) { - hitObject.LifetimeStart = visualiser.GetDisplayStartTime(hitObject.HitObject.StartTime, TimeRange); + hitObject.LifetimeStart = algorithm.GetDisplayStartTime(hitObject.HitObject.StartTime, TimeRange); if (hitObject.HitObject is IHasEndTime endTime) { @@ -129,11 +129,11 @@ namespace osu.Game.Rulesets.UI.Scrolling { case ScrollingDirection.Up: case ScrollingDirection.Down: - hitObject.Height = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); + hitObject.Height = algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); break; case ScrollingDirection.Left: case ScrollingDirection.Right: - hitObject.Width = visualiser.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); + hitObject.Width = algorithm.GetLength(hitObject.HitObject.StartTime, endTime.EndTime, TimeRange, scrollLength); break; } } @@ -161,16 +161,16 @@ namespace osu.Game.Rulesets.UI.Scrolling switch (Direction.Value) { case ScrollingDirection.Up: - hitObject.Y = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); + hitObject.Y = algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Down: - hitObject.Y = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); + hitObject.Y = -algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Left: - hitObject.X = visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); + hitObject.X = algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; case ScrollingDirection.Right: - hitObject.X = -visualiser.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); + hitObject.X = -algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, TimeRange, scrollLength); break; } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index a1fc13ce4d..b0367444bb 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// protected readonly Bindable Direction = new Bindable(); - protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential; + protected virtual ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Sequential; [BackgroundDependencyLoader] private void load() @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.UI.Scrolling protected sealed override HitObjectContainer CreateHitObjectContainer() { - var container = new ScrollingHitObjectContainer(VisualisationMethod); + var container = new ScrollingHitObjectContainer(ScrollAlgorithm); container.Direction.BindTo(Direction); return container; } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs similarity index 93% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs rename to osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs index 09710392c9..cab8ec45a5 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser + public class ConstantScrollAlgorithm : IScrollAlgorithm { public double GetDisplayStartTime(double time, double timeRange) => time - timeRange; diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs similarity index 94% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs rename to osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs index f950e7f375..7d72f93962 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ISpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public interface ISpeedChangeVisualiser + public interface IScrollAlgorithm { /// /// Given a point in time, computes the time at which it enters the time range. @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers float PositionAt(double time, double currentTime, double timeRange, float scrollLength); /// - /// Resets this to a default state. + /// Resets this to a default state. /// void Reset(); } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs similarity index 93% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs rename to osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs index 8b0eacc26b..392d7a1c51 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs @@ -6,13 +6,13 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser + public class OverlappingScrollAlgorithm : IScrollAlgorithm { private readonly MultiplierControlPoint searchPoint; private readonly SortedList controlPoints; - public OverlappingSpeedChangeVisualiser(SortedList controlPoints) + public OverlappingScrollAlgorithm(SortedList controlPoints) { this.controlPoints = controlPoints; diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs similarity index 95% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs rename to osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs index bc63299bc8..ff058cfdcf 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialSpeedChangeVisualiser.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs @@ -7,13 +7,13 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.UI.Scrolling.Visualisers { - public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser + public class SequentialScrollAlgorithm : IScrollAlgorithm { private readonly Dictionary positionCache; private readonly IReadOnlyList controlPoints; - public SequentialSpeedChangeVisualiser(IReadOnlyList controlPoints) + public SequentialScrollAlgorithm(IReadOnlyList controlPoints) { this.controlPoints = controlPoints; From 33056b80987f48c8ba6eccb4cce85e3e675956b7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 2 Nov 2018 19:52:40 +0900 Subject: [PATCH 10/14] Adjust namespaces --- .../{Visualisers => Algorithms}/ConstantScrollAlgorithm.cs | 2 +- .../Scrolling/{Visualisers => Algorithms}/IScrollAlgorithm.cs | 2 +- .../{Visualisers => Algorithms}/OverlappingScrollAlgorithm.cs | 2 +- .../{Visualisers => Algorithms}/SequentialScrollAlgorithm.cs | 2 +- osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game/Rulesets/UI/Scrolling/{Visualisers => Algorithms}/ConstantScrollAlgorithm.cs (94%) rename osu.Game/Rulesets/UI/Scrolling/{Visualisers => Algorithms}/IScrollAlgorithm.cs (97%) rename osu.Game/Rulesets/UI/Scrolling/{Visualisers => Algorithms}/OverlappingScrollAlgorithm.cs (97%) rename osu.Game/Rulesets/UI/Scrolling/{Visualisers => Algorithms}/SequentialScrollAlgorithm.cs (98%) diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs similarity index 94% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs rename to osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs index cab8ec45a5..ed61ed7022 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public class ConstantScrollAlgorithm : IScrollAlgorithm { diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs similarity index 97% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs rename to osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs index 7d72f93962..43bc1b2a2a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/IScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public interface IScrollAlgorithm { diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs similarity index 97% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs rename to osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index 392d7a1c51..f7c097e81d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -4,7 +4,7 @@ using osu.Framework.Lists; using osu.Game.Rulesets.Timing; -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public class OverlappingScrollAlgorithm : IScrollAlgorithm { diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs similarity index 98% rename from osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs rename to osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index ff058cfdcf..54494cfe63 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Visualisers/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using osu.Game.Rulesets.Timing; -namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public class SequentialScrollAlgorithm : IScrollAlgorithm { diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 78032ddba9..45bc95a71c 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -9,7 +9,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.UI.Scrolling.Visualisers; +using osu.Game.Rulesets.UI.Scrolling.Algorithms; namespace osu.Game.Rulesets.UI.Scrolling { From aee7a80e71683b1377e87bab67ecd3689c9bc810 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 17:26:37 +0900 Subject: [PATCH 11/14] ScrollAlgorithm -> ScrollVisualisationMethod --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- ...ScrollAlgorithm.cs => ScrollVisualisationMethod.cs} | 2 +- .../UI/Scrolling/ScrollingHitObjectContainer.cs | 10 +++++----- osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) rename osu.Game/Configuration/{ScrollAlgorithm.cs => ScrollVisualisationMethod.cs} (90%) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 160d784f5f..08b7684677 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - protected override ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Constant; + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Constant; public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) { diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index eab2965160..824c1f817a 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - protected override ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Overlapping; + protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping; private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; diff --git a/osu.Game/Configuration/ScrollAlgorithm.cs b/osu.Game/Configuration/ScrollVisualisationMethod.cs similarity index 90% rename from osu.Game/Configuration/ScrollAlgorithm.cs rename to osu.Game/Configuration/ScrollVisualisationMethod.cs index be302d38f6..cc7dcdbc0e 100644 --- a/osu.Game/Configuration/ScrollAlgorithm.cs +++ b/osu.Game/Configuration/ScrollVisualisationMethod.cs @@ -5,7 +5,7 @@ using System.ComponentModel; namespace osu.Game.Configuration { - public enum ScrollAlgorithm + public enum ScrollVisualisationMethod { [Description("Sequential")] Sequential, diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 45bc95a71c..489604afc9 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -35,22 +35,22 @@ namespace osu.Game.Rulesets.UI.Scrolling private Cached initialStateCache = new Cached(); - public ScrollingHitObjectContainer(ScrollAlgorithm scrollAlgorithm) + public ScrollingHitObjectContainer(ScrollVisualisationMethod visualisationMethod) { RelativeSizeAxes = Axes.Both; TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate(); - switch (scrollAlgorithm) + switch (visualisationMethod) { - case ScrollAlgorithm.Sequential: + case ScrollVisualisationMethod.Sequential: algorithm = new SequentialScrollAlgorithm(ControlPoints); break; - case ScrollAlgorithm.Overlapping: + case ScrollVisualisationMethod.Overlapping: algorithm = new OverlappingScrollAlgorithm(ControlPoints); break; - case ScrollAlgorithm.Constant: + case ScrollVisualisationMethod.Constant: algorithm = new ConstantScrollAlgorithm(); break; } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index b0367444bb..5e2704c9ee 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.UI.Scrolling /// protected readonly Bindable Direction = new Bindable(); - protected virtual ScrollAlgorithm ScrollAlgorithm => ScrollAlgorithm.Sequential; + protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential; [BackgroundDependencyLoader] private void load() @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.UI.Scrolling protected sealed override HitObjectContainer CreateHitObjectContainer() { - var container = new ScrollingHitObjectContainer(ScrollAlgorithm); + var container = new ScrollingHitObjectContainer(VisualisationMethod); container.Direction.BindTo(Direction); return container; } From b9b20607af6e4947b3628ce97818cb0df24371ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Nov 2018 19:55:48 +0900 Subject: [PATCH 12/14] Add IScrollAlgorithm.TimeAt() --- .../ScrollAlgorithms/ConstantScrollTest.cs | 54 +++++++++++++++ .../ScrollAlgorithms/OverlappingScrollTest.cs | 67 +++++++++++++++++++ .../ScrollAlgorithms/SequentialScrollTest.cs | 64 ++++++++++++++++++ .../Algorithms/ConstantScrollAlgorithm.cs | 3 + .../Scrolling/Algorithms/IScrollAlgorithm.cs | 10 +++ .../Algorithms/OverlappingScrollAlgorithm.cs | 28 ++++++++ .../Algorithms/SequentialScrollAlgorithm.cs | 30 +++++++++ 7 files changed, 256 insertions(+) create mode 100644 osu.Game.Tests/ScrollAlgorithms/ConstantScrollTest.cs create mode 100644 osu.Game.Tests/ScrollAlgorithms/OverlappingScrollTest.cs create mode 100644 osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs diff --git a/osu.Game.Tests/ScrollAlgorithms/ConstantScrollTest.cs b/osu.Game.Tests/ScrollAlgorithms/ConstantScrollTest.cs new file mode 100644 index 0000000000..5e01213a48 --- /dev/null +++ b/osu.Game.Tests/ScrollAlgorithms/ConstantScrollTest.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Game.Rulesets.UI.Scrolling.Algorithms; + +namespace osu.Game.Tests.ScrollAlgorithms +{ + [TestFixture] + public class ConstantScrollTest + { + private IScrollAlgorithm algorithm; + + [SetUp] + public void Setup() + { + algorithm = new ConstantScrollAlgorithm(); + } + + [Test] + public void TestDisplayStartTime() + { + Assert.AreEqual(-8000, algorithm.GetDisplayStartTime(2000, 10000)); + Assert.AreEqual(-3000, algorithm.GetDisplayStartTime(2000, 5000)); + Assert.AreEqual(2000, algorithm.GetDisplayStartTime(7000, 5000)); + Assert.AreEqual(7000, algorithm.GetDisplayStartTime(17000, 10000)); + } + + [Test] + public void TestLength() + { + Assert.AreEqual(1f / 5, algorithm.GetLength(0, 1000, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.GetLength(6000, 7000, 5000, 1)); + } + + [Test] + public void TestPosition() + { + Assert.AreEqual(1f / 5, algorithm.PositionAt(1000, 0, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.PositionAt(6000, 5000, 5000, 1)); + } + + [TestCase(1000)] + [TestCase(10000)] + [TestCase(15000)] + [TestCase(20000)] + [TestCase(25000)] + public void TestTime(double time) + { + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 0, 5000, 1), 0, 5000, 1), 0.001); + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 5000, 5000, 1), 5000, 5000, 1), 0.001); + } + } +} diff --git a/osu.Game.Tests/ScrollAlgorithms/OverlappingScrollTest.cs b/osu.Game.Tests/ScrollAlgorithms/OverlappingScrollTest.cs new file mode 100644 index 0000000000..c1a5a0f3c9 --- /dev/null +++ b/osu.Game.Tests/ScrollAlgorithms/OverlappingScrollTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Lists; +using osu.Game.Rulesets.Timing; +using osu.Game.Rulesets.UI.Scrolling.Algorithms; + +namespace osu.Game.Tests.ScrollAlgorithms +{ + [TestFixture] + public class OverlappingScrollTest + { + private IScrollAlgorithm algorithm; + + [SetUp] + public void Setup() + { + var controlPoints = new SortedList + { + new MultiplierControlPoint(0) { Velocity = 1 }, + new MultiplierControlPoint(10000) { Velocity = 2f }, + new MultiplierControlPoint(20000) { Velocity = 0.5f } + }; + + algorithm = new OverlappingScrollAlgorithm(controlPoints); + } + + [Test] + public void TestDisplayStartTime() + { + Assert.AreEqual(1000, algorithm.GetDisplayStartTime(2000, 1000)); // Like constant + Assert.AreEqual(10000, algorithm.GetDisplayStartTime(10500, 1000)); // 10500 - (1000 * 0.5) + Assert.AreEqual(20000, algorithm.GetDisplayStartTime(22000, 1000)); // 23000 - (1000 / 0.5) + } + + [Test] + public void TestLength() + { + Assert.AreEqual(1f / 5, algorithm.GetLength(0, 1000, 5000, 1)); // Like constant + Assert.AreEqual(1f / 5, algorithm.GetLength(10000, 10500, 5000, 1)); // (10500 - 10000) / 0.5 / 5000 + Assert.AreEqual(1f / 5, algorithm.GetLength(20000, 22000, 5000, 1)); // (22000 - 20000) * 0.5 / 5000 + } + + [Test] + public void TestPosition() + { + // Basically same calculations as TestLength() + Assert.AreEqual(1f / 5, algorithm.PositionAt(1000, 0, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.PositionAt(10500, 10000, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.PositionAt(22000, 20000, 5000, 1)); + } + + [TestCase(1000)] + [TestCase(10000)] + [TestCase(15000)] + [TestCase(20000)] + [TestCase(25000)] + [Ignore("Disabled for now because overlapping control points have multiple time values under the same position." + + "Ideally, scrolling should be changed to constant or sequential during editing of hitobjects.")] + public void TestTime(double time) + { + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 0, 5000, 1), 0, 5000, 1), 0.001); + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 5000, 5000, 1), 5000, 5000, 1), 0.001); + } + } +} diff --git a/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs b/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs new file mode 100644 index 0000000000..990fb92e6c --- /dev/null +++ b/osu.Game.Tests/ScrollAlgorithms/SequentialScrollTest.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Lists; +using osu.Game.Rulesets.Timing; +using osu.Game.Rulesets.UI.Scrolling.Algorithms; + +namespace osu.Game.Tests.ScrollAlgorithms +{ + [TestFixture] + public class SequentialScrollTest + { + private IScrollAlgorithm algorithm; + + [SetUp] + public void Setup() + { + var controlPoints = new SortedList + { + new MultiplierControlPoint(0) { Velocity = 1 }, + new MultiplierControlPoint(10000) { Velocity = 2f }, + new MultiplierControlPoint(20000) { Velocity = 0.5f } + }; + + algorithm = new SequentialScrollAlgorithm(controlPoints); + } + + [Test] + public void TestDisplayStartTime() + { + // Sequential scroll algorithm approximates the start time + // This should be fixed in the future + } + + [Test] + public void TestLength() + { + Assert.AreEqual(1f / 5, algorithm.GetLength(0, 1000, 5000, 1)); // Like constant + Assert.AreEqual(1f / 5, algorithm.GetLength(10000, 10500, 5000, 1)); // (10500 - 10000) / 0.5 / 5000 + Assert.AreEqual(1f / 5, algorithm.GetLength(20000, 22000, 5000, 1)); // (22000 - 20000) * 0.5 / 5000 + } + + [Test] + public void TestPosition() + { + // Basically same calculations as TestLength() + Assert.AreEqual(1f / 5, algorithm.PositionAt(1000, 0, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.PositionAt(10500, 10000, 5000, 1)); + Assert.AreEqual(1f / 5, algorithm.PositionAt(22000, 20000, 5000, 1)); + } + + [TestCase(1000)] + [TestCase(10000)] + [TestCase(15000)] + [TestCase(20000)] + [TestCase(25000)] + public void TestTime(double time) + { + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 0, 5000, 1), 0, 5000, 1), 0.001); + Assert.AreEqual(time, algorithm.TimeAt(algorithm.PositionAt(time, 5000, 5000, 1), 5000, 5000, 1), 0.001); + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs index ed61ed7022..5628fb51f3 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs @@ -17,6 +17,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) => (float)((time - currentTime) / timeRange * scrollLength); + public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) + => position * timeRange / scrollLength + currentTime; + public void Reset() { } diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs index 43bc1b2a2a..2ece9bef9b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs @@ -36,6 +36,16 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms /// The absolute spatial position. float PositionAt(double time, double currentTime, double timeRange, float scrollLength); + /// + /// Computes the time which brings a point to a provided spatial position given the current time. + /// + /// The absolute spatial position. + /// The current time. + /// The amount of visible time. + /// The absolute spatial length through . + /// The time at which == . + double TimeAt(float position, double currentTime, double timeRange, float scrollLength); + /// /// Resets this to a default state. /// diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index f7c097e81d..4d9659c820 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -3,6 +3,7 @@ using osu.Framework.Lists; using osu.Game.Rulesets.Timing; +using OpenTK; namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { @@ -36,6 +37,33 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms public float PositionAt(double time, double currentTime, double timeRange, float scrollLength) => (float)((time - currentTime) / timeRange * controlPointAt(time).Multiplier * scrollLength); + public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) + { + // Find the control point relating to the position. + // Note: Due to velocity adjustments, overlapping control points will provide multiple valid time values for a single position + // As such, this operation provides unexpected results by using the latter of the control points. + + int i = 0; + float pos = 0; + + for (; i < controlPoints.Count; i++) + { + float lastPos = pos; + pos = PositionAt(controlPoints[i].StartTime, currentTime, timeRange, scrollLength); + + if (pos > position) + { + i--; + pos = lastPos; + break; + } + } + + i = MathHelper.Clamp(i, 0, controlPoints.Count - 1); + + return controlPoints[i].StartTime + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength; + } + public void Reset() { } diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index 54494cfe63..8f8f546992 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -35,6 +35,36 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms return (float)((relativePositionAtCached(time, timeRange) - timelinePosition) * scrollLength); } + public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) + { + // Convert the position to a length relative to time = 0 + double length = position / scrollLength + relativePositionAt(currentTime, timeRange); + + // We need to consider all timing points until the specified time and not just the currently-active one, + // since each timing point individually affects the positions of _all_ hitobjects after its start time + for (int i = 0; i < controlPoints.Count; i++) + { + var current = controlPoints[i]; + var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; + + // Duration of the current control point + var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime; + + // Figure out the length of control point + var currentLength = currentDuration / timeRange * current.Multiplier; + + if (currentLength > length) + { + // The point is within this control point + return current.StartTime + length * timeRange / current.Multiplier; + } + + length -= currentLength; + } + + return 0; // Should never occur + } + private double relativePositionAtCached(double time, double timeRange) { if (!positionCache.TryGetValue(time, out double existing)) From c77412992e14fe504eafd91a9288761fbd5d7022 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Nov 2018 11:53:34 +0900 Subject: [PATCH 13/14] Merge conditionals --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 77a3ae88a4..cb86ad8083 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -247,7 +247,7 @@ namespace osu.Game.Overlays var track = (current?.TrackLoaded ?? false) ? current.Track : null; - if (track != null && !track.IsDummyDevice) + if (track?.IsDummyDevice == false) { progressBar.EndTime = track.Length; progressBar.CurrentTime = track.CurrentTime; From b9278b3488de78c1dafb2b6c2722935d4a900664 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Nov 2018 12:05:07 +0900 Subject: [PATCH 14/14] Fix brackets --- osu.Game/Overlays/MusicController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index cb86ad8083..f282b757cd 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -245,7 +245,7 @@ namespace osu.Game.Overlays { base.Update(); - var track = (current?.TrackLoaded ?? false) ? current.Track : null; + var track = current?.TrackLoaded ?? false ? current.Track : null; if (track?.IsDummyDevice == false) {