diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 68f5b2d70f..6e9cc26af4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -203,17 +202,44 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSliderPathFromBSplineBuilder() { IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; - BindableList sliderPoints = HitObject.Path.ControlPoints; - sliderPoints.RemoveRange(1, sliderPoints.Count - 1); + if (builderPoints.Count == 0) + return; - // Add the control points from the BSpline builder while converting control points that repeat - // three or more times to a single PathControlPoint with linear type. - for (int i = 1; i < builderPoints.Count; i++) + int lastSegmentStart = 0; + PathType? lastPathType = null; + + HitObject.Path.ControlPoints.Clear(); + + // Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate. + // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. + for (int i = 0; i < builderPoints.Count; i++) { - bool isSharp = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; - sliderPoints.Add(new PathControlPoint(builderPoints[i], isSharp ? PathType.BSpline(3) : null)); - if (isSharp) i += 2; + bool isLastPoint = i == builderPoints.Count - 1; + bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + + if (isNewSegment || isLastPoint) + { + int pointsInSegment = i - lastSegmentStart; + + // Where possible, we can use the simpler LINEAR path type. + PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); + + // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. + if (lastPathType == pathType && lastPathType == PathType.LINEAR) + pathType = null; + + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType)); + for (int j = lastSegmentStart + 1; j < i; j++) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j])); + + if (isLastPoint) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i])); + + // Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type. + lastSegmentStart = (i += 2); + if (pathType != null) lastPathType = pathType; + } } }