| using System; |
| using System.Collections.Generic; |
|
|
| namespace UnityEngine.Timeline |
| { |
| interface IInterval |
| { |
| Int64 intervalStart { get; } |
| Int64 intervalEnd { get; } |
| } |
|
|
| struct IntervalTreeNode |
| { |
| public Int64 center; |
| public int first; |
| public int last; |
| public int left; |
| public int right; |
| } |
|
|
| class IntervalTree<T> where T : IInterval |
| { |
| internal struct Entry |
| { |
| public Int64 intervalStart; |
| public Int64 intervalEnd; |
| public T item; |
| } |
|
|
| const int kMinNodeSize = 10; |
| const int kInvalidNode = -1; |
| const Int64 kCenterUnknown = Int64.MaxValue; |
|
|
| readonly List<Entry> m_Entries = new List<Entry>(); |
| readonly List<IntervalTreeNode> m_Nodes = new List<IntervalTreeNode>(); |
|
|
| |
| |
| |
| public bool dirty { get; internal set; } |
|
|
| |
| |
| |
| public void Add(T item) |
| { |
| if (item == null) |
| return; |
|
|
| m_Entries.Add( |
| new Entry() |
| { |
| intervalStart = item.intervalStart, |
| intervalEnd = item.intervalEnd, |
| item = item |
| } |
| ); |
| dirty = true; |
| } |
|
|
| |
| |
| |
| |
| |
| public void IntersectsWith(Int64 value, List<T> results) |
| { |
| if (m_Entries.Count == 0) |
| return; |
|
|
| if (dirty) |
| { |
| Rebuild(); |
| dirty = false; |
| } |
|
|
| if (m_Nodes.Count > 0) |
| Query(m_Nodes[0], value, results); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public void IntersectsWithRange(Int64 start, Int64 end, List<T> results) |
| { |
| if (start > end) |
| return; |
|
|
| if (m_Entries.Count == 0) |
| return; |
|
|
| if (dirty) |
| { |
| Rebuild(); |
| dirty = false; |
| } |
|
|
| if (m_Nodes.Count > 0) |
| QueryRange(m_Nodes[0], start, end, results); |
| } |
|
|
| |
| |
| |
| |
| public void UpdateIntervals() |
| { |
| bool isDirty = false; |
| for (int i = 0; i < m_Entries.Count; i++) |
| { |
| var n = m_Entries[i]; |
| var s = n.item.intervalStart; |
| var e = n.item.intervalEnd; |
|
|
| isDirty |= n.intervalStart != s; |
| isDirty |= n.intervalEnd != e; |
|
|
| m_Entries[i] = new Entry() |
| { |
| intervalStart = s, |
| intervalEnd = e, |
| item = n.item |
| }; |
| } |
|
|
| dirty |= isDirty; |
| } |
|
|
| private void Query(IntervalTreeNode intervalTreeNode, Int64 value, List<T> results) |
| { |
| for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++) |
| { |
| var entry = m_Entries[i]; |
| if (value >= entry.intervalStart && value < entry.intervalEnd) |
| { |
| results.Add(entry.item); |
| } |
| } |
|
|
| if (intervalTreeNode.center == kCenterUnknown) |
| return; |
| if (intervalTreeNode.left != kInvalidNode && value < intervalTreeNode.center) |
| Query(m_Nodes[intervalTreeNode.left], value, results); |
| if (intervalTreeNode.right != kInvalidNode && value > intervalTreeNode.center) |
| Query(m_Nodes[intervalTreeNode.right], value, results); |
| } |
|
|
| private void QueryRange(IntervalTreeNode intervalTreeNode, Int64 start, Int64 end, List<T> results) |
| { |
| for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++) |
| { |
| var entry = m_Entries[i]; |
| if (end >= entry.intervalStart && start < entry.intervalEnd) |
| { |
| results.Add(entry.item); |
| } |
| } |
|
|
| if (intervalTreeNode.center == kCenterUnknown) |
| return; |
| if (intervalTreeNode.left != kInvalidNode && start < intervalTreeNode.center) |
| QueryRange(m_Nodes[intervalTreeNode.left], start, end, results); |
| if (intervalTreeNode.right != kInvalidNode && end > intervalTreeNode.center) |
| QueryRange(m_Nodes[intervalTreeNode.right], start, end, results); |
| } |
|
|
| private void Rebuild() |
| { |
| m_Nodes.Clear(); |
| m_Nodes.Capacity = m_Entries.Capacity; |
| Rebuild(0, m_Entries.Count - 1); |
| } |
|
|
| private int Rebuild(int start, int end) |
| { |
| IntervalTreeNode intervalTreeNode = new IntervalTreeNode(); |
|
|
| |
| int count = end - start + 1; |
| if (count < kMinNodeSize) |
| { |
| intervalTreeNode = new IntervalTreeNode() { center = kCenterUnknown, first = start, last = end, left = kInvalidNode, right = kInvalidNode }; |
| m_Nodes.Add(intervalTreeNode); |
| return m_Nodes.Count - 1; |
| } |
|
|
| var min = Int64.MaxValue; |
| var max = Int64.MinValue; |
|
|
| for (int i = start; i <= end; i++) |
| { |
| var o = m_Entries[i]; |
| min = Math.Min(min, o.intervalStart); |
| max = Math.Max(max, o.intervalEnd); |
| } |
|
|
| var center = (max + min) / 2; |
| intervalTreeNode.center = center; |
|
|
| |
| int x = start; |
| int y = end; |
| while (true) |
| { |
| while (x <= end && m_Entries[x].intervalEnd < center) |
| x++; |
|
|
| while (y >= start && m_Entries[y].intervalEnd >= center) |
| y--; |
|
|
| if (x > y) |
| break; |
|
|
| var nodeX = m_Entries[x]; |
| var nodeY = m_Entries[y]; |
|
|
| m_Entries[y] = nodeX; |
| m_Entries[x] = nodeY; |
| } |
|
|
| intervalTreeNode.first = x; |
|
|
| |
| y = end; |
| while (true) |
| { |
| while (x <= end && m_Entries[x].intervalStart <= center) |
| x++; |
|
|
| while (y >= start && m_Entries[y].intervalStart > center) |
| y--; |
|
|
| if (x > y) |
| break; |
|
|
| var nodeX = m_Entries[x]; |
| var nodeY = m_Entries[y]; |
|
|
| m_Entries[y] = nodeX; |
| m_Entries[x] = nodeY; |
| } |
|
|
| intervalTreeNode.last = y; |
|
|
| |
| m_Nodes.Add(new IntervalTreeNode()); |
| int index = m_Nodes.Count - 1; |
|
|
| intervalTreeNode.left = kInvalidNode; |
| intervalTreeNode.right = kInvalidNode; |
|
|
| if (start < intervalTreeNode.first) |
| intervalTreeNode.left = Rebuild(start, intervalTreeNode.first - 1); |
|
|
| if (end > intervalTreeNode.last) |
| intervalTreeNode.right = Rebuild(intervalTreeNode.last + 1, end); |
|
|
| m_Nodes[index] = intervalTreeNode; |
| return index; |
| } |
|
|
| public void Clear() |
| { |
| m_Entries.Clear(); |
| m_Nodes.Clear(); |
| } |
| } |
| } |
|
|