mirror of
https://github.com/ppy/osu.git
synced 2025-02-26 13:43:23 +08:00
Add free-hand drawing of sliders to the editor
This commit is contained in:
parent
926636cc03
commit
3f85aa79c5
@ -10,6 +10,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -44,6 +45,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ISliderDrawingSettingsProvider drawingSettingsProvider { get; set; }
|
||||
|
||||
private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder();
|
||||
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||
|
||||
public SliderPlacementBlueprint()
|
||||
@ -73,6 +79,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
{
|
||||
base.LoadComplete();
|
||||
inputManager = GetContainingInputManager();
|
||||
|
||||
drawingSettingsProvider.Tolerance.BindValueChanged(e =>
|
||||
{
|
||||
if (bSplineBuilder.Tolerance != e.NewValue)
|
||||
bSplineBuilder.Tolerance = e.NewValue;
|
||||
updateSliderPathFromBSplineBuilder();
|
||||
}, true);
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
@ -98,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
ApplyDefaultsToHitObject();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
updateCursor();
|
||||
break;
|
||||
}
|
||||
@ -115,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
beginCurve();
|
||||
break;
|
||||
|
||||
case SliderPlacementState.Body:
|
||||
case SliderPlacementState.ControlPoints:
|
||||
if (canPlaceNewControlPoint(out var lastPoint))
|
||||
{
|
||||
// Place a new point by detatching the current cursor.
|
||||
@ -139,9 +152,62 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (e.Button == MouseButton.Left)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SliderPlacementState.Initial:
|
||||
return true;
|
||||
|
||||
case SliderPlacementState.ControlPoints:
|
||||
if (HitObject.Path.ControlPoints.Count < 3)
|
||||
{
|
||||
var lastCp = HitObject.Path.ControlPoints.LastOrDefault();
|
||||
if (lastCp != cursor)
|
||||
return false;
|
||||
|
||||
bSplineBuilder.Clear();
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position);
|
||||
setState(SliderPlacementState.Drawing);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return base.OnDragStart(e);
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
base.OnDrag(e);
|
||||
|
||||
bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position);
|
||||
updateSliderPathFromBSplineBuilder();
|
||||
}
|
||||
|
||||
private void updateSliderPathFromBSplineBuilder()
|
||||
{
|
||||
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.Select(v => new PathControlPoint(v)));
|
||||
}, this);
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
base.OnDragEnd(e);
|
||||
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
endCurve();
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (state == SliderPlacementState.Body && e.Button == MouseButton.Right)
|
||||
if (state == SliderPlacementState.ControlPoints && e.Button == MouseButton.Right)
|
||||
endCurve();
|
||||
base.OnMouseUp(e);
|
||||
}
|
||||
@ -149,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
private void beginCurve()
|
||||
{
|
||||
BeginPlacement(commitStart: true);
|
||||
setState(SliderPlacementState.Body);
|
||||
setState(SliderPlacementState.ControlPoints);
|
||||
}
|
||||
|
||||
private void endCurve()
|
||||
@ -169,6 +235,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
|
||||
private void updatePathType()
|
||||
{
|
||||
if (state == SliderPlacementState.Drawing)
|
||||
{
|
||||
segmentStart.Type = PathType.BSpline(3);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (currentSegmentLength)
|
||||
{
|
||||
case 1:
|
||||
@ -201,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
}
|
||||
|
||||
// Update the cursor position.
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.Body ? SnapType.GlobalGrids : SnapType.All);
|
||||
var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.ControlPoints ? SnapType.GlobalGrids : SnapType.All);
|
||||
cursor.Position = ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||
}
|
||||
else if (cursor != null)
|
||||
@ -248,7 +320,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
private enum SliderPlacementState
|
||||
{
|
||||
Initial,
|
||||
Body,
|
||||
ControlPoints,
|
||||
Drawing,
|
||||
DrawingFinalization
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs
Normal file
12
osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public interface ISliderDrawingSettingsProvider
|
||||
{
|
||||
BindableFloat Tolerance { get; }
|
||||
}
|
||||
}
|
@ -63,6 +63,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
[Cached(typeof(IDistanceSnapProvider))]
|
||||
protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider();
|
||||
|
||||
[Cached(typeof(ISliderDrawingSettingsProvider))]
|
||||
protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -96,8 +99,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
RightToolbox.Add(new TransformToolboxGroup
|
||||
{
|
||||
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler
|
||||
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler,
|
||||
});
|
||||
|
||||
AddInternal(SliderDrawingSettingsProvider);
|
||||
SliderDrawingSettingsProvider.AttachToToolbox(RightToolbox);
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||
|
@ -0,0 +1,68 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider
|
||||
{
|
||||
public BindableFloat Tolerance { get; } = new BindableFloat(0.1f)
|
||||
{
|
||||
MinValue = 0.05f,
|
||||
MaxValue = 1f,
|
||||
Precision = 0.01f
|
||||
};
|
||||
|
||||
private BindableInt sliderTolerance = new BindableInt(10)
|
||||
{
|
||||
MinValue = 5,
|
||||
MaxValue = 100
|
||||
};
|
||||
|
||||
private ExpandableSlider<int> toleranceSlider = null!;
|
||||
|
||||
private EditorToolboxGroup? toolboxGroup;
|
||||
|
||||
public OsuSliderDrawingSettingsProvider()
|
||||
{
|
||||
sliderTolerance.BindValueChanged(v =>
|
||||
{
|
||||
float newValue = v.NewValue / 100f;
|
||||
if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f))
|
||||
Tolerance.Value = newValue;
|
||||
});
|
||||
Tolerance.BindValueChanged(v =>
|
||||
{
|
||||
int newValue = (int)Math.Round(v.NewValue * 100f);
|
||||
if (sliderTolerance.Value != newValue)
|
||||
sliderTolerance.Value = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer)
|
||||
{
|
||||
toolboxContainer.Add(toolboxGroup = new EditorToolboxGroup("drawing")
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
toleranceSlider = new ExpandableSlider<int>
|
||||
{
|
||||
Current = sliderTolerance
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sliderTolerance.BindValueChanged(e =>
|
||||
{
|
||||
toleranceSlider.ContractedLabelText = $"Tolerance: {e.NewValue:N0}";
|
||||
toleranceSlider.ExpandedLabelText = $"Tolerance: {e.NewValue:N0}";
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -292,13 +292,13 @@ namespace osu.Game.Rulesets.Objects
|
||||
switch (type.SplineType)
|
||||
{
|
||||
case SplineType.Linear:
|
||||
return PathApproximator.ApproximateLinear(subControlPoints);
|
||||
return PathApproximator.LinearToPiecewiseLinear(subControlPoints);
|
||||
|
||||
case SplineType.PerfectCurve:
|
||||
if (subControlPoints.Length != 3)
|
||||
break;
|
||||
|
||||
List<Vector2> subPath = PathApproximator.ApproximateCircularArc(subControlPoints);
|
||||
List<Vector2> subPath = PathApproximator.CircularArcToPiecewiseLinear(subControlPoints);
|
||||
|
||||
// If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation.
|
||||
if (subPath.Count == 0)
|
||||
@ -307,10 +307,10 @@ namespace osu.Game.Rulesets.Objects
|
||||
return subPath;
|
||||
|
||||
case SplineType.Catmull:
|
||||
return PathApproximator.ApproximateCatmull(subControlPoints);
|
||||
return PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
||||
}
|
||||
|
||||
return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length);
|
||||
return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length);
|
||||
}
|
||||
|
||||
private void calculateLength()
|
||||
|
Loading…
Reference in New Issue
Block a user