/// <summary> /// Returns node count for given node including it's childs. /// </summary> /// <param name="node">The node</param> /// <param name="count">Starting value for count.</param> /// <returns>The counf on nodes.</returns> private int GetQuadNodeCount(QuadNode node, int count) { if (node == null) { return(count); } foreach (QuadNode childNode in node.Nodes) { if (childNode != null) { count++; } } return(count); }
/// <summary> /// Removes object from it's node. /// </summary> /// <param name="object">The object to remove.</param> public void Remove(WorldObject @object) { if (!_objectToNodeLookup.ContainsKey(@object)) { throw new KeyNotFoundException("QuadTree:Remove() - Object not found in dictionary for removal."); } QuadNode containingNode = _objectToNodeLookup[@object]; // get the object. RemoveObjectFromNode(@object); // remove it. if (containingNode.Parent != null) { CheckChildNodes(containingNode.Parent); // check all child nodes of parent. } }
private void ObjectPositionChanged(object sender, EventArgs e) { var @object = sender as WorldObject; if (@object == null) { return; } QuadNode node = this._objectToNodeLookup[@object]; if (node.Bounds.Contains(@object.Bounds) && !node.HasChildNodes()) { return; } this.RemoveObjectFromNode(@object); Insert(@object); if (node.Parent != null) { CheckChildNodes(node.Parent); } }
/// <summary> /// Expands the root node bounds. /// </summary> /// <param name="newChildBounds"></param> private void ExpandRoot(Rect newChildBounds) { bool isNorth = RootNode.Bounds.Y < newChildBounds.Y; bool isWest = RootNode.Bounds.X < newChildBounds.X; Direction rootDirection = isNorth // find the direction. ? (isWest ? Direction.NorthWest : Direction.NorthEast) : (isWest ? Direction.SouthWest : Direction.SouthEast); double newX = (rootDirection == Direction.NorthWest || rootDirection == Direction.SouthWest) ? RootNode.Bounds.X : RootNode.Bounds.X - RootNode.Bounds.Width; double newY = (rootDirection == Direction.NorthWest || rootDirection == Direction.NorthEast) ? RootNode.Bounds.Y : RootNode.Bounds.Y - RootNode.Bounds.Height; var newRootBounds = new Rect(newX, newY, RootNode.Bounds.Width * 2, RootNode.Bounds.Height * 2); var newRoot = new QuadNode(newRootBounds); this.SetupChildNodes(newRoot); newRoot[rootDirection] = RootNode; this.RootNode = newRoot; }
/// <summary> /// Inserts a new object. /// </summary> /// <param name="object">The object to be inserted.</param> public void Insert(WorldObject @object) { if (RootNode == null) // create a new root-node if it does not exist yet. { var rootSize = new Size(System.Math.Ceiling(@object.Bounds.Width / MinimumLeafSize.Width), System.Math.Ceiling(@object.Bounds.Height / MinimumLeafSize.Height)); double multiplier = System.Math.Max(rootSize.Width, rootSize.Height); rootSize = new Size(MinimumLeafSize.Width * multiplier, MinimumLeafSize.Height * multiplier); var center = new Point(@object.Bounds.X + @object.Bounds.Width / 2, @object.Bounds.Y + @object.Bounds.Height / 2); var rootOrigin = new Point(center.X - rootSize.Width / 2, center.Y - rootSize.Height / 2); this.RootNode = new QuadNode(new Rect(rootOrigin, rootSize)); } while (!RootNode.Bounds.Contains(@object.Bounds)) // if root-node's bounds does not contain object, expand the root. { this.ExpandRoot(@object.Bounds); } this.InsertNodeObject(RootNode, @object); // insert the object to rootNode. }
/// <summary> /// Creates a new QuadTree. /// </summary> /// <param name="minimumLeafSize">The smallest size a leaf will split into.</param> /// <param name="maximumObjectsPerLeaf">Maximum number of objects per left before it's forced to split into sub-quadrans.</param> public QuadTree(Size minimumLeafSize, int maximumObjectsPerLeaf) { this.RootNode = null; this.MinimumLeafSize = minimumLeafSize; this.MaximumObjectsPerLeaf = maximumObjectsPerLeaf; }
/// <summary> /// Checks child nodes of the node. /// </summary> /// <param name="node">The parent node.</param> private void CheckChildNodes(QuadNode node) { if (GetTotalObjectCount(node) > MaximumObjectsPerLeaf) { return; } // Move child objects into this node, and delete sub nodes List <WorldObject> subChildObjects = GetChildObjects(node); foreach (WorldObject childObject in subChildObjects) { if (node.ContainedObjects.Values.Contains(childObject)) { continue; } RemoveObjectFromNode(childObject); AddObjectToNode(node, childObject); } if (node[Direction.NorthWest] != null) { node[Direction.NorthWest].Parent = null; node[Direction.NorthWest] = null; } if (node[Direction.NorthEast] != null) { node[Direction.NorthEast].Parent = null; node[Direction.NorthEast] = null; } if (node[Direction.SouthWest] != null) { node[Direction.SouthWest].Parent = null; node[Direction.SouthWest] = null; } if (node[Direction.SouthEast] != null) { node[Direction.SouthEast].Parent = null; node[Direction.SouthEast] = null; } if (node.Parent != null) { CheckChildNodes(node.Parent); } else { // Its the root node, see if we're down to one quadrant, with none in local storage - if so, ditch the other three. int numQuadrantsWithObjects = 0; QuadNode nodeWithObjects = null; foreach (QuadNode childNode in node.Nodes) { if (childNode == null || GetTotalObjectCount(childNode) <= 0) { continue; } numQuadrantsWithObjects++; nodeWithObjects = childNode; if (numQuadrantsWithObjects > 1) { break; } } if (numQuadrantsWithObjects == 1) // if we have only one quadrand with objects, make it the new rootNode. { foreach (QuadNode childNode in node.Nodes) { if (childNode != nodeWithObjects) { childNode.Parent = null; } } this.RootNode = nodeWithObjects; } } }
/// <summary> /// Adds an object to a given node. /// </summary> /// <param name="node"></param> /// <param name="object"></param> private void AddObjectToNode(QuadNode node, WorldObject @object) { node.ContainedObjects.TryAdd(@object.DynamicID, @object); _objectToNodeLookup.TryAdd(@object, node); @object.PositionChanged += ObjectPositionChanged; }