From 4c9dcdf156afc9ab1b45c05db1cbe949311a3b37 Mon Sep 17 00:00:00 2001 From: gotopie Date: Fri, 2 Nov 2018 19:04:30 -0400 Subject: [PATCH 01/27] 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 779e57f0cab8a91c18e4c40c38acb70067ae2a47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Nov 2018 16:32:11 +0900 Subject: [PATCH 02/27] Change .idea ignore rules to not ignore run configurations --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8f011deabe..f95a04e517 100644 --- a/.gitignore +++ b/.gitignore @@ -252,7 +252,11 @@ paket-files/ .fake/ # JetBrains Rider -.idea/ +.idea/.idea.osu/.idea/*.xml +.idea/.idea.osu/.idea/codeStyles/*.xml +.idea/.idea.osu/.idea/dataSources/*.xml +.idea/.idea.osu/.idea/dictionaries/*.xml +.idea/.idea.osu/*.iml *.sln.iml # CodeRush From 8583fd1380639944316773b602446a5402eaa626 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 30 Oct 2018 17:24:10 +0900 Subject: [PATCH 03/27] 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 04/27] 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 05/27] 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 06/27] 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 07/27] 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 08/27] 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 09/27] 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 10/27] 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 11/27] 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 54ab256c8e01e369ae664408e29e25f5161e12e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Nov 2018 15:38:19 +0900 Subject: [PATCH 12/27] Instantiate a new path rather than setting properties on it # Conflicts: # osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs # osu.Game.Rulesets.Catch/Objects/JuiceStream.cs # osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs # osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs # osu.Game/Rulesets/Objects/SliderPath.cs --- .../TestCaseAutoJuiceStream.cs | 7 +- .../Beatmaps/CatchBeatmapConverter.cs | 4 +- .../Objects/JuiceStream.cs | 27 ++-- osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs | 40 +++--- .../TestCaseSliderSelectionBlueprint.cs | 7 +- .../Beatmaps/OsuBeatmapConverter.cs | 4 +- .../Components/PathControlPointPiece.cs | 16 +-- .../Components/PathControlPointVisualiser.cs | 6 +- .../Sliders/Components/SliderCirclePiece.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 9 +- .../Objects/Drawables/DrawableSlider.cs | 2 +- .../Objects/Drawables/DrawableSliderHead.cs | 2 +- .../Objects/Drawables/DrawableSliderTail.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 30 ++--- .../Visual/TestCaseHitObjectComposer.cs | 6 +- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 +- .../Rulesets/Objects/Legacy/ConvertSlider.cs | 7 +- .../Legacy/Mania/ConvertHitObjectParser.cs | 4 +- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 +- .../Legacy/Taiko/ConvertHitObjectParser.cs | 4 +- osu.Game/Rulesets/Objects/SliderPath.cs | 122 +++++++----------- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 10 -- 22 files changed, 117 insertions(+), 202 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index cac1356c81..bea64302c3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -5,6 +5,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Play; using osu.Game.Tests.Visual; @@ -37,13 +38,11 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { X = 0.5f - width / 2, - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) - }, - PathType = PathType.Linear, - Distance = width * CatchPlayfield.BASE_WIDTH, + }), StartTime = i * 2000, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index fed65c42af..c3dc9499c2 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -34,9 +34,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { StartTime = obj.StartTime, Samples = obj.Samples, - ControlPoints = curveData.ControlPoints, - PathType = curveData.PathType, - Distance = curveData.Distance, + Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH, diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index f1e131932b..a4e04ae837 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -10,7 +10,6 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; -using OpenTK; namespace osu.Game.Rulesets.Catch.Objects { @@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (TickDistance == 0) return; - var length = Path.Distance; + var length = Path.GetDistance(); var tickDistance = Math.Min(TickDistance, length); var spanDuration = length / Velocity; @@ -132,34 +131,24 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime; - public double Distance + private SliderPath path; + + public SliderPath Path { - get { return Path.Distance; } - set { Path.Distance = value; } + get => path; + set => path = value; } - public SliderPath Path { get; } = new SliderPath(); - - public Vector2[] ControlPoints - { - get { return Path.ControlPoints; } - set { Path.ControlPoints = value; } - } + public double Distance => Path.GetDistance(); public List> NodeSamples { get; set; } = new List>(); - public PathType PathType - { - get { return Path.PathType; } - set { Path.PathType = value; } - } - public double? LegacyLastTickOffset { get; set; } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index 0bd6bb5abc..5b638782fb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -18,6 +18,7 @@ using System.Linq; using NUnit.Framework; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -108,13 +109,12 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(239, 176), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(154, 28), new Vector2(52, -34) - }, - Distance = 700, + }, 700), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats), StackHeight = 10 @@ -141,12 +141,11 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-(distance / 2), 0), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(distance, 0), - }, - Distance = distance, + }, distance), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats), StackHeight = stackHeight @@ -161,13 +160,12 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.PerfectCurve, new[] { Vector2.Zero, new Vector2(200, 200), new Vector2(400, 0) - }, - Distance = 600, + }, 600), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -181,10 +179,9 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(150, 75), @@ -192,8 +189,7 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(300, -200), new Vector2(400, 0), new Vector2(430, 0) - }, - Distance = 793.4417, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -207,18 +203,16 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Bezier, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Bezier, new[] { Vector2.Zero, new Vector2(150, 75), new Vector2(200, 100), new Vector2(300, -200), new Vector2(430, 0) - }, - Distance = 480, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -232,10 +226,9 @@ namespace osu.Game.Rulesets.Osu.Tests { var slider = new Slider { - PathType = PathType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(0, 0), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(-200, 0), @@ -243,8 +236,7 @@ namespace osu.Game.Rulesets.Osu.Tests new Vector2(0, -200), new Vector2(-200, -200), new Vector2(0, -200) - }, - Distance = 1000, + }), RepeatCount = repeats, NodeSamples = createEmptySamples(repeats) }; @@ -264,15 +256,13 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-100, 0), - PathType = PathType.Catmull, - ControlPoints = new[] + Path = new SliderPath(PathType.Catmull, new[] { Vector2.Zero, new Vector2(50, -50), new Vector2(150, 50), new Vector2(200, 0) - }, - Distance = 300, + }), RepeatCount = repeats, NodeSamples = repeatSamples }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs index 78e3d76313..cacbcb2cd6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; @@ -35,14 +36,12 @@ namespace osu.Game.Rulesets.Osu.Tests var slider = new Slider { Position = new Vector2(256, 192), - ControlPoints = new[] + Path = new SliderPath(PathType.Bezier, new[] { Vector2.Zero, new Vector2(150, 150), new Vector2(300, 0) - }, - PathType = PathType.Bezier, - Distance = 350 + }) }; slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 }); diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 87c81cdd3b..4fc4f3edc3 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -35,9 +35,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { StartTime = original.StartTime, Samples = original.Samples, - ControlPoints = curveData.ControlPoints, - PathType = curveData.PathType, - Distance = curveData.Distance, + Path = curveData.Path, NodeSamples = curveData.NodeSamples, RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 175e9d79f4..22ad911c21 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using OpenTK; @@ -55,16 +56,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.ControlPoints[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; path.ClearVertices(); - if (index != slider.ControlPoints.Length - 1) + if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.ControlPoints[index + 1] - slider.ControlPoints[index]); + path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnDrag(DragEvent e) { - var newControlPoints = slider.ControlPoints.ToArray(); + var newControlPoints = slider.Path.ControlPoints.ToArray(); if (index == 0) { @@ -96,8 +97,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (isSegmentSeparatorWithPrevious) newControlPoints[index - 1] = newControlPoints[index]; - slider.ControlPoints = newControlPoints; - slider.Path.Calculate(true); + slider.Path = new SliderPath(slider.Path.Type, newControlPoints); return true; } @@ -106,8 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.ControlPoints.Length - 1 && slider.ControlPoints[index + 1] == slider.ControlPoints[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.ControlPoints[index - 1] == slider.ControlPoints[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index db8e879126..ab9d81574a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -19,15 +19,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChild = pieces = new Container { RelativeSizeAxes = Axes.Both }; - slider.ControlPointsChanged += _ => updatePathControlPoints(); + slider.PathChanged += _ => updatePathControlPoints(); updatePathControlPoints(); } private void updatePathControlPoints() { - while (slider.ControlPoints.Length > pieces.Count) + while (slider.Path.ControlPoints.Length > pieces.Count) pieces.Add(new PathControlPointPiece(slider, pieces.Count)); - while (slider.ControlPoints.Length < pieces.Count) + while (slider.Path.ControlPoints.Length < pieces.Count) pieces.Remove(pieces[pieces.Count - 1]); } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs index a91739737f..1ee765f5e0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components this.slider = slider; this.position = position; - slider.ControlPointsChanged += _ => UpdatePosition(); + slider.PathChanged += _ => UpdatePosition(); } protected override void UpdatePosition() diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index e01d71e1f8..223e4df844 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -32,12 +32,11 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var newControlPoints = new Vector2[slider.ControlPoints.Length]; - for (int i = 0; i < slider.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y); + var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; + for (int i = 0; i < slider.Path.ControlPoints.Length; i++) + newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); - slider.ControlPoints = newControlPoints; - slider.Path?.Calculate(); // Recalculate the slider curve + slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 514ae09064..a90182cecb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Ball.Scale = new Vector2(HitObject.Scale); }; - slider.ControlPointsChanged += _ => Body.Refresh(); + slider.PathChanged += _ => Body.Refresh(); } public override Color4 AccentColour diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 6a836679a2..b933364887 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.slider = slider; h.PositionChanged += _ => updatePosition(); - slider.ControlPointsChanged += _ => updatePosition(); + slider.PathChanged += _ => updatePosition(); updatePosition(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index cc88a6718b..6946a55d8e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true; hitCircle.PositionChanged += _ => updatePosition(); - slider.ControlPointsChanged += _ => updatePosition(); + slider.PathChanged += _ => updatePosition(); updatePosition(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index cff742ca29..07e526956a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -22,9 +22,9 @@ namespace osu.Game.Rulesets.Osu.Objects /// private const float base_scoring_distance = 100; - public event Action ControlPointsChanged; + public event Action PathChanged; - public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; public double Duration => EndTime - StartTime; public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); @@ -52,35 +52,23 @@ namespace osu.Game.Rulesets.Osu.Objects } } - public SliderPath Path { get; } = new SliderPath(); + private SliderPath path; - public Vector2[] ControlPoints + public SliderPath Path { - get => Path.ControlPoints; + get => path; set { - if (Path.ControlPoints == value) - return; - Path.ControlPoints = value; + path = value; - ControlPointsChanged?.Invoke(value); + PathChanged?.Invoke(value); if (TailCircle != null) TailCircle.Position = EndPosition; } } - public PathType PathType - { - get { return Path.PathType; } - set { Path.PathType = value; } - } - - public double Distance - { - get { return Path.Distance; } - set { Path.Distance = value; } - } + public double Distance => Path.GetDistance(); public override Vector2 Position { @@ -190,7 +178,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createTicks() { - var length = Path.Distance; + var length = Path.GetDistance(); var tickDistance = MathHelper.Clamp(TickDistance, 0, length); if (tickDistance == 0) return; diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 2629b29c6c..d894d2738e 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -11,6 +11,7 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles; @@ -53,12 +54,11 @@ namespace osu.Game.Tests.Visual new Slider { Position = new Vector2(128, 256), - ControlPoints = new[] + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(216, 0), - }, - Distance = 216, + }), Scale = 0.5f, } }, diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c805c55ed1..b167812c1d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -50,9 +50,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch X = position.X, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index b3d9f3c40c..901cc1ba9f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -3,7 +3,6 @@ using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; -using OpenTK; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -20,11 +19,9 @@ namespace osu.Game.Rulesets.Objects.Legacy /// /// s don't need a curve since they're converted to ruleset-specific hitobjects. /// - public SliderPath Path { get; } = null; - public Vector2[] ControlPoints { get; set; } - public PathType PathType { get; set; } + public SliderPath Path { get; set; } - public double Distance { get; set; } + public double Distance => Path.GetDistance(); public List> NodeSamples { get; set; } public int RepeatCount { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 90b7f3d554..fa5e769d3c 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -31,9 +31,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania return new ConvertSlider { X = position.X, - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index bb41a147b0..e21903dc6d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -51,9 +51,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu Position = position, NewCombo = FirstObject || newCombo, ComboOffset = comboOffset, - ControlPoints = controlPoints, - Distance = Math.Max(0, length), - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, Math.Max(0, length)), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index ae913b3bef..8e1e01a9fd 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -27,9 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko { return new ConvertSlider { - ControlPoints = controlPoints, - Distance = length, - PathType = pathType, + Path = new SliderPath(pathType, controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount }; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 423cd3b069..195e429f2b 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -10,22 +10,31 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public class SliderPath + public readonly struct SliderPath { - public double Distance; + public readonly Vector2[] ControlPoints; + public readonly PathType Type; + public readonly double? ExpectedDistance; - public Vector2[] ControlPoints = Array.Empty(); + public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) + { + ControlPoints = controlPoints; + Type = type; + ExpectedDistance = expectedDistance; - public PathType PathType = PathType.PerfectCurve; + calculatedPath = new List(); + cumulativeLength = new List(); - public Vector2 Offset; + calculatePath(); + calculateCumulativeLength(); + } - private readonly List calculatedPath = new List(); - private readonly List cumulativeLength = new List(); + private readonly List calculatedPath; + private readonly List cumulativeLength; private List calculateSubpath(ReadOnlySpan subControlPoints) { - switch (PathType) + switch (Type) { case PathType.Linear: return PathApproximator.ApproximateLinear(subControlPoints); @@ -77,49 +86,6 @@ namespace osu.Game.Rulesets.Objects } } - private void calculateCumulativeLengthAndTrimPath() - { - double l = 0; - - cumulativeLength.Clear(); - cumulativeLength.Add(l); - - for (int i = 0; i < calculatedPath.Count - 1; ++i) - { - Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; - double d = diff.Length; - - // Shorten slider paths that are too long compared to what's - // in the .osu file. - if (Distance - l < d) - { - calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d); - calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); - - l = Distance; - cumulativeLength.Add(l); - break; - } - - l += d; - cumulativeLength.Add(l); - } - - // Lengthen slider paths that are too short compared to what's - // in the .osu file. - if (l < Distance && calculatedPath.Count > 1) - { - Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; - double d = diff.Length; - - if (d <= 0) - return; - - calculatedPath[calculatedPath.Count - 1] += diff * (float)((Distance - l) / d); - cumulativeLength[calculatedPath.Count - 1] = Distance; - } - } - private void calculateCumulativeLength() { double l = 0; @@ -132,21 +98,33 @@ namespace osu.Game.Rulesets.Objects Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; double d = diff.Length; + // Shorted slider paths that are too long compared to the expected distance + if (ExpectedDistance.HasValue && ExpectedDistance - l < d) + { + calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((ExpectedDistance - l) / d); + calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); + + l = ExpectedDistance.Value; + cumulativeLength.Add(l); + break; + } + l += d; cumulativeLength.Add(l); } - Distance = l; - } + // Lengthen slider paths that are too short compared to the expected distance + if (ExpectedDistance.HasValue && l < ExpectedDistance && calculatedPath.Count > 1) + { + Vector2 diff = calculatedPath[calculatedPath.Count - 1] - calculatedPath[calculatedPath.Count - 2]; + double d = diff.Length; - public void Calculate(bool updateDistance = false) - { - calculatePath(); + if (d <= 0) + return; - if (!updateDistance) - calculateCumulativeLengthAndTrimPath(); - else - calculateCumulativeLength(); + calculatedPath[calculatedPath.Count - 1] += diff * (float)((ExpectedDistance - l) / d); + cumulativeLength[calculatedPath.Count - 1] = ExpectedDistance.Value; + } } private int indexOfDistance(double d) @@ -159,7 +137,7 @@ namespace osu.Game.Rulesets.Objects private double progressToDistance(double progress) { - return MathHelper.Clamp(progress, 0, 1) * Distance; + return MathHelper.Clamp(progress, 0, 1) * GetDistance(); } private Vector2 interpolateVertices(int i, double d) @@ -169,7 +147,7 @@ namespace osu.Game.Rulesets.Objects if (i <= 0) return calculatedPath.First(); - else if (i >= calculatedPath.Count) + if (i >= calculatedPath.Count) return calculatedPath.Last(); Vector2 p0 = calculatedPath[i - 1]; @@ -186,6 +164,8 @@ namespace osu.Game.Rulesets.Objects return p0 + (p1 - p0) * (float)w; } + public double GetDistance() => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) /// to 1 (end of the slider) and stores the generated path in the given list. @@ -195,23 +175,22 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { - if (calculatedPath.Count == 0 && ControlPoints.Length > 0) - Calculate(); - double d0 = progressToDistance(p0); double d1 = progressToDistance(p1); path.Clear(); int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) + { + } - path.Add(interpolateVertices(i, d0) + Offset); + path.Add(interpolateVertices(i, d0)); for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i] + Offset); + path.Add(calculatedPath[i]); - path.Add(interpolateVertices(i, d1) + Offset); + path.Add(interpolateVertices(i, d1)); } /// @@ -222,11 +201,8 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { - if (calculatedPath.Count == 0 && ControlPoints.Length > 0) - Calculate(); - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d) + Offset; + return interpolateVertices(indexOfDistance(d), d); } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 2a0d495e94..a097b62851 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -14,16 +14,6 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// SliderPath Path { get; } - - /// - /// The control points that shape the curve. - /// - Vector2[] ControlPoints { get; } - - /// - /// The type of curve. - /// - PathType PathType { get; } } public static class HasCurveExtensions From 51e4feeda768ec0765e3bbbfefd21a434e9eff9b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 13:55:14 +0900 Subject: [PATCH 13/27] Adjust to new path structure --- .../Sliders/Components/SliderBodyPiece.cs | 2 - .../Sliders/SliderPlacementBlueprint.cs | 40 ++----------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 6fc7d39e6c..06bc265258 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -45,8 +45,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - slider.Path.Calculate(); - var vertices = new List(); slider.Path.GetPathToProgress(vertices, 0, 1); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index add9cb69f3..d59cd35f19 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -1,16 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; -using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using OpenTK; @@ -119,12 +118,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - for (int i = 0; i < segments.Count; i++) - segments[i].Calculate(i == segments.Count - 1 ? (Vector2?)cursor : null); - - HitObject.ControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray(); - HitObject.PathType = HitObject.ControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear; - HitObject.Distance = segments.Sum(s => s.Distance); + var newControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray(); + HitObject.Path = new SliderPath(newControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, newControlPoints); } private void setState(PlacementState newState) @@ -140,41 +135,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private class Segment { - public float Distance { get; private set; } - public readonly List ControlPoints = new List(); public Segment(Vector2 offset) { ControlPoints.Add(offset); } - - public void Calculate(Vector2? cursor = null) - { - Span allControlPoints = stackalloc Vector2[ControlPoints.Count + (cursor.HasValue ? 1 : 0)]; - - for (int i = 0; i < ControlPoints.Count; i++) - allControlPoints[i] = ControlPoints[i]; - if (cursor.HasValue) - allControlPoints[allControlPoints.Length - 1] = cursor.Value; - - List result; - - switch (allControlPoints.Length) - { - case 1: - case 2: - result = PathApproximator.ApproximateLinear(allControlPoints); - break; - default: - result = PathApproximator.ApproximateBezier(allControlPoints); - break; - } - - Distance = 0; - for (int i = 0; i < result.Count - 1; i++) - Distance += Vector2.Distance(result[i], result[i + 1]); - } } } } From 3b88d94793feafa815abf8a3e7ca9d18d6e92294 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:03:54 +0900 Subject: [PATCH 14/27] Make SliderPath.ControlPoints read-only --- .../Sliders/Components/PathControlPointPiece.cs | 9 ++++----- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 22ad911c21..d46fa46c22 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -56,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.Path.ControlPoints[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints.Span[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; @@ -65,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); + path.AddVertex(slider.Path.ControlPoints.Span[index + 1] - slider.Path.ControlPoints.Span[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -106,8 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints.Span[index + 1] == slider.Path.ControlPoints.Span[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints.Span[index - 1] == slider.Path.ControlPoints.Span[index]; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 223e4df844..b66b5d3d39 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Mods var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; for (int i = 0; i < slider.Path.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); + newControlPoints[i] = new Vector2(slider.Path.ControlPoints.Span[i].X, -slider.Path.ControlPoints.Span[i].Y); slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 195e429f2b..5ad1cec6b6 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Objects { public readonly struct SliderPath { - public readonly Vector2[] ControlPoints; + public readonly ReadOnlyMemory ControlPoints; public readonly PathType Type; public readonly double? ExpectedDistance; @@ -73,9 +73,9 @@ namespace osu.Game.Rulesets.Objects { end++; - if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) + if (i == ControlPoints.Length - 1 || ControlPoints.Span[i] == ControlPoints.Span[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.Span.Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) From 3aba462e524a4f769114f197bf7edac5f2cfb3c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:07:48 +0900 Subject: [PATCH 15/27] Make Path.Distance a property again --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 6 +++--- osu.Game.Rulesets.Osu/Objects/Slider.cs | 6 +++--- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index a4e04ae837..d8bd3e0edc 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (TickDistance == 0) return; - var length = Path.GetDistance(); + var length = Path.Distance; var tickDistance = Math.Min(TickDistance, length); var spanDuration = length / Velocity; @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Catch.Objects } } - public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects set => path = value; } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public List> NodeSamples { get; set; } = new List>(); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 07e526956a..cf57f24b83 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects public event Action PathChanged; - public double EndTime => StartTime + this.SpanCount() * Path.GetDistance() / Velocity; + public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double Duration => EndTime - StartTime; public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects } } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public override Vector2 Position { @@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Osu.Objects private void createTicks() { - var length = Path.GetDistance(); + var length = Path.Distance; var tickDistance = MathHelper.Clamp(TickDistance, 0, length); if (tickDistance == 0) return; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 901cc1ba9f..0512a97354 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// public SliderPath Path { get; set; } - public double Distance => Path.GetDistance(); + public double Distance => Path.Distance; public List> NodeSamples { get; set; } public int RepeatCount { get; set; } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 5ad1cec6b6..3f0f0518d6 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Objects private double progressToDistance(double progress) { - return MathHelper.Clamp(progress, 0, 1) * GetDistance(); + return MathHelper.Clamp(progress, 0, 1) * Distance; } private Vector2 interpolateVertices(int i, double d) @@ -164,7 +164,7 @@ namespace osu.Game.Rulesets.Objects return p0 + (p1 - p0) * (float)w; } - public double GetDistance() => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) From 4eef1134a629db002036fdde63ac37f2e476513f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:08:36 +0900 Subject: [PATCH 16/27] Re-order file --- osu.Game/Rulesets/Objects/SliderPath.cs | 86 ++++++++++++------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 3f0f0518d6..b81ccbe1e1 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -16,6 +16,9 @@ namespace osu.Game.Rulesets.Objects public readonly PathType Type; public readonly double? ExpectedDistance; + private readonly List calculatedPath; + private readonly List cumulativeLength; + public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { ControlPoints = controlPoints; @@ -29,8 +32,46 @@ namespace osu.Game.Rulesets.Objects calculateCumulativeLength(); } - private readonly List calculatedPath; - private readonly List cumulativeLength; + public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + + /// + /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) + /// to 1 (end of the slider) and stores the generated path in the given list. + /// + /// The list to be filled with the computed path. + /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). + public void GetPathToProgress(List path, double p0, double p1) + { + double d0 = progressToDistance(p0); + double d1 = progressToDistance(p1); + + path.Clear(); + + int i = 0; + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) + { + } + + path.Add(interpolateVertices(i, d0)); + + for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) + path.Add(calculatedPath[i]); + + path.Add(interpolateVertices(i, d1)); + } + + /// + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) + /// to 1 (end of the path). + /// + /// Ranges from 0 (beginning of the path) to 1 (end of the path). + /// + public Vector2 PositionAt(double progress) + { + double d = progressToDistance(progress); + return interpolateVertices(indexOfDistance(d), d); + } private List calculateSubpath(ReadOnlySpan subControlPoints) { @@ -163,46 +204,5 @@ namespace osu.Game.Rulesets.Objects double w = (d - d0) / (d1 - d0); return p0 + (p1 - p0) * (float)w; } - - public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; - - /// - /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) - /// to 1 (end of the slider) and stores the generated path in the given list. - /// - /// The list to be filled with the computed path. - /// Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). - public void GetPathToProgress(List path, double p0, double p1) - { - double d0 = progressToDistance(p0); - double d1 = progressToDistance(p1); - - path.Clear(); - - int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) - { - } - - path.Add(interpolateVertices(i, d0)); - - for (; i < calculatedPath.Count && cumulativeLength[i] <= d1; ++i) - path.Add(calculatedPath[i]); - - path.Add(interpolateVertices(i, d1)); - } - - /// - /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path) - /// to 1 (end of the path). - /// - /// Ranges from 0 (beginning of the path) to 1 (end of the path). - /// - public Vector2 PositionAt(double progress) - { - double d = progressToDistance(progress); - return interpolateVertices(indexOfDistance(d), d); - } } } From 77d16aa968d2ea6de8194cd3756c49484cfac720 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 14:16:21 +0900 Subject: [PATCH 17/27] Add xmldocs --- osu.Game/Rulesets/Objects/SliderPath.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index b81ccbe1e1..66acbeed68 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -12,13 +12,33 @@ namespace osu.Game.Rulesets.Objects { public readonly struct SliderPath { + /// + /// The control points of the path. + /// public readonly ReadOnlyMemory ControlPoints; + + /// + /// The type of path. + /// public readonly PathType Type; + + /// + /// The user-set distance of the path. If non-null, will match this value, + /// and the path will be shortened/lengthened to match this length. + /// public readonly double? ExpectedDistance; private readonly List calculatedPath; private readonly List cumulativeLength; + /// + /// Creates a new . + /// + /// The type of path. + /// The control points of the path. + /// A user-set distance of the path that may be shorter or longer than the true distance between all + /// . The path will be shortened/lengthened to match this length. + /// If null, the path will use the true distance between all . public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { ControlPoints = controlPoints; @@ -32,6 +52,9 @@ namespace osu.Game.Rulesets.Objects calculateCumulativeLength(); } + /// + /// The distance of the path after lengthening/shortening to account for . + /// public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; /// From d594ce35304218ac8eb613159737a66e1955bc34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:20:38 +0900 Subject: [PATCH 18/27] Revert "Make SliderPath.ControlPoints read-only" This reverts commit 3b88d94793feafa815abf8a3e7ca9d18d6e92294. # Conflicts: # osu.Game/Rulesets/Objects/SliderPath.cs --- .../Sliders/Components/PathControlPointPiece.cs | 9 +++++---- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index d46fa46c22..22ad911c21 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { base.Update(); - Position = slider.StackedPosition + slider.Path.ControlPoints.Span[index]; + Position = slider.StackedPosition + slider.Path.ControlPoints[index]; marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow; @@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (index != slider.Path.ControlPoints.Length - 1) { path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints.Span[index + 1] - slider.Path.ControlPoints.Span[index]); + path.AddVertex(slider.Path.ControlPoints[index + 1] - slider.Path.ControlPoints[index]); } path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); @@ -105,8 +106,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious; - private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints.Span[index + 1] == slider.Path.ControlPoints.Span[index]; + private bool isSegmentSeparatorWithNext => index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[index + 1] == slider.Path.ControlPoints[index]; - private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints.Span[index - 1] == slider.Path.ControlPoints.Span[index]; + private bool isSegmentSeparatorWithPrevious => index > 0 && slider.Path.ControlPoints[index - 1] == slider.Path.ControlPoints[index]; } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index b66b5d3d39..223e4df844 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Mods var newControlPoints = new Vector2[slider.Path.ControlPoints.Length]; for (int i = 0; i < slider.Path.ControlPoints.Length; i++) - newControlPoints[i] = new Vector2(slider.Path.ControlPoints.Span[i].X, -slider.Path.ControlPoints.Span[i].Y); + newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y); slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance); } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 66acbeed68..a174280456 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Objects /// /// The control points of the path. /// - public readonly ReadOnlyMemory ControlPoints; + public readonly Vector2[] ControlPoints; /// /// The type of path. @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.Objects { end++; - if (i == ControlPoints.Length - 1 || ControlPoints.Span[i] == ControlPoints.Span[i + 1]) + if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.Span.Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) From 8ad9b6a02a268f72940259e1f271670d28516876 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:14 +0900 Subject: [PATCH 19/27] Safety for default(SliderPath) --- osu.Game/Rulesets/Objects/SliderPath.cs | 52 ++++++++++++++++++------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index a174280456..27f864e2aa 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -10,26 +10,28 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public readonly struct SliderPath + public struct SliderPath { /// /// The control points of the path. /// public readonly Vector2[] ControlPoints; - /// - /// The type of path. - /// - public readonly PathType Type; - /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. /// public readonly double? ExpectedDistance; - private readonly List calculatedPath; - private readonly List cumulativeLength; + /// + /// The type of path. + /// + public readonly PathType Type; + + private List calculatedPath; + private List cumulativeLength; + + private bool isInitialised; /// /// Creates a new . @@ -41,21 +43,26 @@ namespace osu.Game.Rulesets.Objects /// If null, the path will use the true distance between all . public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { + this = default; + ControlPoints = controlPoints; Type = type; ExpectedDistance = expectedDistance; - calculatedPath = new List(); - cumulativeLength = new List(); - - calculatePath(); - calculateCumulativeLength(); + ensureInitialised(); } /// /// The distance of the path after lengthening/shortening to account for . /// - public double Distance => cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + public double Distance + { + get + { + ensureInitialised(); + return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + } + } /// /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider) @@ -66,6 +73,8 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { + ensureInitialised(); + double d0 = progressToDistance(p0); double d1 = progressToDistance(p1); @@ -92,10 +101,25 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { + ensureInitialised(); + double d = progressToDistance(progress); return interpolateVertices(indexOfDistance(d), d); } + private void ensureInitialised() + { + if (isInitialised) + return; + isInitialised = true; + + calculatedPath = new List(); + cumulativeLength = new List(); + + calculatePath(); + calculateCumulativeLength(); + } + private List calculateSubpath(ReadOnlySpan subControlPoints) { switch (Type) From 0e92b385f01500c85fb6b94ec60793bbb40bab1f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:33 +0900 Subject: [PATCH 20/27] Define default json deserialisation constructor --- osu.Game/Rulesets/Objects/SliderPath.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 27f864e2aa..c5d3a39ab1 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Types; using OpenTK; @@ -41,6 +42,7 @@ namespace osu.Game.Rulesets.Objects /// A user-set distance of the path that may be shorter or longer than the true distance between all /// . The path will be shortened/lengthened to match this length. /// If null, the path will use the true distance between all . + [JsonConstructor] public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { this = default; From 0220ed21b03530088525ce15cd0e56c6d6abb819 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:38:39 +0900 Subject: [PATCH 21/27] Ignore distance for json serialisation --- osu.Game/Rulesets/Objects/SliderPath.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index c5d3a39ab1..2bb903155e 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Objects /// /// The distance of the path after lengthening/shortening to account for . /// + [JsonIgnore] public double Distance { get From f4fd6189f892bd25cc37af571dd7183fc2e667de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 16:53:30 +0900 Subject: [PATCH 22/27] Implement IEquatable --- osu.Game/Rulesets/Objects/SliderPath.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 2bb903155e..548e1680f7 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -11,7 +11,7 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public struct SliderPath + public struct SliderPath : IEquatable { /// /// The control points of the path. @@ -254,5 +254,21 @@ namespace osu.Game.Rulesets.Objects double w = (d - d0) / (d1 - d0); return p0 + (p1 - p0) * (float)w; } + + public bool Equals(SliderPath other) + { + if (ControlPoints == null && other.ControlPoints != null) + return false; + if (other.ControlPoints == null && ControlPoints != null) + return false; + + return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is SliderPath other && Equals(other); + } } } From 1101e161d9155a5e51da153bf9c15fb9e325be4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Nov 2018 17:01:50 +0900 Subject: [PATCH 23/27] Update framework and resources --- osu-resources | 2 +- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-resources b/osu-resources index 9ee64e369f..651e598b01 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 9ee64e369fe6fdafc6aed40f5a35b5f01eb82c53 +Subproject commit 651e598b016b43e31ab1c1b29d5b30c92361b8d9 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c9461ea504..9f7996a5fd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From f3ba4297018b97fb4eacd1b4d118a6aeb364e6a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 17:10:37 +0900 Subject: [PATCH 24/27] Make sure control points is internally initialised --- .../Components/PathControlPointPiece.cs | 1 - osu.Game/Rulesets/Objects/SliderPath.cs | 26 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 22ad911c21..7100d9443e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 548e1680f7..74a312698c 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Objects { public struct SliderPath : IEquatable { - /// - /// The control points of the path. - /// - public readonly Vector2[] ControlPoints; - /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. @@ -29,6 +24,9 @@ namespace osu.Game.Rulesets.Objects /// public readonly PathType Type; + [JsonProperty] + private Vector2[] controlPoints; + private List calculatedPath; private List cumulativeLength; @@ -46,14 +44,27 @@ namespace osu.Game.Rulesets.Objects public SliderPath(PathType type, Vector2[] controlPoints, double? expectedDistance = null) { this = default; + this.controlPoints = controlPoints; - ControlPoints = controlPoints; Type = type; ExpectedDistance = expectedDistance; ensureInitialised(); } + /// + /// The control points of the path. + /// + [JsonIgnore] + public ReadOnlySpan ControlPoints + { + get + { + ensureInitialised(); + return controlPoints.AsSpan(); + } + } + /// /// The distance of the path after lengthening/shortening to account for . /// @@ -116,6 +127,7 @@ namespace osu.Game.Rulesets.Objects return; isInitialised = true; + controlPoints = controlPoints ?? Array.Empty(); calculatedPath = new List(); cumulativeLength = new List(); @@ -166,7 +178,7 @@ namespace osu.Game.Rulesets.Objects if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) { - ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + ReadOnlySpan cpSpan = ControlPoints.Slice(start, end - start); foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) From aee7a80e71683b1377e87bab67ecd3689c9bc810 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Nov 2018 17:26:37 +0900 Subject: [PATCH 25/27] 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 c77412992e14fe504eafd91a9288761fbd5d7022 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Nov 2018 11:53:34 +0900 Subject: [PATCH 26/27] 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 27/27] 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) {