diff --git a/doomsday/engine/engine.pro b/doomsday/engine/engine.pro index 1215858b5f..22e60af419 100644 --- a/doomsday/engine/engine.pro +++ b/doomsday/engine/engine.pro @@ -202,6 +202,7 @@ DENG_HEADERS = \ portable/include/hedge.h \ portable/include/huffman.h \ portable/include/image.h \ + portable/include/kdtree.h \ portable/include/library.h \ portable/include/linedef.h \ portable/include/lumpdirectory.h \ @@ -455,6 +456,7 @@ SOURCES += \ portable/src/hedge.c \ portable/src/huffman.c \ portable/src/image.c \ + portable/src/kdtree.c \ portable/src/library.c \ portable/src/linedef.c \ portable/src/lumpdirectory.c \ diff --git a/doomsday/engine/portable/include/bsp_superblock.h b/doomsday/engine/portable/include/bsp_superblock.h index b0417d6b28..64d633d0b2 100644 --- a/doomsday/engine/portable/include/bsp_superblock.h +++ b/doomsday/engine/portable/include/bsp_superblock.h @@ -114,15 +114,18 @@ int SuperBlock_IterateHEdges(SuperBlock* superblock, int (*callback)(bsp_hedge_t * Retrieve a pointer to a sub-block of this superblock. * * @param superblock SuperBlock instance. - * @param left @c true= pick the "left" child. + * @param left non-zero= pick the "left" child. * * @return Selected child superblock else @c NULL if none. */ -SuperBlock* SuperBlock_Child(SuperBlock* superblock, boolean left); +SuperBlock* SuperBlock_Child(SuperBlock* superblock, int left); int SuperBlock_Traverse2(SuperBlock* superblock, int (*callback)(SuperBlock*, void*), void* parameters); int SuperBlock_Traverse(SuperBlock* superblock, int (*callback)(SuperBlock*, void*)/*, parameters=NULL*/); +int SuperBlock_PostTraverse2(SuperBlock* sb, int(*callback)(SuperBlock*, void*), void* parameters); +int SuperBlock_PostTraverse(SuperBlock* sb, int(*callback)(SuperBlock*, void*)/*, parameters = NULL*/); + /** * Find the axis-aligned bounding box defined by the vertices of all * HEdges within this superblock. If no HEdges are linked then @a bounds @@ -133,21 +136,4 @@ int SuperBlock_Traverse(SuperBlock* superblock, int (*callback)(SuperBlock*, voi */ void SuperBlock_FindHEdgeListBounds(SuperBlock* superblock, AABoxf* bounds); -/** - * @todo The following functions do not belong in this module. - */ - -/** - * Init the superblock allocator. - */ -void BSP_InitSuperBlockAllocator(void); - -/** - * Free all the superblocks on the quick-alloc list. - */ -void BSP_ShutdownSuperBlockAllocator(void); - -SuperBlock* BSP_NewSuperBlock(const AABox* bounds); -void BSP_RecycleSuperBlock(SuperBlock* superblock); - #endif /// LIBDENG_MAP_BSP_SUPERBLOCK diff --git a/doomsday/engine/portable/include/kdtree.h b/doomsday/engine/portable/include/kdtree.h new file mode 100644 index 0000000000..da0e6475be --- /dev/null +++ b/doomsday/engine/portable/include/kdtree.h @@ -0,0 +1,57 @@ +/** + * @file kdtree.h + * Kd-Tree data structure. @ingroup data + * + * Based on glBSP 2.24 (in turn, based on BSP 2.3), which is hosted on + * SourceForge: http://sourceforge.net/projects/glbsp/ + * + * @authors Copyright © 2007-2012 Daniel Swanson + * @authors Copyright © 2000-2007 Andrew Apted + * @authors Copyright © 1998-2000 Colin Reed + * @authors Copyright © 1998-2000 Lee Killough + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LIBDENG_KDTREE +#define LIBDENG_KDTREE + +#include "de_platform.h" + +struct kdtree_s; + +typedef struct kdtree_s KdTree; + +KdTree* KdTree_New(const AABox* bounds); +KdTree* KdTree_NewWithUserData(const AABox* bounds, void* userData); + +void KdTree_Delete(KdTree* kdTree); + +const AABox* KdTree_Bounds(KdTree* kdTree); + +void* KdTree_UserData(KdTree* kdTree); +KdTree* KdTree_SetUserData(KdTree* kdTree, void* userData); + +KdTree* KdTree_Child(KdTree* kdTree, int left); + +KdTree* KdTree_AddChild(KdTree* kdTree, const AABox* bounds, int left, void* userData); + +int KdTree_Traverse2(KdTree* kdTree, int (*callback)(KdTree*, void*), void* parameters); +int KdTree_Traverse(KdTree* kdTree, int (*callback)(KdTree*, void*), void* parameters); + +int KdTree_PostTraverse2(KdTree* kdTree, int(*callback)(KdTree*, void*), void* parameters); +int KdTree_PostTraverse(KdTree* kdTree, int(*callback)(KdTree*, void*)/*, parameters=NULL*/); + +#endif /// LIBDENG_KDTREE diff --git a/doomsday/engine/portable/src/bsp_main.c b/doomsday/engine/portable/src/bsp_main.c index 73e65d1738..30c895c182 100644 --- a/doomsday/engine/portable/src/bsp_main.c +++ b/doomsday/engine/portable/src/bsp_main.c @@ -138,7 +138,7 @@ static SuperBlock* createInitialHEdges(GameMap* map) blockBounds.maxX = blockBounds.minX + 128 * M_CeilPow2(bw); blockBounds.maxY = blockBounds.minY + 128 * M_CeilPow2(bh); - block = BSP_NewSuperBlock(&blockBounds); + block = SuperBlock_New(&blockBounds); for(i = 0; i < map->numLineDefs; ++i) { @@ -269,7 +269,6 @@ boolean BSP_Build(GameMap* map, Vertex*** vertexes, uint* numVertexes) // It begins... startTime = Sys_GetRealTime(); - BSP_InitSuperBlockAllocator(); BSP_InitHPlaneInterceptAllocator(); BSP_InitHEdgeAllocator(); @@ -296,7 +295,7 @@ boolean BSP_Build(GameMap* map, Vertex*** vertexes, uint* numVertexes) VERBOSE2( Con_Message("BuildNodes: Done in %.2f seconds.\n", (Sys_GetRealTime() - buildStartTime) / 1000.0f)); } - BSP_RecycleSuperBlock(hEdgeList); + SuperBlock_Delete(hEdgeList); if(builtOK) { @@ -334,7 +333,6 @@ boolean BSP_Build(GameMap* map, Vertex*** vertexes, uint* numVertexes) // Free temporary storage. BSP_ShutdownHEdgeAllocator(); BSP_ShutdownHPlaneInterceptAllocator(); - BSP_ShutdownSuperBlockAllocator(); // How much time did we spend? VERBOSE2( Con_Message(" Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ); diff --git a/doomsday/engine/portable/src/bsp_node.c b/doomsday/engine/portable/src/bsp_node.c index 22d3ca3823..ca3464ea5a 100644 --- a/doomsday/engine/portable/src/bsp_node.c +++ b/doomsday/engine/portable/src/bsp_node.c @@ -989,8 +989,8 @@ boolean BuildNodes(SuperBlock* hEdgeList, binarytree_t** parent, size_t depth, // Create left and right super blocks. // Copy the bounding box of the edge list to the superblocks. - hEdgeSet[RIGHT] = BSP_NewSuperBlock(SuperBlock_Bounds(hEdgeList)); - hEdgeSet[LEFT] = BSP_NewSuperBlock(SuperBlock_Bounds(hEdgeList)); + hEdgeSet[RIGHT] = SuperBlock_New(SuperBlock_Bounds(hEdgeList)); + hEdgeSet[LEFT] = SuperBlock_New(SuperBlock_Bounds(hEdgeList)); // Divide the half-edges into two lists: left & right. BSP_PartitionHEdges(hEdgeList, hEdgeSet[RIGHT], hEdgeSet[LEFT], hPlane); @@ -1009,7 +1009,7 @@ boolean BuildNodes(SuperBlock* hEdgeList, binarytree_t** parent, size_t depth, builtOK = BuildNodes(hEdgeSet[RIGHT], &subTree, depth + 1, hPlane); BinaryTree_SetChild(*parent, RIGHT, subTree); - BSP_RecycleSuperBlock(hEdgeSet[RIGHT]); + SuperBlock_Delete(hEdgeSet[RIGHT]); if(builtOK) { @@ -1017,7 +1017,7 @@ boolean BuildNodes(SuperBlock* hEdgeList, binarytree_t** parent, size_t depth, BinaryTree_SetChild(*parent, LEFT, subTree); } - BSP_RecycleSuperBlock(hEdgeSet[LEFT]); + SuperBlock_Delete(hEdgeSet[LEFT]); return builtOK; } diff --git a/doomsday/engine/portable/src/bsp_superblock.c b/doomsday/engine/portable/src/bsp_superblock.c index 4f4d56d4cb..171fba5f5c 100644 --- a/doomsday/engine/portable/src/bsp_superblock.c +++ b/doomsday/engine/portable/src/bsp_superblock.c @@ -34,34 +34,31 @@ #include "de_play.h" #include "de_misc.h" -struct superblock_s { - // Parent of this block, or NULL for a top-level block. - struct superblock_s* parent; - - // Coordinates on map for this block, from lower-left corner to - // upper-right corner. Pseudo-inclusive, i.e (x,y) is inside block - // if and only if minX <= x < maxX and minY <= y < maxY. - AABox aaBox; +#include "kdtree.h" - // Sub-blocks. NULL when empty. [0] has the lower coordinates, and - // [1] has the higher coordinates. Division of a square always - // occurs horizontally (e.g. 512x512 -> 256x512 -> 256x256). - struct superblock_s* subs[2]; +/** + * Subblocks: + * RIGHT - has the lower coordinates. + * LEFT - has the higher coordinates. + * Division of a block always occurs horizontally, e.g. 512x512 -> 256x512 -> 256x256. + */ +struct superblock_s { + KdTree* tree; - // Number of real half-edges and minihedges contained by this block - // (including all sub-blocks below it). + /// Number of real half-edges and minihedges contained by this block + /// (including all sub-blocks below it). int realNum; int miniNum; - // List of half-edges completely contained by this block. + /// List of half-edges completely contained by this block. struct bsp_hedge_s* hEdges; }; static __inline boolean isLeaf(SuperBlock* sb) { - assert(sb); - return (sb->aaBox.maxX - sb->aaBox.minX <= 256 && - sb->aaBox.maxY - sb->aaBox.minY <= 256); + const AABox* bounds = SuperBlock_Bounds(sb); + return (bounds->maxX - bounds->minX <= 256 && + bounds->maxY - bounds->minY <= 256); } static void linkHEdge(SuperBlock* sb, bsp_hedge_t* hEdge) @@ -75,30 +72,35 @@ static void linkHEdge(SuperBlock* sb, bsp_hedge_t* hEdge) SuperBlock* SuperBlock_New(const AABox* bounds) { - SuperBlock* sb = M_Calloc(sizeof(*sb)); - memcpy(&sb->aaBox, bounds, sizeof(sb->aaBox)); + SuperBlock* sb = malloc(sizeof *sb); + if(!sb) Con_Error("SuperBlock_New: Failed on allocation of %lu bytes for new SuperBlock.", (unsigned long) sizeof *sb); + sb->tree = KdTree_NewWithUserData(bounds, sb); + sb->realNum = 0; + sb->miniNum = 0; + sb->hEdges = NULL; return sb; } +static int deleteSuperBlock(SuperBlock* sb, void* parameters) +{ + M_Free(sb); + return false; // Continue iteration. +} + void SuperBlock_Delete(SuperBlock* sb) { - uint num; + KdTree* tree; assert(sb); - // Recursively handle sub-blocks. - for(num = 0; num < 2; ++num) - { - if(sb->subs[num]) - SuperBlock_Delete(sb->subs[num]); - } - - BSP_RecycleSuperBlock(sb); + tree = sb->tree; + SuperBlock_PostTraverse(sb, deleteSuperBlock); + KdTree_Delete(tree); } const AABox* SuperBlock_Bounds(SuperBlock* sb) { assert(sb); - return &sb->aaBox; + return KdTree_Bounds(sb->tree); } uint SuperBlock_HEdgeCount(SuperBlock* sb, boolean addReal, boolean addMini) @@ -118,7 +120,7 @@ void SuperBlock_HEdgePush(SuperBlock* sb, bsp_hedge_t* hEdge) for(;;) { int p1, p2, half, midPoint[2]; - SuperBlock* child; + const AABox* bounds; // Update half-edge counts. if(hEdge->lineDef) @@ -133,11 +135,12 @@ void SuperBlock_HEdgePush(SuperBlock* sb, bsp_hedge_t* hEdge) return; } - midPoint[VX] = (sb->aaBox.minX + sb->aaBox.maxX) / 2; - midPoint[VY] = (sb->aaBox.minY + sb->aaBox.maxY) / 2; + bounds = SuperBlock_Bounds(sb); + midPoint[VX] = (bounds->minX + bounds->maxX) / 2; + midPoint[VY] = (bounds->minY + bounds->maxY) / 2; - if(sb->aaBox.maxX - sb->aaBox.minX >= - sb->aaBox.maxY - sb->aaBox.minY) + if(bounds->maxX - bounds->minX >= + bounds->maxY - bounds->minY) { // Wider than tall. p1 = hEdge->v[0]->buildData.pos[VX] >= midPoint[VX]; @@ -167,33 +170,34 @@ void SuperBlock_HEdgePush(SuperBlock* sb, bsp_hedge_t* hEdge) // The hedge lies in one half of this block. Create the sub-block // if it doesn't already exist, and loop back to add the hedge. - if(!sb->subs[half]) + if(!KdTree_Child(sb->tree, half)) { + SuperBlock* child; AABox sub; - if(sb->aaBox.maxX - sb->aaBox.minX >= - sb->aaBox.maxY - sb->aaBox.minY) + bounds = SuperBlock_Bounds(sb); + if(bounds->maxX - bounds->minX >= bounds->maxY - bounds->minY) { - sub.minX = (half? midPoint[VX] : sb->aaBox.minX); - sub.minY = sb->aaBox.minY; + sub.minX = (half? midPoint[VX] : bounds->minX); + sub.minY = bounds->minY; - sub.maxX = (half? sb->aaBox.maxX : midPoint[VX]); - sub.maxY = sb->aaBox.maxY; + sub.maxX = (half? bounds->maxX : midPoint[VX]); + sub.maxY = bounds->maxY; } else { - sub.minX = sb->aaBox.minX; - sub.minY = (half? midPoint[VY] : sb->aaBox.minY); + sub.minX = bounds->minX; + sub.minY = (half? midPoint[VY] : bounds->minY); - sub.maxX = sb->aaBox.maxX; - sub.maxY = (half? sb->aaBox.maxY : midPoint[VY]); + sub.maxX = bounds->maxX; + sub.maxY = (half? bounds->maxY : midPoint[VY]); } - sb->subs[half] = child = BSP_NewSuperBlock(&sub); - child->parent = sb; + child = SuperBlock_New(&sub); + child->tree = KdTree_AddChild(sb->tree, &sub, half, child); } - sb = sb->subs[half]; + sb = KdTree_UserData(KdTree_Child(sb->tree, half)); } } @@ -239,39 +243,58 @@ int SuperBlock_IterateHEdges(SuperBlock* sp, int (*callback)(bsp_hedge_t*, void* return SuperBlock_IterateHEdges2(sp, callback, NULL/*no parameters*/); } -SuperBlock* SuperBlock_Child(SuperBlock* sb, boolean left) +SuperBlock* SuperBlock_Child(SuperBlock* sb, int left) { + KdTree* child; assert(sb); - return sb->subs[left?1:0]; + child = KdTree_Child(sb->tree, left); + if(!child) return NULL; + return (SuperBlock*)KdTree_UserData(child); +} + +typedef struct { + int (*callback)(SuperBlock*, void*); + void* parameters; +} treetraverserparams_t; + +static int SuperBlock_TreeTraverser(KdTree* kd, void* parameters) +{ + treetraverserparams_t* p = (treetraverserparams_t*)parameters; + return p->callback(KdTree_UserData(kd), p->parameters); } int SuperBlock_Traverse2(SuperBlock* sb, int (*callback)(SuperBlock*, void*), void* parameters) { - int num, result; + treetraverserparams_t parm; assert(sb); - if(!callback) return false; // Continue iteration. - result = callback(sb, parameters); - if(result) return result; + parm.callback = callback; + parm.parameters = parameters; + return KdTree_Traverse2(sb->tree, SuperBlock_TreeTraverser, (void*)&parm); +} - // Recursively handle sub-blocks. - for(num = 0; num < 2; ++num) - { - SuperBlock* child = sb->subs[num]; - if(!child) continue; +int SuperBlock_Traverse(SuperBlock* sb, int (*callback)(SuperBlock*, void*)) +{ + return SuperBlock_Traverse2(sb, callback, NULL/*no parameters*/); +} - result = SuperBlock_Traverse2(child, callback, parameters); - if(result) return result; - } +int SuperBlock_PostTraverse2(SuperBlock* sb, int(*callback)(SuperBlock*, void*), + void* parameters) +{ + treetraverserparams_t parm; + assert(sb); + if(!callback) return false; // Continue iteration. - return false; // Continue iteration. + parm.callback = callback; + parm.parameters = parameters; + return KdTree_PostTraverse2(sb->tree, SuperBlock_TreeTraverser, (void*)&parm); } -int SuperBlock_Traverse(SuperBlock* sb, int (*callback)(SuperBlock*, void*)) +int SuperBlock_PostTraverse(SuperBlock* sb, int(*callback)(SuperBlock*, void*)) { - return SuperBlock_Traverse2(sb, callback, NULL/*no parameters*/); + return SuperBlock_PostTraverse2(sb, callback, NULL/*no parameters*/); } static void initAABoxFromHEdgeVertexes(AABoxf* aaBox, const bsp_hedge_t* hEdge) @@ -289,12 +312,11 @@ typedef struct { boolean initialized; } findhedgelistboundsparams_t; -static void findHEdgeListBoundsWorker(SuperBlock* sb, void* parameters) +static int findHEdgeListBoundsWorker(SuperBlock* sb, void* parameters) { findhedgelistboundsparams_t* p = (findhedgelistboundsparams_t*)parameters; AABoxf hEdgeAABox; bsp_hedge_t* hEdge; - uint i; for(hEdge = sb->hEdges; hEdge; hEdge = hEdge->nextInBlock) { @@ -311,14 +333,7 @@ static void findHEdgeListBoundsWorker(SuperBlock* sb, void* parameters) V2_AddToBox(p->bounds.arvec2, hEdgeAABox.max); } - // Recursively handle sub-blocks. - for(i = 0; i < 2; ++i) - { - if(sb->subs[i]) - { - findHEdgeListBoundsWorker(sb->subs[i], parameters); - } - } + return false; // Continue iteration. } void SuperBlock_FindHEdgeListBounds(SuperBlock* sb, AABoxf* aaBox) @@ -327,7 +342,7 @@ void SuperBlock_FindHEdgeListBounds(SuperBlock* sb, AABoxf* aaBox) assert(sb && aaBox); parm.initialized = false; - findHEdgeListBoundsWorker(sb, (void*)&parm); + SuperBlock_Traverse2(sb, findHEdgeListBoundsWorker, (void*)&parm); if(parm.initialized) { V2_CopyBox(aaBox->arvec2, parm.bounds.arvec2); @@ -338,61 +353,3 @@ void SuperBlock_FindHEdgeListBounds(SuperBlock* sb, AABoxf* aaBox) V2_Set(aaBox->min, DDMAXFLOAT, DDMAXFLOAT); V2_Set(aaBox->max, DDMINFLOAT, DDMINFLOAT); } - -/** - * @todo The following does not belong in this module. - */ - -static SuperBlock* quickAllocSupers; - -void BSP_InitSuperBlockAllocator(void) -{ - quickAllocSupers = NULL; -} - -void BSP_ShutdownSuperBlockAllocator(void) -{ - while(quickAllocSupers) - { - SuperBlock* block = quickAllocSupers; - - quickAllocSupers = block->subs[0]; - M_Free(block); - } -} - -SuperBlock* BSP_NewSuperBlock(const AABox* bounds) -{ - SuperBlock* sb; - - if(quickAllocSupers == NULL) - return SuperBlock_New(bounds); - - sb = quickAllocSupers; - quickAllocSupers = sb->subs[0]; - - // Clear out any old rubbish. - memset(sb, 0, sizeof(*sb)); - memcpy(&sb->aaBox, bounds, sizeof(sb->aaBox)); - - return sb; -} - -void BSP_RecycleSuperBlock(SuperBlock* sb) -{ - if(!sb) return; - - if(sb->hEdges) - { - // This can happen, but only under abnormal circumstances. -#if _DEBUG - Con_Error("BSP_RecycleSuperBlock: Superblock contains half-edges!"); -#endif - sb->hEdges = NULL; - } - - // Add block to quick-alloc list. Note that subs[0] is used for - // linking the blocks together. - sb->subs[0] = quickAllocSupers; - quickAllocSupers = sb; -} diff --git a/doomsday/engine/portable/src/kdtree.c b/doomsday/engine/portable/src/kdtree.c new file mode 100644 index 0000000000..2326bac184 --- /dev/null +++ b/doomsday/engine/portable/src/kdtree.c @@ -0,0 +1,174 @@ +/** + * @file kdtree.c + * Kd-Tree data structure implementation. @ingroup data + * + * Based on glBSP 2.24 (in turn, based on BSP 2.3), which is hosted on + * SourceForge: http://sourceforge.net/projects/glbsp/ + * + * @authors Copyright © 2007-2012 Daniel Swanson + * @authors Copyright © 2000-2007 Andrew Apted + * @authors Copyright © 1998-2000 Colin Reed + * @authors Copyright © 1998-2000 Lee Killough + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include + +#include "de_platform.h" +#include "de_console.h" +#include "kdtree.h" + +struct kdtree_s { + /// Parent of this (sub)tree else @c NULL. + struct kdtree_s* parent; + + /// Subtree of this (sub)tree else @c NULL. + struct kdtree_s* subs[2]; + + /// Coordinates for this subtree, from lower-left to upper-right corner. + /// Pseudo-inclusive, i.e (x,y) is inside block if and only if: + /// minX <= x < maxX and minY <= y < maxY. + AABox aaBox; + + /// User data associated with this (sub)tree else @c NULL. + void* userData; +}; + +KdTree* KdTree_NewWithUserData(const AABox* bounds, void* userData) +{ + KdTree* kd = calloc(1, sizeof *kd); + if(!kd) Con_Error("KdTree_New: Failed on allocation of %lu bytes for new KdTree.", sizeof *kd); + memcpy(&kd->aaBox, bounds, sizeof(kd->aaBox)); + kd->userData = userData; + return kd; +} + +KdTree* KdTree_New(const AABox* bounds) +{ + return KdTree_NewWithUserData(bounds, NULL/*no user data*/); +} + +static int deleteKdTree(KdTree* kd, void* parameters) +{ + free(kd); + return false; // Continue iteration. +} + +void KdTree_Delete(KdTree* kd) +{ + KdTree_PostTraverse(kd, deleteKdTree); +} + +const AABox* KdTree_Bounds(KdTree* kd) +{ + assert(kd); + return &kd->aaBox; +} + +void* KdTree_UserData(KdTree* kd) +{ + assert(kd); + return kd->userData; +} + +KdTree* KdTree_SetUserData(KdTree* kd, void* userData) +{ + assert(kd); + kd->userData = userData; + return kd; +} + +KdTree* KdTree_Child(KdTree* kd, int left) +{ + assert(kd); + return kd->subs[left?1:0]; +} + +KdTree* KdTree_AddChild(KdTree* kd, const AABox* bounds, int left, void* userData) +{ + KdTree* child; + assert(kd); + + child = kd->subs[left?1:0]; + if(!child) + { + child = kd->subs[left?1:0] = KdTree_New(bounds); + child->parent = kd; + } + + child->userData = userData; + return child; +} + +int KdTree_Traverse2(KdTree* kd, int (*callback)(KdTree*, void*), + void* parameters) +{ + int num, result; + assert(kd); + + if(!callback) return false; // Continue iteration. + + result = callback(kd, parameters); + if(result) return result; + + // Recursively handle subtrees. + for(num = 0; num < 2; ++num) + { + KdTree* child = kd->subs[num]; + if(!child) continue; + + result = KdTree_Traverse2(child, callback, parameters); + if(result) return result; + } + + return false; // Continue iteration. +} + +int KdTree_Traverse(KdTree* kd, int (*callback)(KdTree*, void*), + void* parameters) +{ + return KdTree_Traverse2(kd, callback, NULL/*no parameters*/); +} + +int KdTree_PostTraverse2(KdTree* kd, int(*callback)(KdTree*, void*), + void* parameters) +{ + int num, result; + assert(kd); + + if(!callback) return false; // Continue iteration. + + // Recursively handle sub-blocks. + for(num = 0; num < 2; ++num) + { + KdTree* child = kd->subs[num]; + if(!child) continue; + + result = KdTree_PostTraverse2(child, callback, parameters); + if(result) return result; + } + + result = callback(kd, parameters); + if(result) return result; + + return false; // Continue iteration. +} + +int KdTree_PostTraverse(KdTree* kd, int(*callback)(KdTree*, void*)) +{ + return KdTree_PostTraverse2(kd, callback, NULL/*no parameters*/); +}