// 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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineTickDisplay : TimelinePart { [Resolved] private EditorBeatmap beatmap { get; set; } [Resolved] private Bindable working { get; set; } [Resolved] private BindableBeatDivisor beatDivisor { get; set; } [Resolved] private OsuColour colours { get; set; } public TimelineTickDisplay() { RelativeSizeAxes = Axes.Both; } [Resolved(canBeNull: true)] private Timeline timeline { get; set; } protected override void Update() { base.Update(); int drawableIndex = 0; double minVisibleTime = double.MinValue; double maxVisibleTime = double.MaxValue; if (timeline != null) { minVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X / DrawWidth * Content.RelativeChildSize.X; maxVisibleTime = ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X / DrawWidth * Content.RelativeChildSize.X; } for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) { var point = beatmap.ControlPointInfo.TimingPoints[i]; var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length; int beat = 0; for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value) { if (t >= minVisibleTime && t <= maxVisibleTime) { var indexInBeat = beat % beatDivisor.Value; if (indexInBeat == 0) { var downbeatPoint = getNextUsablePoint(); downbeatPoint.X = (float)t; downbeatPoint.Colour = BindableBeatDivisor.GetColourFor(1, colours); downbeatPoint.Anchor = Anchor.TopLeft; downbeatPoint.Origin = Anchor.TopCentre; downbeatPoint.Height = 1; } else { var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); var colour = BindableBeatDivisor.GetColourFor(divisor, colours); var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f; var topPoint = getNextUsablePoint(); topPoint.X = (float)t; topPoint.Colour = colour; topPoint.Height = height; topPoint.Anchor = Anchor.TopLeft; topPoint.Origin = Anchor.TopCentre; var bottomPoint = getNextUsablePoint(); bottomPoint.X = (float)t; bottomPoint.Colour = colour; bottomPoint.Anchor = Anchor.BottomLeft; bottomPoint.Origin = Anchor.BottomCentre; bottomPoint.Height = height; } } beat++; } } int usedDrawables = drawableIndex; // save a few drawables beyond the currently used for edge cases. while (drawableIndex < Math.Min(usedDrawables + 16, Count)) Children[drawableIndex++].Hide(); // expire any excess while (drawableIndex < Count) Children[drawableIndex++].Expire(); Drawable getNextUsablePoint() { PointVisualisation point; if (drawableIndex >= Count) Add(point = new PointVisualisation()); else point = Children[drawableIndex++]; point.Show(); return point; } } } }