Add ultimate xr

This commit is contained in:
2024-08-06 21:58:35 +02:00
parent 864033bf10
commit 7165bacd9d
3952 changed files with 2162037 additions and 35 deletions

View File

@@ -0,0 +1,130 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CollectionExt.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace UltimateXR.Extensions.System.Collections
{
/// <summary>
/// <see cref="IReadOnlyCollection{T}" /> and <see cref="ICollection{T}" /> extensions.
/// </summary>
public static class CollectionExt
{
#region Public Methods
/// <summary>
/// Throws an exception if a given index is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
/// </summary>
/// <param name="self">Collection</param>
/// <param name="index">Index to check if it is out of bounds</param>
/// <param name="paramName">Optional argument name</param>
/// <typeparam name="T">Element type</typeparam>
/// <exception cref="IndexOutOfRangeException">When index is out of range and no parameter name was specified</exception>
/// <exception cref="ArgumentOutOfRangeException">When index is out of range and a parameter name was specified</exception>
public static void ThrowIfInvalidIndex<T>(this IReadOnlyCollection<T> self, int index, string paramName = null)
{
if (index >= 0 && index < self.Count)
{
return;
}
if (string.IsNullOrWhiteSpace(paramName))
{
throw new IndexOutOfRangeException($"Index[{index}] out of range for collection of {typeof(T).Name}");
}
throw new ArgumentOutOfRangeException(paramName, index, $"Index[{index}] out of range for collection of {typeof(T).Name}");
}
/// <summary>
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
/// </summary>
/// <param name="self">Collection</param>
/// <param name="index1">Index 1 to check if it is out of bounds</param>
/// <param name="index2">Index 2 to check if it is out of bounds</param>
/// <typeparam name="T">Element type</typeparam>
/// <seealso cref="ThrowIfInvalidIndex{T}" />
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, int index1, int index2)
{
self.ThrowIfInvalidIndex(index1);
self.ThrowIfInvalidIndex(index2);
}
/// <summary>
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
/// </summary>
/// <param name="self">Collection</param>
/// <param name="index1">Index 1 to check if it is out of bounds</param>
/// <param name="index2">Index 2 to check if it is out of bounds</param>
/// <param name="index3">Index 3 to check if it is out of bounds</param>
/// <typeparam name="T">Element type</typeparam>
/// <seealso cref="ThrowIfInvalidIndex{T}" />
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, int index1, int index2, int index3)
{
self.ThrowIfInvalidIndex(index1);
self.ThrowIfInvalidIndex(index2);
self.ThrowIfInvalidIndex(index3);
}
/// <summary>
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
/// </summary>
/// <param name="self">Collection</param>
/// <param name="indexes">Indexes to check</param>
/// <typeparam name="T">Element type</typeparam>
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, params int[] indexes)
{
foreach (int index in indexes)
{
self.ThrowIfInvalidIndex(index);
}
}
/// <summary>
/// Splits a string using <see cref="string.Split(char[])" /> and adds the result to the collection.
/// </summary>
/// <param name="self">Collection to add the split result to</param>
/// <param name="toSplit">String to split</param>
/// <param name="separator">
/// Separator to use for splitting. This will be used to call <see cref="string.Split(char[])" />
/// on <paramref name="toSplit" />
/// </param>
/// <returns>The result collection</returns>
public static ICollection<string> SplitAddRange(this ICollection<string> self, string toSplit, char separator)
{
self.ThrowIfNull(nameof(self));
if (string.IsNullOrWhiteSpace(toSplit))
{
return self;
}
foreach (string s in toSplit.Split(separator))
{
self.Add(s.Trim());
}
return self;
}
/// <summary>
/// Splits a string using <see cref="string.Split(char[])" /> and sets the result in the collection.
/// </summary>
/// <param name="self">Collection to set the split result in</param>
/// <param name="toSplit">String to split</param>
/// <param name="separator">
/// Separator to use for splitting. This will be used to call <see cref="string.Split(char[])" />
/// on <paramref name="toSplit" />
/// </param>
/// <returns>The result collection</returns>
public static ICollection<string> SplitSetRange(this ICollection<string> self, string toSplit, char separator)
{
self.ThrowIfNull(nameof(self));
self.Clear();
return self.SplitAddRange(toSplit, separator);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fbb5811e6ee84042ad95c9df2447cfd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DictionaryExt.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
namespace UltimateXR.Extensions.System.Collections
{
/// <summary>
/// <see cref="IDictionary{TKey,TValue}" /> and <see cref="Dictionary{TKey,TValue}" /> extensions.
/// </summary>
public static class DictionaryExt
{
#region Public Methods
/// <summary>
/// Adds all elements in another dictionary to the dictionary.
/// </summary>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TValue">Value type</typeparam>
/// <param name="self">Destination dictionary</param>
/// <param name="other">Source dictionary</param>
/// <param name="overrideExistingKeys">Determines if duplicated keys must be overriden with other's values</param>
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> self, IDictionary<TKey, TValue> other, bool overrideExistingKeys = false)
{
foreach (var kv in other)
{
if (!self.ContainsKey(kv.Key))
{
self.Add(kv.Key, kv.Value);
}
else if (overrideExistingKeys)
{
self[kv.Key] = kv.Value;
}
}
}
/// <summary>
/// Gets a given value defined by a key in a dictionary. If the key is not found, it is added and the value is given
/// the default value.
/// </summary>
/// <param name="self">Dictionary</param>
/// <param name="key">Key to look for</param>
/// <typeparam name="TKey">Key type</typeparam>
/// <typeparam name="TValue">Value type</typeparam>
/// <returns>
/// Value in the dictionary. If the key doesn't exist it will be added and the return value will be the default
/// value
/// </returns>
public static TValue GetOrAddValue<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key)
where TValue : new()
{
if (!self.TryGetValue(key, out TValue value))
{
value = new TValue();
self.Add(key, value);
}
return value;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7d0cdca585a749e69c9d098ba98c006d
timeCreated: 1620807425

View File

@@ -0,0 +1,318 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EnumerableExt.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UltimateXR.Core;
using UltimateXR.Core.Settings;
using UnityEngine;
using Random = UnityEngine.Random;
namespace UltimateXR.Extensions.System.Collections
{
/// <summary>
/// <see cref="IEnumerable{T}" /> extensions.
/// </summary>
public static class EnumerableExt
{
#region Public Methods
/// <summary>
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
/// </summary>
/// <param name="enumerableA">The first collection to compare</param>
/// <param name="enumerableB">The second collection to compare</param>
/// <returns>True if the collections are equal; otherwise, false</returns>
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB)
{
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b));
}
/// <summary>
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
/// Values are compared using a floating point precision threshold used by
/// <see cref="ObjectExt.ValuesEqual(object,object,float)" />.
/// </summary>
/// <param name="enumerableA">The first collection to compare</param>
/// <param name="enumerableB">The second collection to compare</param>
/// <param name="precisionThreshold">
/// The precision threshold for float comparisons in types supported by
/// <see cref="ObjectExt.ValuesEqual(object,object,float)" />.
/// </param>
/// <returns>True if the collections are equal; otherwise, false</returns>
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, float precisionThreshold)
{
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b, precisionThreshold));
}
/// <summary>
/// Returns a random element from the collection.
/// </summary>
/// <param name="list">Collection to get the random element from</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <returns>Random element from the collection</returns>
/// <remarks>
/// Uses Unity's random number generator (<see cref="UnityEngine.Random.Range(int,int)" />).
/// </remarks>
public static TIn RandomElement<TIn>(this IEnumerable<TIn> list)
{
return list.Any() ? list.ElementAt(Random.Range(0, list.Count())) : default(TIn);
}
/// <summary>
/// Applies an <see cref="Action" /> on all elements in a collection.
/// </summary>
/// <param name="list">Elements to apply the action on</param>
/// <param name="action">Action to apply</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <exception cref="ArgumentException">Any of the parameters was null</exception>
public static void ForEach<TIn>(this IEnumerable<TIn> list, Action<TIn> action)
{
if (list == null)
{
throw new ArgumentException("Argument cannot be null.", nameof(list));
}
if (action == null)
{
throw new ArgumentException("Argument cannot be null.", nameof(action));
}
foreach (TIn value in list)
{
action(value);
}
}
/// <summary>
/// Asynchronously applies a function on all elements in a collection.
/// </summary>
/// <param name="list">Elements to apply the function on</param>
/// <param name="function">Function to apply</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <returns>An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
public static Task ForEachAsync<TIn>(this IEnumerable<TIn> list, Func<TIn, Task> function)
{
return Task.WhenAll(list.Select(function));
}
/// <summary>
/// Asynchronously applies a function to all elements in a collection.
/// </summary>
/// <param name="list">Elements to apply the function on</param>
/// <param name="function">Function to apply</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <typeparam name="TOut">Function return type</typeparam>
/// <returns>An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
public static Task<TOut[]> ForEachAsync<TIn, TOut>(this IEnumerable<TIn> list, Func<TIn, Task<TOut>> function)
{
return Task.WhenAll(list.Select(function));
}
/// <summary>
/// Asynchronously applies an action on all elements in a collection.
/// </summary>
/// <param name="list">Elements to apply the action on</param>
/// <param name="action">Action to apply</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <returns>Task wrapping the Task.WhenAll applying the action on all elements in a collection</returns>
public static Task ForEachThreaded<TIn>(this IEnumerable<TIn> list, Action<TIn> action)
{
void OnFaulted(Task runTask, int itemIndex)
{
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Warnings)
{
Debug.LogWarning($"{UxrConstants.CoreModule} ForEachThreaded::Item[{itemIndex}] faulted (see reason below):");
}
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
{
Debug.LogException(runTask.Exception);
}
}
return Task.WhenAll(list.Select((item, index) => Task.Run(() => action(item)).ContinueWith(runTask => OnFaulted(runTask, index), TaskContinuationOptions.OnlyOnFaulted)));
}
/// <summary>
/// Asynchronously applies a function on all elements in a collection.
/// </summary>
/// <param name="list">Elements to apply the function on</param>
/// <param name="function">Function to apply</param>
/// <typeparam name="TIn">Element type</typeparam>
/// <typeparam name="TOut">Function return type</typeparam>
/// <returns>Task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
public static Task<TOut[]> ForEachThreaded<TIn, TOut>(this IEnumerable<TIn> list, Func<TIn, TOut> function)
{
TOut OnFaulted(Task<TOut> t, int itemIndex)
{
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Warnings)
{
Debug.LogWarning($"{UxrConstants.CoreModule} ForEachThreaded::Item[{itemIndex}] faulted (see reason below):");
}
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
{
Debug.LogException(t.Exception);
}
return default;
}
return Task.WhenAll(list.Select((item, index) => Task.Run(() => function(item)).ContinueWith(t => OnFaulted(t, index), TaskContinuationOptions.OnlyOnFaulted)));
}
/// <summary>
/// Returns the maximal element of the given sequence, based on the given projection.
/// </summary>
/// <remarks>
/// This overload uses the default comparer for the projected type. This operator uses deferred execution. The results
/// are evaluated and cached on first use to returned sequence.
/// </remarks>
/// <typeparam name="TSource">Type of the source sequence</typeparam>
/// <typeparam name="TKey">Type of the projected element</typeparam>
/// <param name="source">Source sequence</param>
/// <param name="selector">Selector to use to pick the results to compare</param>
/// <returns>The maximal element, according to the projection.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="selector" /> is null</exception>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
return source.MaxBy(selector, null!);
}
/// <summary>
/// Returns the maximal element of the given sequence, based on the given projection and the specified comparer for
/// projected values.
/// </summary>
/// <remarks>
/// This operator uses deferred execution. The results are evaluated and cached on first use to returned sequence.
/// </remarks>
/// <typeparam name="TSource">Type of the source sequence</typeparam>
/// <typeparam name="TKey">Type of the projected element</typeparam>
/// <param name="source">Source sequence</param>
/// <param name="selector">Selector to use to pick the results to compare</param>
/// <param name="comparer">Comparer to use to compare projected values</param>
/// <returns>The maximal element, according to the projection.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source" />, <paramref name="selector" /> or <paramref name="comparer" /> is null
/// </exception>
/// <exception cref="T:System.Exception">A delegate callback throws an exception.</exception>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
source.ThrowIfNull(nameof(source));
selector.ThrowIfNull(nameof(selector));
TSource result = default;
TKey keyMax = default;
comparer ??= Comparer<TKey>.Default;
bool isFirst = true;
foreach (TSource s in source)
{
TKey key = selector(s);
if (isFirst)
{
result = s;
keyMax = key;
isFirst = false;
}
else if (comparer.Compare(key, keyMax) > 0)
{
result = s;
keyMax = key;
}
}
return result;
}
/// <summary>
/// Splits a list of strings using CamelCase.
/// </summary>
/// <param name="strings">List of strings</param>
/// <returns>List of strings with added spacing</returns>
public static IEnumerable<string> SplitCamelCase(this IEnumerable<string> strings)
{
foreach (string element in strings)
{
yield return element.SplitCamelCase();
}
}
#endregion
#region Private Methods
/// <summary>
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
/// </summary>
/// <param name="enumerableA">The first collection to compare</param>
/// <param name="enumerableB">The second collection to compare</param>
/// <param name="comparer">Comparison function</param>
/// <returns>True if the collections are equal; otherwise, false</returns>
private static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, Func<object, object, bool> comparer)
{
// If the collections are dictionaries, compare key-value pairs
if (enumerableA is IDictionary dictionaryA && enumerableB is IDictionary dictionaryB)
{
// Ensure both dictionaries have the same number of elements
if (dictionaryA.Count != dictionaryB.Count)
{
return false;
}
// Compare key-value pairs regardless of order
foreach (DictionaryEntry entryA in dictionaryA)
{
if (!dictionaryB.Contains(entryA.Key) || !comparer(entryA.Value, dictionaryB[entryA.Key]))
{
return false;
}
}
return true;
}
// If the collections are lists, do a quick test to check if they have different number of elements
if (enumerableA is IList listA && enumerableB is IList listB)
{
if (listA.Count != listB.Count)
{
return false;
}
}
// If the collections are HashSets, compare elements using SetEquals
if (enumerableA is HashSet<object> hashSetA && enumerableB is HashSet<object> hashSetB)
{
return hashSetA.SetEquals(hashSetB);
}
// For non-dictionary, non-HashSet collections, compare elements
IEnumerator enumeratorA = enumerableA.GetEnumerator();
IEnumerator enumeratorB = enumerableB.GetEnumerator();
while (enumeratorA.MoveNext())
{
if (!enumeratorB.MoveNext() || !comparer(enumeratorA.Current, enumeratorB.Current))
{
return false;
}
}
// Ensure both collections have the same number of elements
return !enumeratorB.MoveNext();
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aded6c93d8bb43a799907d1f47eca9a4
timeCreated: 1586946987

View File

@@ -0,0 +1,161 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ListExt.cs" company="VRMADA">
// Copyright (c) VRMADA, All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UltimateXR.Extensions.System.Collections
{
/// <summary>
/// <see cref="List{T}" /> extensions.
/// </summary>
public static class ListExt
{
#region Public Methods
/// <summary>
/// Gets the index of a given item in a list.
/// </summary>
/// <param name="self">List where to look for the item</param>
/// <param name="item">Item to look for</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>Element index or -1 if not found</returns>
/// <remarks>Equals() is used for comparison</remarks>
public static int IndexOf<T>(this IReadOnlyList<T> self, T item)
{
for (int i = 0; i < self.Count; ++i)
{
if (Equals(self[i], item))
{
return i;
}
}
return -1;
}
/// <summary>
/// Returns the list in reverse order.
/// </summary>
/// <param name="self">List in reverse order</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>List in reverse order</returns>
public static IEnumerable<T> Reversed<T>(this IList<T> self)
{
for (int i = self.Count - 1; i >= 0; --i)
{
yield return self[i];
}
}
/// <summary>
/// Returns a random element from the list.
/// </summary>
/// <param name="self">List to get the random element from</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>Random element from the list</returns>
/// <remarks>
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
/// </remarks>
public static T RandomElement<T>(this IReadOnlyList<T> self)
{
return self.Count > 0 ? self[Random.Range(0, self.Count)] : default;
}
/// <summary>
/// Generates a list of random indices without repetition in a given range.
/// </summary>
/// <param name="minInclusive">The minimum index value (inclusive)</param>
/// <param name="maxExclusive">The maximum value, which will not be included</param>
/// <param name="count"></param>
/// <returns></returns>
public static List<int> GenerateRandomIndicesWithoutRepetition(int minInclusive, int maxExclusive, int count)
{
List<int> newList = new List<int>();
for (int i = minInclusive; i < maxExclusive; ++i)
{
newList.Add(i);
}
return newList.RandomElementsWithoutRepetition(count);
}
/// <summary>
/// Returns a list with n random elements from a list without repetition.
/// </summary>
/// <param name="self">List to get the random elements from</param>
/// <param name="count">Number of random elements to get</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>
/// List with random elements. If <paramref name="count" /> is larger than the list, the result list will be as
/// long as the input list.
/// </returns>
/// <remarks>
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
/// </remarks>
public static List<T> RandomElementsWithoutRepetition<T>(this IReadOnlyList<T> self, int count)
{
List<T> candidates = new List<T>(self);
List<T> randomElements = new List<T>();
for (int i = 0; i < count && candidates.Count > 0; ++i)
{
int randomIndex = Random.Range(0, candidates.Count);
randomElements.Add(candidates[randomIndex]);
candidates.RemoveAt(randomIndex);
}
return randomElements;
}
/// <summary>
/// Returns a list with n random elements from a list without repetition. An additional list can be provided to exclude
/// elements from appearing in the results.
/// </summary>
/// <param name="self">List to get the random elements from</param>
/// <param name="listToExclude">List of elements to exclude from the results</param>
/// <param name="count">Number of random elements to get</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>
/// List with random elements. If <paramref name="count" /> is larger than the list, the result list will be as
/// long as the input list minus the excluded elements.
/// </returns>
/// <remarks>
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
/// </remarks>
public static List<T> RandomElementsWithoutRepetitionExcept<T>(this IReadOnlyList<T> self, IReadOnlyList<T> listToExclude, int count)
{
List<T> candidates = new List<T>(self.Where(p => !listToExclude.Any(p2 => Equals(p2, p))));
List<T> randomElements = new List<T>();
for (int i = 0; i < count && candidates.Count > 0; ++i)
{
int randomIndex = Random.Range(0, candidates.Count);
randomElements.Add(candidates[randomIndex]);
candidates.RemoveAt(randomIndex);
}
return randomElements;
}
/// <summary>
/// Returns a list with the input list elements shuffled.
/// </summary>
/// <param name="self">List to get the random elements from</param>
/// <typeparam name="T">Element type</typeparam>
/// <returns>List with shuffled elements.</returns>
/// <remarks>
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
/// </remarks>
public static List<T> Shuffled<T>(this IReadOnlyList<T> self)
{
return self.RandomElementsWithoutRepetition(self.Count);
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fe3095d1ef7a9c428f15cc9a190b4b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: