From c6e099d7256b2ad2198e63eaa3f705f5f49894fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 3 Dec 2016 10:41:03 +0100 Subject: [PATCH] Fix incorrect way of calculating SliderCurve.PositionAt. --- osu.Game.Mode.Osu/Objects/SliderCurve.cs | 44 +++++++++++++++++++----- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/osu.Game.Mode.Osu/Objects/SliderCurve.cs b/osu.Game.Mode.Osu/Objects/SliderCurve.cs index 4e691196f5..7556f44cda 100644 --- a/osu.Game.Mode.Osu/Objects/SliderCurve.cs +++ b/osu.Game.Mode.Osu/Objects/SliderCurve.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; using OpenTK; +using System.Linq; +using System.Diagnostics; +using osu.Framework.MathUtils; namespace osu.Game.Modes.Osu.Objects { @@ -11,7 +14,8 @@ namespace osu.Game.Modes.Osu.Objects public CurveTypes CurveType; - private List calculatedPath; + private List calculatedPath = new List(); + private List cumulativeLength = new List(); private List calculateSubpath(List subpath) { @@ -26,7 +30,7 @@ namespace osu.Game.Modes.Osu.Objects public void Calculate() { - calculatedPath = new List(); + calculatedPath.Clear(); // Sliders may consist of various subpaths separated by two consecutive vertices // with the same position. The following loop parses these subpaths and computes @@ -48,20 +52,44 @@ namespace osu.Game.Modes.Osu.Objects subpath.Clear(); } } + + cumulativeLength.Clear(); + cumulativeLength.Add(Length = 0); + for (int i = 0; i < calculatedPath.Count - 1; ++i) + { + double d = (calculatedPath[i + 1] - calculatedPath[i]).Length; + + Debug.Assert(d >= 0, "Cumulative lengths have to be strictly increasing."); + cumulativeLength.Add(Length += d); + } } public Vector2 PositionAt(double progress) { progress = MathHelper.Clamp(progress, 0, 1); - double index = progress * (calculatedPath.Count - 1); - int flooredIndex = (int)index; + double d = progress * Length; + int i = cumulativeLength.BinarySearch(d); + if (i < 0) i = ~i; - Vector2 pos = calculatedPath[flooredIndex]; - if (index != flooredIndex) - pos += (calculatedPath[flooredIndex + 1] - pos) * (float)(index - flooredIndex); + if (i >= calculatedPath.Count) + return calculatedPath.Last(); - return pos; + if (i <= 0) + return calculatedPath.First(); + + Vector2 p0 = calculatedPath[i - 1]; + Vector2 p1 = calculatedPath[i]; + + double d0 = cumulativeLength[i - 1]; + double d1 = cumulativeLength[i]; + + // Avoid division by and almost-zero number in case two points are extremely close to each other. + if (Precision.AlmostEquals(d0, d1)) + return p0; + + double w = (d - d0) / (d1 - d0); + return p0 + (p1 - p0) * (float)w; } } } \ No newline at end of file