mirror of
https://github.com/ppy/osu.git
synced 2025-01-30 05:22:54 +08:00
Merge branch 'master' into fix-toolbox-expansion
This commit is contained in:
commit
f222affe88
@ -4,15 +4,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -25,19 +23,29 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private const double beat_length = 100;
|
private const float beat_length = 100;
|
||||||
|
|
||||||
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly EditorClock editorClock;
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
[Cached(typeof(IDistanceSnapProvider))]
|
[Cached(typeof(IDistanceSnapProvider))]
|
||||||
private readonly SnapProvider snapProvider = new SnapProvider();
|
private readonly OsuHitObjectComposer snapProvider = new OsuHitObjectComposer(new OsuRuleset())
|
||||||
|
{
|
||||||
|
// Just used for the snap implementation, so let's hide from vision.
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Alpha = 0,
|
||||||
|
};
|
||||||
|
|
||||||
private TestOsuDistanceSnapGrid grid;
|
private OsuDistanceSnapGrid grid;
|
||||||
|
|
||||||
public TestSceneOsuDistanceSnapGrid()
|
public TestSceneOsuDistanceSnapGrid()
|
||||||
{
|
{
|
||||||
@ -48,14 +56,25 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
Ruleset = new OsuRuleset().RulesetInfo
|
Ruleset = new OsuRuleset().RulesetInfo
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
editorClock = new EditorClock(editorBeatmap);
|
||||||
|
|
||||||
|
base.Content.Children = new Drawable[]
|
||||||
|
{
|
||||||
|
snapProvider,
|
||||||
|
Content
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
{
|
{
|
||||||
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
||||||
editorBeatmap.ControlPointInfo.Clear();
|
editorBeatmap.ControlPointInfo.Clear();
|
||||||
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
||||||
|
snapProvider.DistanceSpacingMultiplier.Value = 1;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -64,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }),
|
grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }),
|
||||||
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -82,25 +101,45 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep($"set beat divisor = {divisor}", () => beatDivisor.Value = divisor);
|
AddStep($"set beat divisor = {divisor}", () => beatDivisor.Value = divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(1.0f)]
|
||||||
|
[TestCase(2.0f)]
|
||||||
|
[TestCase(0.5f)]
|
||||||
|
public void TestDistanceSpacing(float multiplier)
|
||||||
|
{
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorInCentre()
|
public void TestCursorInCentre()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position)));
|
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position)));
|
||||||
assertSnappedDistance((float)beat_length);
|
assertSnappedDistance(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorBeforeMovementPoint()
|
public void TestCursorBeforeMovementPoint()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.49f)));
|
AddStep("move mouse to just before movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.45f)));
|
||||||
assertSnappedDistance((float)beat_length);
|
assertSnappedDistance(beat_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCursorAfterMovementPoint()
|
public void TestCursorAfterMovementPoint()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 1.51f)));
|
AddStep("move mouse to just after movement point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 1.55f)));
|
||||||
assertSnappedDistance((float)beat_length * 2);
|
assertSnappedDistance(beat_length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(0.5f, beat_length * 2)]
|
||||||
|
[TestCase(1, beat_length * 2)]
|
||||||
|
[TestCase(1.5f, beat_length * 1.5f)]
|
||||||
|
[TestCase(2f, beat_length * 2)]
|
||||||
|
public void TestDistanceSpacingAdjust(float multiplier, float expectedDistance)
|
||||||
|
{
|
||||||
|
AddStep($"Set distance spacing to {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddStep("move mouse to point", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 2)));
|
||||||
|
|
||||||
|
assertSnappedDistance(expectedDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -115,13 +154,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
grid = new TestOsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }),
|
grid = new OsuDistanceSnapGrid(new HitCircle { Position = grid_position }, new HitCircle { StartTime = 200 }),
|
||||||
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
new SnappingCursorContainer { GetSnapPosition = v => grid.GetSnappedPosition(grid.ToLocalSpace(v)).position }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2((float)beat_length, 0) * 3f)));
|
AddStep("move mouse outside grid", () => InputManager.MoveMouseTo(grid.ToScreenSpace(grid_position + new Vector2(beat_length, 0) * 3f)));
|
||||||
assertSnappedDistance((float)beat_length * 2);
|
assertSnappedDistance(beat_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () =>
|
private void assertSnappedDistance(float expectedDistance) => AddAssert($"snap distance = {expectedDistance}", () =>
|
||||||
@ -137,6 +176,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
|
|
||||||
private readonly Drawable cursor;
|
private readonly Drawable cursor;
|
||||||
|
|
||||||
|
private InputManager inputManager;
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
public SnappingCursorContainer()
|
public SnappingCursorContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -153,51 +196,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
updatePosition(GetContainingInputManager().CurrentState.Mouse.Position);
|
inputManager = GetContainingInputManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.OnMouseMove(e);
|
base.Update();
|
||||||
|
cursor.Position = GetSnapPosition.Invoke(inputManager.CurrentState.Mouse.Position);
|
||||||
updatePosition(e.ScreenSpaceMousePosition);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePosition(Vector2 screenSpacePosition)
|
|
||||||
{
|
|
||||||
cursor.Position = GetSnapPosition.Invoke(screenSpacePosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestOsuDistanceSnapGrid : OsuDistanceSnapGrid
|
|
||||||
{
|
|
||||||
public new float DistanceSpacing => base.DistanceSpacing;
|
|
||||||
|
|
||||||
public TestOsuDistanceSnapGrid(OsuHitObject hitObject, OsuHitObject nextHitObject = null)
|
|
||||||
: base(hitObject, nextHitObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SnapProvider : IDistanceSnapProvider
|
|
||||||
{
|
|
||||||
public SnapResult FindSnappedPosition(Vector2 screenSpacePosition) =>
|
|
||||||
new SnapResult(screenSpacePosition, null);
|
|
||||||
|
|
||||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
|
||||||
|
|
||||||
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(HitObject referenceObject) => (float)beat_length;
|
|
||||||
|
|
||||||
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
|
||||||
|
|
||||||
public double DistanceToDuration(HitObject referenceObject, float distance) => distance;
|
|
||||||
|
|
||||||
public double FindSnappedDuration(HitObject referenceObject, float distance) => 0;
|
|
||||||
|
|
||||||
public float FindSnappedDistance(HitObject referenceObject, float distance) => 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
||||||
{
|
{
|
||||||
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
||||||
: base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime)
|
: base(hitObject, hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime - 1)
|
||||||
{
|
{
|
||||||
Masking = true;
|
Masking = true;
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
||||||
{
|
{
|
||||||
private const double beat_length = 100;
|
private const double beat_length = 100;
|
||||||
|
private const int beat_snap_distance = 10;
|
||||||
|
|
||||||
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
private static readonly Vector2 grid_position = new Vector2(512, 384);
|
||||||
|
|
||||||
|
private TestDistanceSnapGrid grid;
|
||||||
|
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
@ -39,6 +43,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
|
||||||
|
editorBeatmap.Difficulty.SliderMultiplier = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@ -51,7 +56,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
new TestDistanceSnapGrid()
|
grid = new TestDistanceSnapGrid()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -68,9 +73,22 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
|
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase(1.0)]
|
||||||
public void TestLimitedDistance()
|
[TestCase(2.0)]
|
||||||
|
[TestCase(0.5)]
|
||||||
|
public void TestDistanceSpacing(double multiplier)
|
||||||
{
|
{
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddAssert("distance spacing matches multiplier", () => grid.DistanceBetweenTicks == beat_snap_distance * multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(1.0)]
|
||||||
|
[TestCase(2.0)]
|
||||||
|
[TestCase(0.5)]
|
||||||
|
public void TestLimitedDistance(double multiplier)
|
||||||
|
{
|
||||||
|
const int end_time = 100;
|
||||||
|
|
||||||
AddStep("create limited grid", () =>
|
AddStep("create limited grid", () =>
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -80,14 +98,19 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.SlateGray
|
Colour = Color4.SlateGray
|
||||||
},
|
},
|
||||||
new TestDistanceSnapGrid(100)
|
grid = new TestDistanceSnapGrid(end_time)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddStep($"set distance spacing = {multiplier}", () => snapProvider.DistanceSpacingMultiplier.Value = multiplier);
|
||||||
|
AddStep("check correct interval count", () => Assert.That((end_time / grid.DistanceBetweenTicks) * multiplier, Is.EqualTo(grid.MaxIntervals)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestDistanceSnapGrid : DistanceSnapGrid
|
private class TestDistanceSnapGrid : DistanceSnapGrid
|
||||||
{
|
{
|
||||||
public new float DistanceSpacing => base.DistanceSpacing;
|
public new float DistanceBetweenTicks => base.DistanceBetweenTicks;
|
||||||
|
|
||||||
|
public new int MaxIntervals => base.MaxIntervals;
|
||||||
|
|
||||||
public TestDistanceSnapGrid(double? endTime = null)
|
public TestDistanceSnapGrid(double? endTime = null)
|
||||||
: base(new HitObject(), grid_position, 0, endTime)
|
: base(new HitObject(), grid_position, 0, endTime)
|
||||||
@ -105,7 +128,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
int indexFromPlacement = 0;
|
int indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.X + DistanceBetweenTicks; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -118,7 +141,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.X - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -131,7 +154,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.Y + DistanceBetweenTicks; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -144,7 +167,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
indexFromPlacement = 0;
|
indexFromPlacement = 0;
|
||||||
|
|
||||||
for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
|
for (float s = StartPosition.Y - DistanceBetweenTicks; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceBetweenTicks, indexFromPlacement++)
|
||||||
{
|
{
|
||||||
AddInternal(new Circle
|
AddInternal(new Circle
|
||||||
{
|
{
|
||||||
@ -167,9 +190,11 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0);
|
||||||
|
|
||||||
public IBindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
public Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1);
|
||||||
|
|
||||||
public float GetBeatSnapDistanceAt(HitObject referenceObject) => 10;
|
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
|
||||||
|
|
||||||
|
public float GetBeatSnapDistanceAt(HitObject referenceObject) => beat_snap_distance;
|
||||||
|
|
||||||
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
public float DurationToDistance(HitObject referenceObject, double duration) => (float)duration;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
public abstract class DistancedHitObjectComposer<TObject> : HitObjectComposer<TObject>, IDistanceSnapProvider, IScrollBindingHandler<GlobalAction>
|
public abstract class DistancedHitObjectComposer<TObject> : HitObjectComposer<TObject>, IDistanceSnapProvider, IScrollBindingHandler<GlobalAction>
|
||||||
where TObject : HitObject
|
where TObject : HitObject
|
||||||
{
|
{
|
||||||
protected Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
public Bindable<double> DistanceSpacingMultiplier { get; } = new BindableDouble(1.0)
|
||||||
{
|
{
|
||||||
MinValue = 0.1,
|
MinValue = 0.1,
|
||||||
MaxValue = 6.0,
|
MaxValue = 6.0,
|
||||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A multiplier which changes the ratio of distance travelled per time unit.
|
/// A multiplier which changes the ratio of distance travelled per time unit.
|
||||||
|
/// Importantly, this is provided for manual usage, and not multiplied into any of the methods exposed by this interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="BeatmapInfo.DistanceSpacing"/>
|
/// <seealso cref="BeatmapInfo.DistanceSpacing"/>
|
||||||
IBindable<double> DistanceSpacingMultiplier { get; }
|
IBindable<double> DistanceSpacingMultiplier { get; }
|
||||||
|
@ -30,14 +30,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
Width = crosshair_thickness,
|
Width = crosshair_thickness,
|
||||||
EdgeSmoothness = new Vector2(1),
|
EdgeSmoothness = new Vector2(1),
|
||||||
Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
|
Height = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2),
|
||||||
},
|
},
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
EdgeSmoothness = new Vector2(1),
|
EdgeSmoothness = new Vector2(1),
|
||||||
Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
|
Width = Math.Min(crosshair_max_size, DistanceBetweenTicks * 2),
|
||||||
Height = crosshair_thickness,
|
Height = crosshair_thickness,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -45,19 +45,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
|
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
|
||||||
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
|
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
|
||||||
float maxDistance = new Vector2(dx, dy).Length;
|
float maxDistance = new Vector2(dx, dy).Length;
|
||||||
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing));
|
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceBetweenTicks));
|
||||||
|
|
||||||
for (int i = 0; i < requiredCircles; i++)
|
for (int i = 0; i < requiredCircles; i++)
|
||||||
{
|
{
|
||||||
float radius = (i + 1) * DistanceSpacing * 2;
|
float diameter = (i + 1) * DistanceBetweenTicks * 2;
|
||||||
|
|
||||||
AddInternal(new CircularProgress
|
AddInternal(new CircularProgress
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Position = StartPosition,
|
Position = StartPosition,
|
||||||
Current = { Value = 1 },
|
Current = { Value = 1 },
|
||||||
Size = new Vector2(radius),
|
Size = new Vector2(diameter),
|
||||||
InnerRadius = 4 * 1f / radius,
|
InnerRadius = 4 * 1f / diameter,
|
||||||
Colour = GetColourForIndexFromPlacement(i)
|
Colour = GetColourForIndexFromPlacement(i)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -68,19 +68,37 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (MaxIntervals == 0)
|
if (MaxIntervals == 0)
|
||||||
return (StartPosition, StartTime);
|
return (StartPosition, StartTime);
|
||||||
|
|
||||||
Vector2 direction = position - StartPosition;
|
// This grid implementation factors in the user's distance spacing specification,
|
||||||
if (direction == Vector2.Zero)
|
// which is usually not considered by an `IDistanceSnapProvider`.
|
||||||
direction = new Vector2(0.001f, 0.001f);
|
float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value;
|
||||||
|
|
||||||
float distance = direction.Length;
|
Vector2 travelVector = (position - StartPosition);
|
||||||
|
|
||||||
float radius = DistanceSpacing;
|
if (travelVector == Vector2.Zero)
|
||||||
int radialCount = Math.Clamp((int)MathF.Round(distance / radius), 1, MaxIntervals);
|
return (StartPosition, StartTime);
|
||||||
|
|
||||||
Vector2 normalisedDirection = direction * new Vector2(1f / distance);
|
float travelLength = travelVector.Length;
|
||||||
Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius;
|
|
||||||
|
|
||||||
return (snappedPosition, StartTime + SnapProvider.FindSnappedDuration(ReferenceObject, (snappedPosition - StartPosition).Length));
|
// FindSnappedDistance will always round down, but we want to potentially round upwards.
|
||||||
|
travelLength += DistanceBetweenTicks / 2;
|
||||||
|
|
||||||
|
// When interacting with the resolved snap provider, the distance spacing multiplier should first be removed
|
||||||
|
// to allow for snapping at a non-multiplied ratio.
|
||||||
|
float snappedDistance = SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacingMultiplier);
|
||||||
|
double snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance);
|
||||||
|
|
||||||
|
if (snappedTime > LatestEndTime)
|
||||||
|
{
|
||||||
|
double tickLength = Beatmap.GetBeatLengthAtTime(StartTime);
|
||||||
|
|
||||||
|
snappedDistance = SnapProvider.DurationToDistance(ReferenceObject, MaxIntervals * tickLength);
|
||||||
|
snappedTime = StartTime + SnapProvider.DistanceToDuration(ReferenceObject, snappedDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The multiplier can then be reapplied to the final position.
|
||||||
|
Vector2 snappedPosition = StartPosition + travelVector.Normalized() * snappedDistance * distanceSpacingMultiplier;
|
||||||
|
|
||||||
|
return (snappedPosition, snappedTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The spacing between each tick of the beat snapping grid.
|
/// The spacing between each tick of the beat snapping grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected float DistanceSpacing { get; private set; }
|
protected float DistanceBetweenTicks { get; private set; }
|
||||||
|
|
||||||
|
protected IBindable<double> DistanceSpacingMultiplier { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum number of distance snapping intervals allowed.
|
/// The maximum number of distance snapping intervals allowed.
|
||||||
@ -32,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position which the grid should start.
|
/// The position which the grid should start.
|
||||||
/// The first beat snapping tick is located at <see cref="StartPosition"/> + <see cref="DistanceSpacing"/> away from this point.
|
/// The first beat snapping tick is located at <see cref="StartPosition"/> + <see cref="DistanceBetweenTicks"/> away from this point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Vector2 StartPosition;
|
protected readonly Vector2 StartPosition;
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly double StartTime;
|
protected readonly double StartTime;
|
||||||
|
|
||||||
|
protected readonly double? LatestEndTime;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
protected OsuColour Colours { get; private set; }
|
protected OsuColour Colours { get; private set; }
|
||||||
|
|
||||||
@ -48,15 +52,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected IDistanceSnapProvider SnapProvider { get; private set; }
|
protected IDistanceSnapProvider SnapProvider { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap beatmap { get; set; }
|
protected EditorBeatmap Beatmap { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BindableBeatDivisor beatDivisor { get; set; }
|
private BindableBeatDivisor beatDivisor { get; set; }
|
||||||
|
|
||||||
private IBindable<double> distanceSpacingMultiplier;
|
|
||||||
|
|
||||||
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||||
private readonly double? endTime;
|
|
||||||
|
|
||||||
protected readonly HitObject ReferenceObject;
|
protected readonly HitObject ReferenceObject;
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
|
protected DistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
|
||||||
{
|
{
|
||||||
ReferenceObject = referenceObject;
|
ReferenceObject = referenceObject;
|
||||||
this.endTime = endTime;
|
LatestEndTime = endTime;
|
||||||
|
|
||||||
StartPosition = startPosition;
|
StartPosition = startPosition;
|
||||||
StartTime = startTime;
|
StartTime = startTime;
|
||||||
@ -86,22 +87,21 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
beatDivisor.BindValueChanged(_ => updateSpacing());
|
beatDivisor.BindValueChanged(_ => updateSpacing());
|
||||||
|
|
||||||
distanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy();
|
DistanceSpacingMultiplier = SnapProvider.DistanceSpacingMultiplier.GetBoundCopy();
|
||||||
distanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true);
|
DistanceSpacingMultiplier.BindValueChanged(_ => updateSpacing(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpacing()
|
private void updateSpacing()
|
||||||
{
|
{
|
||||||
DistanceSpacing = (float)(SnapProvider.GetBeatSnapDistanceAt(ReferenceObject) * distanceSpacingMultiplier.Value);
|
float distanceSpacingMultiplier = (float)DistanceSpacingMultiplier.Value;
|
||||||
|
float beatSnapDistance = SnapProvider.GetBeatSnapDistanceAt(ReferenceObject);
|
||||||
|
|
||||||
if (endTime == null)
|
DistanceBetweenTicks = beatSnapDistance * distanceSpacingMultiplier;
|
||||||
|
|
||||||
|
if (LatestEndTime == null)
|
||||||
MaxIntervals = int.MaxValue;
|
MaxIntervals = int.MaxValue;
|
||||||
else
|
else
|
||||||
{
|
MaxIntervals = (int)((LatestEndTime.Value - StartTime) / SnapProvider.DistanceToDuration(ReferenceObject, beatSnapDistance));
|
||||||
// +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors
|
|
||||||
double maxDuration = endTime.Value - StartTime + 1;
|
|
||||||
MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(ReferenceObject, DistanceSpacing));
|
|
||||||
}
|
|
||||||
|
|
||||||
gridCache.Invalidate();
|
gridCache.Invalidate();
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <returns>The applicable colour.</returns>
|
/// <returns>The applicable colour.</returns>
|
||||||
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
|
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
|
||||||
{
|
{
|
||||||
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime);
|
var timingPoint = Beatmap.ControlPointInfo.TimingPointAt(StartTime);
|
||||||
double beatLength = timingPoint.BeatLength / beatDivisor.Value;
|
double beatLength = timingPoint.BeatLength / beatDivisor.Value;
|
||||||
int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
|
int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osuTK.Graphics;
|
using osu.Framework.Layout;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
@ -41,17 +42,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
InternalChild = Box = CreateBox();
|
InternalChild = Box = CreateBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Drawable CreateBox() => new Container
|
protected virtual Drawable CreateBox() => new BoxWithBorders();
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
BorderColour = Color4.White,
|
|
||||||
BorderThickness = SelectionBox.BORDER_RADIUS,
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.1f
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private RectangleF? dragRectangle;
|
private RectangleF? dragRectangle;
|
||||||
|
|
||||||
@ -111,5 +102,75 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
public override void Show() => State = Visibility.Visible;
|
public override void Show() => State = Visibility.Visible;
|
||||||
|
|
||||||
public event Action<Visibility> StateChanged;
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
|
public class BoxWithBorders : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly LayoutValue cache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||||
|
|
||||||
|
public BoxWithBorders()
|
||||||
|
{
|
||||||
|
AddLayout(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!cache.IsValid)
|
||||||
|
{
|
||||||
|
createContent();
|
||||||
|
cache.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createContent()
|
||||||
|
{
|
||||||
|
if (DrawSize == Vector2.Zero)
|
||||||
|
{
|
||||||
|
ClearInternal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make lines the same width independent of display resolution.
|
||||||
|
float lineThickness = DrawWidth > 0
|
||||||
|
? DrawWidth / ScreenSpaceDrawQuad.Width * 2
|
||||||
|
: DrawHeight / ScreenSpaceDrawQuad.Height * 2;
|
||||||
|
|
||||||
|
Padding = new MarginPadding(-lineThickness / 2);
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = lineThickness,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = lineThickness,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = lineThickness,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = lineThickness,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.1f
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user