2019-09-10 12:29:50 +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-05-18 04:08:49 +08:00
using System ;
2019-09-10 12:29:50 +08:00
using System.Collections.Generic ;
using System.Linq ;
2020-01-09 12:43:44 +08:00
using osu.Framework.Utils ;
2019-09-10 12:29:50 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Beatmaps.ControlPoints ;
namespace osu.Game.Rulesets.Objects
{
2019-09-25 05:03:55 +08:00
public class BarLineGenerator < TBarLine >
where TBarLine : class , IBarLine , new ( )
2019-09-10 12:29:50 +08:00
{
/// <summary>
/// The generated bar lines.
/// </summary>
2019-09-25 05:03:55 +08:00
public readonly List < TBarLine > BarLines = new List < TBarLine > ( ) ;
2019-09-10 12:29:50 +08:00
/// <summary>
/// Constructs and generates bar lines for provided beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to generate bar lines for.</param>
public BarLineGenerator ( IBeatmap beatmap )
{
if ( beatmap . HitObjects . Count = = 0 )
return ;
2022-11-21 13:34:35 +08:00
HitObject firstObject = beatmap . HitObjects . First ( ) ;
2019-09-10 12:29:50 +08:00
HitObject lastObject = beatmap . HitObjects . Last ( ) ;
2022-11-21 13:34:35 +08:00
double firstHitTime = firstObject . StartTime ;
2019-11-25 21:37:07 +08:00
double lastHitTime = 1 + lastObject . GetEndTime ( ) ;
2019-09-10 12:29:50 +08:00
var timingPoints = beatmap . ControlPointInfo . TimingPoints ;
if ( timingPoints . Count = = 0 )
return ;
for ( int i = 0 ; i < timingPoints . Count ; i + + )
{
TimingControlPoint currentTimingPoint = timingPoints [ i ] ;
2022-10-10 21:34:29 +08:00
EffectControlPoint currentEffectPoint = beatmap . ControlPointInfo . EffectPointAt ( currentTimingPoint . Time ) ;
2019-09-10 12:29:50 +08:00
int currentBeat = 0 ;
2022-11-21 13:34:35 +08:00
// Don't generate barlines before the hit object or t=0 (whichever is earliest). Some beatmaps use very unrealistic values here (although none are ranked).
// I'm not sure we ever want barlines to appear before the first hitobject, but let's keep some degree of compatibility for now.
// Of note, this will still differ from stable if the first timing control point is t<0 and is not near the first hitobject.
2022-11-23 17:12:03 +08:00
double generationStartTime = Math . Min ( 0 , firstHitTime ) ;
2022-11-21 13:34:35 +08:00
2022-10-10 20:00:26 +08:00
// Stop on the next timing point, or if there is no next timing point stop slightly past the last object
2022-10-08 02:34:31 +08:00
double endTime = i < timingPoints . Count - 1 ? timingPoints [ i + 1 ] . Time : lastHitTime + currentTimingPoint . BeatLength * currentTimingPoint . TimeSignature . Numerator ;
2019-09-10 12:29:50 +08:00
2022-01-23 00:27:27 +08:00
double barLength = currentTimingPoint . BeatLength * currentTimingPoint . TimeSignature . Numerator ;
2019-09-10 12:29:50 +08:00
2022-11-23 17:12:03 +08:00
double startTime ;
if ( currentTimingPoint . Time > generationStartTime )
{
startTime = currentTimingPoint . Time ;
}
else
{
// If the timing point starts before the minimum allowable time for bar lines,
// we still need to compute a start time for generation that is actually properly aligned with the timing point.
int barCount = ( int ) Math . Ceiling ( ( generationStartTime - currentTimingPoint . Time ) / barLength ) ;
startTime = currentTimingPoint . Time + barCount * barLength ;
}
2022-10-10 21:34:29 +08:00
if ( currentEffectPoint . OmitFirstBarLine )
2022-10-08 02:38:32 +08:00
{
startTime + = barLength ;
}
2022-10-10 15:39:40 +08:00
for ( double t = startTime ; Precision . AlmostBigger ( endTime , t ) ; t + = barLength , currentBeat + + )
2019-09-10 12:29:50 +08:00
{
2021-10-27 12:04:41 +08:00
double roundedTime = Math . Round ( t , MidpointRounding . AwayFromZero ) ;
2020-05-18 04:08:49 +08:00
// in the case of some bar lengths, rounding errors can cause t to be slightly less than
// the expected whole number value due to floating point inaccuracies.
// if this is the case, apply rounding.
if ( Precision . AlmostEquals ( t , roundedTime ) )
{
t = roundedTime ;
}
2019-09-25 05:03:55 +08:00
BarLines . Add ( new TBarLine
2019-09-10 12:29:50 +08:00
{
StartTime = t ,
2022-01-23 00:27:27 +08:00
Major = currentBeat % currentTimingPoint . TimeSignature . Numerator = = 0
2019-09-10 12:29:50 +08:00
} ) ;
}
}
}
}
}