PageRenderTime 22ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Addons/WCell.Addons.Terrain/QuadTree.cs

https://github.com/primax/WCell
C# | 331 lines | 181 code | 49 blank | 101 comment | 20 complexity | 499f8d92aa858dc3c97f2920156a9659 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using WCell.Constants;
  7. using WCell.Util.Graphics;
  8. namespace WCell.Addons.Terrain
  9. {
  10. public class zzQuadTree<T> where T : IBounded
  11. {
  12. /// <summary>
  13. /// The root zzQuadTreeNode
  14. /// </summary>
  15. private readonly zzQuadTreeNode<T> m_root;
  16. /// <summary>
  17. /// The bounds of this QuadTree
  18. /// </summary>
  19. private readonly BoundingBox m_rectangle;
  20. /// <summary>
  21. /// An delegate that performs an action on a zzQuadTreeNode
  22. /// </summary>
  23. /// <param name="obj"></param>
  24. public delegate void QTAction(zzQuadTreeNode<T> obj);
  25. public zzQuadTree(BoundingBox rectangle)
  26. {
  27. m_rectangle = rectangle;
  28. m_root = new zzQuadTreeNode<T>(m_rectangle);
  29. }
  30. /// <summary>
  31. /// Get the count of items in the QuadTree
  32. /// </summary>
  33. public int Count { get { return m_root.Count; } }
  34. /// <summary>
  35. /// Insert the feature into the QuadTree
  36. /// </summary>
  37. /// <param name="item"></param>
  38. public void Insert(T item)
  39. {
  40. m_root.Insert(item);
  41. }
  42. /// <summary>
  43. /// Query the QuadTree, returning the items that are in the given area
  44. /// </summary>
  45. /// <param name="area"></param>
  46. /// <returns></returns>
  47. public List<T> Query(BoundingBox area)
  48. {
  49. return m_root.Query(area);
  50. }
  51. /// <summary>
  52. /// Do the specified action for each item in the quadtree
  53. /// </summary>
  54. /// <param name="action"></param>
  55. public void ForEach(QTAction action)
  56. {
  57. m_root.ForEach(action);
  58. }
  59. }
  60. public class zzQuadTreeNode<T> where T : IBounded
  61. {
  62. /// <summary>
  63. /// The contents of this node.
  64. /// Note that the contents have no limit: this is not the standard way to impement a QuadTree
  65. /// </summary>
  66. private readonly List<T> m_contents = new List<T>();
  67. /// <summary>
  68. /// The child nodes of the QuadTree
  69. /// </summary>
  70. private readonly List<zzQuadTreeNode<T>> m_nodes = new List<zzQuadTreeNode<T>>(4);
  71. /// <summary>
  72. /// Construct a quadtree node with the given bounds
  73. /// </summary>
  74. public zzQuadTreeNode(BoundingBox bounds)
  75. {
  76. Bounds = bounds;
  77. }
  78. /// <summary>
  79. /// Is the node empty
  80. /// </summary>
  81. public bool IsEmpty
  82. {
  83. get
  84. {
  85. return (m_nodes.Count == 0 || Bounds.Height == 0 || Bounds.Width == 0);
  86. }
  87. }
  88. /// <summary>
  89. /// Area of the quadtree node
  90. /// </summary>
  91. public BoundingBox Bounds
  92. {
  93. get;
  94. private set;
  95. }
  96. /// <summary>
  97. /// Total number of nodes in this node and all SubNodes
  98. /// </summary>
  99. public int Count
  100. {
  101. get
  102. {
  103. var count = 0;
  104. foreach (var node in m_nodes)
  105. count += node.Count;
  106. count += Contents.Count;
  107. return count;
  108. }
  109. }
  110. /// <summary>
  111. /// Return the contents of this node and all subnodes in the tree below this one.
  112. /// </summary>
  113. public List<T> SubTreeContents
  114. {
  115. get
  116. {
  117. var results = new List<T>();
  118. foreach (var node in m_nodes)
  119. results.AddRange(node.SubTreeContents);
  120. results.AddRange(this.Contents);
  121. return results;
  122. }
  123. }
  124. public List<T> Contents { get { return m_contents; } }
  125. /// <summary>
  126. /// Query the QuadTree for items that are in the given area
  127. /// </summary>
  128. /// <param name="queryArea"></pasram>
  129. /// <returns></returns>
  130. public List<T> Query(BoundingBox queryArea)
  131. {
  132. // create a list of the items that are found
  133. var results = new List<T>();
  134. // this quad contains items that are not entirely contained by
  135. // it's four sub-quads. Iterate through the items in this quad
  136. // to see if they intersect.
  137. foreach (var item in Contents)
  138. {
  139. var bounds = item.Bounds;
  140. if (queryArea.Intersects(ref bounds) != IntersectionType.NoIntersection)
  141. {
  142. results.Add(item);
  143. }
  144. }
  145. foreach (var node in m_nodes)
  146. {
  147. if (node.IsEmpty)
  148. continue;
  149. // Case 1: search area completely contained by sub-quad
  150. // if a node completely contains the query area, go down that branch
  151. // and skip the remaining nodes (break this loop)
  152. if (node.Bounds.Contains(ref queryArea))
  153. {
  154. results.AddRange(node.Query(queryArea));
  155. break;
  156. }
  157. // Case 2: Sub-quad completely contained by search area
  158. // if the query area completely contains a sub-quad,
  159. // just add all the contents of that quad and it's children
  160. // to the result set. You need to continue the loop to test
  161. // the other quads
  162. var bounds = node.Bounds;
  163. if (queryArea.Contains(ref bounds))
  164. {
  165. results.AddRange(node.SubTreeContents);
  166. continue;
  167. }
  168. // Case 3: search area intersects with sub-quad
  169. // traverse into this quad, continue the loop to search other
  170. // quads
  171. if (node.Bounds.Intersects(ref queryArea) == IntersectionType.Intersects)
  172. {
  173. results.AddRange(node.Query(queryArea));
  174. }
  175. }
  176. return results;
  177. }
  178. /// <summary>
  179. /// Insert an item to this node
  180. /// </summary>
  181. public void Insert(T item)
  182. {
  183. // if the item is not contained in this quad, there's a problem
  184. var bounds = item.Bounds;
  185. if (!Bounds.Contains(ref bounds))
  186. {
  187. Trace.TraceWarning("feature is out of the bounds of this quadtree node");
  188. return;
  189. }
  190. // if the subnodes are null create them. may not be sucessfull: see below
  191. // we may be at the smallest allowed size in which case the subnodes will not be created
  192. if (m_nodes.Count == 0)
  193. CreateSubNodes();
  194. // for each subnode:
  195. // if the node contains the item, add the item to that node and return
  196. // this recurses into the node that is just large enough to fit this item
  197. foreach (var node in m_nodes)
  198. {
  199. if (!node.Bounds.Contains(ref bounds)) continue;
  200. node.Insert(item);
  201. return;
  202. }
  203. // if we make it to here, either
  204. // 1) none of the subnodes completely contained the item. or
  205. // 2) we're at the smallest subnode size allowed
  206. // add the item to this node's contents.
  207. Contents.Add(item);
  208. }
  209. /// <summary>
  210. /// Remove an item from this node
  211. /// </summary>
  212. public void Remove(T item)
  213. {
  214. // if the item is not contained in this quad, there's a problem
  215. var bounds = item.Bounds;
  216. if (!Bounds.Contains(ref bounds))
  217. {
  218. Trace.TraceWarning("feature is out of the bounds of this quadtree node");
  219. return;
  220. }
  221. // for each subnode:
  222. // if the node contains the item, add the item to that node and return
  223. // this recurses into the node that is just large enough to fit this item
  224. foreach (var node in m_nodes)
  225. {
  226. if (!node.Bounds.Contains(ref bounds)) continue;
  227. node.Remove(item);
  228. return;
  229. }
  230. // if we make it to here, either
  231. // 1) none of the subnodes completely contained the item. or
  232. // 2) we're at the smallest subnode size allowed
  233. // remove the item from this node's contents.
  234. Contents.Remove(item);
  235. }
  236. public void ForEach(zzQuadTree<T>.QTAction action)
  237. {
  238. action(this);
  239. // draw the child quads
  240. foreach (var node in m_nodes)
  241. node.ForEach(action);
  242. }
  243. /// <summary>
  244. /// Internal method to create the subnodes (partitions space)
  245. /// </summary>
  246. private void CreateSubNodes()
  247. {
  248. // the smallest subnode will be chunk-sized
  249. if (Bounds.Height <= TerrainConstants.ChunkSize)
  250. return;
  251. if (Bounds.Width <= TerrainConstants.ChunkSize)
  252. return;
  253. var halfWidth = (Bounds.Width / 2.0f);
  254. var halfHeight = (Bounds.Height / 2.0f);
  255. var centerPoint = new Vector2(Bounds.Min.X + halfWidth, Bounds.Min.Y + halfHeight);
  256. var bottomMiddle = new Vector2(Bounds.Min.X + halfWidth, Bounds.Min.Y);
  257. var rightMiddle = new Vector2(Bounds.Max.X, Bounds.Max.Y - halfHeight);
  258. var leftMiddle = new Vector2(Bounds.Min.X, Bounds.Min.Y + halfHeight);
  259. var topMiddle = new Vector2(Bounds.Max.X - halfWidth, Bounds.Max.Y);
  260. // Bottom Left
  261. m_nodes.Add(new zzQuadTreeNode<T>(new BoundingBox(Bounds.Min, new Vector3(centerPoint, Bounds.Max.Z))));
  262. // Bottom Right
  263. m_nodes.Add(
  264. new zzQuadTreeNode<T>(new BoundingBox(new Vector3(bottomMiddle, Bounds.Min.Z),
  265. new Vector3(rightMiddle, Bounds.Max.Z))));
  266. // Top Left
  267. m_nodes.Add(
  268. new zzQuadTreeNode<T>(new BoundingBox(new Vector3(leftMiddle, Bounds.Min.Z),
  269. new Vector3(topMiddle, Bounds.Max.Z))));
  270. // Top Right
  271. m_nodes.Add(new zzQuadTreeNode<T>(new BoundingBox(new Vector3(centerPoint, Bounds.Min.Z), Bounds.Max)));
  272. }
  273. }
  274. public interface IBounded
  275. {
  276. BoundingBox Bounds
  277. {
  278. get;
  279. set;
  280. }
  281. }
  282. }