| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 | 
							- using System;
 
- using System.Runtime.CompilerServices;
 
- using LitMotion.Collections;
 
- using Unity.Collections;
 
- using Unity.Collections.LowLevel.Unsafe;
 
- // TODO: Constantize the exception message
 
- namespace LitMotion
 
- {
 
-     internal struct StorageEntry : IEquatable<StorageEntry>
 
-     {
 
-         public int? Next;
 
-         public int DenseIndex;
 
-         public int Version;
 
-         public readonly bool Equals(StorageEntry other)
 
-         {
 
-             return other.Next == Next && other.DenseIndex == DenseIndex && other.Version == Version;
 
-         }
 
-         public override readonly bool Equals(object obj)
 
-         {
 
-             if (obj is StorageEntry entry) return Equals(entry);
 
-             return false;
 
-         }
 
-         public override readonly int GetHashCode()
 
-         {
 
-             return HashCode.Combine(Next, DenseIndex, Version);
 
-         }
 
-     }
 
-     internal unsafe interface IMotionStorage
 
-     {
 
-         bool IsActive(MotionHandle handle);
 
-         void Cancel(MotionHandle handle);
 
-         void Complete(MotionHandle handle);
 
-         ref MotionDataCore GetDataRef(MotionHandle handle);
 
-         ref MotionCallbackData GetCallbackDataRef(MotionHandle handle);
 
-         void Reset();
 
-     }
 
-     internal sealed class StorageEntryList
 
-     {
 
-         public StorageEntryList(int initialCapacity = 32)
 
-         {
 
-             entries = new StorageEntry[initialCapacity];
 
-             Reset();
 
-         }
 
-         StorageEntry[] entries;
 
-         int? freeEntry;
 
-         public StorageEntry this[int index]
 
-         {
 
-             [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-             get => entries[index];
 
-             [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-             set => entries[index] = value;
 
-         }
 
-         public void EnsureCapacity(int capacity)
 
-         {
 
-             var currentLength = entries.Length;
 
-             if (currentLength >= capacity) return;
 
-             Array.Resize(ref entries, capacity);
 
-             for (int i = currentLength; i < entries.Length; i++)
 
-             {
 
-                 entries[i] = new() { Next = i == capacity - 1 ? freeEntry : i + 1, DenseIndex = -1, Version = 1 };
 
-             }
 
-             freeEntry = currentLength;
 
-         }
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         public StorageEntry Alloc(int denseIndex, out int entryIndex)
 
-         {
 
-             // Ensure array capacity
 
-             if (freeEntry == null)
 
-             {
 
-                 var currentLength = entries.Length;
 
-                 EnsureCapacity(entries.Length * 2);
 
-                 freeEntry = currentLength;
 
-             }
 
-             // Find free entry
 
-             entryIndex = freeEntry.Value;
 
-             var entry = entries[entryIndex];
 
-             freeEntry = entry.Next;
 
-             entry.Next = null;
 
-             entry.DenseIndex = denseIndex;
 
-             entries[entryIndex] = entry;
 
-             return entry;
 
-         }
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         public void Free(int index)
 
-         {
 
-             var entry = entries[index];
 
-             entry.Next = freeEntry;
 
-             entry.Version++;
 
-             entries[index] = entry;
 
-             freeEntry = index;
 
-         }
 
-         public void Reset()
 
-         {
 
-             for (int i = 0; i < entries.Length; i++)
 
-             {
 
-                 entries[i] = new() { Next = i == entries.Length - 1 ? null : i + 1, DenseIndex = -1, Version = 1 };
 
-             }
 
-             freeEntry = 0;
 
-         }
 
-     }
 
-     internal sealed class MotionStorage<TValue, TOptions, TAdapter> : IMotionStorage
 
-         where TValue : unmanaged
 
-         where TOptions : unmanaged, IMotionOptions
 
-         where TAdapter : unmanaged, IMotionAdapter<TValue, TOptions>
 
-     {
 
-         public MotionStorage(int id)
 
-         {
 
-             StorageId = id;
 
-             AllocatorHelper = RewindableAllocatorFactory.CreateAllocator();
 
-         }
 
-         // Entry
 
-         readonly StorageEntryList entries = new(InitialCapacity);
 
-         // Data
 
-         public int?[] toEntryIndex = new int?[InitialCapacity];
 
-         public MotionData<TValue, TOptions>[] dataArray = new MotionData<TValue, TOptions>[InitialCapacity];
 
-         public MotionCallbackData[] callbacksArray = new MotionCallbackData[InitialCapacity];
 
-         // Allocator
 
-         AllocatorHelper<RewindableAllocator> AllocatorHelper;
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         public Span<MotionData<TValue, TOptions>> GetDataSpan() => dataArray.AsSpan(0, tail);
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         public Span<MotionCallbackData> GetCallbacksSpan() => callbacksArray.AsSpan(0, tail);
 
-         int tail;
 
-         const int InitialCapacity = 8;
 
-         public int StorageId { get; }
 
-         public int Count => tail;
 
-         public (int EntryIndex, int Version) Append(in MotionData<TValue, TOptions> data, in MotionCallbackData callbacks)
 
-         {
 
-             if (tail == dataArray.Length)
 
-             {
 
-                 var newLength = tail * 2;
 
-                 Array.Resize(ref toEntryIndex, newLength);
 
-                 Array.Resize(ref dataArray, newLength);
 
-                 Array.Resize(ref callbacksArray, newLength);
 
-             }
 
-             var entry = entries.Alloc(tail, out var entryIndex);
 
- #if LITMOTION_ENABLE_MOTION_LOG
 
-             UnityEngine.Debug.Log("[Add] Entry:" + entryIndex + " Dense:" + entry.DenseIndex + " Version:" + entry.Version);
 
- #endif
 
-             var prevAnimationCurve = dataArray[tail].Core.AnimationCurve;
 
-             toEntryIndex[tail] = entryIndex;
 
-             dataArray[tail] = data;
 
-             callbacksArray[tail] = callbacks;
 
-             if (data.Core.Ease == Ease.CustomAnimationCurve)
 
-             {
 
-                 if (!prevAnimationCurve.IsCreated)
 
-                 {
 
- #if LITMOTION_COLLECTIONS_2_0_OR_NEWER
 
-                     prevAnimationCurve = new NativeAnimationCurve(AllocatorHelper.Allocator.Handle);
 
- #else
 
-                     prevAnimationCurve = new UnsafeAnimationCurve(AllocatorHelper.Allocator.Handle);
 
- #endif
 
-                 }
 
-                 prevAnimationCurve.CopyFrom(data.Core.AnimationCurve);
 
-                 dataArray[tail].Core.AnimationCurve = prevAnimationCurve;
 
-             }
 
-             tail++;
 
-             return (entryIndex, entry.Version);
 
-         }
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         void RemoveAt(int denseIndex)
 
-         {
 
-             tail--;
 
-             // swap elements
 
-             dataArray[denseIndex] = dataArray[tail];
 
-             // dataArray[tail] = default;
 
-             callbacksArray[denseIndex] = callbacksArray[tail];
 
-             // callbacksArray[tail] = default;
 
-             // swap entry indexes
 
-             var prevEntryIndex = toEntryIndex[denseIndex];
 
-             var currentEntryIndex = toEntryIndex[denseIndex] = toEntryIndex[tail];
 
-             // toEntryIndex[tail] = default;
 
-             // update entry
 
-             if (currentEntryIndex != null)
 
-             {
 
-                 var index = (int)currentEntryIndex;
 
-                 var entry = entries[index];
 
-                 entry.DenseIndex = denseIndex;
 
-                 entries[index] = entry;
 
-             }
 
-             // free entry
 
-             if (prevEntryIndex != null)
 
-             {
 
-                 entries.Free((int)prevEntryIndex);
 
-             }
 
- #if LITMOTION_ENABLE_MOTION_LOG
 
-             var v = entries[(int)prevEntryIndex].Version - 1;
 
-             UnityEngine.Debug.Log("[Remove] Entry:" + prevEntryIndex + " Dense:" + denseIndex + " Version:" + v);
 
- #endif
 
-         }
 
-         public void RemoveAll(NativeList<int> indexes)
 
-         {
 
-             var entryIndexes = new NativeArray<int>(indexes.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
 
-             var lastCallbacksSpan = GetCallbacksSpan();
 
-             for (int i = 0; i < entryIndexes.Length; i++)
 
-             {
 
-                 entryIndexes[i] = (int)toEntryIndex[indexes[i]];
 
-             }
 
-             for (int i = 0; i < entryIndexes.Length; i++)
 
-             {
 
-                 RemoveAt(entries[entryIndexes[i]].DenseIndex);
 
-             }
 
-             // Avoid Memory leak
 
-             lastCallbacksSpan[tail..].Clear();
 
-             entryIndexes.Dispose();
 
-         }
 
-         public void EnsureCapacity(int capacity)
 
-         {
 
-             if (capacity > dataArray.Length)
 
-             {
 
-                 Array.Resize(ref toEntryIndex, capacity);
 
-                 Array.Resize(ref dataArray, capacity);
 
-                 Array.Resize(ref callbacksArray, capacity);
 
-                 entries.EnsureCapacity(capacity);
 
-             }
 
-         }
 
-         public void Cancel(MotionHandle handle)
 
-         {
 
-             var entry = entries[handle.Index];
 
-             var denseIndex = entry.DenseIndex;
 
-             if (denseIndex < 0 || denseIndex >= dataArray.Length)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-             ref var motion = ref GetDataSpan()[denseIndex];
 
-             var version = entry.Version;
 
-             if (version <= 0 || version != handle.Version || motion.Core.Status == MotionStatus.None)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-             motion.Core.Status = MotionStatus.Canceled;
 
-             ref var callbackData = ref GetCallbacksSpan()[denseIndex];
 
-             try
 
-             {
 
-                 callbackData.OnCancelAction?.Invoke();
 
-             }
 
-             catch (Exception ex)
 
-             {
 
-                 MotionDispatcher.GetUnhandledExceptionHandler()?.Invoke(ex);
 
-             }
 
-         }
 
-         public void Complete(MotionHandle handle)
 
-         {
 
-             var entry = entries[handle.Index];
 
-             var denseIndex = entry.DenseIndex;
 
-             if (denseIndex < 0 || denseIndex >= tail)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-             ref var motion = ref GetDataSpan()[denseIndex];
 
-             var version = entry.Version;
 
-             if (version <= 0 || version != handle.Version || motion.Core.Status == MotionStatus.None)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-             if (motion.Core.Loops < 0)
 
-             {
 
-                 UnityEngine.Debug.LogWarning("[LitMotion] Complete was ignored because it is not possible to complete a motion that loops infinitely. If you want to end the motion, call Cancel() instead.");
 
-                 return;
 
-             }
 
-             ref var callbackData = ref GetCallbacksSpan()[denseIndex];
 
-             if (callbackData.IsCallbackRunning)
 
-             {
 
-                 throw new InvalidOperationException("Recursion of Complete call was detected.");
 
-             }
 
-             callbackData.IsCallbackRunning = true;
 
-             // To avoid duplication of Complete processing, it is treated as canceled internally.
 
-             motion.Core.Status = MotionStatus.Canceled;
 
-             var endProgress = motion.Core.LoopType switch
 
-             {
 
-                 LoopType.Restart => 1f,
 
-                 LoopType.Yoyo => motion.Core.Loops % 2 == 0 ? 0f : 1f,
 
-                 LoopType.Incremental => motion.Core.Loops,
 
-                 _ => 1f
 
-             };
 
-             var easedEndProgress = motion.Core.Ease switch
 
-             {
 
-                 Ease.CustomAnimationCurve => motion.Core.AnimationCurve.Evaluate(endProgress),
 
-                 _ => EaseUtility.Evaluate(endProgress, motion.Core.Ease),
 
-             };
 
-             var endValue = default(TAdapter).Evaluate(
 
-                 ref motion.StartValue,
 
-                 ref motion.EndValue,
 
-                 ref motion.Options,
 
-                 new() { Progress = easedEndProgress }
 
-             );
 
-             try
 
-             {
 
-                 callbackData.InvokeUnsafe(endValue);
 
-             }
 
-             catch (Exception ex)
 
-             {
 
-                 MotionDispatcher.GetUnhandledExceptionHandler()?.Invoke(ex);
 
-             }
 
-             try
 
-             {
 
-                 callbackData.OnCompleteAction?.Invoke();
 
-             }
 
-             catch (Exception ex)
 
-             {
 
-                 MotionDispatcher.GetUnhandledExceptionHandler()?.Invoke(ex);
 
-             }
 
-             callbackData.IsCallbackRunning = false;
 
-         }
 
-         public bool IsActive(MotionHandle handle)
 
-         {
 
-             var entry = entries[handle.Index];
 
-             var denseIndex = entry.DenseIndex;
 
-             if (denseIndex < 0 || denseIndex >= dataArray.Length) return false;
 
-             var version = entry.Version;
 
-             if (version <= 0 || version != handle.Version) return false;
 
-             var motion = dataArray[denseIndex];
 
-             return motion.Core.Status is MotionStatus.Scheduled or MotionStatus.Delayed or MotionStatus.Playing;
 
-         }
 
-         public ref MotionCallbackData GetCallbackDataRef(MotionHandle handle)
 
-         {
 
-             CheckIndex(handle);
 
-             return ref callbacksArray[entries[handle.Index].DenseIndex];
 
-         }
 
-         public ref MotionDataCore GetDataRef(MotionHandle handle)
 
-         {
 
-             CheckIndex(handle);
 
-             return ref UnsafeUtility.As<MotionData<TValue, TOptions>, MotionDataCore>(ref dataArray[entries[handle.Index].DenseIndex]);
 
-         }
 
-         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 
-         void CheckIndex(MotionHandle handle)
 
-         {
 
-             var entry = entries[handle.Index];
 
-             var denseIndex = entry.DenseIndex;
 
-             if (denseIndex < 0 || denseIndex >= dataArray.Length)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-             var version = entry.Version;
 
-             if (version <= 0 || version != handle.Version || dataArray[denseIndex].Core.Status == MotionStatus.None)
 
-             {
 
-                 throw new ArgumentException("Motion has been destroyed or no longer exists.");
 
-             }
 
-         }
 
-         public void Reset()
 
-         {
 
-             entries.Reset();
 
-             toEntryIndex.AsSpan().Clear();
 
-             dataArray.AsSpan().Clear();
 
-             callbacksArray.AsSpan().Clear();
 
-             tail = 0;
 
-             AllocatorHelper.Allocator.Rewind();
 
-         }
 
-     }
 
- }
 
 
  |