From 020b08b4500c811702380b2c3286427a36d2e3b3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Nov 2019 14:55:05 +0900 Subject: [PATCH] Initial implementation of limited distance snap --- .../TestSceneOsuDistanceSnapGrid.cs | 2 +- .../Edit/OsuDistanceSnapGrid.cs | 4 +-- .../Edit/OsuHitObjectComposer.cs | 33 ++++++++++++------- .../Editor/TestSceneDistanceSnapGrid.cs | 2 +- .../Components/CircularDistanceSnapGrid.cs | 12 ++++--- .../Compose/Components/DistanceSnapGrid.cs | 21 +++++++++++- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs index a9a6097182..aba1d8fa1d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuDistanceSnapGrid.cs @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Tests public new float DistanceSpacing => base.DistanceSpacing; public TestOsuDistanceSnapGrid(OsuHitObject hitObject) - : base(hitObject) + : base(hitObject, null) { } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs index 79cd51a7f4..9b00204d51 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs @@ -8,8 +8,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuDistanceSnapGrid : CircularDistanceSnapGrid { - public OsuDistanceSnapGrid(OsuHitObject hitObject) - : base(hitObject, hitObject.StackedEndPosition) + public OsuDistanceSnapGrid(OsuHitObject hitObject, OsuHitObject nextHitObject) + : base(hitObject, nextHitObject, hitObject.StackedEndPosition) { Masking = true; } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index fcf2772219..a021f70598 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -60,25 +61,33 @@ namespace osu.Game.Rulesets.Osu.Edit var objects = selectedHitObjects.ToList(); if (objects.Count == 0) + return createGrid(h => h.StartTime <= EditorClock.CurrentTime); + + double minTime = objects.Min(h => h.StartTime); + return createGrid(h => h.StartTime < minTime); + } + + private OsuDistanceSnapGrid createGrid(Func hitObjectSelector) + { + int lastIndex = -1; + + for (int i = 0; i < EditorBeatmap.HitObjects.Count; i++) { - var lastObject = EditorBeatmap.HitObjects.LastOrDefault(h => h.StartTime <= EditorClock.CurrentTime); + HitObject hitObject = EditorBeatmap.HitObjects[i]; - if (lastObject == null) - return null; + if (!hitObjectSelector(hitObject)) + break; - return new OsuDistanceSnapGrid(lastObject); + lastIndex = i; } - else - { - double minTime = objects.Min(h => h.StartTime); - var lastObject = EditorBeatmap.HitObjects.LastOrDefault(h => h.StartTime < minTime); + if (lastIndex == -1) + return null; - if (lastObject == null) - return null; + OsuHitObject lastObject = EditorBeatmap.HitObjects[lastIndex]; + OsuHitObject nextObject = lastIndex == EditorBeatmap.HitObjects.Count - 1 ? null : EditorBeatmap.HitObjects[lastIndex + 1]; - return new OsuDistanceSnapGrid(lastObject); - } + return new OsuDistanceSnapGrid(lastObject, nextObject); } } } diff --git a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs index b8c31d5dbb..0681dff528 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Editor public new float DistanceSpacing => base.DistanceSpacing; public TestDistanceSnapGrid(HitObject hitObject, Vector2 centrePosition) - : base(hitObject, centrePosition) + : base(hitObject, null, centrePosition) { } diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index f45115e1e4..0f2bae6305 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -12,8 +12,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { public abstract class CircularDistanceSnapGrid : DistanceSnapGrid { - protected CircularDistanceSnapGrid(HitObject hitObject, Vector2 centrePosition) - : base(hitObject, centrePosition) + protected CircularDistanceSnapGrid(HitObject hitObject, HitObject nextHitObject, Vector2 centrePosition) + : base(hitObject, nextHitObject, centrePosition) { } @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Compose.Components float dx = Math.Max(centrePosition.X, DrawWidth - centrePosition.X); float dy = Math.Max(centrePosition.Y, DrawHeight - centrePosition.Y); float maxDistance = new Vector2(dx, dy).Length; - int requiredCircles = (int)(maxDistance / DistanceSpacing); + int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing)); for (int i = 0; i < requiredCircles; i++) { @@ -65,15 +65,17 @@ namespace osu.Game.Screens.Edit.Compose.Components public override (Vector2 position, double time) GetSnappedPosition(Vector2 position) { - Vector2 direction = position - CentrePosition; + if (MaxIntervals == 0) + return (CentrePosition, StartTime); + Vector2 direction = position - CentrePosition; if (direction == Vector2.Zero) direction = new Vector2(0.001f, 0.001f); float distance = direction.Length; float radius = DistanceSpacing; - int radialCount = Math.Max(1, (int)Math.Round(distance / radius)); + int radialCount = MathHelper.Clamp((int)Math.Round(distance / radius), 1, MaxIntervals); Vector2 normalisedDirection = direction * new Vector2(1f / distance); Vector2 snappedPosition = CentrePosition + normalisedDirection * radialCount * radius; diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 193474093f..475b6e7274 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; @@ -29,6 +30,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// protected double StartTime { get; private set; } + /// + /// The maximum number of distance snapping intervals allowed. + /// + protected int MaxIntervals { get; private set; } + /// /// The position which the grid is centred on. /// The first beat snapping tick is located at + in the desired direction. @@ -49,12 +55,15 @@ namespace osu.Game.Screens.Edit.Compose.Components private readonly Cached gridCache = new Cached(); private readonly HitObject hitObject; + private readonly HitObject nextHitObject; - protected DistanceSnapGrid(HitObject hitObject, Vector2 centrePosition) + protected DistanceSnapGrid(HitObject hitObject, [CanBeNull] HitObject nextHitObject, Vector2 centrePosition) { this.hitObject = hitObject; + this.nextHitObject = nextHitObject; CentrePosition = centrePosition; + RelativeSizeAxes = Axes.Both; } @@ -74,6 +83,16 @@ namespace osu.Game.Screens.Edit.Compose.Components private void updateSpacing() { DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(StartTime); + + if (nextHitObject == null) + MaxIntervals = int.MaxValue; + else + { + // +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 = nextHitObject.StartTime - StartTime + 1; + MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(StartTime, DistanceSpacing)); + } + gridCache.Invalidate(); }