123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- using System.Collections.Generic;
- namespace Pathfinding.Util {
- /// <summary>
- /// Holds a lookup datastructure to quickly find objects inside rectangles.
- /// Objects of type T occupy an integer rectangle in the grid and they can be
- /// moved efficiently. You can query for all objects that touch a specified
- /// rectangle that runs in O(m*k+r) time where m is the number of objects that
- /// the query returns, k is the average number of cells that an object
- /// occupies and r is the area of the rectangle query.
- ///
- /// All objects must be contained within a rectangle with one point at the origin
- /// (inclusive) and one at <see cref="size"/> (exclusive) that is specified in the constructor.
- /// </summary>
- public class GridLookup<T> where T : class {
- Int2 size;
- Item[] cells;
- /// <summary>
- /// Linked list of all items.
- /// Note that the first item in the list is a dummy item and does not contain any data.
- /// </summary>
- Root all = new Root();
- Dictionary<T, Root> rootLookup = new Dictionary<T, Root>();
- Stack<Item> itemPool = new Stack<Item>();
- public GridLookup (Int2 size) {
- this.size = size;
- cells = new Item[size.x*size.y];
- for (int i = 0; i < cells.Length; i++) cells[i] = new Item();
- }
- internal class Item {
- public Root root;
- public Item prev, next;
- }
- public class Root {
- /// <summary>Underlying object</summary>
- public T obj;
- /// <summary>Next item in the linked list of all roots</summary>
- public Root next;
- /// <summary>Previous item in the linked list of all roots</summary>
- internal Root prev;
- internal IntRect previousBounds = new IntRect(0, 0, -1, -1);
- /// <summary>References to an item in each grid cell that this object is contained inside</summary>
- internal List<Item> items = new List<Item>();
- internal bool flag;
- }
- /// <summary>Linked list of all items</summary>
- public Root AllItems {
- get {
- return all.next;
- }
- }
- public void Clear () {
- rootLookup.Clear();
- all.next = null;
- foreach (var item in cells) item.next = null;
- }
- public Root GetRoot (T item) {
- Root root;
- rootLookup.TryGetValue(item, out root);
- return root;
- }
- /// <summary>
- /// Add an object to the lookup data structure.
- /// Returns: A handle which can be used for Move operations
- /// </summary>
- public Root Add (T item, IntRect bounds) {
- var root = new Root {
- obj = item,
- prev = all,
- next = all.next
- };
- all.next = root;
- if (root.next != null) root.next.prev = root;
- rootLookup.Add(item, root);
- Move(item, bounds);
- return root;
- }
- /// <summary>Removes an item from the lookup data structure</summary>
- public void Remove (T item) {
- Root root;
- if (!rootLookup.TryGetValue(item, out root)) {
- return;
- }
- // Make the item occupy no cells at all
- Move(item, new IntRect(0, 0, -1, -1));
- rootLookup.Remove(item);
- root.prev.next = root.next;
- if (root.next != null) root.next.prev = root.prev;
- }
- /// <summary>Move an object to occupy a new set of cells</summary>
- public void Move (T item, IntRect bounds) {
- Root root;
- if (!rootLookup.TryGetValue(item, out root)) {
- throw new System.ArgumentException("The item has not been added to this object");
- }
- var prev = root.previousBounds;
- if (prev == bounds) return;
- // Remove all
- for (int i = 0; i < root.items.Count; i++) {
- Item ob = root.items[i];
- ob.prev.next = ob.next;
- if (ob.next != null) ob.next.prev = ob.prev;
- }
- root.previousBounds = bounds;
- int reusedItems = 0;
- for (int z = bounds.ymin; z <= bounds.ymax; z++) {
- for (int x = bounds.xmin; x <= bounds.xmax; x++) {
- Item ob;
- if (reusedItems < root.items.Count) {
- ob = root.items[reusedItems];
- } else {
- ob = itemPool.Count > 0 ? itemPool.Pop() : new Item();
- ob.root = root;
- root.items.Add(ob);
- }
- reusedItems++;
- ob.prev = cells[x + z*size.x];
- ob.next = ob.prev.next;
- ob.prev.next = ob;
- if (ob.next != null) ob.next.prev = ob;
- }
- }
- for (int i = root.items.Count-1; i >= reusedItems; i--) {
- Item ob = root.items[i];
- ob.root = null;
- ob.next = null;
- ob.prev = null;
- root.items.RemoveAt(i);
- itemPool.Push(ob);
- }
- }
- /// <summary>
- /// Returns all objects of a specific type inside the cells marked by the rectangle.
- /// Note: For better memory usage, consider pooling the list using Pathfinding.Util.ListPool after you are done with it
- /// </summary>
- public List<U> QueryRect<U>(IntRect r) where U : class, T {
- List<U> result = Pathfinding.Util.ListPool<U>.Claim();
- // Loop through tiles and check which objects are inside them
- for (int z = r.ymin; z <= r.ymax; z++) {
- var zs = z*size.x;
- for (int x = r.xmin; x <= r.xmax; x++) {
- Item c = cells[x + zs];
- // Note, first item is a dummy, so it is ignored
- while (c.next != null) {
- c = c.next;
- var obj = c.root.obj as U;
- if (!c.root.flag && obj != null) {
- c.root.flag = true;
- result.Add(obj);
- }
- }
- }
- }
- // Reset flags
- for (int z = r.ymin; z <= r.ymax; z++) {
- var zs = z*size.x;
- for (int x = r.xmin; x <= r.xmax; x++) {
- Item c = cells[x + zs];
- while (c.next != null) {
- c = c.next;
- c.root.flag = false;
- }
- }
- }
- return result;
- }
- }
- }
|