namespace Zinnia.Tracking.Collision.Active.Operation { using System.Linq; using UnityEngine; using UnityEngine.Events; using Zinnia.Extension; /// /// Slices a selection of the collection from the given for the given and provides the sliced collection and the remaining collection separately. /// public class Slicer : MonoBehaviour { #region Index Settings [Header("Index Settings")] [Tooltip("The zero-based index to start the slice at. A negative value counts backwards from the last index in the collection.")] [SerializeField] private int startIndex; /// /// The zero-based index to start the slice at. A negative value counts backwards from the last index in the collection. /// public int StartIndex { get { return startIndex; } set { startIndex = value; } } [Tooltip("The number of elements in the slice.")] [SerializeField] private uint length = 1; /// /// The number of elements in the slice. /// public uint Length { get { return length; } set { length = value; } } #endregion #region State Events /// /// Emitted when the Sliced list has changed since last slice. /// [Header("State Events")] public UnityEvent SlicedChanged = new UnityEvent(); /// /// Emitted when the Sliced list has remained unchanged since last slice. /// public UnityEvent SlicedUnchanged = new UnityEvent(); /// /// Emitted when the Remained list has changed since last slice. /// public UnityEvent RemainedChanged = new UnityEvent(); /// /// Emitted when the Remained list has remained unchanged since last slice. /// public UnityEvent RemainedUnchanged = new UnityEvent(); #endregion #region Data Events /// /// Emitted when the sliced elements are taken from the collection. /// [Header("Data Events")] public ActiveCollisionsContainer.ActiveCollisionUnityEvent Sliced = new ActiveCollisionsContainer.ActiveCollisionUnityEvent(); /// /// Emitted when the remaining elements are left after slicing. /// public ActiveCollisionsContainer.ActiveCollisionUnityEvent Remained = new ActiveCollisionsContainer.ActiveCollisionUnityEvent(); #endregion /// /// The elements that have been sliced out of the list. /// public ActiveCollisionsContainer.EventData SlicedList { get; protected set; } = new ActiveCollisionsContainer.EventData(); /// /// The elements that are still remaining in the list after a slice. /// public ActiveCollisionsContainer.EventData RemainingList { get; protected set; } = new ActiveCollisionsContainer.EventData(); /// /// The cached list. /// protected ActiveCollisionsContainer.EventData cachedSlicedList = new ActiveCollisionsContainer.EventData(); /// /// The cached list. /// protected ActiveCollisionsContainer.EventData cachedRemainingList = new ActiveCollisionsContainer.EventData(); /// /// Slices the collision collection. /// /// The original collision collection. public virtual void DoSlice(ActiveCollisionsContainer.EventData originalList) { Slice(originalList); } /// /// Slices the collision collection. /// /// The original collision collection. /// The sliced collection. public virtual ActiveCollisionsContainer.EventData Slice(ActiveCollisionsContainer.EventData originalList) { SlicedList.Clear(); RemainingList.Clear(); if (!this.CheckIsActiveAndEnabled()) { return SlicedList; } CreateSlicedList(originalList); CreateRemainedList(originalList); return SlicedList; } /// /// Creates the contents of the sliced list. /// /// The full list to slice. protected virtual void CreateSlicedList(ActiveCollisionsContainer.EventData originalList) { int collectionCount = originalList.ActiveCollisions.Count; int actualStartIndex = GetStartIndex(StartIndex, collectionCount); int actualLength = GetRangeLength(actualStartIndex, (int)Length, collectionCount); for (int index = actualStartIndex; index < actualStartIndex + actualLength; index++) { SlicedList.ActiveCollisions.Add(originalList.ActiveCollisions[index]); } if (!SlicedList.ActiveCollisions.SequenceEqual(cachedSlicedList.ActiveCollisions)) { SlicedChanged?.Invoke(); } else { SlicedUnchanged?.Invoke(); } Sliced?.Invoke(SlicedList); cachedSlicedList.ActiveCollisions = SlicedList.ActiveCollisions.GetRange(0, SlicedList.ActiveCollisions.Count); } /// /// Creates the contents of the remaining list. /// /// The full list to slice. protected virtual void CreateRemainedList(ActiveCollisionsContainer.EventData originalList) { foreach (CollisionNotifier.EventData originalCollision in originalList.ActiveCollisions) { if (!SlicedList.ActiveCollisions.Contains(originalCollision)) { RemainingList.ActiveCollisions.Add(originalCollision); } } if (!RemainingList.ActiveCollisions.SequenceEqual(cachedRemainingList.ActiveCollisions)) { RemainedChanged?.Invoke(); } else { RemainedUnchanged?.Invoke(); } Remained?.Invoke(RemainingList); cachedRemainingList.ActiveCollisions = RemainingList.ActiveCollisions.GetRange(0, RemainingList.ActiveCollisions.Count); } /// /// Slices the collision collection. /// /// The original collision collection. /// The collection of remaining elements that were not included in the sliced collection. /// The sliced collection. public virtual ActiveCollisionsContainer.EventData Slice(ActiveCollisionsContainer.EventData originalList, out ActiveCollisionsContainer.EventData remaining) { ActiveCollisionsContainer.EventData returnList = Slice(originalList); remaining = (this.CheckIsActiveAndEnabled() ? RemainingList : originalList); return returnList; } /// /// Gets the actual start index even if the index is a negative value. /// /// The index to start from. /// The total length of the entire collection /// The actual start index to start from. protected virtual int GetStartIndex(int checkIndex, int count) { return Mathf.Clamp(checkIndex < 0 ? count + checkIndex : checkIndex, 0, count); } /// /// Gets the actual valid length for the proposed range. /// /// The index to start from. /// The length of elements to return. /// The total length of the entire collection /// The actual valid length for the given range. protected virtual int GetRangeLength(int checkIndex, int checkLength, int count) { int returnLength = checkLength; int actualLength = checkIndex + checkLength; if (actualLength >= count) { int offset = actualLength - count; returnLength = checkLength - offset; } return returnLength; } } }