diff --git a/GameKit/Dependencies/GameKit.Dependencies.asmdef b/GameKit/Dependencies/GameKit.Dependencies.asmdef index 438b416..751eaca 100644 --- a/GameKit/Dependencies/GameKit.Dependencies.asmdef +++ b/GameKit/Dependencies/GameKit.Dependencies.asmdef @@ -2,7 +2,10 @@ "name": "GameKit.Dependencies", "rootNamespace": "", "references": [ - "GUID:6055be8ebefd69e48b49212b09b47b2f" + "GUID:6055be8ebefd69e48b49212b09b47b2f", + "GUID:d8b63aba1907145bea998dd612889d6b", + "GUID:2665a8d13d1b3f18800f46e256720795", + "GUID:e0cd26848372d4e5c891c569017e11f1" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/GameKit/Dependencies/Utilities/Dictionaries.cs b/GameKit/Dependencies/Utilities/Dictionaries.cs index 2b1041c..d7d6507 100644 --- a/GameKit/Dependencies/Utilities/Dictionaries.cs +++ b/GameKit/Dependencies/Utilities/Dictionaries.cs @@ -4,12 +4,11 @@ namespace GameKit.Dependencies.Utilities { public static class DictionaryFN { - /// /// Uses a hacky way to TryGetValue on a dictionary when using IL2CPP and on mobile. /// This is to support older devices that don't properly handle IL2CPP builds. /// - public static bool TryGetValueIL2CPP(this IDictionary dict, TKey key, out TValue value) + public static bool TryGetValueIL2CPP(this IReadOnlyDictionary dict, TKey key, out TValue value) { #if ENABLE_IL2CPP && UNITY_IOS || UNITY_ANDROID if (dict.ContainsKey(key)) @@ -28,36 +27,53 @@ public static bool TryGetValueIL2CPP(this IDictionary - /// Returns values as an allocated list. + /// Returns values as a list. /// /// - public static List ValuesToList(this IDictionary dict) + public static List ValuesToList(this IReadOnlyDictionary dict, bool useCache) { - List result = new(dict.Count); - dict.ValuesToList(ref result); + List result = useCache ? CollectionCaches.RetrieveList() : new(dict.Count); + + //No need to clear the list since it's already clear. + dict.ValuesToList(ref result, clearLst: false); + return result; } /// - /// Clears a collection and populates it with this dictionaries values. + /// Adds values to a list. /// - public static void ValuesToList(this IDictionary dict, ref List result) + public static void ValuesToList(this IReadOnlyDictionary dict, ref List result, bool clearLst) { - result.Clear(); + if (clearLst) + result.Clear(); + foreach (TValue item in dict.Values) result.Add(item); } - + /// - /// Clears a collection and populates it with this dictionaries keys. + /// Returns keys as a list. /// - public static void KeysToList(this IDictionary dict, ref List result) + public static List KeysToList(this IReadOnlyDictionary dict, bool useCache) + { + List result = useCache ? CollectionCaches.RetrieveList() : new(dict.Count); + + //No need to clear the list since it's already clear. + dict.KeysToList(ref result, clearLst: false); + + return result; + } + + /// + /// Adds keys to a list. + /// + public static void KeysToList(this IReadOnlyDictionary dict, ref List result, bool clearLst) { result.Clear(); + foreach (TKey item in dict.Keys) result.Add(item); } - } - } \ No newline at end of file diff --git a/GameKit/Dependencies/Utilities/Floats.cs b/GameKit/Dependencies/Utilities/Floats.cs index 418b313..5b7a908 100644 --- a/GameKit/Dependencies/Utilities/Floats.cs +++ b/GameKit/Dependencies/Utilities/Floats.cs @@ -1,9 +1,9 @@ using System; +using System.Runtime.CompilerServices; using UnityEngine; namespace GameKit.Dependencies.Utilities { - public static class Floats { /// @@ -14,9 +14,10 @@ public static class Floats /// /// Sets a source float to value if equal to or greater than tolerance. /// - /// Float to check against tolerance. - /// Tolerance float must be equal to or greater than to change to value. - /// Value source is set to when breaking tolerance. + /// Float to check against tolerance. + /// Tolerance float must be equal to or greater than to change to value. + /// Value source is set to when breaking tolerance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float SetIfOverTolerance(this float source, float tolerance, float value) { if (source >= tolerance) @@ -28,9 +29,10 @@ public static float SetIfOverTolerance(this float source, float tolerance, float /// /// Sets a source float to value if equal to or less than tolerance. /// - /// Float to check against tolerance. - /// Tolerance float must be equal to or less than to change to value. - /// Value source is set to when breaking tolerance. + /// Float to check against tolerance. + /// Tolerance float must be equal to or less than to change to value. + /// Value source is set to when breaking tolerance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float SetIfUnderTolerance(this float source, float tolerance, float value) { if (source <= tolerance) @@ -43,38 +45,39 @@ public static float SetIfUnderTolerance(this float source, float tolerance, floa /// Returns how much time is left on an endTime. Returns -1 if no time is left. /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float TimeRemainingValue(this float endTime) { float remaining = endTime - Time.time; - //None remaining. + // None remaining. if (remaining < 0f) return -1f; - return (endTime - Time.time); + return endTime - Time.time; } /// /// Returns how much time is left on an endTime. Returns -1 if no time is left. /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int TimeRemainingValue(this float endTime, bool useFloor = true) { float remaining = endTime - Time.time; - //None remaining. + // None remaining. if (remaining < 0f) return -1; - float result = (endTime - Time.time); - return (useFloor) ? Mathf.FloorToInt(result) : Mathf.CeilToInt(result); + float result = endTime - Time.time; + return useFloor ? Mathf.FloorToInt(result) : Mathf.CeilToInt(result); } - /// /// Returns time remaining as a string using hh:mm:ss. /// - /// - /// Number of places to return. 1 is seconds, 2 is minutes, 3 is hours. If a placement does not exist it is replaced with 00. - /// True to return an empty string when value is 0 or less. + /// + /// Number of places to return. 1 is seconds, 2 is minutes, 3 is hours. If a placement does not exist it is replaced with 00. + /// True to return an empty string when value is 0 or less. /// public static string TimeRemainingText(this float value, byte segments, bool emptyOnZero = false) { @@ -91,13 +94,13 @@ public static string TimeRemainingText(this float value, byte segments, bool emp string timeText; if (segments == 1) { - seconds += (minutes * 60); - seconds += (hours * 3600); + seconds += minutes * 60; + seconds += hours * 3600; timeText = string.Format("{0:D2}", seconds); } else if (segments == 2) { - minutes += (hours * 60); + minutes += hours * 60; timeText = string.Format("{0:D2}:{1:D2}", minutes, seconds); } else @@ -109,17 +112,17 @@ public static string TimeRemainingText(this float value, byte segments, bool emp } /// - /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. /// - /// Inclusive minimum value. - /// Inclusive maximum value. + /// Inclusive minimum value. + /// Inclusive maximum value. /// public static float RandomInclusiveRange(float minimum, float maximum) { double min = Convert.ToDouble(minimum); double max = Convert.ToDouble(maximum); - double result = (_random.NextDouble() * (max - min)) + min; + double result = _random.NextDouble() * (max - min) + min; return Convert.ToSingle(result); } @@ -135,29 +138,31 @@ public static float Random01() /// /// Returns if a target float is within variance of the source float. /// - /// - /// - /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Near(this float a, float b, float tolerance = 0.01f) { - return (Mathf.Abs(a - b) <= tolerance); + return Mathf.Abs(a - b) <= tolerance; } /// /// Clamps a float and returns if the float required clamping. /// - /// - /// - /// - /// + /// + /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Clamp(float value, float min, float max, ref bool clamped) { - clamped = (value < min); + clamped = value < min; if (clamped) return min; - clamped = (value > min); + clamped = value > min; if (clamped) return max; @@ -168,65 +173,65 @@ public static float Clamp(float value, float min, float max, ref bool clamped) /// /// Returns a float after being adjusted by the specified variance. /// - /// - /// + /// + /// /// public static float Variance(this float source, float variance) { float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance); - return (source * pickedVariance); + return source * pickedVariance; } + /// /// Sets a float value to result after being adjusted by the specified variance. /// - /// - /// + /// + /// /// public static void Variance(this float source, float variance, ref float result) { float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance); - result = (source * pickedVariance); + result = source * pickedVariance; } /// /// Returns negative-one, zero, or postive-one of a value instead of just negative-one or positive-one. /// - /// Value to sign. + /// Value to sign. /// Precise sign. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float PreciseSign(float value) { if (value == 0f) return 0f; else - return (Mathf.Sign(value)); + return Mathf.Sign(value); } /// /// Returns if a float is within a range. /// - /// Value of float. - /// Minimum of range. - /// Maximum of range. + /// Value of float. + /// Minimum of range. + /// Maximum of range. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool InRange(this float source, float rangeMin, float rangeMax) { - return (source >= rangeMin && source <= rangeMax); + return source >= rangeMin && source <= rangeMax; } /// /// Randomly flips a float value. /// - /// + /// /// public static float RandomlyFlip(this float value) { if (Ints.RandomInclusiveRange(0, 1) == 0) return value; else - return (value *= -1f); + return value *= -1f; } - } - - } \ No newline at end of file diff --git a/GameKit/Dependencies/Utilities/Quaternions.cs b/GameKit/Dependencies/Utilities/Quaternions.cs index b67b47c..bfac65d 100644 --- a/GameKit/Dependencies/Utilities/Quaternions.cs +++ b/GameKit/Dependencies/Utilities/Quaternions.cs @@ -1,19 +1,31 @@ -using UnityEngine; +using Unity.Mathematics; +using UnityEngine; namespace GameKit.Dependencies.Utilities { - public static class Quaternions { - /// /// Returns how fast an object must rotate over duration to reach goal. /// - /// Quaternion to measure distance against. - /// How long it should take to move to goal. - /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// Quaternion to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// + public static float GetRate(this Quaternion a, Quaternion goal, float duration, out float angle, uint interval = 1, float tolerance = 0f) + { + angle = a.Angle(goal, true); + return angle / (duration * interval); + } + + /// + /// Returns how fast an object must rotate over duration to reach goal. + /// + /// Quaternion to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. /// - public static float GetRate(this Quaternion a, Quaternion goal, float duration, out float angle, uint interval = 1, float tolerance = 0f) + public static float GetRate(this quaternion a, quaternion goal, float duration, out float angle, uint interval = 1, float tolerance = 0f) { angle = a.Angle(goal, true); return angle / (duration * interval); @@ -22,43 +34,74 @@ public static float GetRate(this Quaternion a, Quaternion goal, float duration, /// /// Subtracts b quaternion from a. /// - public static Quaternion Subtract(this Quaternion a, Quaternion b) => (Quaternion.Inverse(b) * a); + public static Quaternion Subtract(this Quaternion a, Quaternion b) => Quaternion.Inverse(b) * a; + + /// + /// Subtracts b quaternion from a. + /// + public static quaternion Subtract(this quaternion a, quaternion b) => math.mul(math.inverse(b), a); + /// /// Adds quaternion b onto quaternion a. /// - public static Quaternion Add(this Quaternion a, Quaternion b) => (a * b); + public static Quaternion Add(this Quaternion a, Quaternion b) => a * b; + + /// + /// Adds quaternion b onto quaternion a. + /// + public static quaternion Add(this quaternion a, quaternion b) => math.mul(a, b); /// /// Returns if two quaternions match. /// - /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return a match even when not true due to error tolerance. + /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return a match even when not true due to error tolerance. /// public static bool Matches(this Quaternion a, Quaternion b, bool precise = false) { if (precise) - return (a.w == b.w && a.x == b.x && a.y == b.y && a.z == b.z); + return a.w == b.w && a.x == b.x && a.y == b.y && a.z == b.z; else - return (a == b); + return a == b; } /// /// Returns the angle between two quaterions. /// - /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return 0f due to error tolerance, even while there is a difference. + /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return 0f due to error tolerance, even while there is a difference. /// public static float Angle(this Quaternion a, Quaternion b, bool precise = false) { if (precise) { - //This is run Unitys implementation without the error tolerance. - float dot = (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w); - return (Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1f)) * 2f * 57.29578f); + // This is run Unitys implementation without the error tolerance. + float dot = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + return Mathf.Acos(Mathf.Min(Mathf.Abs(dot), 1f)) * 2f * 57.29578f; } else { return Quaternion.Angle(a, b); } } - } + + /// + /// Returns the angle between two quaterions. + /// + /// True to use a custom implementation with no error tolerance. False to use Unity's implementation which may return 0f due to error tolerance, even while there is a difference. + /// + public static float Angle(this quaternion a, quaternion b, bool precise = false) + { + if (!precise) + { + float d = math.dot(a.value, b.value); + float c = math.saturate(math.abs(d)); + return math.degrees(2f * math.acos(c)); + } + quaternion an = math.normalize(a); + quaternion bn = math.normalize(b); + float dn = math.dot(an.value, bn.value); + float cn = math.saturate(math.abs(dn)); + return math.degrees(2f * math.acos(cn)); + } + } } \ No newline at end of file diff --git a/GameKit/Dependencies/Utilities/Types/StripedRingQueue.cs b/GameKit/Dependencies/Utilities/Types/StripedRingQueue.cs new file mode 100644 index 0000000..eccd238 --- /dev/null +++ b/GameKit/Dependencies/Utilities/Types/StripedRingQueue.cs @@ -0,0 +1,370 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + /// + /// A striped ring-buffer that stores N independent queues addressed by i = 0..N-1. + /// Backing storage uses NativeList-based stripes with a fixed per-queue capacity. + /// Designed for job-friendly, per-index parallel work without cross contention. + /// + public struct StripedRingQueue : IDisposable + where T : unmanaged + { + /// + /// Backing storage for all stripes; length equals _queueCount * _capacity. + /// + [NativeDisableParallelForRestriction] private NativeList _data; + /// + /// Per-queue head (read index) + /// Advances on dequeue operations modulo _capacity. + /// + [NativeDisableParallelForRestriction] private NativeList _head; + /// + /// Per-queue item count. + /// Always clamped to the range [0.._capacity]. + /// + [NativeDisableParallelForRestriction] private NativeList _count; + /// + /// Compact metadata buffer stored in native memory: + /// [0] = fixed per-queue capacity, [1] = current queue count. + /// + [NativeDisableParallelForRestriction] private NativeArray _meta; + + /// + /// True when internal lists are allocated and usable. + /// + public bool IsCreated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _data.IsCreated && _head.IsCreated && _count.IsCreated && _meta.IsCreated; + } + /// + /// Fixed capacity per queue (ring size). + /// + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _meta[0]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _meta[0] = value; + } + /// + /// Number of independent queues (stripes). + /// + public int QueueCount + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _meta[1]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private set => _meta[1] = value; + } + /// + /// Total addressable storage, equal to QueueCount * Capacity. + /// + public int TotalCapacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => QueueCount * Capacity; + } + + /// + /// Indexer for direct access by queue index and raw ring index (0..Capacity-1). + /// Does not account for head/count; use GetCount/Clear/Enqueue/Dequeue for logical queue semantics. + /// + public T this[int queueIndex, int simulatedIndex] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int offset = GetRealOffset(queueIndex, simulatedIndex); + return _data[offset]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + int offset = GetRealOffset(queueIndex, simulatedIndex); + _data[offset] = value; + } + } + + /// + /// Constructs the striped ring with an initial queue count and per-queue capacity. + /// Allocates NativeList storage and zeroes head/count for all stripes. + /// + public StripedRingQueue(int initialQueueCount, int capacity, Allocator allocator) + { + if (initialQueueCount < 0) throw new ArgumentOutOfRangeException(nameof(initialQueueCount)); + if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity)); + + _meta = new NativeArray(2, allocator, NativeArrayOptions.UninitializedMemory); + _meta[0] = capacity; + _meta[1] = initialQueueCount; + + _data = new NativeList(math.max(1, initialQueueCount * capacity), allocator); + _head = new NativeList(math.max(1, initialQueueCount), allocator); + _count = new NativeList(math.max(1, initialQueueCount), allocator); + + _data.ResizeUninitialized(initialQueueCount * capacity); + _head.ResizeUninitialized(initialQueueCount); + _count.ResizeUninitialized(initialQueueCount); + + for (int i = 0; i < initialQueueCount; i++) + { + _head[i] = 0; + _count[i] = 0; + } + } + + /// + /// Disposes all internal lists synchronously. + /// Ensure that no jobs are accessing this storage when disposing. + /// + public void Dispose() + { + if (_data.IsCreated) _data.Dispose(); + if (_head.IsCreated) _head.Dispose(); + if (_count.IsCreated) _count.Dispose(); + if (_meta.IsCreated) _meta.Dispose(); + } + + /// + /// Schedules disposal of internal lists and returns a combined JobHandle. + /// Use this to free storage once dependent jobs have completed. + /// + public JobHandle Dispose(JobHandle inputDeps) + { + JobHandle h = inputDeps; + if (_data.IsCreated) h = _data.Dispose(h); + if (_head.IsCreated) h = _head.Dispose(h); + if (_count.IsCreated) h = _count.Dispose(h); + if (_meta.IsCreated) h = _meta.Dispose(h); + return h; + } + + /// + /// Adds a new empty queue (stripe) and returns its index. + /// Grows the data buffer by Capacity and zeroes the stripe's head/count. + /// + public int AddQueue() + { + int capacity = Capacity; + int queueCount = QueueCount; + + int newIndex = queueCount; + + int newDataLen = (newIndex + 1) * capacity; + if (_data.Capacity < newDataLen) _data.Capacity = newDataLen; + _data.ResizeUninitialized(newDataLen); + + _head.Add(0); + _count.Add(0); + + QueueCount = newIndex + 1; + return newIndex; + } + + /// + /// Removes the queue at the given index by swapping with the last stripe, + /// then shrinking storage by one stripe. Data swap is O(Capacity). + /// + public void RemoveQueueAtSwapBack(int index) + { + int queueCount = QueueCount; + int capacity = Capacity; + + int last = queueCount - 1; + if ((uint)index >= (uint)queueCount) + throw new ArgumentOutOfRangeException(nameof(index)); + if (last < 0) + return; + + if (index != last) + { + int a = index * capacity; + int b = last * capacity; + + for (int k = 0; k < capacity; k++) + { + (_data[a + k], _data[b + k]) = (_data[b + k], _data[a + k]); + } + } + + _data.ResizeUninitialized(_data.Length - capacity); + _head.RemoveAtSwapBack(index); + _count.RemoveAtSwapBack(index); + + QueueCount = last; + } + + /// + /// Returns the current number of items in queue i. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetCount(int i) => _count[i]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int BaseOffset(int i) => i * Capacity; + + /// + /// Returns the real index of the collection using a simulated index. + /// + /// + /// + /// True to allow an index be returned from an unused portion of the buffer so long as it is within bounds. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetRealOffset(int queueIndex, int simulatedIndex, bool allowUnusedBuffer = false) + { + int capacity = Capacity; + int queueCount = QueueCount; + + if ((uint)queueIndex >= (uint)queueCount) + throw new ArgumentOutOfRangeException(nameof(queueIndex)); + if ((uint)simulatedIndex >= (uint)capacity) + throw new ArgumentOutOfRangeException(nameof(simulatedIndex)); + + int count = _count[queueIndex]; + if (simulatedIndex >= count && !allowUnusedBuffer) + throw new ArgumentOutOfRangeException( + nameof(simulatedIndex), + $"Index {simulatedIndex} >= item count {count} in queue {queueIndex}"); + + int head = _head[queueIndex]; + int offset = (head + simulatedIndex) % capacity; + return BaseOffset(queueIndex) + offset; + } + + /// + /// Clears queue i by resetting head and count to zero. + /// Stored values remain but are considered invalid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear(int i) + { + _head[i] = 0; + _count[i] = 0; + } + + /// + /// Enqueues 'value' into queue i; overwrites the oldest item when full. + /// Main-thread only unless no concurrent access to the same i is guaranteed. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Enqueue(int i, in T value) + { + int capacity = Capacity; + + int h = _head[i]; + int c = _count[i]; + int baseOff = BaseOffset(i); + int tail = (h + c) % capacity; + + _data[baseOff + tail] = value; + + if (c < capacity) + { + _count[i] = c + 1; + } + else + { + _head[i] = (h + 1) % capacity; // overwrite oldest + } + } + + /// + /// Tries to dequeue one item from queue i into 'value'. + /// Returns false when the queue is empty. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(int i, out T value) + { + int c = _count[i]; + if (c == 0) + { + value = default; + return false; + } + + int capacity = Capacity; + + int h = _head[i]; + int baseOff = BaseOffset(i); + value = _data[baseOff + h]; + + _head[i] = (h + 1) % capacity; + _count[i] = c - 1; + return true; + } + + /// + /// Dequeues up to 'n' items from queue i and returns how many were removed. + /// The last removed item (if any) is written to 'last'. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int DequeueUpTo(int i, int n, out T last) + { + int c = _count[i]; + int drop = math.clamp(n, 0, c); + if (drop == 0) + { + last = default; + return 0; + } + + int capacity = Capacity; + + int h = _head[i]; + int baseOff = BaseOffset(i); + int lastIdx = (h + drop - 1) % capacity; + + last = _data[baseOff + lastIdx]; + + _head[i] = (h + drop) % capacity; + _count[i] = c - drop; + return drop; + } + + /// + /// Peeks the next entry from i queue. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek(int i) + { + int c = _count[i]; + if (c == 0) + throw new InvalidOperationException($"{nameof(StripedRingQueue)} of type {typeof(T).Name} is empty."); + + int h = _head[i]; + int baseOff = BaseOffset(i); + return _data[baseOff + h]; + } + + /// + /// Tries to peek the next entry from queue i. + /// + /// + /// Peeked entry. + /// True if an entry existed to peek. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek(int i, out T result) + { + int c = _count[i]; + if (c == 0) + { + result = default; + return false; + } + + int h = _head[i]; + int baseOff = BaseOffset(i); + result = _data[baseOff + h]; + return true; + } + } +} \ No newline at end of file diff --git a/GameKit/Dependencies/Utilities/Vectors.cs b/GameKit/Dependencies/Utilities/Vectors.cs index b20b87a..6102fc0 100644 --- a/GameKit/Dependencies/Utilities/Vectors.cs +++ b/GameKit/Dependencies/Utilities/Vectors.cs @@ -1,10 +1,10 @@ using System; using System.Runtime.CompilerServices; +using Unity.Mathematics; using UnityEngine; namespace GameKit.Dependencies.Utilities { - public static class Vectors { /// @@ -20,36 +20,57 @@ public static class Vectors /// /// Returns how fast an object must move over duration to reach goal. /// - /// Vector3 to measure distance against. - /// How long it should take to move to goal. - /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// Vector3 to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetRate(this Vector3 a, Vector3 b, float duration, out float distance, uint interval = 1) { distance = Vector3.Distance(a, b); return distance / (duration * interval); } + + /// + /// Returns how fast an object must move over duration to reach goal. + /// + /// Vector3 to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float GetRate(this float3 a, float3 b, float duration, out float distance, uint interval = 1) + { + distance = math.distance(a, b); + return distance / (duration * interval); + } + /// /// Adds a Vector2 X/Y onto a Vector3. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Add(this Vector3 v3, Vector2 v2) { - return (v3 + new Vector3(v2.x, v2.y, 0f)); + return v3 + new Vector3(v2.x, v2.y, 0f); } + /// /// Subtracts a Vector2 X/Y from a Vector3. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Subtract(this Vector3 v3, Vector2 v2) { - return (v3 - new Vector3(v2.x, v2.y, 0f)); + return v3 - new Vector3(v2.x, v2.y, 0f); } + /// /// Calculates the linear parameter t that produces the interpolant value within the range [a, b]. /// - /// - /// - /// + /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float InverseLerp(Vector3 a, Vector3 b, Vector3 value) { Vector3 ab = b - a; @@ -60,29 +81,32 @@ public static float InverseLerp(Vector3 a, Vector3 b, Vector3 value) /// /// Returns if the target Vector3 is within variance of the source Vector3. /// - /// Source vector. - /// Target vector. - /// How close the target vector must be to be considered close. + /// Source vector. + /// Target vector. + /// How close the target vector must be to be considered close. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Near(this Vector3 a, Vector3 b, float tolerance = 0.01f) { - return (Vector3.Distance(a, b) <= tolerance); + return Vector3.Distance(a, b) <= tolerance; } /// /// Returns if any values within a Vector3 are NaN. /// - /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNan(this Vector3 source) { - return (float.IsNaN(source.x) || float.IsNaN(source.y) || float.IsNaN(source.z)); + return float.IsNaN(source.x) || float.IsNaN(source.y) || float.IsNaN(source.z); } /// /// Lerp between three Vector3 values. /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Lerp3(Vector3 a, Vector3 b, Vector3 c, float percent) { Vector3 r0 = Vector3.Lerp(a, b, percent); @@ -93,9 +117,10 @@ public static Vector3 Lerp3(Vector3 a, Vector3 b, Vector3 c, float percent) /// /// Lerp between three Vector3 values. /// - /// - /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Lerp3(Vector3[] vectors, float percent) { if (vectors.Length < 3) @@ -110,9 +135,10 @@ public static Vector3 Lerp3(Vector3[] vectors, float percent) /// /// Multiplies a Vector3 by another. /// - /// - /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Multiply(this Vector3 src, Vector3 multiplier) { return new(src.x * multiplier.x, src.y * multiplier.y, src.z * multiplier.z); @@ -120,31 +146,31 @@ public static Vector3 Multiply(this Vector3 src, Vector3 multiplier) #region Fast. /* Fast checks are property of: - * Copyright (c) 2020 Maxim Munnig Schmidt - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ + * Copyright (c) 2020 Maxim Munnig Schmidt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ /// /// Fast Distance. /// - /// - /// + /// + /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FastDistance(Vector3 a, Vector3 b) @@ -154,35 +180,39 @@ public static float FastDistance(Vector3 a, Vector3 b) var distz = a.z - b.z; return (float)Math.Sqrt(distx * distx + disty * disty + distz * distz); } + /// /// Fast SqrMagnitude. /// - /// + /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float FastSqrMagnitude(Vector3 vector) { return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z; } + /// /// Fast Normalize. /// - /// + /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 FastNormalize(Vector3 value) { - float mag = (float)Math.Sqrt(value.x * value.x + value.y * value.y + value.z * value.z); //Magnitude(value); + float mag = (float)Math.Sqrt(value.x * value.x + value.y * value.y + value.z * value.z); // Magnitude(value); if (mag > FLOAT_EPSILON) { Vector3 result; result.x = value.x / mag; result.y = value.y / mag; result.z = value.z / mag; - return result;// value / mag; + return result; // value / mag; } else + { return VECTOR3_ZERO; + } } #endregion #endregion @@ -191,10 +221,11 @@ public static Vector3 FastNormalize(Vector3 value) /// /// Returns how fast an object must move over duration to reach goal. /// - /// Vector3 to measure distance against. - /// How long it should take to move to goal. - /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// Vector3 to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetRate(this Vector2 a, Vector2 goal, float duration, out float distance, uint interval = 1) { distance = Vector2.Distance(a, goal); @@ -204,11 +235,12 @@ public static float GetRate(this Vector2 a, Vector2 goal, float duration, out fl /// /// Lerp between three Vector2 values. /// - /// - /// - /// - /// + /// + /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Lerp3(Vector2 a, Vector2 b, Vector2 c, float percent) { Vector2 r0 = Vector2.Lerp(a, b, percent); @@ -219,9 +251,10 @@ public static Vector2 Lerp3(Vector2 a, Vector2 b, Vector2 c, float percent) /// /// Lerp between three Vector2 values. /// - /// - /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Lerp2(Vector2[] vectors, float percent) { if (vectors.Length < 3) @@ -233,19 +266,17 @@ public static Vector2 Lerp2(Vector2[] vectors, float percent) return Lerp3(vectors[0], vectors[1], vectors[2], percent); } - /// /// Multiplies a Vector2 by another. /// - /// - /// + /// + /// /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Multiply(this Vector2 src, Vector2 multiplier) { return new(src.x * multiplier.x, src.y * multiplier.y); } #endregion - } - } \ No newline at end of file