// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { /// /// Visualises connections between s. /// public class FollowPointRenderer : CompositeDrawable { /// /// All the s contained by this . /// internal IReadOnlyList Connections => connections; private readonly List connections = new List(); public override bool RemoveCompletedTransforms => false; /// /// Adds the s around a . /// This includes s leading into , and s exiting . /// /// The to add s for. public void AddFollowPoints(DrawableOsuHitObject hitObject) => addConnection(new FollowPointConnection(hitObject).With(g => g.StartTime.BindValueChanged(_ => onStartTimeChanged(g)))); /// /// Removes the s around a . /// This includes s leading into , and s exiting . /// /// The to remove s for. public void RemoveFollowPoints(DrawableOsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject)); /// /// Adds a to this . /// /// The to add. /// The index of in . private void addConnection(FollowPointConnection connection) { AddInternal(connection); // Groups are sorted by their start time when added such that the index can be used to post-process other surrounding connections int index = connections.AddInPlace(connection, Comparer.Create((g1, g2) => g1.StartTime.Value.CompareTo(g2.StartTime.Value))); if (index < connections.Count - 1) { // Update the connection's end point to the next connection's start point // h1 -> -> -> h2 // connection nextGroup FollowPointConnection nextConnection = connections[index + 1]; connection.End = nextConnection.Start; } else { // The end point may be non-null during re-ordering connection.End = null; } if (index > 0) { // Update the previous connection's end point to the current connection's start point // h1 -> -> -> h2 // prevGroup connection FollowPointConnection previousConnection = connections[index - 1]; previousConnection.End = connection.Start; } } /// /// Removes a from this . /// /// The to remove. /// Whether was removed. private void removeGroup(FollowPointConnection connection) { RemoveInternal(connection); int index = connections.IndexOf(connection); if (index > 0) { // Update the previous connection's end point to the next connection's start point // h1 -> -> -> h2 -> -> -> h3 // prevGroup connection nextGroup // The current connection's end point is used since there may not be a next connection FollowPointConnection previousConnection = connections[index - 1]; previousConnection.End = connection.End; } connections.Remove(connection); } private void onStartTimeChanged(FollowPointConnection connection) { // Naive but can be improved if performance becomes an issue removeGroup(connection); addConnection(connection); } } }