GridLookup.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using System.Collections.Generic;
  2. namespace Pathfinding.Util {
  3. /// <summary>
  4. /// Holds a lookup datastructure to quickly find objects inside rectangles.
  5. /// Objects of type T occupy an integer rectangle in the grid and they can be
  6. /// moved efficiently. You can query for all objects that touch a specified
  7. /// rectangle that runs in O(m*k+r) time where m is the number of objects that
  8. /// the query returns, k is the average number of cells that an object
  9. /// occupies and r is the area of the rectangle query.
  10. ///
  11. /// All objects must be contained within a rectangle with one point at the origin
  12. /// (inclusive) and one at <see cref="size"/> (exclusive) that is specified in the constructor.
  13. /// </summary>
  14. public class GridLookup<T> where T : class {
  15. Int2 size;
  16. Item[] cells;
  17. /// <summary>
  18. /// Linked list of all items.
  19. /// Note that the first item in the list is a dummy item and does not contain any data.
  20. /// </summary>
  21. Root all = new Root();
  22. Dictionary<T, Root> rootLookup = new Dictionary<T, Root>();
  23. Stack<Item> itemPool = new Stack<Item>();
  24. public GridLookup (Int2 size) {
  25. this.size = size;
  26. cells = new Item[size.x*size.y];
  27. for (int i = 0; i < cells.Length; i++) cells[i] = new Item();
  28. }
  29. internal class Item {
  30. public Root root;
  31. public Item prev, next;
  32. }
  33. public class Root {
  34. /// <summary>Underlying object</summary>
  35. public T obj;
  36. /// <summary>Next item in the linked list of all roots</summary>
  37. public Root next;
  38. /// <summary>Previous item in the linked list of all roots</summary>
  39. internal Root prev;
  40. internal IntRect previousBounds = new IntRect(0, 0, -1, -1);
  41. /// <summary>References to an item in each grid cell that this object is contained inside</summary>
  42. internal List<Item> items = new List<Item>();
  43. internal bool flag;
  44. }
  45. /// <summary>Linked list of all items</summary>
  46. public Root AllItems {
  47. get {
  48. return all.next;
  49. }
  50. }
  51. public void Clear () {
  52. rootLookup.Clear();
  53. all.next = null;
  54. foreach (var item in cells) item.next = null;
  55. }
  56. public Root GetRoot (T item) {
  57. Root root;
  58. rootLookup.TryGetValue(item, out root);
  59. return root;
  60. }
  61. /// <summary>
  62. /// Add an object to the lookup data structure.
  63. /// Returns: A handle which can be used for Move operations
  64. /// </summary>
  65. public Root Add (T item, IntRect bounds) {
  66. var root = new Root {
  67. obj = item,
  68. prev = all,
  69. next = all.next
  70. };
  71. all.next = root;
  72. if (root.next != null) root.next.prev = root;
  73. rootLookup.Add(item, root);
  74. Move(item, bounds);
  75. return root;
  76. }
  77. /// <summary>Removes an item from the lookup data structure</summary>
  78. public void Remove (T item) {
  79. Root root;
  80. if (!rootLookup.TryGetValue(item, out root)) {
  81. return;
  82. }
  83. // Make the item occupy no cells at all
  84. Move(item, new IntRect(0, 0, -1, -1));
  85. rootLookup.Remove(item);
  86. root.prev.next = root.next;
  87. if (root.next != null) root.next.prev = root.prev;
  88. }
  89. /// <summary>Move an object to occupy a new set of cells</summary>
  90. public void Move (T item, IntRect bounds) {
  91. Root root;
  92. if (!rootLookup.TryGetValue(item, out root)) {
  93. throw new System.ArgumentException("The item has not been added to this object");
  94. }
  95. var prev = root.previousBounds;
  96. if (prev == bounds) return;
  97. // Remove all
  98. for (int i = 0; i < root.items.Count; i++) {
  99. Item ob = root.items[i];
  100. ob.prev.next = ob.next;
  101. if (ob.next != null) ob.next.prev = ob.prev;
  102. }
  103. root.previousBounds = bounds;
  104. int reusedItems = 0;
  105. for (int z = bounds.ymin; z <= bounds.ymax; z++) {
  106. for (int x = bounds.xmin; x <= bounds.xmax; x++) {
  107. Item ob;
  108. if (reusedItems < root.items.Count) {
  109. ob = root.items[reusedItems];
  110. } else {
  111. ob = itemPool.Count > 0 ? itemPool.Pop() : new Item();
  112. ob.root = root;
  113. root.items.Add(ob);
  114. }
  115. reusedItems++;
  116. ob.prev = cells[x + z*size.x];
  117. ob.next = ob.prev.next;
  118. ob.prev.next = ob;
  119. if (ob.next != null) ob.next.prev = ob;
  120. }
  121. }
  122. for (int i = root.items.Count-1; i >= reusedItems; i--) {
  123. Item ob = root.items[i];
  124. ob.root = null;
  125. ob.next = null;
  126. ob.prev = null;
  127. root.items.RemoveAt(i);
  128. itemPool.Push(ob);
  129. }
  130. }
  131. /// <summary>
  132. /// Returns all objects of a specific type inside the cells marked by the rectangle.
  133. /// Note: For better memory usage, consider pooling the list using Pathfinding.Util.ListPool after you are done with it
  134. /// </summary>
  135. public List<U> QueryRect<U>(IntRect r) where U : class, T {
  136. List<U> result = Pathfinding.Util.ListPool<U>.Claim();
  137. // Loop through tiles and check which objects are inside them
  138. for (int z = r.ymin; z <= r.ymax; z++) {
  139. var zs = z*size.x;
  140. for (int x = r.xmin; x <= r.xmax; x++) {
  141. Item c = cells[x + zs];
  142. // Note, first item is a dummy, so it is ignored
  143. while (c.next != null) {
  144. c = c.next;
  145. var obj = c.root.obj as U;
  146. if (!c.root.flag && obj != null) {
  147. c.root.flag = true;
  148. result.Add(obj);
  149. }
  150. }
  151. }
  152. }
  153. // Reset flags
  154. for (int z = r.ymin; z <= r.ymax; z++) {
  155. var zs = z*size.x;
  156. for (int x = r.xmin; x <= r.xmax; x++) {
  157. Item c = cells[x + zs];
  158. while (c.next != null) {
  159. c = c.next;
  160. c.root.flag = false;
  161. }
  162. }
  163. }
  164. return result;
  165. }
  166. }
  167. }