From 6dcd9427ac809999a5357f075a00fa41cb40c141 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Aug 2021 01:42:57 +0900 Subject: [PATCH] Remove bindable usage in `PathControlPoint` This is quite a breaking change, but I think it is beneficial due to the large amount of usage of this class. I originally intended just to remove the allocations of the two delegates handling the `Changed` flow internally, but as nothing was really using the bindables for anything more than a general "point has changed" case, this felt like a better direction. --- .../JuiceStreamPathTest.cs | 8 +- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Blueprints/Components/EditablePath.cs | 2 +- .../Edit/CatchSelectionHandler.cs | 2 +- .../Mods/CatchModMirror.cs | 4 +- .../Objects/JuiceStream.cs | 2 +- .../Objects/JuiceStreamPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 4 +- .../TestSceneSliderPlacementBlueprint.cs | 4 +- .../TestSceneSliderSelectionBlueprint.cs | 2 +- .../PathControlPointConnectionPiece.cs | 4 +- .../Components/PathControlPointPiece.cs | 34 ++-- .../Components/PathControlPointVisualiser.cs | 4 +- .../Sliders/SliderPlacementBlueprint.cs | 12 +- .../Sliders/SliderSelectionBlueprint.cs | 14 +- .../Edit/OsuSelectionHandler.cs | 20 +-- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Utils/OsuHitObjectGenerationUtils.cs | 8 +- .../Formats/LegacyBeatmapDecoderTest.cs | 152 +++++++++--------- .../Visual/Gameplay/TestSceneSliderPath.cs | 18 +-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 16 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 12 +- osu.Game/Rulesets/Objects/PathControlPoint.cs | 41 +++-- osu.Game/Rulesets/Objects/SliderPath.cs | 8 +- .../Rulesets/Objects/SliderPathExtensions.cs | 14 +- 25 files changed, 203 insertions(+), 188 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 5e4b6d9e1a..8fa96fb8c9 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests } while (rng.Next(2) != 0); int length = sliderPath.ControlPoints.Count - start + 1; - sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; } while (rng.Next(3) != 0); if (rng.Next(5) == 0) @@ -210,13 +210,13 @@ namespace osu.Game.Rulesets.Catch.Tests path.ConvertToSliderPath(sliderPath, sliderStartY); Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3)); - Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X)); + Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X)); assertInvariants(path.Vertices, true); foreach (var point in sliderPath.ControlPoints) { - Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null); - Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); + Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); } for (int i = 0; i < 10; i++) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 3a5322ce82..a891ec6c0a 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps case JuiceStream juiceStream: // Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead. - lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X; + lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.X; // Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead. lastStartTime = juiceStream.StartTime; diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index 8aaeef045f..1a43a10c81 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components path.ConvertFromSliderPath(sliderPath); // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. - if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear)) + if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear)) { path.ResampleVertices(hitObject.NestedHitObjects .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. diff --git a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs index 36072d7fcb..8cb0804ab7 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchSelectionHandler.cs @@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Catch.Edit juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX); foreach (var point in juiceStream.Path.ControlPoints) - point.Position.Value *= new Vector2(-1, 1); + point.Position *= new Vector2(-1, 1); EditorBeatmap.Update(juiceStream); return true; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs index 932c8cad85..a97e940a64 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModMirror.cs @@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Catch.Mods /// private static void mirrorJuiceStreamPath(JuiceStream juiceStream) { - var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + point.Position = new Vector2(-point.Position.X, point.Position.Y); juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value); } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 3088d024d1..a8ad34fcbe 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index f1cdb39e91..7207833fe6 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Catch.Objects for (int i = 1; i < vertices.Count; i++) { - sliderPath.ControlPoints[^1].Type.Value = PathType.Linear; + sliderPath.ControlPoints[^1].Type = PathType.Linear; float deltaX = vertices[i].X - lastPosition.X; double length = vertices[i].Distance - currentDistance; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 35b79aa8ac..53a9f06eee 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("last connection displayed", () => { - var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position.Value == new Vector2(300)); + var lastConnection = visualiser.Connections.Last(c => c.ControlPoint.Position == new Vector2(300)); return lastConnection.DrawWidth > 50; }); } @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertControlPointPathType(int controlPointIndex, PathType? type) { - AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type.Value == type); + AddAssert($"point {controlPointIndex} is {type}", () => slider.Path.ControlPoints[controlPointIndex].Type == type); } private void addContextMenuItemStep(string contextMenuText) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 8235e1bc79..e724015905 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -385,10 +385,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); - private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type.Value == type); + private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type); private void assertControlPointPosition(int index, Vector2 position) => - AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1)); + AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1)); private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs index 0d828a79c8..cc43eb3852 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep($"move mouse to control point {index}", () => { - Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value; + Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position; InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position)); }); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index eb7011e8b0..d66c9ea4bf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updateConnectingPath() { - Position = slider.StackedPosition + ControlPoint.Position.Value; + Position = slider.StackedPosition + ControlPoint.Position; path.ClearVertices(); @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return; path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[nextIndex].Position.Value - ControlPoint.Position.Value); + path.AddVertex(slider.Path.ControlPoints[nextIndex].Position - ControlPoint.Position); path.OriginPosition = path.PositionInBoundingBox(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 5b476526c9..2cc95e1891 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable sliderPosition; private IBindable sliderScale; - private IBindable controlPointPosition; public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) { @@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components updatePathType(); }); - controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay()); + controlPoint.Changed += updateMarkerDisplay; Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; @@ -117,9 +116,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components sliderPosition = slider.PositionBindable.GetBoundCopy(); sliderPosition.BindValueChanged(_ => updateMarkerDisplay()); - controlPointPosition = ControlPoint.Position.GetBoundCopy(); - controlPointPosition.BindValueChanged(_ => updateMarkerDisplay()); - sliderScale = slider.ScaleBindable.GetBoundCopy(); sliderScale.BindValueChanged(_ => updateMarkerDisplay()); @@ -174,8 +170,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (e.Button == MouseButton.Left) { - dragStartPosition = ControlPoint.Position.Value; - dragPathType = PointsInSegment[0].Type.Value; + dragStartPosition = ControlPoint.Position; + dragPathType = PointsInSegment[0].Type; changeHandler?.BeginChange(); return true; @@ -186,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override void OnDrag(DragEvent e) { - Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position.Value).ToArray(); + Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray(); var oldPosition = slider.Position; var oldStartTime = slider.StartTime; @@ -202,15 +198,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // Since control points are relative to the position of the slider, they all need to be offset backwards by the delta for (int i = 1; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position.Value -= movementDelta; + slider.Path.ControlPoints[i].Position -= movementDelta; } else - ControlPoint.Position.Value = dragStartPosition + (e.MousePosition - e.MouseDownPosition); + ControlPoint.Position = dragStartPosition + (e.MousePosition - e.MouseDownPosition); if (!slider.Path.HasValidLength) { for (var i = 0; i < slider.Path.ControlPoints.Count; i++) - slider.Path.ControlPoints[i].Position.Value = oldControlPoints[i]; + slider.Path.ControlPoints[i].Position = oldControlPoints[i]; slider.Position = oldPosition; slider.StartTime = oldStartTime; @@ -218,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } // Maintain the path type in case it got defaulted to bezier at some point during the drag. - PointsInSegment[0].Type.Value = dragPathType; + PointsInSegment[0].Type = dragPathType; } protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange(); @@ -230,19 +226,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type.Value != PathType.PerfectCurve) + if (ControlPoint.Type != PathType.PerfectCurve) return; if (PointsInSegment.Count > 3) - ControlPoint.Type.Value = PathType.Bezier; + ControlPoint.Type = PathType.Bezier; if (PointsInSegment.Count != 3) return; - ReadOnlySpan points = PointsInSegment.Select(p => p.Position.Value).ToArray(); + ReadOnlySpan points = PointsInSegment.Select(p => p.Position).ToArray(); RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); if (boundingBox.Width >= 640 || boundingBox.Height >= 480) - ControlPoint.Type.Value = PathType.Bezier; + ControlPoint.Type = PathType.Bezier; } /// @@ -250,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updateMarkerDisplay() { - Position = slider.StackedPosition + ControlPoint.Position.Value; + Position = slider.StackedPosition + ControlPoint.Position; markerRing.Alpha = IsSelected.Value ? 1 : 0; @@ -265,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private Color4 getColourFromNodeType() { - if (!(ControlPoint.Type.Value is PathType pathType)) + if (!(ControlPoint.Type is PathType pathType)) return colours.Yellow; switch (pathType) @@ -284,6 +280,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } } - public LocalisableString TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty; + public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty; } } 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 5bbdf9688f..ac1953c632 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -173,12 +173,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int thirdPointIndex = indexInSegment + 2; if (piece.PointsInSegment.Count > thirdPointIndex + 1) - piece.PointsInSegment[thirdPointIndex].Type.Value = piece.PointsInSegment[0].Type.Value; + piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; break; } - piece.ControlPoint.Type.Value = type; + piece.ControlPoint.Type = type; } [Resolved(CanBeNull = true)] diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 8b20df9a68..b9e4ed6fcb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Debug.Assert(lastPoint != null); segmentStart = lastPoint; - segmentStart.Type.Value = PathType.Linear; + segmentStart.Type = PathType.Linear; currentSegmentLength = 1; } @@ -153,15 +153,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { case 1: case 2: - segmentStart.Type.Value = PathType.Linear; + segmentStart.Type = PathType.Linear; break; case 3: - segmentStart.Type.Value = PathType.PerfectCurve; + segmentStart.Type = PathType.PerfectCurve; break; default: - segmentStart.Type.Value = PathType.Bezier; + segmentStart.Type = PathType.Bezier; break; } } @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // The cursor does not overlap a previous control point, so it can be added if not already existing. if (cursor == null) { - HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } }); + HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero }); // The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier). currentSegmentLength++; @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Update the cursor position. - cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; + cursor.Position = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position; } else if (cursor != null) { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index e810d2fe0c..89724876fa 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Debug.Assert(placementControlPointIndex != null); - HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position; + HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position = e.MousePosition - HitObject.Position; } protected override void OnDragEnd(DragEndEvent e) @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders for (int i = 0; i < controlPoints.Count - 1; i++) { - float dist = new Line(controlPoints[i].Position.Value, controlPoints[i + 1].Position.Value).DistanceToPoint(position); + float dist = new Line(controlPoints[i].Position, controlPoints[i + 1].Position).DistanceToPoint(position); if (dist < minDistance) { @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Move the control points from the insertion index onwards to make room for the insertion - controlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } }); + controlPoints.Insert(insertionIndex, new PathControlPoint { Position = position }); return insertionIndex; } @@ -207,8 +207,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { // The first control point in the slider must have a type, so take it from the previous "first" one // Todo: Should be handled within SliderPath itself - if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type.Value == null) - controlPoints[1].Type.Value = controlPoints[0].Type.Value; + if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type == null) + controlPoints[1].Type = controlPoints[0].Type; controlPoints.Remove(c); } @@ -222,9 +222,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0) - Vector2 first = controlPoints[0].Position.Value; + Vector2 first = controlPoints[0].Position; foreach (var c in controlPoints) - c.Position.Value -= first; + c.Position -= first; HitObject.Position += first; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index 358a44e0e6..4a57d36eb4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Edit { foreach (var point in slider.Path.ControlPoints) { - point.Position.Value = new Vector2( - (direction == Direction.Horizontal ? -1 : 1) * point.Position.Value.X, - (direction == Direction.Vertical ? -1 : 1) * point.Position.Value.Y + point.Position = new Vector2( + (direction == Direction.Horizontal ? -1 : 1) * point.Position.X, + (direction == Direction.Vertical ? -1 : 1) * point.Position.Y ); } } @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (h is IHasPath path) { foreach (var point in path.Path.ControlPoints) - point.Position.Value = RotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta); + point.Position = RotatePointAroundOrigin(point.Position, Vector2.Zero, delta); } } @@ -163,9 +163,9 @@ namespace osu.Game.Rulesets.Osu.Edit private void scaleSlider(Slider slider, Vector2 scale) { - referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type.Value).ToList(); + referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList(); - Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value)); + Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position)); // Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0. scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size; @@ -178,13 +178,13 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var point in slider.Path.ControlPoints) { - oldControlPoints.Enqueue(point.Position.Value); - point.Position.Value *= pathRelativeDeltaScale; + oldControlPoints.Enqueue(point.Position); + point.Position *= pathRelativeDeltaScale; } // Maintain the path types in case they were defaulted to bezier at some point during scaling for (int i = 0; i < slider.Path.ControlPoints.Count; ++i) - slider.Path.ControlPoints[i].Type.Value = referencePathTypes[i]; + slider.Path.ControlPoints[i].Type = referencePathTypes[i]; //if sliderhead or sliderend end up outside playfield, revert scaling. Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider }); @@ -194,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Edit return; foreach (var point in slider.Path.ControlPoints) - point.Position.Value = oldControlPoints.Dequeue(); + point.Position = oldControlPoints.Dequeue(); } private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 8ba9597dc3..c4420b1e87 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index 57ec51cf64..bfd6ac3ad3 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -119,9 +119,9 @@ namespace osu.Game.Rulesets.Osu.Utils slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); - var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y); + point.Position = new Vector2(-point.Position.X, point.Position.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); } @@ -140,9 +140,9 @@ namespace osu.Game.Rulesets.Osu.Utils 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 controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray(); + var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); foreach (var point in controlPoints) - point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); + point.Position = new Vector2(point.Position.X, -point.Position.Y); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index dd4c24698c..12633ee8c9 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -666,111 +666,111 @@ namespace osu.Game.Tests.Beatmaps.Formats // Multi-segment var first = ((IHasPath)decoded.HitObjects[0]).Path; - Assert.That(first.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(first.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve)); - Assert.That(first.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244))); - Assert.That(first.ControlPoints[1].Type.Value, Is.EqualTo(null)); + Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); + Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null)); - Assert.That(first.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3))); - Assert.That(first.ControlPoints[2].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(first.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(68, 15))); - Assert.That(first.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(first.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(259, -132))); - Assert.That(first.ControlPoints[4].Type.Value, Is.EqualTo(null)); - Assert.That(first.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(92, -107))); - Assert.That(first.ControlPoints[5].Type.Value, Is.EqualTo(null)); + Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); + Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15))); + Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132))); + Assert.That(first.ControlPoints[4].Type, Is.EqualTo(null)); + Assert.That(first.ControlPoints[5].Position, Is.EqualTo(new Vector2(92, -107))); + Assert.That(first.ControlPoints[5].Type, Is.EqualTo(null)); // Single-segment var second = ((IHasPath)decoded.HitObjects[1]).Path; - Assert.That(second.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(second.ControlPoints[0].Type.Value, Is.EqualTo(PathType.PerfectCurve)); - Assert.That(second.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(161, -244))); - Assert.That(second.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(second.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(376, -3))); - Assert.That(second.ControlPoints[2].Type.Value, Is.EqualTo(null)); + Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); + Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); + Assert.That(second.ControlPoints[2].Type, Is.EqualTo(null)); // Implicit multi-segment var third = ((IHasPath)decoded.HitObjects[2]).Path; - Assert.That(third.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(third.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(third.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(0, 192))); - Assert.That(third.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(224, 192))); - Assert.That(third.ControlPoints[2].Type.Value, Is.EqualTo(null)); + Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192))); + Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192))); + Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null)); - Assert.That(third.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(224, 0))); - Assert.That(third.ControlPoints[3].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(third.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(224, -192))); - Assert.That(third.ControlPoints[4].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(480, -192))); - Assert.That(third.ControlPoints[5].Type.Value, Is.EqualTo(null)); - Assert.That(third.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(480, 0))); - Assert.That(third.ControlPoints[6].Type.Value, Is.EqualTo(null)); + Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0))); + Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192))); + Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192))); + Assert.That(third.ControlPoints[5].Type, Is.EqualTo(null)); + Assert.That(third.ControlPoints[6].Position, Is.EqualTo(new Vector2(480, 0))); + Assert.That(third.ControlPoints[6].Type, Is.EqualTo(null)); // Last control point duplicated var fourth = ((IHasPath)decoded.HitObjects[3]).Path; - Assert.That(fourth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(fourth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fourth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1))); - Assert.That(fourth.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2))); - Assert.That(fourth.ControlPoints[2].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fourth.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(fourth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fourth.ControlPoints[4].Type.Value, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); + Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); + Assert.That(fourth.ControlPoints[2].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fourth.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(fourth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fourth.ControlPoints[4].Type, Is.EqualTo(null)); // Last control point in segment duplicated var fifth = ((IHasPath)decoded.HitObjects[4]).Path; - Assert.That(fifth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(fifth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fifth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1))); - Assert.That(fifth.ControlPoints[1].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2))); - Assert.That(fifth.ControlPoints[2].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fifth.ControlPoints[3].Type.Value, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3))); - Assert.That(fifth.ControlPoints[4].Type.Value, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); + Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); + Assert.That(fifth.ControlPoints[2].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[3].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fifth.ControlPoints[3].Type, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[4].Position, Is.EqualTo(new Vector2(3, 3))); + Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null)); - Assert.That(fifth.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(4, 4))); - Assert.That(fifth.ControlPoints[5].Type.Value, Is.EqualTo(PathType.Bezier)); - Assert.That(fifth.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(5, 5))); - Assert.That(fifth.ControlPoints[6].Type.Value, Is.EqualTo(null)); + Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4))); + Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5))); + Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null)); // Implicit perfect-curve multi-segment(Should convert to bezier to match stable) var sixth = ((IHasPath)decoded.HitObjects[5]).Path; - Assert.That(sixth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(sixth.ControlPoints[0].Type.Value == PathType.Bezier); - Assert.That(sixth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145))); - Assert.That(sixth.ControlPoints[1].Type.Value == null); - Assert.That(sixth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75))); + Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); + Assert.That(sixth.ControlPoints[1].Type == null); + Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(sixth.ControlPoints[2].Type.Value == PathType.Bezier); - Assert.That(sixth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145))); - Assert.That(sixth.ControlPoints[3].Type.Value == null); - Assert.That(sixth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20))); - Assert.That(sixth.ControlPoints[4].Type.Value == null); + Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); + Assert.That(sixth.ControlPoints[3].Type == null); + Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); + Assert.That(sixth.ControlPoints[4].Type == null); // Explicit perfect-curve multi-segment(Should not convert to bezier) var seventh = ((IHasPath)decoded.HitObjects[6]).Path; - Assert.That(seventh.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero)); - Assert.That(seventh.ControlPoints[0].Type.Value == PathType.PerfectCurve); - Assert.That(seventh.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145))); - Assert.That(seventh.ControlPoints[1].Type.Value == null); - Assert.That(seventh.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75))); + Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); + Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); + Assert.That(seventh.ControlPoints[1].Type == null); + Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(seventh.ControlPoints[2].Type.Value == PathType.PerfectCurve); - Assert.That(seventh.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145))); - Assert.That(seventh.ControlPoints[3].Type.Value == null); - Assert.That(seventh.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20))); - Assert.That(seventh.ControlPoints[4].Type.Value == null); + Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); + Assert.That(seventh.ControlPoints[3].Type == null); + Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); + Assert.That(seventh.ControlPoints[4].Type == null); } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 606395c289..9750838433 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestAddControlPoint() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100)))); - AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = { Value = new Vector2(100) } })); + AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) })); } [Test] public void TestInsertControlPoint() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100)))); - AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = { Value = new Vector2(0, 100) } })); + AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) })); } [Test] @@ -95,14 +95,14 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestChangePathType() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - AddStep("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier); + AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier); } [Test] public void TestAddSegmentByChangingType() { AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); - AddStep("change second point type to bezier", () => path.ControlPoints[1].Type.Value = PathType.Bezier); + AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier); } [Test] @@ -111,10 +111,10 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type.Value = PathType.Bezier; + path.ControlPoints[1].Type = PathType.Bezier; }); - AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null); + AddStep("change second point type to null", () => path.ControlPoints[1].Type = null); } [Test] @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type.Value = PathType.Bezier; + path.ControlPoints[1].Type = PathType.Bezier; }); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); @@ -185,8 +185,8 @@ namespace osu.Game.Tests.Visual.Gameplay private List createSegment(PathType type, params Vector2[] controlPoints) { - var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList(); - points[0].Type.Value = type; + var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList(); + points[0].Type = type; return points; } } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fade7183a3..246dc991d5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -323,22 +323,22 @@ namespace osu.Game.Beatmaps.Formats { PathControlPoint point = pathData.Path.ControlPoints[i]; - if (point.Type.Value != null) + if (point.Type != null) { // We've reached a new (explicit) segment! // Explicit segments have a new format in which the type is injected into the middle of the control point string. // To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point. // One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments - bool needsExplicitSegment = point.Type.Value != lastType || point.Type.Value == PathType.PerfectCurve; + bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve; // Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable. // Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder. if (i > 1) { // We need to use the absolute control point position to determine equality, otherwise floating point issues may arise. - Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position.Value; - Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position.Value; + Vector2 p1 = position + pathData.Path.ControlPoints[i - 1].Position; + Vector2 p2 = position + pathData.Path.ControlPoints[i - 2].Position; if ((int)p1.X == (int)p2.X && (int)p1.Y == (int)p2.Y) needsExplicitSegment = true; @@ -346,7 +346,7 @@ namespace osu.Game.Beatmaps.Formats if (needsExplicitSegment) { - switch (point.Type.Value) + switch (point.Type) { case PathType.Bezier: writer.Write("B|"); @@ -365,18 +365,18 @@ namespace osu.Game.Beatmaps.Formats break; } - lastType = point.Type.Value; + lastType = point.Type; } else { // New segment with the same type - duplicate the control point - writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}|")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}|")); } } if (i != 0) { - writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}")); + writer.Write(FormattableString.Invariant($"{position.X + point.Position.X}:{position.Y + point.Position.Y}")); writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ","); } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index e8a5463cce..0942a7264d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -323,7 +323,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } // The first control point must have a definite type. - vertices[0].Type.Value = type; + vertices[0].Type = type; // A path can have multiple implicit segments of the same type if there are two sequential control points with the same position. // To handle such cases, this code may return multiple path segments with the final control point in each segment having a non-null type. @@ -337,7 +337,7 @@ namespace osu.Game.Rulesets.Objects.Legacy while (++endIndex < vertices.Length - endPointLength) { // Keep incrementing while an implicit segment doesn't need to be started - if (vertices[endIndex].Position.Value != vertices[endIndex - 1].Position.Value) + if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; // The last control point of each segment is not allowed to start a new implicit segment. @@ -345,7 +345,7 @@ namespace osu.Game.Rulesets.Objects.Legacy continue; // Force a type on the last point, and return the current control point set as a segment. - vertices[endIndex - 1].Type.Value = type; + vertices[endIndex - 1].Type = type; yield return vertices.AsMemory().Slice(startIndex, endIndex - startIndex); // Skip the current control point - as it's the same as the one that's just been returned. @@ -360,11 +360,11 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] vertexSplit = value.Split(':'); Vector2 pos = new Vector2((int)Parsing.ParseDouble(vertexSplit[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(vertexSplit[1], Parsing.MAX_COORDINATE_VALUE)) - startPos; - point = new PathControlPoint { Position = { Value = pos } }; + point = new PathControlPoint { Position = pos }; } - static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Value.Y - p[0].Position.Value.Y) * (p[2].Position.Value.X - p[0].Position.Value.X) - - (p[1].Position.Value.X - p[0].Position.Value.X) * (p[2].Position.Value.Y - p[0].Position.Value.Y)); + static bool isLinear(PathControlPoint[] p) => Precision.AlmostEquals(0, (p[1].Position.Y - p[0].Position.Y) * (p[2].Position.X - p[0].Position.X) + - (p[1].Position.X - p[0].Position.X) * (p[2].Position.Y - p[0].Position.Y)); } private PathControlPoint[] mergePointsLists(List> controlPointList) diff --git a/osu.Game/Rulesets/Objects/PathControlPoint.cs b/osu.Game/Rulesets/Objects/PathControlPoint.cs index f11917f4f4..53eb430fa3 100644 --- a/osu.Game/Rulesets/Objects/PathControlPoint.cs +++ b/osu.Game/Rulesets/Objects/PathControlPoint.cs @@ -3,7 +3,6 @@ using System; using Newtonsoft.Json; -using osu.Framework.Bindables; using osu.Game.Rulesets.Objects.Types; using osuTK; @@ -11,31 +10,55 @@ namespace osu.Game.Rulesets.Objects { public class PathControlPoint : IEquatable { + private Vector2 position; + /// /// The position of this . /// [JsonProperty] - public readonly Bindable Position = new Bindable(); + public Vector2 Position + { + get => position; + set + { + if (value == position) + return; + + position = value; + Changed?.Invoke(); + } + } + + private PathType? type; /// /// The type of path segment starting at this . /// If null, this will be a part of the previous path segment. /// [JsonProperty] - public readonly Bindable Type = new Bindable(); + public PathType? Type + { + get => type; + set + { + if (value == type) + return; + + type = value; + Changed?.Invoke(); + } + } /// /// Invoked when any property of this is changed. /// - internal event Action Changed; + public event Action Changed; /// /// Creates a new . /// public PathControlPoint() { - Position.ValueChanged += _ => Changed?.Invoke(); - Type.ValueChanged += _ => Changed?.Invoke(); } /// @@ -46,10 +69,10 @@ namespace osu.Game.Rulesets.Objects public PathControlPoint(Vector2 position, PathType? type = null) : this() { - Position.Value = position; - Type.Value = type; + Position = position; + Type = type; } - public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value; + public bool Equals(PathControlPoint other) => Position == other?.Position && Type == other.Type; } } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 55ef0bc5f6..9cc215589b 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Objects foreach (PathControlPoint point in ControlPoints) { - if (point.Type.Value != null) + if (point.Type != null) { if (!found) pointsInCurrentSegment.Clear(); @@ -215,18 +215,18 @@ namespace osu.Game.Rulesets.Objects Vector2[] vertices = new Vector2[ControlPoints.Count]; for (int i = 0; i < ControlPoints.Count; i++) - vertices[i] = ControlPoints[i].Position.Value; + vertices[i] = ControlPoints[i].Position; int start = 0; for (int i = 0; i < ControlPoints.Count; i++) { - if (ControlPoints[i].Type.Value == null && i < ControlPoints.Count - 1) + if (ControlPoints[i].Type == null && i < ControlPoints.Count - 1) continue; // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type.Value ?? PathType.Linear; + var segmentType = ControlPoints[start].Type ?? PathType.Linear; foreach (Vector2 t in calculateSubPath(segmentVertices, segmentType)) { diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 1438c2f128..663746bfca 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset) { var points = sliderPath.ControlPoints.ToArray(); - positionalOffset = points.Last().Position.Value; + positionalOffset = points.Last().Position; sliderPath.ControlPoints.Clear(); @@ -28,17 +28,13 @@ namespace osu.Game.Rulesets.Objects for (var i = 0; i < points.Length; i++) { var p = points[i]; - p.Position.Value -= positionalOffset; + p.Position -= positionalOffset; // propagate types forwards to last null type if (i == points.Length - 1) - p.Type.Value = lastType; - else if (p.Type.Value != null) - { - var newType = p.Type.Value; - p.Type.Value = lastType; - lastType = newType; - } + p.Type = lastType; + else if (p.Type != null) + (p.Type, lastType) = (lastType, p.Type); sliderPath.ControlPoints.Insert(0, p); }