//////////////////////////////////////////////////////////////////////////////////////// // RAVEN STANDARD TEMPLATE LIBRARY // (c) 2002 Activision // // // KD Tree // ------- // // // // NOTES: // // // //////////////////////////////////////////////////////////////////////////////////////// #if !defined(RATL_KDTREE_VS_INC) #define RATL_KDTREE_VS_INC //////////////////////////////////////////////////////////////////////////////////////// // Includes //////////////////////////////////////////////////////////////////////////////////////// #if defined(RA_DEBUG_LINKING) #pragma message("...including kdtree_vs.h") #endif #if !defined(RAGL_COMMON_INC) #include "ragl_common.h" #endif namespace ragl { //////////////////////////////////////////////////////////////////////////////////////// // The List Class //////////////////////////////////////////////////////////////////////////////////////// template class kdtree_vs : public ratl::ratl_base { public: //////////////////////////////////////////////////////////////////////////////////// // Capacity Enum //////////////////////////////////////////////////////////////////////////////////// enum { CAPACITY = SIZE, NULL_NODE = SIZE+2, // Invalid Node ID TARG_NODE = SIZE+3 // Used To Mark Nodes Add Location }; //////////////////////////////////////////////////////////////////////////////////// // Constructor //////////////////////////////////////////////////////////////////////////////////// kdtree_vs() : mRoot(NULL_NODE) { } //////////////////////////////////////////////////////////////////////////////////// // How Many Objects Are In This Tree //////////////////////////////////////////////////////////////////////////////////// int size() const { return (mPool.size()); } //////////////////////////////////////////////////////////////////////////////////// // Are There Any Objects In This Tree? //////////////////////////////////////////////////////////////////////////////////// bool empty() const { return (mRoot==NULL_NODE); } //////////////////////////////////////////////////////////////////////////////////// // Is This List Filled? //////////////////////////////////////////////////////////////////////////////////// bool full() const { return (mPool.full()); } //////////////////////////////////////////////////////////////////////////////////// // Clear All Elements //////////////////////////////////////////////////////////////////////////////////// void clear() { mRoot = NULL_NODE; mPool.clear(); } //////////////////////////////////////////////////////////////////////////////////// // Add A New Element To The Tree //////////////////////////////////////////////////////////////////////////////////// void add(const T& data) { // CREATE: New //-------------------------------------------- int nNew = mPool.alloc(); mPool[nNew].mData = data; mPool[nNew].mLeft = NULL_NODE; mPool[nNew].mRight = NULL_NODE; // LINK: (nNew)->(Parent) //-------------------------------------------- if (mRoot==NULL_NODE) { mRoot = nNew; mPool[nNew].mParent = NULL_NODE; return; } // LINK: (nNew)->(Parent) //-------------------------------------------- mPool[nNew].mParent = find_index(data, mRoot, 0, true, true); // LINK: (Parent)->(nNew) //-------------------------------------------- if (mPool[mPool[nNew].mParent].mLeft==TARG_NODE) { mPool[mPool[nNew].mParent].mLeft = nNew; } else if (mPool[mPool[nNew].mParent].mRight==TARG_NODE) { mPool[mPool[nNew].mParent].mRight = nNew; } // Hey! It Didn't Mark Any Targets, Which Means We Found An Exact match To This Data //------------------------------------------------------------------------------------ else { mPool.free(nNew); } } //////////////////////////////////////////////////////////////////////////////////// // Does (data) Exist In The Tree? //////////////////////////////////////////////////////////////////////////////////// bool find(const T& data) { assert(mRoot!=NULL_NODE); // If You Hit This Assert, You Are Asking For Data On An Empty Tree int node = find_index(data, mRoot, 0, true, true); // Exact Find, Or Found Root? //---------------------------- if (mPool[node].mData==data || mPool[node].mParent==NULL_NODE) { return true; } return false; } //////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////// class range_query { public: range_query() {} public: ratl::vector_vs mReported; T mMins; T mMaxs; }; //////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////// void find(range_query& query) { if (mRoot!=NULL_NODE) { query.mReported.clear(); tree_search(query); } } private: //////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////// class node { public: int mParent; int mLeft; int mRight; T mData; }; //////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////// class range_bounds { public: int mMins[DIMENSION]; int mMaxs[DIMENSION]; }; //////////////////////////////////////////////////////////////////////////////////// // This Private Function Of The Class Does A Standard Binary Tree Search //////////////////////////////////////////////////////////////////////////////////// int find_index(const T& data, int curNode, int curDimension, bool returnClosest, bool markTarget) { // Did We Just Go Off The End Of The Tree Or Find The Data We Were Looking For? //------------------------------------------------------------------------------ if (curNode==NULL_NODE || mPool[curNode].mData==data) { return curNode; } // Calculate The Next Dimension For Searching //-------------------------------------------- int nextDimension = curDimension+1; if (nextDimension>=DIMENSION) { nextDimension = 0; } // Search Recursivly Down The Tree Either Left (For Data > Current Node), Or Right //--------------------------------------------------------------------------------- int findRecursive; bool goLeft = (data[curDimension] < mPool[curNode].mData[curDimension]); if (goLeft) { findRecursive = find_index(data, mPool[curNode].mLeft, nextDimension, returnClosest, markTarget); } else { findRecursive = find_index(data, mPool[curNode].mRight, nextDimension, returnClosest, markTarget); } // Success! //---------- if (findRecursive!=NULL_NODE) { return findRecursive; } // If We Want To Return The CLOSEST Node, And We Went Off The End, Then Return This One //-------------------------------------------------------------------------------------- if (returnClosest) { // If We Are Asked To Mark The Target, We Mark (TARG_NODE) At Either mLeft or mRight, // Depending On Where The Node Should Have Been //---------------------------------------------------------------------------------- if (markTarget) { if (goLeft) { mPool[curNode].mLeft = TARG_NODE; } else { mPool[curNode].mRight = TARG_NODE; } } // Go Ahead And Return This Node, It's The One We Would Have Put As The Child return curNode; } // Return The Results Of The Recursive Call //------------------------------------------ return NULL_NODE; } //////////////////////////////////////////////////////////////////////////////////// // This function just sets up the range bounds and starts the recursive tree search //////////////////////////////////////////////////////////////////////////////////// void tree_search(range_query& query) { range_bounds bounds; for (int i=0; i=DIMENSION) { nextDimension = 0; } // Test To See If Our Subtree Is In Range //---------------------------------------- ESide Side = tree_search_bounds_in_range(query, bounds); // If The Bounds Are Contained Entirely Within The Query Range, We Report The Sub Tree //------------------------------------------------------------------------------------- if (Side==Side_AllIn) { tree_search_report_sub_tree(query, curNode); } // Otherwise, If Our Bounds Intersect The Query Range, We Need To Look Further //----------------------------------------------------------------------------- else if (Side==Side_In) { // Test The Left Child //--------------------- if (mPool[curNode].mLeft!=NULL_NODE) { int OldMaxs = bounds.mMaxs[curDimension]; if ( !bounds.mMins[curDimension] || ((mPool[curNode].mData[curDimension]) < (mPool[bounds.mMins[curDimension]].mData[curDimension])) ) { bounds.mMins[curDimension] = curNode; } tree_search(query, mPool[curNode].mLeft, nextDimension, bounds); bounds.mMaxs[curDimension] = OldMaxs; // Restore Old Maxs For The Right Child Search } // Test The Right Child //---------------------- if (mPool[curNode].mRight!=NULL_NODE) { if ( !bounds.mMaxs[curDimension] || ((mPool[bounds.mMaxs[curDimension]].mData[curDimension]) < (mPool[curNode].mData[curDimension])) ) { bounds.mMaxs[curDimension] = curNode; } tree_search(query, mPool[curNode].mRight, nextDimension, bounds); } } } //////////////////////////////////////////////////////////////////////////////////// // This Function Returns True If The Node Is Within The Query Range //////////////////////////////////////////////////////////////////////////////////// bool tree_search_node_in_range(range_query& query, node& n) { for (int dim=0; dim mPool; // The Allocation Data Pool int mRoot; // The Beginning Of The Tree }; } #endif