From 08bb25347c8746736dd81b299cc18d9d58272011 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 20:27:05 +0900 Subject: [PATCH 1/7] Make DrawableSlider contain the slider body --- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSlider.cs | 19 +- .../Objects/Drawables/DrawableSliderTail.cs | 6 +- .../Objects/Drawables/Pieces/SliderBody.cs | 37 +++- osu.Game.Rulesets.Osu/Objects/Slider.cs | 14 +- .../Tests/TestCaseNewSliderBody.cs | 171 ++++++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 1 + 7 files changed, 232 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index db704b0553..3e1b64bb86 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables List curve = drawableSlider.Body.CurrentCurve; var positionOnCurve = isRepeatAtEnd ? end : start; - Position = positionOnCurve + drawableSlider.HitObject.StackOffset; + Position = positionOnCurve - curve[0] + drawableSlider.HitObject.StackOffset; if (curve.Count < 2) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 391e0ff023..866631468a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { slider = s; + Position = s.StackedPosition; + DrawableSliderTail tail; Container ticks; Container repeatPoints; @@ -39,20 +41,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body = new SliderBody(s) { AccentColour = AccentColour, - Position = s.StackedPosition, PathWidth = s.Scale * 64, }, - ticks = new Container(), - repeatPoints = new Container(), + ticks = new Container { RelativeSizeAxes = Axes.Both }, + repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, Ball = new SliderBall(s) { + BypassAutoSizeAxes = Axes.Both, Scale = new Vector2(s.Scale), AccentColour = AccentColour, AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle), - tail = new DrawableSliderTail(s.TailCircle) + HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.StackedPosition }, + tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.StackedPosition } }; components.Add(Body); @@ -112,6 +114,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; + + Size = Body.Size; + OriginPosition = Body.PathOffset; + + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); + Ball.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 8835fc2b29..b907aea8c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -19,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderTail(HitCircle hitCircle) : base(hitCircle) { - AlwaysPresent = true; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + AlwaysPresent = true; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index a83ee3a2e1..8c0eb7ff7d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -29,6 +29,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { path.PathWidth = value; } } + /// + /// Offset in absolute coordinates from the start of the curve. + /// + public Vector2 PathOffset { get; private set; } + + public readonly List CurrentCurve = new List(); + public readonly Bindable SnakingIn = new Bindable(); public readonly Bindable SnakingOut = new Bindable(); @@ -75,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private int textureWidth => (int)PathWidth * 2; + private Vector2 topLeftOffset; + private readonly Slider slider; public SliderBody(Slider s) { @@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { container = new BufferedContainer { + RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] { @@ -107,11 +117,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (updateSnaking(p0, p1)) { - // Autosizing does not give us the desired behaviour here. - // We want the container to have the same size as the slider, - // and to be positioned such that the slider head is at (0,0). - container.Size = path.Size; - container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - CurrentCurve[0]); + // The path is generated such that its size encloses it. This change of size causes the path + // to move around while snaking, so we need to offset it to make sure it maintains the + // same position as when it is fully snaked. + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; container.ForceRedraw(); } @@ -121,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load() { reloadTexture(); + computeSize(); } private void reloadTexture() @@ -164,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.Texture = texture; } - public readonly List CurrentCurve = new List(); + private void computeSize() + { + // Generate the entire curve + slider.Curve.GetPathToProgress(CurrentCurve, 0, 1); + foreach (Vector2 p in CurrentCurve) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + PathOffset = path.PositionInBoundingBox(CurrentCurve[0]); + } + private bool updateSnaking(double p0, double p1) { if (SnakedStart == p0 && SnakedEnd == p1) return false; @@ -176,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.ClearVertices(); foreach (Vector2 p in CurrentCurve) - path.AddVertex(p - CurrentCurve[0]); + path.AddVertex(p); return true; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index ce6c88a340..4905972e6f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects HeadCircle = new HitCircle { StartTime = StartTime, - Position = StackedPosition, + Position = this.PositionAt(0), IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour, Samples = Samples, @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle = new HitCircle { StartTime = EndTime, - Position = StackedEndPosition, + Position = this.PositionAt(1), IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour }; @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpanIndex = span, SpanStartTime = spanStartTime, StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Curve.PositionAt(distanceProgress), + Position = Curve.PositionAt(distanceProgress) - Curve.PositionAt(0), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects RepeatIndex = repeatIndex, SpanDuration = SpanDuration, StartTime = StartTime + repeat * SpanDuration, - Position = Curve.PositionAt(repeat % 2), + Position = Curve.PositionAt(repeat % 2) - Curve.PositionAt(0), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -184,4 +184,10 @@ namespace osu.Game.Rulesets.Osu.Objects } } } + + public static class SliderExtensions + { + public static Vector2 PositionAt(this Slider slider, double progress) + => ((IHasCurve)slider).PositionAt(progress) - slider.Curve.PositionAt(0); + } } diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs new file mode 100644 index 0000000000..a23bfb11ca --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs @@ -0,0 +1,171 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestCaseNewSliderBody : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(Path) }; + + private readonly NewSliderBody body; + + public TestCaseNewSliderBody() + { + Add(body = new NewSliderBody(new SliderCurve + { + ControlPoints = new List + { + new Vector2(-200, 0), + new Vector2(-50, 75), + new Vector2(0, 100), + new Vector2(100, -200), + new Vector2(230, 0) + }, + Distance = 480, + CurveType = CurveType.Bezier + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + + AddSliderStep("In", 0f, 1f, 0f, v => inLength = v); + AddSliderStep("Out", 0f, 1f, 1f, v => outLength = v); + AddSliderStep("Path Width", 0f, 100f, 10f, v => body.PathWidth = v); + } + + private float _inLength; + + private float inLength + { + set + { + _inLength = value; + body.UpdateSnaking(_inLength, _outLength); + } + } + + private float _outLength; + + private float outLength + { + set + { + _outLength = value; + body.UpdateSnaking(_inLength, _outLength); + } + } + + private class NewSliderBody : CompositeDrawable + { + private readonly Path path; + private readonly SliderCurve curve; + + public NewSliderBody(SliderCurve curve) + { + this.curve = curve; + + InternalChild = path = new Path(); + } + + [BackgroundDependencyLoader] + private void load() + { + reloadTexture(); + computeSize(); + } + + public float PathWidth + { + get => path.PathWidth; + set { path.PathWidth = value; reloadTexture(); } + } + + private void reloadTexture() + { + var textureWidth = (int)PathWidth * 2; + + //initialise background + var texture = new Texture(textureWidth, 1); + var upload = new TextureUpload(textureWidth * 4); + var bytes = upload.Data; + + const float aa_portion = 0.02f; + const float border_portion = 0.128f; + const float gradient_portion = 1 - border_portion; + + const float opacity_at_centre = 0.3f; + const float opacity_at_edge = 0.8f; + + for (int i = 0; i < textureWidth; i++) + { + float progress = (float)i / (textureWidth - 1); + + if (progress <= border_portion) + { + bytes[i * 4] = (byte)(Color4.White.R * 255); + bytes[i * 4 + 1] = (byte)(Color4.White.G * 255); + bytes[i * 4 + 2] = (byte)(Color4.White.B * 255); + bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (Color4.White.A * 255)); + } + else + { + progress -= border_portion; + + bytes[i * 4] = (byte)(Color4.Blue.R * 255); + bytes[i * 4 + 1] = (byte)(Color4.Blue.G * 255); + bytes[i * 4 + 2] = (byte)(Color4.Blue.B * 255); + bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (Color4.Blue.A * 255)); + } + } + + texture.SetData(upload); + path.Texture = texture; + } + + private Vector2 topLeftOffset; + + private void computeSize() + { + // Compute the final size + var fullPath = new List(); + curve.GetPathToProgress(fullPath, 0, 1); + + foreach (Vector2 p in fullPath) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + OriginPosition = path.PositionInBoundingBox(fullPath[0]); + } + + public void UpdateSnaking(float t0, float t1) + { + var curvePath = new List(); + curve.GetPathToProgress(curvePath, t0, t1); + + path.ClearVertices(); + foreach (Vector2 p in curvePath) + path.AddVertex(p); + + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 7838fb7707..53075728ad 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -124,6 +124,7 @@ + From 8c90abe0dc826bdc3487cfc2a064dd6348000a87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 20:51:26 +0900 Subject: [PATCH 2/7] Make slider control points relative to start position --- .../Objects/Drawables/DrawableRepeatPoint.cs | 7 +++---- .../Objects/Drawables/DrawableSlider.cs | 17 ++++++----------- .../Objects/Drawables/Pieces/SliderBall.cs | 3 ++- osu.Game.Rulesets.Osu/Objects/Slider.cs | 18 ++++++------------ .../Objects/Legacy/ConvertHitObjectParser.cs | 8 +++++--- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 3e1b64bb86..79a4714e33 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -78,8 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; List curve = drawableSlider.Body.CurrentCurve; - var positionOnCurve = isRepeatAtEnd ? end : start; - Position = positionOnCurve - curve[0] + drawableSlider.HitObject.StackOffset; + Position = isRepeatAtEnd ? end : start; if (curve.Count < 2) return; @@ -90,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // find the next vector2 in the curve which is not equal to our current position to infer a rotation. for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) { - if (curve[i] == positionOnCurve) + if (curve[i] == Position) continue; - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X)); + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 866631468a..c2f3d4a314 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -53,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.StackedPosition }, - tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.StackedPosition } + HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.Position - s.Position }, + tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position } }; components.Add(Body); @@ -67,10 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var tick in s.NestedHitObjects.OfType()) { - var drawableTick = new DrawableSliderTick(tick) - { - Position = tick.StackedPosition - }; + var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; ticks.Add(drawableTick); components.Add(drawableTick); @@ -79,10 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var repeatPoint in s.NestedHitObjects.OfType()) { - var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) - { - Position = repeatPoint.StackedPosition - }; + var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; repeatPoints.Add(drawableRepeatPoint); components.Add(drawableRepeatPoint); @@ -109,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.StackedPositionAt(completionProgress); + HeadCircle.Position = slider.PositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 61db10b694..4cfe3c3445 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Game.Rulesets.Objects.Types; using OpenTK; using OpenTK.Graphics; @@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - Position = slider.StackedPositionAt(completionProgress); + Position = slider.PositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 4905972e6f..308fc37270 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset; - public override Vector2 EndPosition => this.PositionAt(1); + public Vector2 StackedPositionAt(double t) => StackedPosition + this.PositionAt(t); + public override Vector2 EndPosition => Position + this.PositionAt(1); public SliderCurve Curve { get; } = new SliderCurve(); @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects HeadCircle = new HitCircle { StartTime = StartTime, - Position = this.PositionAt(0), + Position = Position, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour, Samples = Samples, @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle = new HitCircle { StartTime = EndTime, - Position = this.PositionAt(1), + Position = EndPosition, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour }; @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpanIndex = span, SpanStartTime = spanStartTime, StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Curve.PositionAt(distanceProgress) - Curve.PositionAt(0), + Position = Position + Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects RepeatIndex = repeatIndex, SpanDuration = SpanDuration, StartTime = StartTime + repeat * SpanDuration, - Position = Curve.PositionAt(repeat % 2) - Curve.PositionAt(0), + Position = Position + Curve.PositionAt(repeat % 2), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -184,10 +184,4 @@ namespace osu.Game.Rulesets.Osu.Objects } } } - - public static class SliderExtensions - { - public static Vector2 PositionAt(this Slider slider, double progress) - => ((IHasCurve)slider).PositionAt(progress) - slider.Curve.PositionAt(0); - } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 5fdc9a07e1..2fcf3205c1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -41,9 +41,11 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if ((type & ConvertHitObjectType.Slider) > 0) { + var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1])); + CurveType curveType = CurveType.Catmull; double length = 0; - var points = new List { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; + var points = new List { Vector2.Zero }; string[] pointsplit = split[5].Split('|'); foreach (string t in pointsplit) @@ -69,7 +71,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture))); + points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); } int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); @@ -134,7 +136,7 @@ namespace osu.Game.Rulesets.Objects.Legacy for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples); + result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples); } else if ((type & ConvertHitObjectType.Spinner) > 0) { From 1e1df2aafff35fb7bda56cead1d1437bbe0b514a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 21:03:45 +0900 Subject: [PATCH 3/7] Fix up testcases --- osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 40 +++++++++---------- .../Visual/TestCaseEditorSelectionLayer.cs | 6 +-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index 90a0a450a7..ed212b48cd 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-(distance / 2), 0), - new Vector2(distance / 2, 0), + Vector2.Zero, + new Vector2(distance, 0), }, Distance = distance, RepeatCount = repeats, @@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(0, 200), - new Vector2(200, 0) + Vector2.Zero, + new Vector2(200, 200), + new Vector2(400, 0) }, Distance = 600, RepeatCount = repeats, @@ -163,12 +163,12 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), + Vector2.Zero, + new Vector2(150, 75), new Vector2(200, 0), - new Vector2(230, 0) + new Vector2(300, -200), + new Vector2(400, 0), + new Vector2(430, 0) }, Distance = 793.4417, RepeatCount = repeats, @@ -190,11 +190,11 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), - new Vector2(230, 0) + Vector2.Zero, + new Vector2(150, 75), + new Vector2(200, 100), + new Vector2(300, -200), + new Vector2(430, 0) }, Distance = 480, RepeatCount = repeats, @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(0, 0), + Vector2.Zero, new Vector2(-200, 0), new Vector2(0, 0), new Vector2(0, -200), @@ -247,10 +247,10 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Catmull, ControlPoints = new List { - new Vector2(-100, 0), - new Vector2(-50, -50), - new Vector2(50, 50), - new Vector2(100, 0) + Vector2.Zero, + new Vector2(50, -50), + new Vector2(150, 50), + new Vector2(200, 0) }, Distance = 300, RepeatCount = repeats, diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 5e0c0e165c..60ddff64ba 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -35,13 +35,13 @@ namespace osu.Game.Tests.Visual new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, new Slider { + Position = new Vector2(128, 256), ControlPoints = new List { - new Vector2(128, 256), - new Vector2(344, 256), + Vector2.Zero, + new Vector2(216, 0), }, Distance = 400, - Position = new Vector2(128, 256), Velocity = 1, TickDistance = 100, Scale = 0.5f, From 996e605e61628523cabd5d676753d1918ad872ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:39:44 +0900 Subject: [PATCH 4/7] Remove temporary testcase --- .../Tests/TestCaseNewSliderBody.cs | 171 ------------------ .../osu.Game.Rulesets.Osu.csproj | 1 - 2 files changed, 172 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs deleted file mode 100644 index a23bfb11ca..0000000000 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs +++ /dev/null @@ -1,171 +0,0 @@ -// 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 osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.OpenGL.Textures; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Tests.Visual; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Tests -{ - public class TestCaseNewSliderBody : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(Path) }; - - private readonly NewSliderBody body; - - public TestCaseNewSliderBody() - { - Add(body = new NewSliderBody(new SliderCurve - { - ControlPoints = new List - { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), - new Vector2(230, 0) - }, - Distance = 480, - CurveType = CurveType.Bezier - }) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }); - - AddSliderStep("In", 0f, 1f, 0f, v => inLength = v); - AddSliderStep("Out", 0f, 1f, 1f, v => outLength = v); - AddSliderStep("Path Width", 0f, 100f, 10f, v => body.PathWidth = v); - } - - private float _inLength; - - private float inLength - { - set - { - _inLength = value; - body.UpdateSnaking(_inLength, _outLength); - } - } - - private float _outLength; - - private float outLength - { - set - { - _outLength = value; - body.UpdateSnaking(_inLength, _outLength); - } - } - - private class NewSliderBody : CompositeDrawable - { - private readonly Path path; - private readonly SliderCurve curve; - - public NewSliderBody(SliderCurve curve) - { - this.curve = curve; - - InternalChild = path = new Path(); - } - - [BackgroundDependencyLoader] - private void load() - { - reloadTexture(); - computeSize(); - } - - public float PathWidth - { - get => path.PathWidth; - set { path.PathWidth = value; reloadTexture(); } - } - - private void reloadTexture() - { - var textureWidth = (int)PathWidth * 2; - - //initialise background - var texture = new Texture(textureWidth, 1); - var upload = new TextureUpload(textureWidth * 4); - var bytes = upload.Data; - - const float aa_portion = 0.02f; - const float border_portion = 0.128f; - const float gradient_portion = 1 - border_portion; - - const float opacity_at_centre = 0.3f; - const float opacity_at_edge = 0.8f; - - for (int i = 0; i < textureWidth; i++) - { - float progress = (float)i / (textureWidth - 1); - - if (progress <= border_portion) - { - bytes[i * 4] = (byte)(Color4.White.R * 255); - bytes[i * 4 + 1] = (byte)(Color4.White.G * 255); - bytes[i * 4 + 2] = (byte)(Color4.White.B * 255); - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (Color4.White.A * 255)); - } - else - { - progress -= border_portion; - - bytes[i * 4] = (byte)(Color4.Blue.R * 255); - bytes[i * 4 + 1] = (byte)(Color4.Blue.G * 255); - bytes[i * 4 + 2] = (byte)(Color4.Blue.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (Color4.Blue.A * 255)); - } - } - - texture.SetData(upload); - path.Texture = texture; - } - - private Vector2 topLeftOffset; - - private void computeSize() - { - // Compute the final size - var fullPath = new List(); - curve.GetPathToProgress(fullPath, 0, 1); - - foreach (Vector2 p in fullPath) - path.AddVertex(p); - - Size = path.Size; - - topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - OriginPosition = path.PositionInBoundingBox(fullPath[0]); - } - - public void UpdateSnaking(float t0, float t1) - { - var curvePath = new List(); - curve.GetPathToProgress(curvePath, t0, t1); - - path.ClearVertices(); - foreach (Vector2 p in curvePath) - path.AddVertex(p); - - var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - path.Position = topLeftOffset - newTopLeftOffset; - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 53075728ad..7838fb7707 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -124,7 +124,6 @@ - From 066abfbdbc37cd184bbc7d9e6a8d35c2e51ca280 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:43:36 +0900 Subject: [PATCH 5/7] Rename PositionAt -> CurvePositionAt to represent its new meaning --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++-- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index c2f3d4a314..560d13801a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.PositionAt(completionProgress); + HeadCircle.Position = slider.CurvePositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 4cfe3c3445..1921c51889 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - Position = slider.PositionAt(completionProgress); + Position = slider.CurvePositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 308fc37270..61056832e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public Vector2 StackedPositionAt(double t) => StackedPosition + this.PositionAt(t); - public override Vector2 EndPosition => Position + this.PositionAt(1); + public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); + public override Vector2 EndPosition => Position + this.CurvePositionAt(1); public SliderCurve Curve { get; } = new SliderCurve(); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index c03bdb240e..251ad3e3cd 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// The position on the curve. - public static Vector2 PositionAt(this IHasCurve obj, double progress) + public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// From 50d1183ec2deb5182484874404ad0c40d47f04d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:53:02 +0900 Subject: [PATCH 6/7] Division-by-zero safety + reference our own size --- .../Objects/Drawables/DrawableSlider.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 560d13801a..39908e9fa7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -113,9 +113,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = Body.Size; OriginPosition = Body.PathOffset; - foreach (var obj in NestedHitObjects) - obj.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); - Ball.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); + if (DrawSize.X > 0 && DrawSize.Y > 0) + { + var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = childAnchorPosition; + Ball.RelativeAnchorPosition = childAnchorPosition; + } } protected override void CheckForJudgements(bool userTriggered, double timeOffset) From f5fc9cdfba7d66d1f85e997d15b205803c9d6603 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:59:55 +0900 Subject: [PATCH 7/7] Fix catch now having incorrect offsets --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index be1e360fce..a3e5aba2db 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = lastTickTime, ComboColour = ComboColour, - X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = spanStartTime + t, ComboColour = ComboColour, - X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Catch.Objects Samples = Samples, ComboColour = ComboColour, StartTime = spanStartTime + spanDuration, - X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH + X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH }); } } public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime;