From 13f16077d452333ac0e62948fa5fa886f0fdab7b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 1 Feb 2018 20:04:41 +0900 Subject: [PATCH] Implement Catmull-Rom curve approximator --- osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 34 +++++++++ .../Rulesets/Objects/CatmullApproximator.cs | 70 +++++++++++++++++++ osu.Game/Rulesets/Objects/SliderCurve.cs | 2 + osu.Game/osu.Game.csproj | 1 + 4 files changed, 107 insertions(+) create mode 100644 osu.Game/Rulesets/Objects/CatmullApproximator.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index b8e04de880..ea3ad88cdd 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -19,6 +19,7 @@ using System.Linq; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Tests @@ -71,6 +72,10 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6)); AddStep("Perfect Curve", testCurve); + + AddStep("Catmull", () => testCatmull()); + AddStep("Catmull 1 Repeat", () => testCatmull(1)); + // TODO more curve types? } @@ -131,6 +136,35 @@ namespace osu.Game.Rulesets.Osu.Tests addSlider(slider, 2, 3); } + private void testCatmull(int repeats = 0) => createCatmull(repeats); + + private void createCatmull(int repeats = 0) + { + var repeatSamples = new List>(); + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); + + var slider = new Slider + { + StartTime = Time.Current + 1000, + Position = new Vector2(-100, 0), + ComboColour = Color4.LightSeaGreen, + CurveType = CurveType.Catmull, + ControlPoints = new List + { + new Vector2(-100, 0), + new Vector2(-50, -50), + new Vector2(50, 50), + new Vector2(100, 0) + }, + Distance = 300, + RepeatCount = repeats, + RepeatSamples = repeatSamples + }; + + addSlider(slider, 3, 1); + } + private void addSlider(Slider slider, float circleSize, double speedMultiplier) { var cpi = new ControlPointInfo(); diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs new file mode 100644 index 0000000000..364b4e995a --- /dev/null +++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs @@ -0,0 +1,70 @@ +// 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 OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class CatmullApproximator + { + /// + /// The amount of pieces to calculate for each controlpoint quadruplet. + /// + private const int detail = 50; + + private readonly List controlPoints; + + public CatmullApproximator(List controlPoints) + { + this.controlPoints = controlPoints; + } + + + /// + /// Creates a piecewise-linear approximation of a Catmull-Rom spline. + /// + /// A list of vectors representing the piecewise-linear approximation. + public List CreateCatmull() + { + var result = new List(); + + for (int i = 0; i < controlPoints.Count - 1; i++) + { + var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; + var v2 = controlPoints[i]; + var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; + var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; + + for (int c = 0; c < detail; c++) + { + result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)c / detail)); + result.Add(findPoint(ref v1, ref v2, ref v3, ref v4, (float)(c + 1) / detail)); + } + } + + return result; + } + + /// + /// Finds a point on the spline at the position of a parameter. + /// + /// The first vector. + /// The second vector. + /// The third vector. + /// The fourth vector. + /// The parameter at which to find the point on the spline, in the range [0, 1]. + /// The point on the spline at . + private Vector2 findPoint(ref Vector2 vec1, ref Vector2 vec2, ref Vector2 vec3, ref Vector2 vec4, float t) + { + float t2 = t * t; + float t3 = t * t2; + + Vector2 result; + result.X = 0.5f * (2f * vec2.X + (-vec1.X + vec3.X) * t + (2f * vec1.X - 5f * vec2.X + 4f * vec3.X - vec4.X) * t2 + (-vec1.X + 3f * vec2.X - 3f * vec3.X + vec4.X) * t3); + result.Y = 0.5f * (2f * vec2.Y + (-vec1.Y + vec3.Y) * t + (2f * vec1.Y - 5f * vec2.Y + 4f * vec3.Y - vec4.Y) * t2 + (-vec1.Y + 3f * vec2.Y - 3f * vec3.Y + vec4.Y) * t3); + + return result; + } + } +} diff --git a/osu.Game/Rulesets/Objects/SliderCurve.cs b/osu.Game/Rulesets/Objects/SliderCurve.cs index ae79d62ec8..2b16f463df 100644 --- a/osu.Game/Rulesets/Objects/SliderCurve.cs +++ b/osu.Game/Rulesets/Objects/SliderCurve.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Objects break; return subpath; + case CurveType.Catmull: + return new CatmullApproximator(subControlPoints).CreateCatmull(); } return new BezierApproximator(subControlPoints).CreateBezier(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 12e2f45d76..4944613828 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -340,6 +340,7 @@ +