using Microsoft.VisualBasic.CompilerServices; using Newtonsoft.Json.Linq; using RhythmBase.Components; using RhythmBase.Events; using RhythmBase.Exceptions; using RhythmBase.Utils; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.RegularExpressions; namespace RhythmBase.Extensions { /// /// Extensions /// [StandardModule] public static partial class Extensions { private static (float start, float end) GetRange(OrderedEventCollection e, Index index) { (float, float) GetRange; try { IBaseEvent firstEvent = e.First(); IBaseEvent lastEvent = e.Last(); GetRange = index.IsFromEnd ? (lastEvent.Beat._calculator!.BarBeatToBeatOnly((uint)(lastEvent.Beat.BarBeat.bar - index.Value), 1f), lastEvent.Beat._calculator.BarBeatToBeatOnly((uint)(lastEvent.Beat.BarBeat.bar - index.Value + 1), 1f)) : (firstEvent.Beat._calculator!.BarBeatToBeatOnly((uint)index.Value, 1f), firstEvent.Beat._calculator.BarBeatToBeatOnly((uint)(index.Value + 1), 1f)); } catch { throw new ArgumentOutOfRangeException(nameof(index)); } return GetRange; } private static (float start, float end) GetRange(OrderedEventCollection e, Range range) { (float start, float end) GetRange; try { IBaseEvent firstEvent = e.First(); IBaseEvent lastEvent = e.Last(); GetRange = ((range.Start.IsFromEnd ? lastEvent.Beat._calculator!.BarBeatToBeatOnly((uint)(((ulong)lastEvent.Beat.BarBeat.bar) - (ulong)((long)range.Start.Value)), 1f) : firstEvent.Beat._calculator!.BarBeatToBeatOnly((uint)Math.Max(range.Start.Value, 1), 1f), range.End.IsFromEnd ? lastEvent.Beat._calculator!.BarBeatToBeatOnly((uint)(((ulong)lastEvent.Beat.BarBeat.bar) - (ulong)((long)range.End.Value) + 1UL), 1f) : firstEvent.Beat._calculator!.BarBeatToBeatOnly((uint)(range.End.Value + 1), 1f))); } catch { throw new ArgumentOutOfRangeException(nameof(range)); } return GetRange; } /// /// Null or equal. /// /// one item. /// another item. /// /// /// When neither item is empty,
Returns true only if both are equal
/// when one of the two is empty,
Returns true.
/// when both are empty,
Returns false.
///
///
public static bool NullableEquals(this float? e, float? obj) => ((e != null && obj != null) && e.Value == obj.Value) || (e == null && obj == null); /// /// /// /// public static bool IsNullOrEmpty([NotNullWhen(false)] this string? e) => e == null || e.Length == 0; /// /// Make strings follow the Upper Camel Case. /// /// The result. public static string ToUpperCamelCase(this string e) { char[] S = [.. e]; S[0] = (S[0].ToString().ToUpper()[0]); return string.Join("", [new string(S)]); } /// /// Make a specific key of a JObject follow the Upper Camel Case. /// internal static void ToUpperCamelCase(this JObject e, string key) { JToken token = e[key] ?? throw new NullReferenceException(); e.Remove(key); e[key.ToUpperCamelCase()] = token; } /// /// Make strings follow the Lower Camel Case. /// /// The result. public static string ToLowerCamelCase(this string e) { char[] S = [.. e]; S[0] = (S[0].ToString().ToLower()[0]); return string.Join("", [new string(S)]); } /// /// Make a specific key of a JObject follow the Lower Camel Case. /// internal static void ToLowerCamelCase(this JObject e, string key) { JToken token = e[key] ?? throw new NullReferenceException(); e.Remove(key); e[key.ToLowerCamelCase()] = token; } /// /// Convert color format from RGBA to ARGB /// public static int RgbaToArgb(this int Rgba) => (Rgba >> 8 & 16777215) | (Rgba << 24 & -16777216); /// /// Convert color format from ARGB to RGBA /// public static int ArgbToRgba(this int Argb) => (Argb >> 24 & 255) | (Argb << 8 & -256); /// /// Calculate the fraction of equal to the nearest floating point number. /// /// /// 2.236f.FixFraction(4) == 2.25f /// float.Pi.FixFraction(5) == 3.2f /// float.E.Fixfraction(2) == 2.5f /// /// /// /// The float number. /// Indicate what fraction this is. /// public static float FixFraction(this float beat, uint splitBase) => (float)(Math.Round((double)(beat * splitBase)) / splitBase); /// /// Calculate the fraction of equal to the nearest floating point number. /// public static RDBeat FixFraction(this RDBeat beat, uint splitBase) => new(beat.BeatOnly.FixFraction(splitBase)); /// /// Converting enumeration constants to in-game colors。 /// /// Collection /// The in-game color. public static RDColor ToColor(this Bookmark.BookmarkColors e) => e switch { Bookmark.BookmarkColors.Blue => RDColor.FromRgba(11, 125, 206), Bookmark.BookmarkColors.Red => RDColor.FromRgba(219, 41, 41), Bookmark.BookmarkColors.Yellow => RDColor.FromRgba(212, 212, 51), Bookmark.BookmarkColors.Green => RDColor.FromRgba(54, 215, 54), _ => throw new NotSupportedException(), }; /// /// Add a range of events. /// /// Collection /// public static void AddRange(this OrderedEventCollection e, IEnumerable items) where TEvent : IBaseEvent { foreach (TEvent item in items) e.Add(item); } /// /// Filters a sequence of events based on a predicate. /// /// Collection /// A function to test each event for a condition. public static IEnumerable Where(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => ((IEnumerable)e.eventsBeatOrder .SelectMany(i => i.Value)).Where(predicate); /// /// Filters a sequence of events located at a time. /// /// Collection /// Specified beat. public static IEnumerable Where(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent { IEnumerable Where = []; if (e.eventsBeatOrder.TryGetValue(beat, out TypedEventCollection? value)) Where = value.Cast().AsEnumerable(); return Where; } /// /// Filters a sequence of events located at a range of time. /// /// Collection /// Specified start beat. /// Specified end beat. public static IEnumerable Where(this OrderedEventCollection e, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.eventsBeatOrder .TakeWhile(i => i.Key < endBeat) .SkipWhile(i => i.Key < startBeat) .SelectMany(i => i.Value.OfType()); /// /// Filters a sequence of events located at a bar. /// /// Collection /// Specified bar. public static IEnumerable Where(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent { var (start, end) = GetRange(e, bar); return e.eventsBeatOrder .TakeWhile(i => i.Key.BeatOnly < end) .SkipWhile(i => i.Key.BeatOnly < start) .SelectMany(i => i.Value.OfType()); } /// /// Filters a sequence of events located at a range of beat. /// /// Collection /// Specified beat range. /// public static IEnumerable Where(this OrderedEventCollection e, RDRange range) where TEvent : IBaseEvent => (IEnumerable)e.eventsBeatOrder .TakeWhile(i => range.End == null || i.Key < range.End) .SkipWhile(i => range.Start != null && i.Key < range.Start) .SelectMany(i => i.Value); /// /// Filters a sequence of events located at a range of bar. /// /// Collection /// Specified bar range. public static IEnumerable Where(this OrderedEventCollection e, Range bars) where TEvent : IBaseEvent { var (start, end) = GetRange(e, bars); return e.eventsBeatOrder .TakeWhile(i => i.Key.BeatOnly < end) .SkipWhile(i => i.Key.BeatOnly < start) .SelectMany(i => i.Value.OfType()); } /// /// Filters a sequence of events based on a predicate in specified beat. /// /// Collection /// A function to test each event for a condition. /// Specified beat. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.Where(beat).Where(predicate); /// /// Filters a sequence of events based on a predicate in specified range of beat. /// /// Collection /// A function to test each event for a condition. /// Specified start beat. /// Specified end beat. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.Where(startBeat, endBeat).Where(predicate); /// /// Filters a sequence of events based on a predicate in specified range of beat. /// /// Collection /// A function to test each event for a condition. /// Specified beat range. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDRange range) where TEvent : IBaseEvent => e.Where(range).Where(predicate); /// /// Filters a sequence of events based on a predicate in specified bar. /// /// Collection /// A function to test each event for a condition. /// Specified bar. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.Where(bar).Where(predicate); /// /// Filters a sequence of events based on a predicate in specified range of bar. /// /// Collection /// A function to test each event for a condition. /// Specified bar range. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, Range bars) where TEvent : IBaseEvent => e.Where(bars).Where(predicate); /// /// Filters a sequence of events in specified event type. /// /// /// Collection public static IEnumerable Where(this OrderedEventCollection e) where TEvent : IBaseEvent { EventType[] enums = EventTypeUtils.ToEnums(); return e.eventsBeatOrder .Where(i => i.Value._types .Any(enums.Contains)) .SelectMany(i => i.Value).OfType(); } /// /// Filters a sequence of events located at a beat in specified event type. /// /// Specified event type. /// Collection /// Specified beat. public static IEnumerable Where(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent { TypedEventCollection value; return (e.eventsBeatOrder.TryGetValue(beat, out value!) ? value.OfType() : []) ?? []; } /// /// Filters a sequence of events located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// Specified start beat. /// Specified end beat. public static IEnumerable Where(this OrderedEventCollection e, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.eventsBeatOrder .TakeWhile(i => i.Key < endBeat) .SkipWhile(i => i.Key < startBeat) .SelectMany(i => i.Value.OfType()); /// /// Filters a sequence of events located at a bar in specified event type. /// /// Specified event type. /// Collection /// Specified bar. public static IEnumerable Where(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent { (float, float) rg = GetRange(e, bar); return e.eventsBeatOrder .TakeWhile((KeyValuePair> i) => i.Key.BeatOnly < rg.Item2) .SkipWhile((KeyValuePair> i) => i.Key.BeatOnly < rg.Item1) .SelectMany(i => i.Value.OfType()); } /// /// Filters a sequence of events located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// Specified beat range. public static IEnumerable Where(this OrderedEventCollection e, RDRange range) where TEvent : IBaseEvent => e.eventsBeatOrder .TakeWhile(i => range.End == null || i.Key < range.End) .SkipWhile(i => range.Start != null && i.Key < range.Start) .SelectMany(i => i.Value.OfType()); /// /// Filters a sequence of events located at a range of bar in specified event type. /// /// Specified event type. /// Collection /// Specified bar range. public static IEnumerable Where(this OrderedEventCollection e, Range bars) where TEvent : IBaseEvent { (float start, float end) = GetRange(e, bars); return e.eventsBeatOrder .TakeWhile(i => i.Key.BeatOnly < end) .SkipWhile(i => i.Key.BeatOnly < start) .SelectMany(i => i.Value.OfType()); } /// /// Filters a sequence of events based on a predicate located at a range of bar in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. public static IEnumerable Where(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().Where(predicate); /// /// Filters a sequence of events based on a predicate located at a beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified beat. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.Where(beat).Where(predicate); /// /// Filters a sequence of events based on a predicate located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified start beat. /// Specified end beat. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.Where(startBeat, endBeat).Where(predicate); /// /// Filters a sequence of events based on a predicate located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified beat range. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, RDRange range) where TEvent : IBaseEvent => e.Where(range).Where(predicate); /// /// Filters a sequence of events based on a predicate located at a bar in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified bar. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.Where(bar).Where(predicate); /// /// Filters a sequence of events based on a predicate located at a range of bar in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified bar range. public static IEnumerable Where(this OrderedEventCollection e, Func predicate, Range bars) where TEvent : IBaseEvent => e.Where(bars).Where(predicate); /// /// Remove a sequence of events based on a predicate. /// /// Collection /// A function to test each event for a condition. public static int RemoveAll(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate))); /// /// Remove a sequence of events located at a time. /// /// Collection /// Specified beat. public static int RemoveAll(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(beat))); /// /// Remove a sequence of events located at a range of time. /// /// Collection /// Specified start beat. /// Specified end beat. public static int RemoveAll(this OrderedEventCollection e, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(startBeat, endBeat))); /// /// Remove a sequence of events located at a bar. /// /// Collection /// Specified bar. public static int RemoveAll(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(bar))); /// /// Remove a sequence of events located at a range of beat. /// /// Collection /// Specified beat range. /// public static int RemoveAll(this OrderedEventCollection e, RDRange range) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(range))); /// /// Remove a sequence of events located at a range of bar. /// /// Collection /// Specified bar range. public static int RemoveAll(this OrderedEventCollection e, Range bars) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(bars))); /// /// Remove a sequence of events based on a predicate in specified beat. /// /// Collection /// A function to test each event for a condition. /// Specified beat. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, beat))); /// /// Remove a sequence of events based on a predicate in specified range of beat. /// /// Collection /// A function to test each event for a condition. /// Specified start beat. /// Specified end beat. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, startBeat, endBeat))); /// /// Remove a sequence of events based on a predicate in specified range of beat. /// /// Collection /// A function to test each event for a condition. /// Specified beat range. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDRange range) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, range))); /// /// Remove a sequence of events based on a predicate in specified bar. /// /// Collection /// A function to test each event for a condition. /// Specified bar. public static int RemoveAll(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, bar))); /// /// Remove a sequence of events based on a predicate in specified range of bar. /// /// Collection /// A function to test each event for a condition. /// Specified bar range. public static int RemoveAll(this OrderedEventCollection e, Func predicate, Range bars) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, bars))); /// /// Remove a sequence of events in specified event type. /// /// /// Collection public static int RemoveAll(this OrderedEventCollection e) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where())); /// /// Remove a sequence of events located at a beat in specified event type. /// /// Specified event type. /// Collection /// Specified beat. public static int RemoveAll(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(beat))); /// /// Remove a sequence of events located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// Specified start beat. /// Specified end beat. public static int RemoveAll(this OrderedEventCollection e, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(startBeat, endBeat))); /// /// Filters a sequence of events located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// Specified beat range. public static int RemoveAll(this OrderedEventCollection e, RDRange range) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(range))); /// /// Remove a sequence of events located at a bar in specified event type. /// /// Specified event type. /// Collection /// Specified bar. public static int RemoveAll(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(bar))); /// /// Remove a sequence of events located at a range of bar in specified event type. /// /// Specified event type. /// Collection /// Specified bar range. public static int RemoveAll(this OrderedEventCollection e, Range bars) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(bars))); /// /// Remove a sequence of events based on a predicate located at a range of bar in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. public static int RemoveAll(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate))); /// /// Remove a sequence of events based on a predicate located at a beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified beat. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, beat))); /// /// Remove a sequence of events based on a predicate located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified start beat. /// Specified end beat. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDBeat startBeat, RDBeat endBeat) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, startBeat, endBeat))); /// /// Remove a sequence of events based on a predicate located at a range of beat in specified event type. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// Specified beat range. public static int RemoveAll(this OrderedEventCollection e, Func predicate, RDRange range) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, range))); /// /// Filters a sequence of events based on a predicate located at a bar in specified event type. /// /// Specified event type. /// A function to test each event for a condition. /// Specified bar. /// Collection public static int RemoveAll(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, bar))); /// /// Filters a sequence of events based on a predicate located at a range of bar in specified event type. /// /// Specified event type. /// A function to test each event for a condition. /// Specified bar range. /// Collection public static int RemoveAll(this OrderedEventCollection e, Func predicate, Range bars) where TEvent : IBaseEvent => e.RemoveRange(new List(e.Where(predicate, bars))); /// /// Returns the first element of the collection. /// /// Collection public static TEvent First(this OrderedEventCollection e) where TEvent : IBaseEvent => (TEvent)e.eventsBeatOrder.First().Value.First(); /// /// Returns the first element of the collection that satisfies a specified condition. /// /// A function to test each event for a condition. /// Collection public static TEvent First(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.ConcatAll().First(predicate); /// /// Returns the first element of the collection in specified event type. /// /// Collection public static TEvent First(this OrderedEventCollection e) where TEvent : IBaseEvent => e.Where().First(); /// /// Returns the first element of the collection that satisfies a specified condition in specified event type. /// /// A function to test each event for a condition. /// Collection public static TEvent First(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().First(predicate); /// /// Returns the first event in the collection or the default value if the collection is empty. /// /// The type of event in the collection. /// The ordered event collection. /// The first event in the collection or the default value if the collection is empty. public static TEvent? FirstOrDefault(this OrderedEventCollection e) where TEvent : IBaseEvent { TypedEventCollection value = e.eventsBeatOrder.FirstOrDefault().Value; return (TEvent?)(value?.FirstOrDefault()); } /// /// Returns the first element of the collection, or if collection contains no elements. /// /// The default value to return if contains no elements. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, TEvent defaultValue) where TEvent : IBaseEvent => e.ConcatAll().FirstOrDefault(defaultValue); /// /// Returns the first element of the collection that satisfies a specified condition, or if matches no elements. /// /// A function to test each event for a condition. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.ConcatAll().OfType().FirstOrDefault(predicate); /// /// Returns the first element of the collection that satisfies a specified condition, or if matches no elements. /// /// A function to test each event for a condition. /// The default value to return if matches no elements. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, Func predicate, TEvent defaultValue) where TEvent : IBaseEvent => e.ConcatAll().FirstOrDefault(predicate, defaultValue); /// /// Returns the first element of the collection in specified event type, or if matches no elements. /// /// Specified event type. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e) where TEvent : IBaseEvent => e.Where().FirstOrDefault(); /// /// Returns the first element of the collection in specified event type, or if matches no elements. /// /// Specified event type. /// The default value to return if matches no elements. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, TEvent defaultValue) where TEvent : IBaseEvent => e.Where().FirstOrDefault(defaultValue); /// /// Returns the first element of the collection that satisfies a specified condition in specified event type, or if matches no elements. /// /// Specified event type. /// A function to test each event for a condition. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().FirstOrDefault(predicate); /// /// Returns the first element of the collection that satisfies a specified condition in specified event type, or if matches no elements. /// /// Specified event type. /// A function to test each event for a condition. /// The default value to return if matches no elements. /// Collection public static TEvent? FirstOrDefault(this OrderedEventCollection e, Func predicate, TEvent defaultValue) where TEvent : IBaseEvent => e.Where().FirstOrDefault(predicate, defaultValue); /// /// Returns the last element of the collection. /// public static TEvent Last(this OrderedEventCollection e) where TEvent : IBaseEvent => (TEvent)e.eventsBeatOrder.Last().Value.Last(); /// /// Returns the last element of the collection that satisfies a specified condition. /// /// A function to test each event for a condition. /// Collection public static TEvent Last(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.ConcatAll().Last(predicate); /// /// Returns the last element of the collection in specified event type. /// /// Collection public static TEvent Last(this OrderedEventCollection e) where TEvent : IBaseEvent => e.Where().Last(); /// /// Returns the last element of the collection that satisfies a specified condition in specified event type. /// /// A function to test each event for a condition. /// Collection public static TEvent Last(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().Last(predicate); /// /// Returns the last element of the collection, or if collection contains no elements. /// public static TEvent? LastOrDefault(this OrderedEventCollection e) where TEvent : IBaseEvent { IEnumerable value = e.eventsBeatOrder.LastOrDefault().Value.AsEnumerable(); return (TEvent?)(value?.LastOrDefault()); } /// /// Returns the last element of the collection, or if collection contains no elements. /// /// Collection /// The default value to return if contains no elements. public static TEvent LastOrDefault(this OrderedEventCollection e, TEvent defaultValue) where TEvent : IBaseEvent => e.ConcatAll().LastOrDefault(defaultValue); /// /// Returns the last element of the collection that satisfies a specified condition, or if matches no elements. /// /// Collection /// A function to test each event for a condition. public static TEvent? LastOrDefault(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.ConcatAll().LastOrDefault(predicate); /// /// Returns the last element of the collection that satisfies a specified condition, or if matches no elements. /// /// Collection /// A function to test each event for a condition. /// The default value to return if matches no elements. public static TEvent LastOrDefault(this OrderedEventCollection e, Func predicate, TEvent defaultValue) where TEvent : IBaseEvent => e.ConcatAll().LastOrDefault(predicate, defaultValue); /// /// Returns the last element of the collection in specified event type, or if matches no elements. /// /// Specified event type. /// Collection public static TEvent? LastOrDefault(this OrderedEventCollection e) where TEvent : IBaseEvent => e.Where().LastOrDefault(); /// /// Returns the last element of the collection in specified event type, or if matches no elements. /// /// Specified event type. /// Collection /// The default value to return if matches no elements. public static TEvent LastOrDefault(this OrderedEventCollection e, TEvent defaultValue) where TEvent : IBaseEvent => e.Where().LastOrDefault(defaultValue); /// /// Returns the last element of the collection that satisfies a specified condition in specified event type, or if matches no elements. /// /// Specified event type. /// Collection /// A function to test each event for a condition. public static TEvent? LastOrDefault(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().LastOrDefault(predicate); /// /// Returns the last element of the collection that satisfies a specified condition in specified event type, or if matches no elements. /// /// Specified event type. /// Collection /// A function to test each event for a condition. /// The default value to return if matches no elements. public static TEvent LastOrDefault(this OrderedEventCollection e, Func predicate, TEvent defaultValue) where TEvent : IBaseEvent => e.Where().LastOrDefault(predicate, defaultValue); /// /// Returns events from a collection as long as it less than or equal to . /// /// Collection /// Specified beat. public static IEnumerable TakeWhile(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent { foreach (TEvent item in e.Where()) if (item.Beat <= beat) yield return item; else break; } /// /// Returns events from a collection as long as it less than or equal to . /// /// Collection /// Specified bar. public static IEnumerable TakeWhile(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent { TEvent firstEvent = e.First(); TEvent lastEvent = e.Last(); return e.TakeWhile(checked(bar.IsFromEnd ? lastEvent.Beat._calculator!.BeatOf((uint)(lastEvent.Beat.BarBeat.bar - (uint)bar.Value + 1U), 1f) : firstEvent.Beat._calculator!.BeatOf((uint)(bar.Value + 1), 1f))); } /// /// Returns events from a collection as long as a specified condition is true. /// /// Collection /// A function to test each event for a condition. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => (IEnumerable)e.eventsBeatOrder .SelectMany(i => i.Value) .TakeWhile((Func)(object)predicate); /// /// Returns events from a collection as long as a specified condition is true and also less than or equal to . /// /// A function to test each event for a condition. /// Collection /// Specified beat. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.TakeWhile(beat).TakeWhile(predicate); /// /// Returns events from a collection as long as a specified condition is true and also less than or equal to . /// /// A function to test each event for a condition. /// Collection /// Specified bar. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.TakeWhile(bar).TakeWhile(predicate); /// /// Returns events from a collection in specified event type as long as it less than or equal to . /// /// Specified event type. /// Collection /// Specified beat. public static IEnumerable TakeWhile(this OrderedEventCollection e, RDBeat beat) where TEvent : IBaseEvent { foreach (TEvent item in e.Where()) if (item.Beat <= beat) yield return item; else break; } /// /// Returns events from a collection in specified event type as long as it less than or in . /// /// Specified event type. /// Collection /// Specified bar. public static IEnumerable TakeWhile(this OrderedEventCollection e, Index bar) where TEvent : IBaseEvent { IBaseEvent firstEvent = e.First(); IBaseEvent lastEvent = e.Last(); return e.TakeWhile(checked(bar.IsFromEnd ? lastEvent.Beat._calculator!.BeatOf((uint)(lastEvent.Beat.BarBeat.bar - (ulong)bar.Value + 1U), 1f) : firstEvent.Beat._calculator!.BeatOf((uint)(bar.Value + 1), 1f))); } /// /// Returns events from a collection in specified event type as long as a specified condition is true. /// /// Specified event type. /// Collection /// Specified condition. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate) where TEvent : IBaseEvent => e.Where().TakeWhile(predicate); /// /// Returns events from a collection in specified event type as long as a specified condition is true and its beat less than or equal to . /// /// Specified event type. /// Collection /// Specified condition. /// Specified beat. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate, RDBeat beat) where TEvent : IBaseEvent => e.TakeWhile(beat).TakeWhile(predicate); /// /// Returns events from a collection in specified event type as long as a specified condition is true and its beat less than or equal to . /// /// Specified event type. /// Collection /// Specified condition. /// Specified beat. public static IEnumerable TakeWhile(this OrderedEventCollection e, Func predicate, Index bar) where TEvent : IBaseEvent => e.TakeWhile(bar).TakeWhile(predicate); /// /// Remove a range of events. /// /// Collection /// A range of events. /// The number of events successfully removed. public static int RemoveRange(this OrderedEventCollection e, IEnumerable items) where TEvent : IBaseEvent { int count = 0; foreach (var item in items) count += e.Remove(item) ? 1 : 0; return count; } /// /// Remove a range of events. /// /// Collection /// A range of events. /// The number of events successfully removed. public static int RemoveRange(this OrderedEventCollection e, IEnumerable items) where TEvent : IBaseEvent { int count = 0; foreach (var item in items) count += e.Remove(item) ? 1 : 0; return count; } /// /// Get all the hit of the level. /// public static IEnumerable GetHitBeat(this RDLevel e) { List L = []; foreach (RowEventCollection item in e.Rows) { L.AddRange(item.HitBeats()); } return L; } /// /// Get all the hit event of the level. /// public static IEnumerable GetHitEvents(this RDLevel e) => e.Where(IsHitable); /// /// Get all events with the specified tag. /// /// Collection /// Tag name. /// Indicates whether the label is strictly matched. /// If , determine If it contains the specified tag. /// If , determine If it Is equal to the specified tag. /// An , categorized by tag name. public static IEnumerable> GetTaggedEvents(this OrderedEventCollection e, string name, bool strict) where TEvent : IBaseEvent { if (name.IsNullOrEmpty()) return []; if (strict) return e .Where(i => i.Tag == name).GroupBy(i => i.Tag); else return e .Where(i => i.Tag?.Contains(name) ?? false).GroupBy(i => i.Tag); } /// /// Get all classic beat events and their variants. /// private static IEnumerable ClassicBeats(this RowEventCollection e) => e.Where((BaseBeat i) => i.Type == EventType.AddClassicBeat | i.Type == EventType.AddFreeTimeBeat | i.Type == EventType.PulseFreeTimeBeat); /// /// Get all oneshot beat events. /// private static IEnumerable OneshotBeats(this RowEventCollection e) => e.Where((BaseBeat i) => i.Type == EventType.AddOneshotBeat); /// /// Get all hits of all beats. /// public static IEnumerable HitBeats(this RowEventCollection e) { RowType rowType = e.RowType; IEnumerable HitBeats; if (rowType != RowType.Classic) { if (rowType != RowType.Oneshot) throw new RhythmBaseException("How?"); HitBeats = e.OneshotBeats().SelectMany((BaseBeat i) => i.HitTimes()); } else HitBeats = e.ClassicBeats().SelectMany((BaseBeat i) => i.HitTimes()); return HitBeats; } /// /// Get an instance of the beat associated with the level. /// /// RDLevel /// Total number of 1-based beats. public static RDBeat BeatOf(this RDLevel e, float beatOnly) => e.Calculator.BeatOf(beatOnly); /// /// Get an instance of the beat associated with the level. /// /// RDLevel /// The 1-based bar. /// The 1-based beat of the bar. public static RDBeat BeatOf(this RDLevel e, uint bar, float beat) => e.Calculator.BeatOf(bar, beat); /// /// Get an instance of the beat associated with the level. /// /// RDLevel /// Total time span of the beat. public static RDBeat BeatOf(this RDLevel e, TimeSpan timeSpan) => e.Calculator.BeatOf(timeSpan); /// /// Get the row beat status /// /// /// /// /// public static SortedDictionary GetRowBeatStatus(this RowEventCollection e) { SortedDictionary L = []; RowType rowType = e.RowType; switch (rowType) { case RowType.Classic: int[] value = new int[7]; L.Add(0f, value); foreach (IBaseEvent beat in e) switch (beat.Type) { case EventType.AddClassicBeat: AddClassicBeat trueBeat = (AddClassicBeat)beat; int i = 0; do { int[] statusArray = L[beat.Beat.BeatOnly] ?? new int[7]; int[] array = statusArray; int num = i; ref int ptr = ref array[num]; array[num] = ptr + 1; L[beat.Beat.BeatOnly] = statusArray; i++; } while (i <= 6); break; default: throw new NotImplementedException(); } break; case RowType.Oneshot: throw new NotImplementedException(); default: throw new RhythmBaseException("How"); } return L; } /// /// Get all beats of the row. /// public static IEnumerable Beats(this RowEventCollection e) { RowType rowType = e.RowType; IEnumerable Beats; if (rowType != RowType.Classic) { if (rowType != RowType.Oneshot) throw new RhythmBaseException("How?"); Beats = e.OneshotBeats(); } else Beats = e.ClassicBeats(); return Beats; } /// /// Returns all previous events of the same type, including events of the same beat but executed before itself. /// public static IEnumerable Before(this TEvent e) where TEvent : IBaseEvent => e.Beat.BaseLevel?.Where(e.Beat.BaseLevel.DefaultBeat, e.Beat) ?? []; /// /// Returns all previous events of the specified type, including events of the same beat but executed before itself. /// public static IEnumerable Before(this IBaseEvent e) where TEvent : IBaseEvent => e.Beat.BaseLevel?.Where(e.Beat.BaseLevel.DefaultBeat, e.Beat) ?? []; /// /// Returns all events of the same type that follow, including events of the same beat but executed after itself. /// public static IEnumerable After(this TEvent e) where TEvent : IBaseEvent => e.Beat.BaseLevel?.Where(i => i.Beat > e.Beat) ?? []; /// /// Returns all events of the specified type that follow, including events of the same beat but executed after itself. /// public static IEnumerable After(this IBaseEvent e) where TEvent : IBaseEvent => e.Beat.BaseLevel?.Where(i => i.Beat > e.Beat) ?? []; /// /// Returns the previous event of the same type, including events of the same beat but executed before itself. /// public static TEvent Front(this TEvent e) where TEvent : IBaseEvent => e.Before().Last(); /// /// Returns the previous event of the specified type, including events of the same beat but executed before itself. /// public static TEvent Front(this IBaseEvent e) where TEvent : IBaseEvent => e.Before().Last(); /// /// Returns the previous event of the same type, including events of the same beat but executed before itself. Returns if it does not exist. /// public static TEvent? FrontOrDefault(this TEvent e) where TEvent : IBaseEvent => e.Before().LastOrDefault(); /// /// Returns the previous event of the specified type, including events of the same beat but executed before itself. Returns if it does not exist. /// public static TEvent? FrontOrDefault(this IBaseEvent e) where TEvent : IBaseEvent => e.Before().LastOrDefault(); /// /// Returns the next event of the same type, including events of the same beat but executed after itself. /// public static TEvent Next(this TEvent e) where TEvent : IBaseEvent => e.After().First(); /// /// Returns the next event of the specified type, including events of the same beat but executed after itself. /// public static TEvent Next(this IBaseEvent e) where TEvent : IBaseEvent => e.After().First(); /// /// Returns the next event of the same type, including events of the same beat but executed after itself. Returns if it does not exist. /// public static TEvent? NextOrDefault(this TEvent e) where TEvent : IBaseEvent => e.After().FirstOrDefault(); /// /// Returns the next event of the specified type, including events of the same beat but executed after itself. Returns if it does not exist. /// public static TEvent? NextOrDefault(this IBaseEvent e) where TEvent : IBaseEvent => e.After().FirstOrDefault(); //? /// /// Shallow copy. /// public static TEvent MemberwiseClone(this TEvent e) where TEvent : IBaseEvent, new() => (TEvent)e.MClone(); internal static object MClone(this object e) { if (e != null) { Type type = e.GetType(); object copy = Activator.CreateInstance(type)!; PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo p in properties) { if (p.CanWrite) { p.SetValue(copy, p.GetValue(e)); } } return copy; } throw new NullReferenceException(); } #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 public enum Wavetype { BoomAndRush, Spring, Spike, SpikeHuge, Ball, Single } public enum ShockWaveType { size, distortion, duration } public enum Particle { HitExplosion, leveleventexplosion } #pragma warning restore CS1591 // 缺少对公共可见类型或成员的 XML 注释 } }