/** * Find node that contain element * * Return QuadtreeNode that has element **/ public QuadtreeNode FindNode(IQuadtreeAgent element){ if (element == null) return null; if (boundary.ContainPoint2D (element.GetCenter()) == CollisionResult.None) { //element never added to quadtree #if DEBUG Debug.LogWarning("Unable to find element, element never exist under this quadtree node"); #endif return null; } //Search child node that contain element position if (nodes != null) { int nodeIndex = IndexOfNode (element); //also check element boundary within node boundary if (nodeIndex >= 0 && (element.GetShape().IntersectWithShape(nodes[nodeIndex].boundary) == CollisionResult.Fit)) return nodes [nodeIndex].FindNode(element); } if (elements.Contains (element) || overlapElements.Contains (element)) return this; //Something went wrong element within boundary but not in list return null; }
/** * Find possible elements that might contact with given element * from root node * * Param includeSelf is true given element might be in the result, default is false **/ public List<IQuadtreeAgent> FindElementsFromRoot(IQuadtreeAgent element, bool includeSelf = false){ //If not root then find root and find elements if (parentNode != null) return rootQuadtree ().FindElements (element, includeSelf); return FindElements (element, includeSelf); }
/** * Find element from root quadtree * * Return QuadtreeNode that has element **/ public QuadtreeNode FindNodeFromRoot(IQuadtreeAgent element){ if (element == null) return null; //If this quadtree is not root if (parentNode != null) return rootQuadtree ().FindNode (element); return FindNode (element); }
/** * Remove element under this quadtree node **/ public bool Remove(IQuadtreeAgent element){ bool removed = false; //If element in this node then remove it if (elements.Contains (element) || overlapElements.Contains (element)) { elements.Remove (element); overlapElements.Remove (element); removed = true; } if (!removed) { //search child if(nodes != null){ int nodeIndex = IndexOfNode (element); if (nodeIndex >= 0) { CollisionResult r = element.GetShape().IntersectWithShape (nodes [nodeIndex].boundary); switch (r) { case CollisionResult.Fit://can only fit in one child node removed = nodes [nodeIndex].Remove (element); break; } } } } if (removed == true) organizeTree = true; #if DEBUG if(!removed) Debug.LogError("Can not remove element"); #endif return removed; }
/** * Return index of child node under this node * * This is noly check for element's positon not boundary * * Return -1 if not found **/ int IndexOfNode(IQuadtreeAgent element){ if ((element == null) || (nodes == null)) return -1; IEnumerator er = nodes.GetEnumerator (); int nodeIndex = 0; while (er.MoveNext ()) { if ((er.Current as QuadtreeNode).boundary.ContainPoint2D (element.GetCenter ()) != CollisionResult.None) { return nodeIndex; } nodeIndex++; } #if DEBUG Debug.LogError("Element "+((QtAgent)element).agGameObject.name+" does not located in this node fix me"); #endif return -1; }
/** * Find elements that might contact with given element * under this node. * * * Param includeSelf is true given element might be in the result, default is false * * Param upwardSearch true query will find the node that query shape complete fit in in first * place then start search downward from that node. It is recommend to leave value as true for * more accurate result. * * Param compare a function you can provide and do addition check base on result it found **/ public List<IQuadtreeAgent> FindElements(IQuadtreeAgent element, bool upwardSearch = true, bool includeSelf = false, OnCompare compare = null){ if (element == null) { #if DEBUG Debug.LogError("Can't find elements, given element is null"); #endif return null; } /** * Upward search from this node * * This will go up until the node wihch element shape complete fit in **/ if (upwardSearch) { //If this is the node element shape complete fit in CollisionResult cResult = element.GetShape().IntersectWithShape(boundary); //we found the node then start from this node if (cResult == CollisionResult.Fit) { //downward search from parent if there is parent node //the reason from parent node is that parent node's overlap element //might be contact this shape if (parentNode != null) { //from parent node we start downward search return parentNode.FindElements (element, false, includeSelf, compare); } } else {//continue search upward //If there is a parent node otherwsie this is root node and start from //here downward search if (parentNode != null) { //Continue finding upward until the node element shape can totally fit in return parentNode.FindElements (element, true, includeSelf, compare); } } } /** * Downward search from this node * * Downward search only search elements in child node include this node **/ List<IQuadtreeAgent> result = new List<IQuadtreeAgent> (); CollisionResult r = element.GetShape ().IntersectWithShape (boundary); //if not contact with element shape if (r == CollisionResult.None) return result; //search child nodes IEnumerator er = nodes.GetEnumerator(); while (er.MoveNext ()) { //downward search child result.AddRange ((er.Current as QuadtreeNode).FindElements (element, false, includeSelf, compare)); } //add this node's elements result.AddRange (elements); result.AddRange (overlapElements); if (!includeSelf) { result.Remove (element); } if (compare != null) { List<IQuadtreeAgent> filterResult = new List<IQuadtreeAgent>(); List<IQuadtreeAgent>.Enumerator qer = result.GetEnumerator (); while (qer.MoveNext ()) { if (compare (qer.Current)) filterResult.Add (qer.Current); } return filterResult; } return result; }
/** * Add element to quadtree immediately under this * quadtree node or deeper child node **/ public bool Add(IQuadtreeAgent newElement){ if (newElement == null) { #if DEBUG Debug.LogError("Node does not implement IQuadtree"); #endif return false; } //If we have child node if (nodes != null) { int nodeIndex = IndexOfNode (newElement); //can't find index of node if (nodeIndex < 0) { #if DEBUG Debug.LogError("No child node found"+" node boundary center "+this.boundary.Center + "level "+Depth +" xMin " + boundary.xMin + " xMax "+boundary.xMax +" yMin " + boundary.yMin + " yMax "+boundary.yMax +" element center "+newElement.GetCenter() + " element last center "+ (newElement as QtAgent).lastPosition); #endif return false; } //check if element's boundary fit in the //child node otherwise element is overlapping multiple node CollisionResult iResult = newElement.GetShape().IntersectWithShape(nodes[nodeIndex].boundary); switch (iResult) { case CollisionResult.Fit: //Element's boudnary fit in child node's boundary, add it to child node return nodes [nodeIndex].Add (newElement); case CollisionResult.Overlap: //Element's boudnary overlap multiple child node, add it to this node overlapElements.Add (newElement); //notify element it has been added to this quadtree node newElement.AddToQuadtreeNode (this); return true; case CollisionResult.None: //Element's boundary not intersect with child node //Something went wrong #if DEBUG Debug.LogError("Unable to add element, element position within node but not intersect with any child node"); #endif return false; } return false; } elements.Add (newElement); //notify element it has been added to this quadtree node newElement.AddToQuadtreeNode (this); //Split if needed if (elements.Count > elementCapacity) { Split (); //Distribute elements to corespond child node List<IQuadtreeAgent>.Enumerator er = elements.GetEnumerator(); while (er.MoveNext ()) { if (boundary.ContainPoint2D (er.Current.GetCenter ()) != CollisionResult.None) { if (!Add (er.Current)) { #if DEBUG Debug.LogWarning("Distribute element to child node fail, elemnt is out of node boundary." + "We need to add from root"); #endif //add element from root if can't add to child node rootQuadtree ().Add (er.Current); } } else { rootQuadtree ().Add (er.Current); } } //clear elements elements.Clear(); } return true; }
/** * Add new element to quadtree at next frame * This will start from root quadtree node **/ public void AddNextFrame(IQuadtreeAgent newElement){ elementsNextFrame.Add (newElement); }