From 8aa64fc6645412f2386898218ff629fad7883382 Mon Sep 17 00:00:00 2001
From: cosmonaut <evan@moonside.games>
Date: Sat, 19 Mar 2022 10:48:23 -0700
Subject: [PATCH] do not return duplicate values from spatial hash

---
 src/Collision/SpatialHash2D.cs | 45 +++++++++++++++++++++++++++++-----
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/src/Collision/SpatialHash2D.cs b/src/Collision/SpatialHash2D.cs
index 9a1237c..4943387 100644
--- a/src/Collision/SpatialHash2D.cs
+++ b/src/Collision/SpatialHash2D.cs
@@ -19,6 +19,7 @@ namespace MoonWorks.Collision
 		public int MinY { get; private set; } = 0;
 		public int MaxY { get; private set; } = 0;
 
+		private Queue<HashSet<T>> hashSetPool = new Queue<HashSet<T>>();
 
 		public SpatialHash2D(int cellSize)
 		{
@@ -68,6 +69,8 @@ namespace MoonWorks.Collision
 		/// </summary>
 		public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(T id, IHasAABB2D shape, Transform2D transform2D)
 		{
+			var returned = AcquireHashSet();
+
 			var box = shape.TransformedAABB(transform2D);
 			var (minX, minY) = Hash(box.Min);
 			var (maxX, maxY) = Hash(box.Max);
@@ -86,15 +89,21 @@ namespace MoonWorks.Collision
 					{
 						foreach (var t in hashDictionary[key])
 						{
-							var (otherShape, otherTransform) = IDLookup[t];
-							if (!id.Equals(t) && AABB2D.TestOverlap(box, otherShape.TransformedAABB(otherTransform)))
+							if (!returned.Contains(t))
 							{
-								yield return (t, otherShape, otherTransform);
+								var (otherShape, otherTransform) = IDLookup[t];
+								if (!id.Equals(t) && AABB2D.TestOverlap(box, otherShape.TransformedAABB(otherTransform)))
+								{
+									returned.Add(t);
+									yield return (t, otherShape, otherTransform);
+								}
 							}
 						}
 					}
 				}
 			}
+
+			FreeHashSet(returned);
 		}
 
 
@@ -105,6 +114,8 @@ namespace MoonWorks.Collision
 		/// <returns></returns>
 		public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(AABB2D aabb)
 		{
+			var returned = AcquireHashSet();
+
 			var (minX, minY) = Hash(aabb.Min);
 			var (maxX, maxY) = Hash(aabb.Max);
 
@@ -122,15 +133,20 @@ namespace MoonWorks.Collision
 					{
 						foreach (var t in hashDictionary[key])
 						{
-							var (otherShape, otherTransform) = IDLookup[t];
-							if (AABB2D.TestOverlap(aabb, otherShape.TransformedAABB(otherTransform)))
+							if (!returned.Contains(t))
 							{
-								yield return (t, otherShape, otherTransform);
+								var (otherShape, otherTransform) = IDLookup[t];
+								if (AABB2D.TestOverlap(aabb, otherShape.TransformedAABB(otherTransform)))
+								{
+									yield return (t, otherShape, otherTransform);
+								}
 							}
 						}
 					}
 				}
 			}
+
+			FreeHashSet(returned);
 		}
 
 		/// <summary>
@@ -150,5 +166,22 @@ namespace MoonWorks.Collision
 		{
 			return ((long) left << 32) | ((uint) right);
 		}
+
+		private HashSet<T> AcquireHashSet()
+		{
+			if (hashSetPool.Count == 0)
+			{
+				hashSetPool.Enqueue(new HashSet<T>());
+			}
+
+			var hashSet = hashSetPool.Dequeue();
+			hashSet.Clear();
+			return hashSet;
+		}
+
+		private void FreeHashSet(HashSet<T> hashSet)
+		{
+			hashSetPool.Enqueue(hashSet);
+		}
 	}
 }