Skip to content

Commit

Permalink
Merge pull request #29446 from OliBomby/last-anchor
Browse files Browse the repository at this point in the history
Fix path control points losing curve type on save/reload or undo
  • Loading branch information
smoogipoo authored Sep 2, 2024
2 parents 282c42d + eefd7cf commit 5d09aaa
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 6 deletions.
47 changes: 47 additions & 0 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderChangeStates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using osuTK;

namespace osu.Game.Rulesets.Osu.Tests.Editor
{
public partial class TestSceneSliderChangeStates : TestSceneOsuEditor
{
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);

[TestCase(SplineType.Catmull)]
[TestCase(SplineType.BSpline)]
[TestCase(SplineType.Linear)]
[TestCase(SplineType.PerfectCurve)]
public void TestSliderRetainsCurveTypes(SplineType splineType)
{
Slider? slider = null;
PathType pathType = new PathType(splineType);

AddStep("add slider", () => EditorBeatmap.Add(slider = new Slider
{
StartTime = 500,
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, pathType),
new PathControlPoint(new Vector2(200, 0), pathType),
})
}));
AddAssert("slider has correct spline type", () => ((Slider)EditorBeatmap.HitObjects[0]).Path.ControlPoints.All(p => p.Type == pathType));
AddStep("remove object", () => EditorBeatmap.Remove(slider));
AddAssert("slider removed", () => EditorBeatmap.HitObjects.Count == 0);
addUndoSteps();
AddAssert("slider not removed", () => EditorBeatmap.HitObjects.Count == 1);
AddAssert("slider has correct spline type", () => ((Slider)EditorBeatmap.HitObjects[0]).Path.ControlPoints.All(p => p.Type == pathType));
}

private void addUndoSteps() => AddStep("undo", () => Editor.Undo());
}
}
2 changes: 1 addition & 1 deletion osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ private void addPathData(TextWriter writer, IHasPath pathData, Vector2 position)
// 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 != lastType || point.Type == PathType.PERFECT_CURVE;
bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECT_CURVE || i == pathData.Path.ControlPoints.Count - 1;

// 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.
Expand Down
16 changes: 11 additions & 5 deletions osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,13 +351,19 @@ private IEnumerable<ArraySegment<PathControlPoint>> convertPoints(PathType type,
{
int endPointLength = endPoint == null ? 0 : 1;

if (vertices.Length + endPointLength != 3)
type = PathType.BEZIER;
else if (isLinear(points[0], points[1], endPoint ?? points[2]))
if (FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION)
{
// osu-stable special-cased colinear perfect curves to a linear path
type = PathType.LINEAR;
if (vertices.Length + endPointLength != 3)
type = PathType.BEZIER;
else if (isLinear(points[0], points[1], endPoint ?? points[2]))
{
// osu-stable special-cased colinear perfect curves to a linear path
type = PathType.LINEAR;
}
}
else if (vertices.Length + endPointLength > 3)
// Lazer supports perfect curves with less than 3 points and colinear points
type = PathType.BEZIER;
}

// The first control point must have a definite type.
Expand Down

0 comments on commit 5d09aaa

Please sign in to comment.