2019-10-10 20:55:48 +08:00
// 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.
2020-01-28 10:59:21 +08:00
using System ;
2019-10-10 20:55:48 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
using osu.Framework.Graphics.Containers ;
2020-02-24 19:52:15 +08:00
using osu.Framework.Layout ;
2019-10-10 20:55:48 +08:00
using osu.Game.Graphics ;
2019-10-25 11:34:49 +08:00
using osu.Game.Rulesets.Edit ;
2019-10-10 20:55:48 +08:00
using osuTK ;
namespace osu.Game.Screens.Edit.Compose.Components
{
2019-10-17 14:32:02 +08:00
/// <summary>
/// A grid which takes user input and returns a quantized ("snapped") position and time.
/// </summary>
public abstract class DistanceSnapGrid : CompositeDrawable
2019-10-10 20:55:48 +08:00
{
2019-10-11 14:41:01 +08:00
/// <summary>
2019-10-25 11:34:49 +08:00
/// The spacing between each tick of the beat snapping grid.
2019-10-11 14:41:01 +08:00
/// </summary>
2019-10-25 11:34:49 +08:00
protected float DistanceSpacing { get ; private set ; }
2019-10-10 20:55:48 +08:00
2019-10-11 14:41:01 +08:00
/// <summary>
2019-12-10 15:00:09 +08:00
/// The maximum number of distance snapping intervals allowed.
2019-10-11 14:41:01 +08:00
/// </summary>
2019-12-10 15:00:09 +08:00
protected int MaxIntervals { get ; private set ; }
2019-10-10 20:55:48 +08:00
2019-11-06 13:55:05 +08:00
/// <summary>
2019-12-10 15:00:09 +08:00
/// 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.
2019-11-06 13:55:05 +08:00
/// </summary>
2019-12-10 15:00:09 +08:00
protected readonly Vector2 StartPosition ;
2019-11-06 13:55:05 +08:00
2019-10-11 14:41:21 +08:00
/// <summary>
2019-12-10 15:00:09 +08:00
/// The snapping time at <see cref="StartPosition"/>.
2019-10-11 14:41:21 +08:00
/// </summary>
2019-12-10 15:00:09 +08:00
protected readonly double StartTime ;
2019-10-10 20:55:48 +08:00
[Resolved]
2019-10-23 15:58:56 +08:00
protected OsuColour Colours { get ; private set ; }
2019-10-10 20:55:48 +08:00
[Resolved]
2020-05-20 16:48:43 +08:00
protected IPositionSnapProvider SnapProvider { get ; private set ; }
2019-10-10 20:55:48 +08:00
[Resolved]
2019-12-27 18:46:33 +08:00
private EditorBeatmap beatmap { get ; set ; }
2019-10-10 20:55:48 +08:00
2019-10-25 11:34:49 +08:00
[Resolved]
2019-10-25 16:25:46 +08:00
private BindableBeatDivisor beatDivisor { get ; set ; }
2019-10-25 11:34:49 +08:00
2020-02-24 19:52:15 +08:00
private readonly LayoutValue gridCache = new LayoutValue ( Invalidation . RequiredParentSizeToFit ) ;
2019-12-10 15:00:09 +08:00
private readonly double? endTime ;
2019-10-10 20:55:48 +08:00
2019-12-10 15:00:09 +08:00
/// <summary>
/// Creates a new <see cref="DistanceSnapGrid"/>.
/// </summary>
/// <param name="startPosition">The position at which the grid should start. The first tick is located one distance spacing length away from this point.</param>
/// <param name="startTime">The snapping time at <see cref="StartPosition"/>.</param>
/// <param name="endTime">The time at which the snapping grid should end. If null, the grid will continue until the bounds of the screen are exceeded.</param>
protected DistanceSnapGrid ( Vector2 startPosition , double startTime , double? endTime = null )
2019-10-10 20:55:48 +08:00
{
2019-12-10 15:00:09 +08:00
this . endTime = endTime ;
StartPosition = startPosition ;
StartTime = startTime ;
2019-11-06 13:55:05 +08:00
2019-10-10 20:55:48 +08:00
RelativeSizeAxes = Axes . Both ;
2020-02-24 19:52:15 +08:00
AddLayout ( gridCache ) ;
2019-10-10 20:55:48 +08:00
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
beatDivisor . BindValueChanged ( _ = > updateSpacing ( ) , true ) ;
}
private void updateSpacing ( )
{
2019-10-25 16:25:46 +08:00
DistanceSpacing = SnapProvider . GetBeatSnapDistanceAt ( StartTime ) ;
2019-11-06 13:55:05 +08:00
2019-12-10 15:00:09 +08:00
if ( endTime = = null )
2019-11-06 13:55:05 +08:00
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
2019-12-10 15:00:09 +08:00
double maxDuration = endTime . Value - StartTime + 1 ;
2019-11-06 13:55:05 +08:00
MaxIntervals = ( int ) ( maxDuration / SnapProvider . DistanceToDuration ( StartTime , DistanceSpacing ) ) ;
}
2019-10-10 20:55:48 +08:00
gridCache . Invalidate ( ) ;
}
protected override void Update ( )
{
base . Update ( ) ;
if ( ! gridCache . IsValid )
{
ClearInternal ( ) ;
2020-01-28 10:59:21 +08:00
CreateContent ( ) ;
2019-10-10 20:55:48 +08:00
gridCache . Validate ( ) ;
}
}
/// <summary>
2019-10-11 14:41:01 +08:00
/// Creates the content which visualises the grid ticks.
2019-10-10 20:55:48 +08:00
/// </summary>
2020-01-28 10:59:21 +08:00
protected abstract void CreateContent ( ) ;
2019-10-10 20:55:48 +08:00
/// <summary>
2019-10-11 14:46:11 +08:00
/// Snaps a position to this grid.
2019-10-10 20:55:48 +08:00
/// </summary>
2019-10-17 14:32:02 +08:00
/// <param name="position">The original position in coordinate space local to this <see cref="DistanceSnapGrid"/>.</param>
2019-10-25 11:34:49 +08:00
/// <returns>A tuple containing the snapped position in coordinate space local to this <see cref="DistanceSnapGrid"/> and the respective time value.</returns>
public abstract ( Vector2 position , double time ) GetSnappedPosition ( Vector2 position ) ;
2019-10-24 17:09:20 +08:00
2019-10-10 20:55:48 +08:00
/// <summary>
/// Retrieves the applicable colour for a beat index.
/// </summary>
2020-01-28 10:59:21 +08:00
/// <param name="placementIndex">The 0-based beat index from the point of placement.</param>
2019-10-10 20:55:48 +08:00
/// <returns>The applicable colour.</returns>
2020-01-28 10:59:21 +08:00
protected ColourInfo GetColourForIndexFromPlacement ( int placementIndex )
2019-10-10 20:55:48 +08:00
{
2020-01-28 10:59:21 +08:00
var timingPoint = beatmap . ControlPointInfo . TimingPointAt ( StartTime ) ;
2020-01-29 14:16:48 +08:00
var beatLength = timingPoint . BeatLength / beatDivisor . Value ;
var beatIndex = ( int ) Math . Round ( ( StartTime - timingPoint . Time ) / beatLength ) ;
2020-01-28 10:59:21 +08:00
var colour = BindableBeatDivisor . GetColourFor ( BindableBeatDivisor . GetDivisorForBeatIndex ( beatIndex + placementIndex + 1 , beatDivisor . Value ) , Colours ) ;
2019-10-10 20:55:48 +08:00
2020-01-28 10:59:21 +08:00
int repeatIndex = placementIndex / beatDivisor . Value ;
2019-10-10 20:55:48 +08:00
return colour . MultiplyAlpha ( 0.5f / ( repeatIndex + 1 ) ) ;
}
}
}