kdtree.cpp

Go to the documentation of this file.
00001 /*
00002  * kdtree.cpp
00003  *
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of the kdtree API.  See kdtree.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "kdtree.h"             // always include our own header first!
00036 
00037 #include "perf/perf.h"
00038 #include "util/distribution.h"
00039 
00040 
00041 namespace kdtree {
00042 
00043 
00044 
00045 // interface destructors
00046 Entry::~Entry(void) throw() { }
00047 
00048 static const float s_eps                = 1.0e-9;       // epsilon
00049 
00050 ////////////////////////////////////////////////////////////////////////////////
00051 //
00052 //      static helper methods
00053 //
00054 ////////////////////////////////////////////////////////////////////////////////
00055 
00056 eSort
00057 sortPoint
00058 (
00059 IN const plane_t& plane,
00060 IN const point3d_t& point
00061 )
00062 throw()
00063 {
00064         float delta = 0.0;
00065         switch (plane.axis) {
00066         case eAxis_X:
00067                 delta = point.x - plane.value;
00068                 break;
00069 
00070         case eAxis_Y:
00071                 delta = point.y - plane.value;
00072                 break;
00073 
00074         case eAxis_Z:
00075                 delta = point.z - plane.value;
00076                 break;
00077         
00078         default:
00079                 return eSort_Invalid;
00080         }
00081 
00082         if (delta < -s_eps)
00083                 return eSort_Left;
00084         if (delta > s_eps)
00085                 return eSort_Right;
00086         
00087         // must be in plane!
00088         return eSort_Straddle;
00089 }
00090 
00091 
00092 
00093 eSort
00094 sortRect
00095 (
00096 IN const plane_t& plane,
00097 IN const rect3d_t& r
00098 )
00099 throw()
00100 {
00101         // either all corners are in same sort, or it straddles
00102         // (we ignore straddling corners)
00103         eSort sort = eSort_Invalid;
00104         for (int i = 0; i < rect3d_t::eMaxCorners; ++i) {
00105                 point3d_t p = r.getCorner(i);
00106                 eSort val = sortPoint(plane, p);
00107                 if (eSort_Straddle == val)
00108                         continue;       // skip this corner
00109 
00110                 if (eSort_Invalid == sort) {
00111                         sort = val;
00112                 } else if (sort != val) {
00113                         return eSort_Straddle;
00114                 }
00115         }
00116 
00117         // return the result
00118         if (eSort_Invalid == sort) {
00119                 // we can get here for thin rectangles that entirely straddle
00120                 //   the sort plane!  For instance, if a very flat object (2D
00121                 //   rect) was chosen as the sort plane, the 2D rect will be
00122                 //   entirely contained in the sort plane itself.
00123                 return eSort_Straddle;
00124         }
00125         return sort;
00126 }
00127 
00128 
00129 
00130 static eAxis
00131 getLongAxis
00132 (
00133 IN const rect3d_t& r
00134 )
00135 throw()
00136 {
00137         float dx = r.x1 - r.x0;
00138         float dy = r.y1 - r.y0;
00139         float dz = r.z1 - r.z0;
00140 
00141         eAxis axis = eAxis_Invalid;
00142 
00143         if (dx > dy) {
00144                 if (dx > dz) {
00145                         axis = eAxis_X;
00146                 } else {
00147                         axis = eAxis_Z;
00148                 }
00149         } else if (dy > dz) {
00150                 axis = eAxis_Y;
00151         } else {
00152                 axis = eAxis_Z;
00153         }
00154 
00155         return axis;
00156 }
00157 
00158 
00159 
00160 
00161 static void
00162 simpleSplit
00163 (
00164 IN const rect3d_t& r,
00165 OUT plane_t& plane
00166 )
00167 throw()
00168 {
00169         plane.axis = getLongAxis(r);
00170         switch (plane.axis) {
00171         case eAxis_X:
00172                 plane.value = 0.5 * (r.x0 + r.x1);
00173                 break;
00174 
00175         case eAxis_Y:
00176                 plane.value = 0.5 * (r.y0 + r.y1);
00177                 break;
00178 
00179         case eAxis_Z:
00180                 plane.value = 0.5 * (r.z0 + r.z1);
00181                 break;
00182 
00183         default:
00184                 ASSERT(false, "Bad axis: %d", plane.axis);
00185         }
00186 }
00187 
00188 
00189 
00190 static void
00191 measureSplit
00192 (
00193 IN const plane_t& plane,
00194 IN const Node::vec_entries_t& vec,
00195 OUT int& nLeft,
00196 OUT int& nRight
00197 )
00198 throw()
00199 {
00200         // walk all entries and find nLeft & nRight
00201         nLeft = nRight = 0;
00202         for (Node::vec_entries_t::const_iterator i = vec.begin();
00203              i != vec.end(); ++i) {
00204                 const Node::entry_rec_t& er = *i;
00205                 eSort sort = sortRect(plane, er.rect);
00206                 if (eSort_Left == sort) {
00207                         ++nLeft;
00208                 } else if (eSort_Right == sort) {
00209                         ++nRight;
00210                 }
00211         }
00212 }
00213 
00214 
00215 
00216 static void
00217 bestSplit
00218 (
00219 IN int offset,
00220 IN const Node::vec_entries_t& vec,
00221 OUT plane_t& plane,
00222 OUT int& nLeft,
00223 OUT int& nRight
00224 )
00225 {
00226         ASSERT(offset >= 0 && offset < 3, "Bad offset: %d", offset);
00227         plane.clear();
00228         plane.axis = (eAxis) (offset + 1);
00229         //DPRINTF("Finding best split using axis = %d", plane.axis);
00230 
00231         // walk through all entries and populate distribution with relevant
00232         //   coordinate
00233         distribution_t<float> dist;
00234         for (Node::vec_entries_t::const_iterator i = vec.begin();
00235              i != vec.end(); ++i) {
00236                 const Node::entry_rec_t& er = *i;
00237                 const float * p0 = &er.rect.x0;
00238                 p0 += offset;
00239                 //DPRINTF("  sample: %f", *p0);
00240                 dist.addSample(*p0);
00241         }
00242 
00243         // a naive approach is to use the median value, but that only
00244         // works for randomly-distributed objects.  For degenerate cases
00245         // (many objects clustered at a particular coordinate value) the
00246         // median breaks down.  This is especially true when there are a
00247         // small number of objects.
00248         //
00249         // So in general we need to perform a binary search to find the best
00250         // value.
00251         //
00252         // We search for the first point (walking right) where the nLeft
00253         // count is larger than nRight.  Then we either use that point, or the
00254         // point immediately to the left.
00255         int N = dist.size();
00256         ASSERT(N > 1, "Not enough entries");
00257         int start = 0;          // starting edge for binary search
00258         int end = N - 1;        // ending edge for binary search
00259 
00260         int nLeftStart, nLeftEnd;
00261         plane.value = dist.getValueAtIndex(start);
00262         measureSplit(plane, vec, nLeftStart, nRight);
00263         plane.value = dist.getValueAtIndex(end);
00264         measureSplit(plane, vec, nLeftEnd, nRight);
00265 
00266         int mid = end / 2;
00267         //DPRINTF("Performing binary search [0 - %d]:", end);
00268         //DPRINTF("  nLeftStart=%d  nLeftEnd=%d", nLeftStart, nLeftEnd);
00269         while (true) {
00270 
00271                 // how many objects are here?
00272                 int nLeftMid;
00273                 plane.value = dist.getValueAtIndex(mid);
00274                 measureSplit(plane, vec, nLeftMid, nRight);
00275 
00276                 //DPRINTF("  start=%d(%d)  end=%d(%d)  mid=%d(%d)",
00277                 //    start, nLeftStart, end, nLeftEnd, mid, nLeftMid);
00278 
00279                 // go left or right--or terminate!
00280                 if (nLeftMid > nRight) {
00281                         //DPRINTF("Go left!");
00282                         // need to go left
00283                         // terminate?
00284                         if (mid - start < 2) {
00285                                 // we are adjacent to the start!
00286                                 if (nLeftStart > 0) {
00287                                         mid = start;
00288                                 }
00289                                 break;
00290                         }
00291 
00292                         // midpoint is the new end point
00293                         end = mid;
00294                         nLeftEnd = nLeftMid;
00295                 } else if (nLeftMid < nRight) {
00296                         //DPRINTF("Go right!");
00297                         // need to go right
00298                         // terminate?
00299                         if (end - mid < 2) {
00300                                 // we are adjacent to the end!
00301                                 if (!nLeftMid) {
00302                                         mid = end;
00303                                 }
00304                                 break;
00305                         }
00306 
00307                         // midpoint is the new start point
00308                         start = mid;
00309                         nLeftStart = nLeftMid;
00310                 } else {
00311                         //DPRINTF("Equal!");
00312                         // nLeft == nRight!
00313                         break;
00314                 }
00315 
00316                 // new midpoint
00317                 mid = (start + end) / 2;
00318         }
00319 
00320         // exited the search--what did we find?
00321         //DPRINTF("  Stopped search with idx=%d", mid);
00322         plane.value = dist.getValueAtIndex(mid);
00323         //DPRINTF("  Coordinate value: %f", plane.value);
00324         measureSplit(plane, vec, nLeft, nRight);
00325         //DPRINTF("  Final nLeft=%d, nRight=%d", nLeft, nRight);
00326 }
00327 
00328 
00329 
00330 static void
00331 balancedSplit
00332 (
00333 IN const rect3d_t& r,
00334 OUT plane_t& plane,
00335 IN const Node::vec_entries_t& vec
00336 )
00337 throw()
00338 {
00339         // loop through all 3 axes and try splitting along each one
00340         plane_t planes[3];
00341         int N = vec.size();
00342         ASSERT(N > 1, "Only works with 2 or more entries!");
00343         int iPreferred = getLongAxis(r) - 1;
00344         int iBest = 0;          // default is to split along x-direction!
00345         float dBest = 1.0;
00346         for (int i = 0; i < 3; ++i) {
00347                 int nLeft, nRight;
00348                 bestSplit(i, vec, planes[i], nLeft, nRight);
00349 
00350                 // rLeft is the ratio of entities cleanly on the left
00351                 float rLeft = (1.0 * nLeft) / (1.0 * N);
00352                 float rRight = (1.0 * nRight) / (1.0 * N);
00353 
00354                 //DPRINTF("For offset %d, rLeft=%f and rRight=%f",
00355                 //   i, rLeft, rRight);
00356 
00357                 // we want both rLeft and rRight to be 0.5
00358                 // find the distance^2 from the (0.5, 0.5) value
00359                 float dLeft = rLeft - 0.5;
00360                 float dRight = rRight - 0.5;
00361                 float d2 = (dLeft * dLeft) + (dRight * dRight);
00362                 //DPRINTF("  d2 = %f", d2);
00363                 if (d2 < dBest) {
00364                         iBest = i;
00365                         dBest = d2;
00366                 } else if (d2 == dBest && i == iPreferred) {
00367                         // use preferred axis if tiebreaker
00368                         iBest = i;
00369                 }
00370         }
00371 
00372         // which was best?
00373         // DPRINTF("Best offset/axis = %d", iBest);
00374         ASSERT(iBest >= 0 && iBest < 3, "Bad best offset: %d", iBest);
00375 
00376         // return that plane
00377         plane = planes[iBest];
00378 }
00379 
00380 
00381 
00382 /*
00383  * Saving these for posterity because they were nontrivial.
00384  *
00385  * But I've decided not to support arbitrary sort planes for these kd-trees,
00386  * and instead follow convention (and performance) by only allowing axis-aligned
00387  * sort planes.
00388  *
00389 static void
00390 checkCorners
00391 (
00392 IN const rect3d_t&r,
00393 IN const plane_t& plane,
00394 IN const eSort sort,
00395 OUT rect3d_t& out
00396 )
00397 throw()
00398 {
00399         out.clear();
00400         bool set = false;
00401 
00402         for (int i = 0; i < 8; ++i) {
00403                 point3d_t p = r.getCorner(i);
00404                 eSort psort = sortPoint(plane, p);
00405                 if (eSort_Straddle == psort || sort == psort) {
00406                         // point qualifies!
00407                         if (!set) {
00408                                 // this is the first point we've found
00409                                 set = true;
00410                                 out.setToPoint(p);
00411                         } else {
00412                                 // make sure rect includes
00413                                 out.includePoint(p);
00414                         }
00415                 }
00416         }
00417 
00418         ASSERT(set, "none of the rectangle corners are in the partition?");
00419 }
00420 
00421 
00422 
00423 static void
00424 getLeftRightBounds
00425 (
00426 IN const rect3d_t& r,
00427 IN const plane_t& plane,
00428 OUT rect3d_t& left,
00429 OUT rect3d_t& right
00430 )
00431 throw()
00432 {
00433         // this was a surprisingly hard function!  That is, given an axis-
00434         // aligned bounding box (rect3d_r) and an arbirary, split the rect into
00435         // left and right subrects ("left" and "right" as determined by
00436         // sorting).
00437 
00438         // A subrect must be another axis-aligned bounding box that completely
00439         // contains all points originally in the parent box that are now
00440         // partitioned on one side of the plane.  In some cases this can mean
00441         // that both subrects are equal to the parent rect!
00442 
00443         // r.dump("Splitting this rect");
00444         // plane.dump("with this plane");
00445 
00446         // first, populate with any original rect corners now partitioned
00447         checkCorners(r, plane, eSort_Left,  left);
00448         checkCorners(r, plane, eSort_Right, right);
00449 
00450         // include any points on rect edges
00451         for (int i = 0; i < 12; ++i) {
00452                 point3d_t p0, p1;
00453                 r.getEdge(i, p0, p1);
00454 
00455                 // assume p(t) = (1 - t) p0 + t p1.  For what t does
00456                 //  p(t) lie in the plane?
00457 
00458                 float d = plane.distance(p0);
00459                 float denom = dotProduct(plane.n, p1 - p0);
00460                 if (denom > -s_eps && denom < s_eps) {
00461                         // edge is parallel to plane!
00462                         continue;
00463                 }
00464 
00465                 float t = - d / denom;
00466                 if (t < 0 || t > 1) {
00467                         // t is outside edge!
00468                         continue;
00469                 }
00470 
00471                 // got here?  then we have a valid point on the edge
00472                 point3d_t p = (1 - t) * p0 + t * p1;
00473                 // p.dump("  edge point!");
00474                 left.includePoint(p);
00475                 right.includePoint(p);
00476         }
00477 }
00478 */
00479 
00480 
00481 
00482 static void
00483 getLeftRightBounds
00484 (
00485 IN const rect3d_t& r,
00486 IN const plane_t& plane,
00487 OUT rect3d_t& left,
00488 OUT rect3d_t& right
00489 )
00490 throw()
00491 {
00492         left = r;
00493         right = r;
00494 
00495         switch (plane.axis) {
00496         case eAxis_X:
00497                 left.x1 = right.x0 = plane.value;
00498                 break;
00499 
00500         case eAxis_Y:
00501                 left.y1 = right.y0 = plane.value;
00502                 break;
00503 
00504         case eAxis_Z:
00505                 left.z1 = right.z0 = plane.value;
00506                 break;
00507 
00508         default:
00509                 ASSERT(false, "Invalid axis: %d", plane.axis);
00510         }
00511 }
00512 
00513 
00514 
00515 ////////////////////////////////////////////////////////////////////////////////
00516 //
00517 //      Node implementation
00518 //
00519 ////////////////////////////////////////////////////////////////////////////////
00520 
00521 Node::Node(void)
00522 throw()
00523 {
00524         m_user = NULL;
00525         m_left = m_right = NULL;
00526         m_bounds.clear();
00527         m_sortPlane.clear();
00528 }
00529 
00530 
00531 
00532 Node::~Node(void)
00533 throw()
00534 {
00535 }
00536 
00537 
00538 
00539 void
00540 Node::initialize
00541 (
00542 IN const rect3d_t& bounds
00543 )
00544 {
00545         m_bounds = bounds;
00546 }
00547 
00548 
00549 
00550 int
00551 Node::getTreeDepth
00552 (
00553 void
00554 )
00555 const
00556 throw()
00557 {
00558         int maxChild = (m_left) ? m_left->getTreeDepth() : 0;
00559         if (m_right) {
00560                 int n = m_right->getTreeDepth();
00561                 if (n > maxChild) {
00562                         maxChild = n;
00563                 }
00564         }
00565         return maxChild + 1;
00566 }
00567 
00568 
00569 
00570 int
00571 Node::getNodeCount
00572 (
00573 void
00574 )
00575 const
00576 throw()
00577 {
00578         int iLeft = (m_left) ? m_left->getNodeCount() : 0;
00579         int iRight = (m_right) ? m_right->getNodeCount() : 0;
00580         return iLeft + iRight + 1;
00581 }
00582 
00583 
00584 
00585 int
00586 Node::getStaticEntryCount
00587 (
00588 void
00589 )
00590 const
00591 throw()
00592 {
00593         int iLeft = (m_left) ? m_left->getStaticEntryCount() : 0;
00594         int iRight = (m_right) ? m_right->getStaticEntryCount() : 0;
00595         return iLeft + iRight + m_entries.size();
00596 }
00597 
00598 
00599 
00600 ////////////////////////////////////////////////////////////////////////////////
00601 //
00602 //      Node -- kdtree::Node class interface methods
00603 //
00604 ////////////////////////////////////////////////////////////////////////////////
00605 
00606 void
00607 Node::addStaticEntry
00608 (
00609 IN smart_ptr<Entry>& entry,
00610 IN const rect3d_t& r
00611 )
00612 {
00613         // ASSERT(entry) -- we don't care if it is null!
00614 
00615         // this had better be contained!
00616         if (!m_bounds.containsRect(r)) {
00617                 m_bounds.dump("Node boundary");
00618                 r.dump("Entity box");
00619                 ASSERT_THROW(false,
00620                     "Incoming entry is not contained by kd-tree node");
00621         }
00622 
00623         if (m_left || m_right) {
00624                 // sort!
00625                 eSort sort = sortRect(m_sortPlane, r);
00626                 switch (sort) {
00627                 case eSort_Left:
00628                         m_left->addStaticEntry(entry, r);
00629                         return;
00630                 case eSort_Right:
00631                         m_right->addStaticEntry(entry, r);
00632                         return;
00633                 case eSort_Straddle:
00634                         break;  // add to this node
00635                 default:
00636                         ASSERT(false, "bad sort");
00637                 }
00638         }
00639 
00640         // no child nodes or straddle--just add to our local collection
00641         entry_rec_t er;
00642         er.entry = entry;
00643         er.rect = r;
00644         m_entries.push_back(er);
00645 }
00646 
00647 
00648 
00649 void
00650 Node::addDynamicEntry
00651 (
00652 IN smart_ptr<dynamic_entry_t>& dyn
00653 )
00654 {
00655         ASSERT(dyn, "null");
00656         ASSERT(!dyn->next, "should only pass in an unlinked entry");
00657 
00658         // if we are a parent, attempt to recurse
00659         if (m_left) {
00660                 switch (sortRect(m_sortPlane, dyn->rect)) {
00661                 case eSort_Left:
00662                         m_left->addDynamicEntry(dyn);
00663                         return;
00664 
00665                 case eSort_Right:
00666                         m_right->addDynamicEntry(dyn);
00667                         return;
00668 
00669                 default:
00670                         // any other result, we add to our list
00671                         break;
00672                 }
00673         }
00674 
00675         // incoming node is the new head of our list
00676         dyn->next = m_dynamic;
00677         m_dynamic = dyn;
00678 }
00679 
00680 
00681 
00682 bool
00683 Node::removeStaticEntry
00684 (
00685 IN Entry * entry
00686 )
00687 throw()
00688 {
00689         ASSERT(entry, "null");
00690 
00691         // TODO: make this more efficient?  Set?  List?
00692         // Client use cases vary.
00693         for (vec_entries_t::iterator i = m_entries.begin();
00694              i != m_entries.end(); ++i) {
00695                 entry_rec_t& er = *i;
00696                 if (entry == er.entry) {
00697                         m_entries.erase(i);
00698                         return true;
00699                 }
00700         }
00701 
00702         // not found!
00703         return false;
00704 }
00705 
00706 
00707 
00708 smart_ptr<Node::dynamic_entry_t>
00709 Node::popDynamicEntry
00710 (
00711 void
00712 )
00713 throw()
00714 {
00715         smart_ptr<dynamic_entry_t> dyn = m_dynamic;
00716         if (dyn) {
00717                 m_dynamic = dyn->next;
00718                 dyn->next = NULL;
00719         }
00720         return dyn;
00721 }
00722 
00723 
00724 
00725 eAction
00726 Node::iterateStaticEntries
00727 (
00728 IN const rect3d_t& r,
00729 IN iteration_callback callback,
00730 IN void * context
00731 )
00732 {
00733         ASSERT(r.isValid(), "invalid");
00734         ASSERT(callback, "null");
00735         // ASSERT(context) -- we don't care
00736 
00737         // always iterate through any entities here
00738         for (vec_entries_t::iterator i = m_entries.begin();
00739              i != m_entries.end();) {
00740                 vec_entries_t::iterator next = i;
00741                 ++next;
00742 
00743                 entry_rec_t& er = *i;
00744                 if (r.intersectsRect(er.rect)) {
00745                         // e.bounds.dump("Intersects!");
00746                         // entity overlaps with search rect!
00747                         eAction act = callback(context, er.entry, er.rect);
00748                         if (eAction_Remove & act) {
00749                                 m_entries.erase(i);
00750                         }
00751                         if (eAction_Halt & act)
00752                                 return eAction_Halt;
00753                 }
00754 
00755                 // next!
00756                 i = next;
00757         }
00758 
00759         // anything to recurse?
00760         if (!m_left)
00761                 return eAction_Continue;
00762 
00763         // see what to recurse
00764         eSort sort = sortRect(m_sortPlane, r);
00765 //      DPRINTF("Sorted: %s", getSortName(sort));
00766         if (eSort_Left == sort || eSort_Straddle == sort) {
00767                 eAction act = m_left->iterateStaticEntries(r, callback, context);
00768                 if (eAction_Halt & act)
00769                         return eAction_Halt;
00770         }
00771         if (eSort_Right == sort || eSort_Straddle == sort) {
00772                 eAction act = m_right->iterateStaticEntries(r, callback, context);
00773                 if (eAction_Halt & act)
00774                         return eAction_Halt;
00775         }
00776 
00777         // success!
00778         return eAction_Continue;
00779 }
00780 
00781 
00782 
00783 ////////////////////////////////////////////////////////////////////////////////
00784 //
00785 //      Node -- private helper methods
00786 //
00787 ////////////////////////////////////////////////////////////////////////////////
00788 
00789 void
00790 Node::subdivide
00791 (
00792 IN eStrategy strategy,
00793 IN float param
00794 )
00795 {
00796         // boring leaf?  then return right away
00797         int minSize = (param < 2) ? 2 : param;
00798         if (!m_left && (int) m_entries.size() <= minSize) {
00799                 return;
00800         }
00801 
00802         // do we need to split this node?
00803         if (!m_left) {
00804                 this->subdivideInternal(strategy, param);
00805         }
00806 
00807         // sort!
00808         vec_entries_t vecNew;
00809         for (vec_entries_t::iterator i = m_entries.begin();
00810              i != m_entries.end(); ++i) {
00811                 entry_rec_t& er = *i;
00812 
00813                 // does this entry go in a child?
00814                 eSort sort = sortRect(m_sortPlane, er.rect);
00815                 if (eSort_Left == sort) {
00816                         m_left->addStaticEntry(er.entry, er.rect);
00817                 } else if (eSort_Right == sort) {
00818                         m_right->addStaticEntry(er.entry, er.rect);
00819                 } else if (eSort_Straddle == sort) {
00820                          vecNew.push_back(er);
00821                 } else {
00822                         ASSERT(false, "bad sort result");
00823                 }
00824         }
00825 
00826         // nuke old collection, reset to new
00827         m_entries.clear();
00828         m_entries = vecNew;
00829 
00830         // subdivide if it will help
00831         int nLeft = m_left->getStaticEntryCount();
00832         int nRight = m_right->getStaticEntryCount();
00833         if (!nLeft || !nRight) {
00834                 // degenerate--no point in subdividing
00835                 return;
00836         }
00837 
00838         // may be worth subdividing
00839         m_left->subdivide(strategy, param);
00840         m_right->subdivide(strategy, param);
00841 
00842 //      DPRINTF("After subdivide:");
00843 //      DPRINTF("  Tree depth: %d", this->getTreeDepth());
00844 //      DPRINTF("  Node count: %d", this->getNodeCount());
00845 //      DPRINTF("  Entry count: %d", this->getEntryCount());
00846 //      DPRINTF("  Our own entry list %d", m_entries.size());
00847 }
00848 
00849 
00850 
00851 void
00852 Node::subdivideInternal
00853 (
00854 IN eStrategy strategy,
00855 IN float param
00856 )
00857 {
00858         ASSERT(!m_left && !m_right, "already have children?");
00859 
00860         // determine boundary
00861         switch (strategy) {
00862         case eStrategy_Naive:
00863                 simpleSplit(m_bounds, m_sortPlane);
00864                 break;
00865 
00866         case eStrategy_Balance:
00867                 balancedSplit(m_bounds, m_sortPlane, m_entries);
00868                 break;
00869 
00870         default:
00871                 ASSERT(false, "Unknown strategy: %d", strategy);
00872         }
00873         //m_sortPlane.dump("sort plane");
00874 
00875         // get left and right child boundaries
00876         rect3d_t rLeft, rRight;
00877         getLeftRightBounds(m_bounds, m_sortPlane, rLeft, rRight);       
00878         //rLeft.dump("  left");
00879         //rRight.dump("  right");
00880         ASSERT(rLeft.isValid(), "invalid");
00881         ASSERT(rRight.isValid(), "invalid");
00882 
00883         // create left and right child nodes
00884         m_left = Node::create(rLeft);
00885         ASSERT(m_left, "failed to create left child");
00886         m_right = Node::create(rRight);
00887         ASSERT(m_right, "failed to create right child");
00888 }
00889 
00890 
00891 
00892 ////////////////////////////////////////////////////////////////////////////////
00893 //
00894 //      public API
00895 //
00896 ////////////////////////////////////////////////////////////////////////////////
00897 
00898 smart_ptr<Node>
00899 Node::create
00900 (
00901 IN const rect3d_t& bounds
00902 )
00903 {
00904         ASSERT(bounds.isValid(), "invalid");
00905 
00906         smart_ptr<Node> local = new Node;
00907         ASSERT(local, "out of memory");
00908 
00909         local->initialize(bounds);
00910 
00911         return local;
00912 }
00913 
00914 
00915 
00916 };      // kdtree namespace
00917 
00918