// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik // #if UNITY_EDITOR using System.Collections.Generic; using UnityEngine; namespace Animancer.Editor.TransitionLibraries { /// A list of items in the organised by group. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionGroupCache public class TransitionGroupCache { /************************************************************************************************************************/ private static readonly List Transitions = new(); private readonly List Items = new(); private readonly List ItemToGroup = new(); private readonly Dictionary ItemToIndex = new(); /************************************************************************************************************************/ /// The total number of items in this cache. public int Count => Items.Count; /************************************************************************************************************************/ /// Returns the index of the specified `item` in this cache. public int IndexOf(object item) => item != null && ItemToIndex.TryGetValue(item, out var index) ? index : -1; /************************************************************************************************************************/ /// Tries to get the item at the specified index. public bool TryGet(int index, out object item) => Items.TryGet(index, out item); /************************************************************************************************************************/ /// Returns the item at the specified index. public object GetItem(int index) => Items[index]; /// Returns the group containing the item at the specified index. public TransitionGroup GetGroup(int index) => ItemToGroup[index]; /************************************************************************************************************************/ /// /// Converts the `index` to a value for the , /// meaning it skips any items inside groups. /// public int ItemToGroupIndex(int index) { index = Mathf.Clamp(index, 0, Items.Count - 1); for (int i = index; i >= 0; i--) { var group = ItemToGroup[i]; if (group != null && !ReferenceEquals(group, Items[i])) index--; } return index; } /************************************************************************************************************************/ /// Gathers the items from the specified library. public void GatherTransitionsAndGroups( TransitionAssetBase[] transitions, TransitionLibraryEditorDataInternal editorData) => GatherTransitionsAndGroups(transitions, editorData.TransitionGroups); /// Gathers the items from the specified library. public void GatherTransitionsAndGroups( TransitionAssetBase[] transitions, List groups) { Items.Clear(); Transitions.Clear(); ItemToGroup.Clear(); ItemToIndex.Clear(); Transitions.AddRange(transitions); SortGroups(groups); GatherGroupedTransitions(groups); GatherUnGroupedItems(); GatherGroupedItems(groups); GatherItemIndices(); Transitions.Clear(); } /************************************************************************************************************************/ /// /// Sorts the `groups` by /// and removes any nulls. /// public static void SortGroups(List groups) { var previousGroupIndex = int.MinValue; var outOfOrder = false; for (int i = 0; i < groups.Count; i++) { var group = groups[i]; if (group == null) { groups.RemoveAt(i); i--; continue; } var groupIndex = group.Index; if (groupIndex < previousGroupIndex) { outOfOrder = true; } else if (groupIndex == previousGroupIndex) { groupIndex++; group.Index = groupIndex; } previousGroupIndex = groupIndex; } if (outOfOrder) groups.Sort(static (a, b) => a.Index.CompareTo(b.Index)); } /************************************************************************************************************************/ /// /// Grabs items from the /// to fill the . /// private void GatherGroupedTransitions(List groups) { for (int iGroup = 0; iGroup < groups.Count; iGroup++) { var group = groups[iGroup]; group.Transitions.Clear(); for (int iTransition = 0; iTransition < group.TransitionIndices.Count; iTransition++) { var transitionIndex = group.TransitionIndices[iTransition]; if (!Transitions.TryGetObject(transitionIndex, out var transition)) continue; Transitions[transitionIndex] = null; group.Transitions.Add(transition); } } } /************************************************************************************************************************/ /// Copies un-grouped transitions over to the . private void GatherUnGroupedItems() { for (int i = 0; i < Transitions.Count; i++) { var transition = Transitions[i]; if (transition == null) continue; Items.Add(transition); ItemToGroup.Add(null); } } /************************************************************************************************************************/ /// Copies groups and grouped transitions over to the . private void GatherGroupedItems(List groups) { var expandedItemOffset = 0; for (int iGroup = 0; iGroup < groups.Count; iGroup++) { var group = groups[iGroup]; group.Index = Mathf.Clamp(group.Index, 0, Transitions.Count + iGroup + 1); var index = group.Index + expandedItemOffset; index = Mathf.Clamp(index, 0, Items.Count); Items.Insert(index, group); ItemToGroup.Insert(index, group); if (!group.IsExpanded) continue; expandedItemOffset += group.Transitions.Count; for (int iTransition = 0; iTransition < group.Transitions.Count; iTransition++) { index++; Items.Insert(index, group.Transitions[iTransition]); ItemToGroup.Insert(index, group); } } } /************************************************************************************************************************/ /// Assigns the for each of the . private void GatherItemIndices() { for (int i = 0; i < Items.Count; i++) ItemToIndex[Items[i]] = i; } /************************************************************************************************************************/ } } #endif