From 90ec6895d100dea67e282c75a57779ffa87d0f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Tue, 14 Nov 2023 16:52:45 +0900 Subject: [PATCH] Automatic red control point generation & corner threshold --- .../Sliders/SliderPlacementBlueprint.cs | 28 +++++++++-- .../Edit/ISliderDrawingSettingsProvider.cs | 1 + .../Edit/OsuSliderDrawingSettingsProvider.cs | 46 +++++++++++++++++-- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c722f0fdbc..69e2a40689 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -86,6 +86,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }, true); + + drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + { + if (bSplineBuilder.CornerThreshold != e.NewValue) + bSplineBuilder.CornerThreshold = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); } [Resolved] @@ -100,8 +107,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case SliderPlacementState.Initial: BeginPlacement(); - double? nearestSliderVelocity = (editorBeatmap.HitObjects - .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; + double? nearestSliderVelocity = (editorBeatmap + .HitObjects + .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1; HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); @@ -193,9 +201,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Scheduler.AddOnce(static self => { - var cps = self.bSplineBuilder.GetControlPoints(); - self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); - self.HitObject.Path.ControlPoints.AddRange(cps.Skip(1).Select(v => new PathControlPoint(v))); + var cps = self.bSplineBuilder.ControlPoints; + var sliderCps = self.HitObject.Path.ControlPoints; + sliderCps.RemoveRange(1, sliderCps.Count - 1); + + // 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 < cps.Count; i++) + { + bool isSharp = i < cps.Count - 2 && cps[i] == cps[i + 1] && cps[i] == cps[i + 2]; + sliderCps.Add(new PathControlPoint(cps[i], isSharp ? PathType.BSpline(3) : null)); + if (isSharp) + i += 2; + } }, this); } diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs index 1138588259..31ed98e1dd 100644 --- a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Osu.Edit public interface ISliderDrawingSettingsProvider { BindableFloat Tolerance { get; } + BindableFloat CornerThreshold { get; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 643732d90a..1fe1326f38 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -12,20 +12,34 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment { - public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) + public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) + { + MinValue = 0.05f, + MaxValue = 3f, + Precision = 0.01f + }; + + private readonly BindableInt sliderTolerance = new BindableInt(50) + { + MinValue = 5, + MaxValue = 100 + }; + + public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f) { MinValue = 0.05f, MaxValue = 1f, Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(10) + private readonly BindableInt sliderCornerThreshold = new BindableInt(40) { MinValue = 5, MaxValue = 100 }; private ExpandableSlider toleranceSlider = null!; + private ExpandableSlider cornerThresholdSlider = null!; protected override void LoadComplete() { @@ -33,16 +47,28 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(v => { - float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value)) + float newValue = v.NewValue / 33f; + if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => { - int newValue = (int)Math.Round(v.NewValue * 100f); + int newValue = (int)Math.Round(v.NewValue * 33f); if (sliderTolerance.Value != newValue) sliderTolerance.Value = newValue; }); + sliderCornerThreshold.BindValueChanged(v => + { + float newValue = v.NewValue / 100f; + if (!Precision.AlmostEquals(newValue, CornerThreshold.Value, 1e-7f)) + CornerThreshold.Value = newValue; + }); + CornerThreshold.BindValueChanged(v => + { + int newValue = (int)Math.Round(v.NewValue * 100f); + if (sliderCornerThreshold.Value != newValue) + sliderCornerThreshold.Value = newValue; + }); } public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) @@ -54,6 +80,10 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider = new ExpandableSlider { Current = sliderTolerance + }, + cornerThresholdSlider = new ExpandableSlider + { + Current = sliderCornerThreshold } } }); @@ -63,6 +93,12 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; }, true); + + sliderCornerThreshold.BindValueChanged(e => + { + cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; + }, true); } } }