// Copyright (c) 2014-2015 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using System; using SharpNav.Geometry; #if MONOGAME using Vector3 = Microsoft.Xna.Framework.Vector3; #elif OPENTK using Vector3 = OpenTK.Vector3; #elif SHARPDX using Vector3 = SharpDX.Vector3; #endif namespace SharpNav.Collections.Generic { /// /// A is a uniform 2d grid that can efficiently retrieve items near a specified grid cell. /// /// An equatable type. public class ProximityGrid where T : IEquatable { #region Fields private const int Invalid = -1; //private float cellSize; private float invCellSize; private Item[] pool; private int poolHead; private int[] buckets; private BBox2i bounds; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The size of the item array /// The size of each cell public ProximityGrid(int poolSize, float cellSize) { //this.cellSize = cellSize; this.invCellSize = 1.0f / cellSize; //allocate hash buckets this.buckets = new int[MathHelper.NextPowerOfTwo(poolSize)]; //allocate pool of items this.poolHead = 0; this.pool = new Item[poolSize]; for (int i = 0; i < this.pool.Length; i++) this.pool[i] = new Item(); this.bounds = new BBox2i(Vector2i.Max, Vector2i.Min); Clear(); } #endregion #region Methods /// /// Reset all the data /// public void Clear() { for (int i = 0; i < buckets.Length; i++) buckets[i] = Invalid; poolHead = 0; this.bounds = new BBox2i(Vector2i.Max, Vector2i.Min); } /// /// Take all the coordinates within a certain range and add them all to an array /// /// The value. /// Minimum x-coordinate /// Minimum y-coordinate /// Maximum x-coordinate /// Maximum y-coordinate public void AddItem(T value, float minX, float minY, float maxX, float maxY) { int invMinX = (int)Math.Floor(minX * invCellSize); int invMinY = (int)Math.Floor(minY * invCellSize); int invMaxX = (int)Math.Floor(maxX * invCellSize); int invMaxY = (int)Math.Floor(maxY * invCellSize); bounds.Min.X = Math.Min(bounds.Min.X, invMinX); bounds.Min.Y = Math.Min(bounds.Min.Y, invMinY); bounds.Max.X = Math.Max(bounds.Max.X, invMaxX); bounds.Max.Y = Math.Max(bounds.Max.Y, invMaxY); for (int y = invMinY; y <= invMaxY; y++) { for (int x = invMinX; x <= invMaxX; x++) { if (poolHead < pool.Length) { int h = HashPos2(x, y, buckets.Length); int idx = poolHead; poolHead++; pool[idx].X = x; pool[idx].Y = y; pool[idx].Value = value; pool[idx].Next = buckets[h]; buckets[h] = idx; } } } } /// /// Take all the items within a certain range and add their ids to an array. /// /// The minimum x-coordinate /// The minimum y-coordinate /// The maximum x-coordinate /// The maximum y-coordinate /// The array of values /// The maximum number of values that can be stored /// The number of unique values public int QueryItems(float minX, float minY, float maxX, float maxY, T[] values, int maxVals) { int invMinX = (int)Math.Floor(minX * invCellSize); int invMinY = (int)Math.Floor(minY * invCellSize); int invMaxX = (int)Math.Floor(maxX * invCellSize); int invMaxY = (int)Math.Floor(maxY * invCellSize); int n = 0; for (int y = invMinY; y <= invMaxY; y++) { for (int x = invMinX; x <= invMaxX; x++) { int h = HashPos2(x, y, buckets.Length); int idx = buckets[h]; while (idx != Invalid) { if (pool[idx].X == x && pool[idx].Y == y) { //check if the id exists already int i = 0; while (i != n && !values[i].Equals(pool[idx].Value)) i++; //item not found, add it if (i == n) { if (n >= maxVals) return n; values[n++] = pool[idx].Value; } } idx = pool[idx].Next; } } } return n; } /// /// Gets the number of items at a specific location. /// /// The X coordinate. /// The Y coordinate. /// The number of items at the specified coordinates. public int GetItemCountAtLocation(int x, int y) { int n = 0; int h = HashPos2(x, y, buckets.Length); int idx = buckets[h]; while (idx != Invalid) { Item item = pool[idx]; if (item.X == x && item.Y == y) n++; idx = item.Next; } return n; } /// /// Hash function /// /// The x-coordinate /// The y-coordinate /// Total size of hash table /// A hash value public static int HashPos2(int x, int y, int n) { return ((x * 73856093) ^ (y * 19349663)) & (n - 1); } #endregion /// /// An "item" is simply a coordinate on the proximity grid /// private class Item { public T Value { get; set; } public int X { get; set; } public int Y { get; set; } public int Next { get; set; } } } }