From d4f0fc0fdee85accf84de96d6388e0a9ba2ecd0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Mar 2025 16:12:32 +0900 Subject: [PATCH 1/2] Disallow adjusting slider repeats with more lenient check condition --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6c0d5af247..f60d1b023b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -441,7 +441,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); int proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1); - if (proposedCount == repeatHitObject.RepeatCount || Precision.AlmostEquals(lengthOfOneRepeat, 0)) + if (proposedCount == repeatHitObject.RepeatCount || Precision.AlmostEquals(lengthOfOneRepeat, 0, 1)) return; repeatHitObject.RepeatCount = proposedCount; From 23891b1994c296d7e6761010deeb334f6c1af103 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Mar 2025 16:17:44 +0900 Subject: [PATCH 2/2] Fix edge case allowing almost-zero-length sliders to be placed during distance snapping --- .../Edit/Blueprints/Components/SelectionEditablePath.cs | 2 +- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs | 2 +- osu.Game/Rulesets/Objects/SliderPath.cs | 5 ++++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs index 26b26641d3..654ef006a5 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/SelectionEditablePath.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components { base.UpdateHitObjectFromPath(hitObject); - if (hitObject.Path.ControlPoints.Count <= 1 || !hitObject.Path.HasValidLength) + if (hitObject.Path.ControlPoints.Count <= 1 || !hitObject.Path.HasValidLengthForPlacement) EditorBeatmap?.Remove(hitObject); } } 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 bc3d27fd68..5ae9b194be 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -484,7 +484,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // Snap the path to the current beat divisor before checking length validity. hitObject.SnapTo(distanceSnapProvider); - if (!hitObject.Path.HasValidLength) + if (!hitObject.Path.HasValidLengthForPlacement) { for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++) hitObject.Path.ControlPoints[i].Position = oldControlPoints[i]; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index a747d4fce8..d934eb5a9e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder { Degree = 4 }; - protected override bool IsValidForPlacement => HitObject.Path.HasValidLength; + protected override bool IsValidForPlacement => HitObject.Path.HasValidLengthForPlacement; public SliderPlacementBlueprint() : base(new Slider()) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 9978c46027..d6150f85db 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -476,7 +476,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HitObject.SnapTo(distanceSnapProvider); // If there are 0 or 1 remaining control points, or the slider has an invalid length, it is in a degenerate form and should be deleted - if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLength) + if (controlPoints.Count <= 1 || !HitObject.Path.HasValidLengthForPlacement) { placementHandler?.Delete(HitObject); return; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs index 4c3db207f2..9a5d3c3bc1 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionScaleHandler.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Edit Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider }); (bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad); - if (xInBounds && yInBounds && slider.Path.HasValidLength) + if (xInBounds && yInBounds && slider.Path.HasValidLengthForPlacement) return; for (int i = 0; i < slider.Path.ControlPoints.Count; i++) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 5550815370..eb591ec530 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -31,7 +31,10 @@ namespace osu.Game.Rulesets.Objects /// public readonly Bindable ExpectedDistance = new Bindable(); - public bool HasValidLength => Precision.DefinitelyBigger(Distance, 0); + /// + /// Should be used to check whether placement can continue after a user editor operation. + /// + public bool HasValidLengthForPlacement => Precision.DefinitelyBigger(Distance, 0, 1); /// /// The control points of the path.