QuadTree.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System;
  2. using System.Collections.Generic;
  3. using FarseerPhysics.Collision;
  4. using Microsoft.Xna.Framework;
  5. public class Element<T>
  6. {
  7. public QuadTree<T> Parent;
  8. public AABB Span;
  9. public T Value;
  10. public Element(T value, AABB span)
  11. {
  12. Span = span;
  13. Value = value;
  14. Parent = null;
  15. }
  16. }
  17. public class QuadTree<T>
  18. {
  19. public int MaxBucket;
  20. public int MaxDepth;
  21. public List<Element<T>> Nodes;
  22. public AABB Span;
  23. public QuadTree<T>[] SubTrees;
  24. public QuadTree(AABB span, int maxbucket, int maxdepth)
  25. {
  26. Span = span;
  27. Nodes = new List<Element<T>>();
  28. MaxBucket = maxbucket;
  29. MaxDepth = maxdepth;
  30. }
  31. public bool IsPartitioned
  32. {
  33. get { return SubTrees != null; }
  34. }
  35. /// <summary>
  36. /// returns the quadrant of span that entirely contains test. if none, return 0.
  37. /// </summary>
  38. /// <param name="span"></param>
  39. /// <param name="test"></param>
  40. /// <returns></returns>
  41. private int Partition(AABB span, AABB test)
  42. {
  43. if (span.Q1.Contains(ref test)) return 1;
  44. if (span.Q2.Contains(ref test)) return 2;
  45. if (span.Q3.Contains(ref test)) return 3;
  46. if (span.Q4.Contains(ref test)) return 4;
  47. return 0;
  48. }
  49. public void AddNode(Element<T> node)
  50. {
  51. if (!IsPartitioned)
  52. {
  53. if (Nodes.Count >= MaxBucket && MaxDepth > 0) //bin is full and can still subdivide
  54. {
  55. //
  56. //partition into quadrants and sort existing nodes amonst quads.
  57. //
  58. Nodes.Add(node); //treat new node just like other nodes for partitioning
  59. SubTrees = new QuadTree<T>[4];
  60. SubTrees[0] = new QuadTree<T>(Span.Q1, MaxBucket, MaxDepth - 1);
  61. SubTrees[1] = new QuadTree<T>(Span.Q2, MaxBucket, MaxDepth - 1);
  62. SubTrees[2] = new QuadTree<T>(Span.Q3, MaxBucket, MaxDepth - 1);
  63. SubTrees[3] = new QuadTree<T>(Span.Q4, MaxBucket, MaxDepth - 1);
  64. List<Element<T>> remNodes = new List<Element<T>>();
  65. //nodes that are not fully contained by any quadrant
  66. foreach (Element<T> n in Nodes)
  67. {
  68. switch (Partition(Span, n.Span))
  69. {
  70. case 1: //quadrant 1
  71. SubTrees[0].AddNode(n);
  72. break;
  73. case 2:
  74. SubTrees[1].AddNode(n);
  75. break;
  76. case 3:
  77. SubTrees[2].AddNode(n);
  78. break;
  79. case 4:
  80. SubTrees[3].AddNode(n);
  81. break;
  82. default:
  83. n.Parent = this;
  84. remNodes.Add(n);
  85. break;
  86. }
  87. }
  88. Nodes = remNodes;
  89. }
  90. else
  91. {
  92. node.Parent = this;
  93. Nodes.Add(node);
  94. //if bin is not yet full or max depth has been reached, just add the node without subdividing
  95. }
  96. }
  97. else //we already have children nodes
  98. {
  99. //
  100. //add node to specific sub-tree
  101. //
  102. switch (Partition(Span, node.Span))
  103. {
  104. case 1: //quadrant 1
  105. SubTrees[0].AddNode(node);
  106. break;
  107. case 2:
  108. SubTrees[1].AddNode(node);
  109. break;
  110. case 3:
  111. SubTrees[2].AddNode(node);
  112. break;
  113. case 4:
  114. SubTrees[3].AddNode(node);
  115. break;
  116. default:
  117. node.Parent = this;
  118. Nodes.Add(node);
  119. break;
  120. }
  121. }
  122. }
  123. /// <summary>
  124. /// tests if ray intersects AABB
  125. /// </summary>
  126. /// <param name="aabb"></param>
  127. /// <returns></returns>
  128. public static bool RayCastAABB(AABB aabb, Vector2 p1, Vector2 p2)
  129. {
  130. AABB segmentAABB = new AABB();
  131. {
  132. Vector2.Min(ref p1, ref p2, out segmentAABB.LowerBound);
  133. Vector2.Max(ref p1, ref p2, out segmentAABB.UpperBound);
  134. }
  135. if (!AABB.TestOverlap(aabb, segmentAABB)) return false;
  136. Vector2 rayDir = p2 - p1;
  137. Vector2 rayPos = p1;
  138. Vector2 norm = new Vector2(-rayDir.Y, rayDir.X); //normal to ray
  139. if (norm.Length() == 0.0)
  140. return true; //if ray is just a point, return true (iff point is within aabb, as tested earlier)
  141. norm.Normalize();
  142. float dPos = Vector2.Dot(rayPos, norm);
  143. Vector2[] verts = aabb.GetVertices();
  144. float d0 = Vector2.Dot(verts[0], norm) - dPos;
  145. for (int i = 1; i < 4; i++)
  146. {
  147. float d = Vector2.Dot(verts[i], norm) - dPos;
  148. if (Math.Sign(d) != Math.Sign(d0))
  149. //return true if the ray splits the vertices (ie: sign of dot products with normal are not all same)
  150. return true;
  151. }
  152. return false;
  153. }
  154. public void QueryAABB(Func<Element<T>, bool> callback, ref AABB searchR)
  155. {
  156. Stack<QuadTree<T>> stack = new Stack<QuadTree<T>>();
  157. stack.Push(this);
  158. while (stack.Count > 0)
  159. {
  160. QuadTree<T> qt = stack.Pop();
  161. if (!AABB.TestOverlap(ref searchR, ref qt.Span))
  162. continue;
  163. foreach (Element<T> n in qt.Nodes)
  164. if (AABB.TestOverlap(ref searchR, ref n.Span))
  165. {
  166. if (!callback(n)) return;
  167. }
  168. if (qt.IsPartitioned)
  169. foreach (QuadTree<T> st in qt.SubTrees)
  170. stack.Push(st);
  171. }
  172. }
  173. public void RayCast(Func<RayCastInput, Element<T>, float> callback, ref RayCastInput input)
  174. {
  175. Stack<QuadTree<T>> stack = new Stack<QuadTree<T>>();
  176. stack.Push(this);
  177. float maxFraction = input.MaxFraction;
  178. Vector2 p1 = input.Point1;
  179. Vector2 p2 = p1 + (input.Point2 - input.Point1) * maxFraction;
  180. while (stack.Count > 0)
  181. {
  182. QuadTree<T> qt = stack.Pop();
  183. if (!RayCastAABB(qt.Span, p1, p2))
  184. continue;
  185. foreach (Element<T> n in qt.Nodes)
  186. {
  187. if (!RayCastAABB(n.Span, p1, p2))
  188. continue;
  189. RayCastInput subInput;
  190. subInput.Point1 = input.Point1;
  191. subInput.Point2 = input.Point2;
  192. subInput.MaxFraction = maxFraction;
  193. float value = callback(subInput, n);
  194. if (value == 0.0f)
  195. return; // the client has terminated the raycast.
  196. if (value <= 0.0f)
  197. continue;
  198. maxFraction = value;
  199. p2 = p1 + (input.Point2 - input.Point1) * maxFraction; //update segment endpoint
  200. }
  201. if (IsPartitioned)
  202. foreach (QuadTree<T> st in qt.SubTrees)
  203. stack.Push(st);
  204. }
  205. }
  206. public void GetAllNodesR(ref List<Element<T>> nodes)
  207. {
  208. nodes.AddRange(Nodes);
  209. if (IsPartitioned)
  210. foreach (QuadTree<T> st in SubTrees) st.GetAllNodesR(ref nodes);
  211. }
  212. public void RemoveNode(Element<T> node)
  213. {
  214. node.Parent.Nodes.Remove(node);
  215. }
  216. public void Reconstruct()
  217. {
  218. List<Element<T>> allNodes = new List<Element<T>>();
  219. GetAllNodesR(ref allNodes);
  220. Clear();
  221. allNodes.ForEach(AddNode);
  222. }
  223. public void Clear()
  224. {
  225. Nodes.Clear();
  226. SubTrees = null;
  227. }
  228. }