IntervalTree.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. using System;
  2. using System.Collections.Generic;
  3. namespace UnityEngine.Timeline
  4. {
  5. interface IInterval
  6. {
  7. Int64 intervalStart { get; }
  8. Int64 intervalEnd { get; }
  9. }
  10. struct IntervalTreeNode // interval node,
  11. {
  12. public Int64 center; // midpoint for this node
  13. public int first; // index of first element of this node in m_Entries
  14. public int last; // index of the last element of this node in m_Entries
  15. public int left; // index in m_Nodes of the left subnode
  16. public int right; // index in m_Nodes of the right subnode
  17. }
  18. class IntervalTree<T> where T : IInterval
  19. {
  20. internal struct Entry
  21. {
  22. public Int64 intervalStart;
  23. public Int64 intervalEnd;
  24. public T item;
  25. }
  26. const int kMinNodeSize = 10; // the minimum number of entries to have subnodes
  27. const int kInvalidNode = -1;
  28. const Int64 kCenterUnknown = Int64.MaxValue; // center hasn't been calculated. indicates no children
  29. readonly List<Entry> m_Entries = new List<Entry>();
  30. readonly List<IntervalTreeNode> m_Nodes = new List<IntervalTreeNode>();
  31. /// <summary>
  32. /// Whether the tree will be rebuilt on the next query
  33. /// </summary>
  34. public bool dirty { get; internal set; }
  35. /// <summary>
  36. /// Add an IInterval to the tree
  37. /// </summary>
  38. public void Add(T item)
  39. {
  40. if (item == null)
  41. return;
  42. m_Entries.Add(
  43. new Entry()
  44. {
  45. intervalStart = item.intervalStart,
  46. intervalEnd = item.intervalEnd,
  47. item = item
  48. }
  49. );
  50. dirty = true;
  51. }
  52. /// <summary>
  53. /// Query the tree at a particular time
  54. /// </summary>
  55. /// <param name="value"></param>
  56. /// <param name="results"></param>
  57. public void IntersectsWith(Int64 value, List<T> results)
  58. {
  59. if (m_Entries.Count == 0)
  60. return;
  61. if (dirty)
  62. {
  63. Rebuild();
  64. dirty = false;
  65. }
  66. if (m_Nodes.Count > 0)
  67. Query(m_Nodes[0], value, results);
  68. }
  69. /// <summary>
  70. /// Query the tree at a particular range of time
  71. /// </summary>
  72. /// <param name="start"></param>
  73. /// <param name="end"></param>
  74. /// <param name="results"></param>
  75. public void IntersectsWithRange(Int64 start, Int64 end, List<T> results)
  76. {
  77. if (start > end)
  78. return;
  79. if (m_Entries.Count == 0)
  80. return;
  81. if (dirty)
  82. {
  83. Rebuild();
  84. dirty = false;
  85. }
  86. if (m_Nodes.Count > 0)
  87. QueryRange(m_Nodes[0], start, end, results);
  88. }
  89. /// <summary>
  90. /// Updates the intervals from their source. Use this to detect if the data in the tree
  91. /// has changed.
  92. /// </summary>
  93. public void UpdateIntervals()
  94. {
  95. bool isDirty = false;
  96. for (int i = 0; i < m_Entries.Count; i++)
  97. {
  98. var n = m_Entries[i];
  99. var s = n.item.intervalStart;
  100. var e = n.item.intervalEnd;
  101. isDirty |= n.intervalStart != s;
  102. isDirty |= n.intervalEnd != e;
  103. m_Entries[i] = new Entry()
  104. {
  105. intervalStart = s,
  106. intervalEnd = e,
  107. item = n.item
  108. };
  109. }
  110. dirty |= isDirty;
  111. }
  112. private void Query(IntervalTreeNode intervalTreeNode, Int64 value, List<T> results)
  113. {
  114. for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++)
  115. {
  116. var entry = m_Entries[i];
  117. if (value >= entry.intervalStart && value < entry.intervalEnd)
  118. {
  119. results.Add(entry.item);
  120. }
  121. }
  122. if (intervalTreeNode.center == kCenterUnknown)
  123. return;
  124. if (intervalTreeNode.left != kInvalidNode && value < intervalTreeNode.center)
  125. Query(m_Nodes[intervalTreeNode.left], value, results);
  126. if (intervalTreeNode.right != kInvalidNode && value > intervalTreeNode.center)
  127. Query(m_Nodes[intervalTreeNode.right], value, results);
  128. }
  129. private void QueryRange(IntervalTreeNode intervalTreeNode, Int64 start, Int64 end, List<T> results)
  130. {
  131. for (int i = intervalTreeNode.first; i <= intervalTreeNode.last; i++)
  132. {
  133. var entry = m_Entries[i];
  134. if (end >= entry.intervalStart && start < entry.intervalEnd)
  135. {
  136. results.Add(entry.item);
  137. }
  138. }
  139. if (intervalTreeNode.center == kCenterUnknown)
  140. return;
  141. if (intervalTreeNode.left != kInvalidNode && start < intervalTreeNode.center)
  142. QueryRange(m_Nodes[intervalTreeNode.left], start, end, results);
  143. if (intervalTreeNode.right != kInvalidNode && end > intervalTreeNode.center)
  144. QueryRange(m_Nodes[intervalTreeNode.right], start, end, results);
  145. }
  146. private void Rebuild()
  147. {
  148. m_Nodes.Clear();
  149. m_Nodes.Capacity = m_Entries.Capacity;
  150. Rebuild(0, m_Entries.Count - 1);
  151. }
  152. private int Rebuild(int start, int end)
  153. {
  154. IntervalTreeNode intervalTreeNode = new IntervalTreeNode();
  155. // minimum size, don't subdivide
  156. int count = end - start + 1;
  157. if (count < kMinNodeSize)
  158. {
  159. intervalTreeNode = new IntervalTreeNode() {center = kCenterUnknown, first = start, last = end, left = kInvalidNode, right = kInvalidNode};
  160. m_Nodes.Add(intervalTreeNode);
  161. return m_Nodes.Count - 1;
  162. }
  163. var min = Int64.MaxValue;
  164. var max = Int64.MinValue;
  165. for (int i = start; i <= end; i++)
  166. {
  167. var o = m_Entries[i];
  168. min = Math.Min(min, o.intervalStart);
  169. max = Math.Max(max, o.intervalEnd);
  170. }
  171. var center = (max + min) / 2;
  172. intervalTreeNode.center = center;
  173. // first pass, put every thing left of center, left
  174. int x = start;
  175. int y = end;
  176. while (true)
  177. {
  178. while (x <= end && m_Entries[x].intervalEnd < center)
  179. x++;
  180. while (y >= start && m_Entries[y].intervalEnd >= center)
  181. y--;
  182. if (x > y)
  183. break;
  184. var nodeX = m_Entries[x];
  185. var nodeY = m_Entries[y];
  186. m_Entries[y] = nodeX;
  187. m_Entries[x] = nodeY;
  188. }
  189. intervalTreeNode.first = x;
  190. // second pass, put every start passed the center right
  191. y = end;
  192. while (true)
  193. {
  194. while (x <= end && m_Entries[x].intervalStart <= center)
  195. x++;
  196. while (y >= start && m_Entries[y].intervalStart > center)
  197. y--;
  198. if (x > y)
  199. break;
  200. var nodeX = m_Entries[x];
  201. var nodeY = m_Entries[y];
  202. m_Entries[y] = nodeX;
  203. m_Entries[x] = nodeY;
  204. }
  205. intervalTreeNode.last = y;
  206. // reserve a place
  207. m_Nodes.Add(new IntervalTreeNode());
  208. int index = m_Nodes.Count - 1;
  209. intervalTreeNode.left = kInvalidNode;
  210. intervalTreeNode.right = kInvalidNode;
  211. if (start < intervalTreeNode.first)
  212. intervalTreeNode.left = Rebuild(start, intervalTreeNode.first - 1);
  213. if (end > intervalTreeNode.last)
  214. intervalTreeNode.right = Rebuild(intervalTreeNode.last + 1, end);
  215. m_Nodes[index] = intervalTreeNode;
  216. return index;
  217. }
  218. public void Clear()
  219. {
  220. m_Entries.Clear();
  221. m_Nodes.Clear();
  222. }
  223. }
  224. }