1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 02:32:55 +08:00

Replace interval collection with a more-specific immutable component

Covers all small changes into one commit:
 - Remove generics and use `double` type instead.
 - Make the component immutable and not enumerable for simplicity of it's worth.
 - Make the component more-specific (to time period tracking)
 - Apply small adjustments to the component
This commit is contained in:
Salman Ahmed 2020-04-29 04:43:49 +03:00
parent bf124a5cc6
commit 00918ecb6d
No known key found for this signature in database
GPG Key ID: ED81FD33FD9B58BC
2 changed files with 90 additions and 111 deletions

View File

@ -1,111 +0,0 @@
// 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.
using System;
using System.Collections;
using System.Collections.Generic;
using osu.Framework.Lists;
namespace osu.Game.Lists
{
/// <summary>
/// Represents a list of intervals that can be used for whether a specific value falls into one of them.
/// </summary>
/// <typeparam name="T">The type of interval values.</typeparam>
public class IntervalList<T> : IEnumerable<Interval<T>>
where T : struct, IConvertible
{
private static readonly IComparer<T> type_comparer = Comparer<T>.Default;
private readonly SortedList<Interval<T>> intervals = new SortedList<Interval<T>>((x, y) => type_comparer.Compare(x.Start, y.Start));
private int nearestIndex;
public Interval<T> this[int i]
{
get => intervals[i];
set => intervals[i] = value;
}
/// <summary>
/// Whether the provided value is in any interval added to this list.
/// </summary>
/// <param name="value">The value to check for.</param>
public bool IsInAnyInterval(T value)
{
if (intervals.Count == 0)
return false;
// Clamp the nearest index in case there were intervals
// removed from the list causing the index to go out of range.
nearestIndex = Math.Clamp(nearestIndex, 0, intervals.Count - 1);
if (type_comparer.Compare(value, this[nearestIndex].End) > 0)
{
while (type_comparer.Compare(value, this[nearestIndex].End) > 0 && nearestIndex < intervals.Count - 1)
nearestIndex++;
}
else
{
while (type_comparer.Compare(value, this[nearestIndex].Start) < 0 && nearestIndex > 0)
nearestIndex--;
}
var nearestInterval = this[nearestIndex];
return type_comparer.Compare(value, nearestInterval.Start) >= 0 &&
type_comparer.Compare(value, nearestInterval.End) <= 0;
}
/// <summary>
/// Adds a new interval to the list.
/// </summary>
/// <param name="start">The start value of the interval.</param>
/// <param name="end">The end value of the interval.</param>
public void Add(T start, T end) => Add(new Interval<T>(start, end));
/// <summary>
/// Adds a new interval to the list
/// </summary>
/// <param name="interval">The interval to add.</param>
public void Add(Interval<T> interval) => intervals.Add(interval);
/// <summary>
/// Removes an existing interval from the list.
/// </summary>
/// <param name="interval">The interval to remove.</param>
/// <returns>Whether the provided interval exists in the list and has been removed.</returns>
public bool Remove(Interval<T> interval) => intervals.Remove(interval);
/// <summary>
/// Removes all intervals from the list.
/// </summary>
public void Clear() => intervals.Clear();
public IEnumerator<Interval<T>> GetEnumerator() => intervals.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public readonly struct Interval<T>
where T : struct, IConvertible
{
/// <summary>
/// The start value of this interval.
/// </summary>
public readonly T Start;
/// <summary>
/// The end value of this interval.
/// </summary>
public readonly T End;
public Interval(T start, T end)
{
if (Comparer<T>.Default.Compare(start, end) >= 0)
throw new ArgumentException($"Invalid interval, {nameof(start)} must be less than {nameof(end)}", nameof(start));
Start = start;
End = end;
}
}
}

View File

@ -0,0 +1,90 @@
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Utils
{
/// <summary>
/// Represents a tracking component used for whether a
/// specific time falls into any of the provided periods.
/// </summary>
public class PeriodTracker
{
private readonly List<Period> periods = new List<Period>();
private int nearestIndex;
/// <summary>
/// The list of periods to add to the tracker for using the required check methods.
/// </summary>
public IEnumerable<Period> Periods
{
set
{
var sortedValue = value?.ToList();
sortedValue?.Sort();
if (sortedValue != null && periods.SequenceEqual(sortedValue))
return;
periods.Clear();
nearestIndex = 0;
if (value?.Any() != true)
return;
periods.AddRange(sortedValue);
}
}
/// <summary>
/// Whether the provided time is in any of the added periods.
/// </summary>
/// <param name="time">The time value to check for.</param>
public bool Contains(double time)
{
if (periods.Count == 0)
return false;
if (time > periods[nearestIndex].End)
{
while (time > periods[nearestIndex].End && nearestIndex < periods.Count - 1)
nearestIndex++;
}
else
{
while (time < periods[nearestIndex].Start && nearestIndex > 0)
nearestIndex--;
}
var nearest = periods[nearestIndex];
return time >= nearest.Start && time <= nearest.End;
}
}
public readonly struct Period : IComparable<Period>
{
/// <summary>
/// The start time of this period.
/// </summary>
public readonly double Start;
/// <summary>
/// The end time of this period.
/// </summary>
public readonly double End;
public Period(double start, double end)
{
if (start >= end)
throw new ArgumentException($"Invalid period provided, {nameof(start)} must be less than {nameof(end)}", nameof(start));
Start = start;
End = end;
}
public int CompareTo(Period other) => Start.CompareTo(other.Start);
}
}