123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- using System;
- using System.Collections.Generic;
- namespace UnityEngine.Timeline
- {
- interface IInterval
- {
- Int64 intervalStart { get; }
- Int64 intervalEnd { get; }
- }
- struct IntervalTreeNode // interval node,
- {
- public Int64 center; // midpoint for this node
- public int first; // index of first element of this node in m_Entries
- public int last; // index of the last element of this node in m_Entries
- public int left; // index in m_Nodes of the left subnode
- public int right; // index in m_Nodes of the right subnode
- }
- class IntervalTree<T> where T : IInterval
- {
- internal struct Entry
- {
- public Int64 intervalStart;
- public Int64 intervalEnd;
- public T item;
- }
- const int kMinNodeSize = 10; // the minimum number of entries to have subnodes
- const int kInvalidNode = -1;
- const Int64 kCenterUnknown = Int64.MaxValue; // center hasn't been calculated. indicates no children
- readonly List<Entry> m_Entries = new List<Entry>();
- readonly List<IntervalTreeNode> m_Nodes = new List<IntervalTreeNode>();
- /// <summary>
- /// Whether the tree will be rebuilt on the next query
- /// </summary>
- public bool dirty { get; internal set; }
- /// <summary>
- /// Add an IInterval to the tree
- /// </summary>
- public void Add(T item)
- {
- if (item == null)
- return;
- m_Entries.Add(
- new Entry()
- {
- intervalStart = item.intervalStart,
- intervalEnd = item.intervalEnd,
- item = item
- }
- );
- dirty = true;
- }
- /// <summary>
- /// Query the tree at a particular time
- /// </summary>
- /// <param name="value"></param>
- /// <param name="results"></param>
- 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);
- }
- /// <summary>
- /// Query the tree at a particular range of time
- /// </summary>
- /// <param name="start"></param>
- /// <param name="end"></param>
- /// <param name="results"></param>
- 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);
- }
- /// <summary>
- /// Updates the intervals from their source. Use this to detect if the data in the tree
- /// has changed.
- /// </summary>
- 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();
- // minimum size, don't subdivide
- 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;
- // first pass, put every thing left of center, left
- 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;
- // second pass, put every start passed the center right
- 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;
- // reserve a place
- 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();
- }
- }
- }
|