test-dynamic.cpp

Go to the documentation of this file.
00001 /*
00002  * test-dynamic.cpp
00003  *
00004  * Copyright (C) 2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Simple test of the kdtree API - especially dynamic objects
00008  */
00009 
00010 // includes --------------------------------------------------------------------
00011 #include "common/wave_ex.h"
00012 #include "kdtree/kdtree.h"
00013 #include "perf/perf.h"
00014 
00015 #include <iostream>
00016 
00017 
00018 
00019 static const float s_max                        = 5000.0;
00020 
00021 
00022 ////////////////////////////////////////////////////////////////////////////////
00023 //
00024 //      static helper methods
00025 //
00026 ////////////////////////////////////////////////////////////////////////////////
00027 
00028 class Test : public kdtree::Entry {
00029 public:
00030         ~Test(void) throw() { }
00031 };
00032 
00033 
00034 static float
00035 getRandom
00036 (
00037 IN float q
00038 )
00039 {
00040         ASSERT(q > 0, "bad q: %f", q);
00041 
00042         return (q * rand()) / RAND_MAX;
00043 }
00044 
00045 
00046 
00047 static void
00048 getRandomRect
00049 (
00050 IN const rect3d_t& bounds,
00051 IN float size,
00052 OUT rect3d_t& r
00053 )
00054 {
00055         ASSERT(bounds.isValid(), "invalid");
00056 
00057         r.x0 = bounds.x0 + getRandom(bounds.x1 - bounds.x0 - size);
00058         r.y0 = bounds.y0 + getRandom(bounds.y1 - bounds.y0 - size);
00059         r.z0 = bounds.z0 + getRandom(bounds.z1 - bounds.z0 - size);
00060 
00061         r.x1 = r.x0 + size;
00062         r.y1 = r.y0 + size;
00063         r.z1 = r.z0 + size;
00064 }
00065 
00066 
00067 
00068 static kdtree::eAction
00069 countCallback
00070 (
00071 IN void * context,
00072 IN kdtree::Entry * e,
00073 IN const rect3d_t& r
00074 )
00075 {
00076         long * pCount = (long *) context;
00077         ASSERT(pCount, "null context?");
00078 
00079         *pCount = (*pCount) + 1;
00080 
00081         return kdtree::eAction_Continue;
00082 }
00083 
00084 
00085 /*
00086 static kdtree::eAction
00087 printCallback
00088 (
00089 IN void * context,
00090 IN kdtree::entry_t& e
00091 )
00092 {
00093         long * pCount = (long *) context;
00094         ASSERT(pCount, "null context?");
00095 
00096         DPRINTF("Found entry in search rect");
00097         e.bounds.dump("  entry bounds");
00098 
00099         *pCount = (*pCount) + 1;
00100 
00101         return kdtree::eAction_Continue;
00102 }
00103  */
00104 
00105 
00106 static kdtree::eAction
00107 findCallback
00108 (
00109 IN void * context,
00110 IN kdtree::Entry * e,
00111 IN const rect3d_t& r
00112 )
00113 {
00114         if (e == context) {
00115                 // we have a match!
00116                 return kdtree::eAction_Halt;
00117         }
00118 
00119         // not a match...
00120         return kdtree::eAction_Continue;
00121 }
00122 
00123 
00124 
00125 static kdtree::eAction
00126 removeCallback
00127 (
00128 IN void * context,
00129 IN kdtree::Entry * e,
00130 IN const rect3d_t& r
00131 )
00132 {
00133         if (e == context) {
00134                 // matches!
00135                 return (kdtree::eAction)
00136                     (kdtree::eAction_Remove | kdtree::eAction_Halt);
00137         }
00138 
00139         // not a match...
00140         return kdtree::eAction_Continue;
00141 }
00142 
00143 
00144 
00145 static int
00146 popAllDynamicEntries
00147 (
00148 IN kdtree::Node * node
00149 )
00150 {
00151         int count = 0;
00152 
00153         // pop all locally
00154         while (smart_ptr<kdtree::Node::dynamic_entry_t> pde =
00155                node->popDynamicEntry()) {
00156                 ++count;
00157         }
00158 
00159         kdtree::Node * left = node->getLeftChild();
00160         kdtree::Node * right = node->getRightChild();
00161 
00162         if (left) {
00163                 count += popAllDynamicEntries(left);
00164         }
00165         if (right) {
00166                 count += popAllDynamicEntries(right);
00167         }
00168 
00169         return count;
00170 }
00171 
00172 
00173 static void
00174 doTest1
00175 (
00176 IN int nCount,
00177 IN int subdivide
00178 )
00179 {
00180         perf::Timer timer("doTest1");
00181         ASSERT(nCount > 0, "bad count: %d", nCount);
00182         ASSERT(subdivide > 0, "Bad subdivide: %d", subdivide);
00183 
00184         rect3d_t bounds;
00185 
00186         DPRINTF(
00187             "Running test with %d entries, subdivide at %d entries per node",
00188             nCount, subdivide);
00189 
00190         bounds.x0 = -s_max;
00191         bounds.x1 = +s_max;
00192         bounds.y0 = -(s_max / 2.5);
00193         bounds.y1 = +(s_max / 2.5);
00194         bounds.z0 = -(s_max / 5);
00195         bounds.z1 = +(s_max / 5);
00196         //bounds.dump("bounds");
00197 
00198         smart_ptr<kdtree::Node> root = kdtree::Node::create(bounds);
00199         ASSERT(root, "failed to create root node");
00200 
00201         for (int i = 0; i < nCount; ++i) {
00202                 rect3d_t r;
00203                 getRandomRect(bounds, getRandom(10), r);
00204                 //e.bounds.dump("  adding");
00205                 smart_ptr<kdtree::Entry> e;
00206 
00207                 {
00208                         perf::Timer timer("addEntry");
00209                         root->addStaticEntry(e, r);
00210                 }
00211         }
00212 
00213         DPRINTF("Tree depth: %d", root->getTreeDepth());
00214         DPRINTF("Node count: %d", root->getNodeCount());
00215         DPRINTF("Entry count: %d", root->getStaticEntryCount());
00216         if (root->getStaticEntryCount() != nCount) {
00217                 WAVE_EX(wex);
00218                 wex << "Count of entries in tree does not match what was added";
00219         }
00220 
00221         // okay, subdivide
00222         DPRINTF("Subdividing...");
00223         {
00224                 perf::Timer timer("subdivide");
00225                 root->subdivide(kdtree::eStrategy_Naive, subdivide);
00226         }
00227         DPRINTF("Tree depth: %d", root->getTreeDepth());
00228         DPRINTF("Node count: %d", root->getNodeCount());
00229         DPRINTF("Entry count: %d", root->getStaticEntryCount());
00230 
00231         long count = 0;
00232         {
00233                 perf::Timer timer("full iteration");
00234                 root->iterateStaticEntries(bounds, countCallback, (void *) &count);
00235         }
00236         if (count != nCount) {
00237                 WAVE_EX(wex);
00238                 wex << "Iteration should return all entities!";
00239         }
00240 
00241         rect3d_t sub;
00242         getRandomRect(bounds, 1000, sub);
00243         sub.dump("Search rect");
00244         count = 0;
00245         {
00246                 // warning: don't use the print callback here, it is slow
00247                 //      (writing to console)
00248                 perf::Timer timer("sub iteration");
00249                 root->iterateStaticEntries(sub, countCallback, (void *) &count);
00250         }
00251         DPRINTF("Found %ld entries in sub rect", count);
00252 
00253         // now add dynamic objects
00254         DPRINTF("Adding %d dynamic objects", nCount);
00255         for (int i = 0; i < nCount; ++i) {
00256                 smart_ptr<kdtree::Node::dynamic_entry_t> pde =
00257                     new kdtree::Node::dynamic_entry_t;
00258                 ASSERT(pde, "out of memory");
00259 
00260                 getRandomRect(bounds, 1.0 + getRandom(4.0), pde->rect);
00261 
00262                 root->addDynamicEntry(pde);
00263         }
00264 
00265         // walk tree and get all dynamic entries
00266         int popped = popAllDynamicEntries(root);
00267         DPRINTF("Popped %d dynamic entries", popped);
00268         ASSERT_THROW(popped == nCount, "should have popped as many as added");
00269 
00270         // pop again on an empty tree
00271         ASSERT_THROW(0 == popAllDynamicEntries(root),
00272             "Should not have found any dynamic entries remaining");
00273 }
00274 
00275 
00276 
00277 ////////////////////////////////////////////////////////////////////////////////
00278 //
00279 //      entry point
00280 //
00281 ////////////////////////////////////////////////////////////////////////////////
00282 
00283 int
00284 main
00285 (
00286 IN int argc,
00287 IN const char * argv[]
00288 )
00289 {
00290         int nCount = 50 * 1000;
00291         int nDivide = 32;
00292         if (argc > 1) {
00293                 nCount = atoi(argv[1]);
00294         } else {
00295                 DPRINTF("No count provided, so using count = %d", nCount);
00296         }
00297         if (argc > 2) {
00298                 nDivide = atoi(argv[2]);
00299         } else {
00300                 DPRINTF("No subdivide threshold specified, using %d", nDivide);
00301         }
00302         int retval = 0;
00303 
00304         try {
00305                 perf::Timer timer("Total test time");
00306 
00307                 doTest1(nCount, nDivide);
00308 
00309         } catch (std::exception& e) {
00310                 DPRINTF("Exception: %s", e.what());
00311                 retval = 1;
00312         }
00313 
00314         perf::dumpTimingSummary(std::cerr);
00315 
00316         return retval;
00317 }
00318