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.
2019-11-06 13:55:05 +08:00
using JetBrains.Annotations ;
2019-10-10 20:55:48 +08:00
using osu.Framework.Allocation ;
using osu.Framework.Caching ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
using osu.Framework.Graphics.Containers ;
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 osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.Objects.Types ;
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-10-25 11:34:49 +08:00
/// The snapping time at <see cref="CentrePosition"/>.
2019-10-11 14:41:01 +08:00
/// </summary>
2019-10-25 11:34:49 +08:00
protected double StartTime { get ; private set ; }
2019-10-10 20:55:48 +08:00
2019-11-06 13:55:05 +08:00
/// <summary>
/// The maximum number of distance snapping intervals allowed.
/// </summary>
protected int MaxIntervals { get ; private set ; }
2019-10-11 14:41:21 +08:00
/// <summary>
/// The position which the grid is centred on.
/// The first beat snapping tick is located at <see cref="CentrePosition"/> + <see cref="DistanceSpacing"/> in the desired direction.
/// </summary>
protected readonly Vector2 CentrePosition ;
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]
2019-10-25 16:25:46 +08:00
protected IDistanceSnapProvider SnapProvider { get ; private set ; }
2019-10-10 20:55:48 +08:00
[Resolved]
2019-10-25 16:25:46 +08:00
private IEditorBeatmap 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
2019-10-10 20:55:48 +08:00
private readonly Cached gridCache = new Cached ( ) ;
private readonly HitObject hitObject ;
2019-11-06 13:55:05 +08:00
private readonly HitObject nextHitObject ;
2019-10-10 20:55:48 +08:00
2019-11-06 13:55:05 +08:00
protected DistanceSnapGrid ( HitObject hitObject , [ CanBeNull ] HitObject nextHitObject , Vector2 centrePosition )
2019-10-10 20:55:48 +08:00
{
this . hitObject = hitObject ;
2019-11-06 13:55:05 +08:00
this . nextHitObject = nextHitObject ;
2019-10-10 20:55:48 +08:00
2019-10-25 11:34:49 +08:00
CentrePosition = centrePosition ;
2019-11-06 13:55:05 +08:00
2019-10-10 20:55:48 +08:00
RelativeSizeAxes = Axes . Both ;
}
[BackgroundDependencyLoader]
private void load ( )
{
2019-10-25 11:34:49 +08:00
StartTime = ( hitObject as IHasEndTime ) ? . EndTime ? ? hitObject . StartTime ;
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
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 ) ) ;
}
2019-10-10 20:55:48 +08:00
gridCache . Invalidate ( ) ;
}
public override bool Invalidate ( Invalidation invalidation = Invalidation . All , Drawable source = null , bool shallPropagate = true )
{
if ( ( invalidation & Invalidation . RequiredParentSizeToFit ) > 0 )
gridCache . Invalidate ( ) ;
return base . Invalidate ( invalidation , source , shallPropagate ) ;
}
protected override void Update ( )
{
base . Update ( ) ;
if ( ! gridCache . IsValid )
{
ClearInternal ( ) ;
2019-10-11 14:41:40 +08:00
CreateContent ( CentrePosition ) ;
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>
2019-10-11 14:41:40 +08:00
protected abstract void CreateContent ( Vector2 centrePosition ) ;
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>
/// <param name="index">The 0-based beat index.</param>
/// <returns>The applicable colour.</returns>
protected ColourInfo GetColourForBeatIndex ( int index )
{
2019-10-11 13:01:36 +08:00
int beat = ( index + 1 ) % beatDivisor . Value ;
2019-10-23 15:58:56 +08:00
ColourInfo colour = Colours . Gray5 ;
2019-10-10 20:55:48 +08:00
2019-10-11 13:01:36 +08:00
for ( int i = 0 ; i < BindableBeatDivisor . VALID_DIVISORS . Length ; i + + )
2019-10-10 20:55:48 +08:00
{
2019-10-11 13:01:36 +08:00
int divisor = BindableBeatDivisor . VALID_DIVISORS [ i ] ;
2019-10-10 20:55:48 +08:00
2019-10-11 13:01:36 +08:00
if ( ( beat * divisor ) % beatDivisor . Value = = 0 )
{
2019-10-23 15:58:56 +08:00
colour = BindableBeatDivisor . GetColourFor ( divisor , Colours ) ;
2019-10-11 13:01:36 +08:00
break ;
2019-10-10 20:55:48 +08:00
}
}
int repeatIndex = index / beatDivisor . Value ;
return colour . MultiplyAlpha ( 0.5f / ( repeatIndex + 1 ) ) ;
}
}
}