using System.Collections.Generic; using System.Linq; using UnityEngine.Timeline; namespace UnityEditor.Timeline { static class EditModeMixUtils { static readonly List k_UnrecoverablePlacements = new List { PlacementValidity.InvalidIsWithin, PlacementValidity.InvalidStartsInBlend, PlacementValidity.InvalidContainsBlend, PlacementValidity.InvalidOverlapWithNonBlendableClip }; public static bool CanInsert(IEnumerable itemsGroups) { foreach (var itemsGroup in itemsGroups) { List siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items).ToList(); foreach (var item in itemsGroup.items) { var placementValidity = GetPlacementValidity(item, siblings); if (k_UnrecoverablePlacements.Contains(placementValidity)) { return false; } } } return true; } //Corrects clips durations to fit at insertion point, if needed public static void PrepareItemsForInsertion(IEnumerable itemsGroups) { foreach (var itemsGroup in itemsGroups) { var siblings = ItemsUtils.GetItemsExcept(itemsGroup.targetTrack, itemsGroup.items); foreach (var item in itemsGroup.items.OfType()) { var eatenItems = siblings.Where(c => EditModeUtils.IsItemWithinRange(c, item.start, item.end)).ToList(); var intersectedItem = EditModeUtils.GetFirstIntersectedItem(siblings, item.end); if (intersectedItem != null) eatenItems.Add(intersectedItem); var blendableItems = eatenItems.OfType(); if (blendableItems.Any()) { var minTime = blendableItems.Min(c => c.end - c.rightBlendDuration); if (item.end > minTime) item.SetEnd(minTime, false); } } } } public static PlacementValidity GetPlacementValidity(ITimelineItem item, IEnumerable otherItems) { if (item.duration <= 0.0) return PlacementValidity.Valid; //items without any duration can always be placed var sortedItems = otherItems.Where(i => i.duration > 0.0).OrderBy(c => c.start); var candidates = new List(); foreach (var sortedItem in sortedItems) { if ((DiscreteTime)sortedItem.start >= (DiscreteTime)item.end) { // No need to process further break; } if ((DiscreteTime)sortedItem.end <= (DiscreteTime)item.start) { // Skip continue; } candidates.Add(sortedItem); } var discreteStart = (DiscreteTime)item.start; var discreteEnd = (DiscreteTime)item.end; // Note: Order of tests matters for (int i = 0, n = candidates.Count; i < n; i++) { var candidate = candidates[i]; var blendItem = item as IBlendable; if (blendItem != null && blendItem.supportsBlending) { if (EditModeUtils.Contains(candidate.start, candidate.end, item)) return PlacementValidity.InvalidIsWithin; if (i < n - 1) { var nextCandidate = candidates[i + 1]; var discreteNextCandidateStart = (DiscreteTime)nextCandidate.start; var discreteCandidateEnd = (DiscreteTime)candidate.end; if (discreteCandidateEnd > discreteNextCandidateStart) { if (discreteStart >= discreteNextCandidateStart) { // Note: In case the placement is fully within a blend, // InvalidStartsInBlend MUST have priority return PlacementValidity.InvalidStartsInBlend; } if (discreteEnd > discreteNextCandidateStart && discreteEnd <= discreteCandidateEnd) return PlacementValidity.InvalidEndsInBlend; if (discreteStart < discreteNextCandidateStart && discreteEnd > discreteCandidateEnd) return PlacementValidity.InvalidContainsBlend; } } if (EditModeUtils.Contains(item.start, item.end, candidate)) return PlacementValidity.InvalidContains; } else { if (EditModeUtils.Overlaps(item, candidate.start, candidate.end) || EditModeUtils.Overlaps(candidate, item.start, item.end)) return PlacementValidity.InvalidOverlapWithNonBlendableClip; } } return PlacementValidity.Valid; } } }