summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-05-23 10:08:29 +0800
committerchai <215380520@qq.com>2024-05-23 10:08:29 +0800
commit8722a9920c1f6119bf6e769cba270e63097f8e25 (patch)
tree2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs
parent3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff)
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs220
1 files changed, 220 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs
new file mode 100644
index 0000000..f412ab7
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Utilities/GridLookup.cs
@@ -0,0 +1,220 @@
+using System.Collections.Generic;
+
+namespace Pathfinding.Graphs.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;
+
+ public UnityEngine.Vector3 previousPosition = new UnityEngine.Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
+ public UnityEngine.Quaternion previousRotation;
+ }
+
+ /// <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) {
+ rootLookup.TryGetValue(item, out Root 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) {
+ if (!rootLookup.TryGetValue(item, out Root 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;
+ }
+
+ public void Dirty (T item) {
+ if (!rootLookup.TryGetValue(item, out Root root)) {
+ return;
+ }
+
+ root.previousPosition = new UnityEngine.Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
+ }
+
+ /// <summary>Move an object to occupy a new set of cells</summary>
+ public void Move (T item, IntRect bounds) {
+ if (!rootLookup.TryGetValue(item, out Root 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;
+ }
+
+ public void Resize (IntRect newBounds) {
+ var newCells = new Item[newBounds.Width * newBounds.Height];
+ for (int z = 0; z < size.y; z++) {
+ for (int x = 0; x < size.x; x++) {
+ if (newBounds.Contains(x, z)) {
+ newCells[(x - newBounds.xmin) + (z - newBounds.ymin) * newBounds.Width] = cells[x + z*size.x];
+ }
+ }
+ }
+ for (int i = 0; i < newCells.Length; i++) {
+ if (newCells[i] == null) newCells[i] = new Item();
+ }
+ this.size = new Int2(newBounds.Width, newBounds.Height);
+ this.cells = newCells;
+ var root = this.AllItems;
+ var offset = new Int2(-newBounds.xmin, -newBounds.ymin);
+ var bounds = new IntRect(0, 0, newBounds.Width - 1, newBounds.Height - 1);
+ while (root != null) {
+ root.previousBounds = IntRect.Intersection(root.previousBounds.Offset(offset), bounds);
+ root = root.next;
+ }
+ }
+ }
+}