library of assembled shared sources

http://lass.cocamware.com

kd_tree.inl

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 
00044 
00045 #ifndef LASS_GUARDIAN_OF_INCLUSION_SPAT_KD_TREE_INL
00046 #define LASS_GUARDIAN_OF_INCLUSION_SPAT_KD_TREE_INL
00047 
00048 #include "spat_common.h"
00049 #include "kd_tree.h"
00050 
00051 #ifdef LASS_SPAT_KD_TREE_DIAGNOSTICS
00052 #   include "../io/xml_o_file.h"
00053 #   include "../meta/select.h"
00054 #   include "../prim/aabb_2d.h"
00055 #   include "../prim/aabb_3d.h"
00056 #endif
00057 
00058 namespace lass
00059 {
00060 namespace spat
00061 {
00062 
00063 // --- public --------------------------------------------------------------------------------------
00064 
00065 /** Constructs an empty k-d tree
00066  */
00067 template <class O, class OT>
00068 KdTree<O, OT>::KdTree():
00069     begin_(),
00070     end_()
00071 {
00072 }
00073 
00074 
00075 
00076 /** Constructs a k-d tree from objects in range [first, last).
00077  *  @warning [first, last) must stay a valid range during the entire lifespan of the k-d tree!
00078  */
00079 template <class O, class OT>
00080 KdTree<O, OT>::KdTree(TObjectIterator first, TObjectIterator last):
00081     begin_(first),
00082     end_(last)
00083 {
00084     size_ = std::distance(begin_, end_);
00085     heap_.resize(size_, Node(end_));
00086 
00087     TObjectIterators input;
00088     for (TObjectIterator i = begin_; i != end_; ++i)
00089     {
00090         input.push_back(i);
00091     }
00092 
00093     balance(0, input.begin(), input.end());
00094 }
00095 
00096 
00097 
00098 /** Resets to an empty tree.
00099  */
00100 template <class O, class OT>
00101 void KdTree<O, OT>::reset()
00102 {
00103     TSelf temp;
00104     swap(temp);
00105 }
00106 
00107 
00108 
00109 /** Resets to a new k-d tree of objects in range [first, last).
00110  *  @warning [first, last) must stay a valid range during the entire lifespan of the k-d tree!
00111  */
00112 template <class O, class OT>
00113 void KdTree<O, OT>::reset(TObjectIterator first, TObjectIterator last)
00114 {
00115     TSelf temp(first, last);
00116     swap(temp);
00117 }
00118 
00119 
00120 
00121 /** Locates the object that's nearest to a target position
00122  */
00123 template <class O, class OT>
00124 typename KdTree<O, OT>::Neighbour
00125 KdTree<O, OT>::nearestNeighbour(const TPoint& target) const
00126 {
00127     if (isEmpty())
00128     {
00129         LASS_THROW("can't locate nearest neighbour in empty KdTree");
00130     }
00131 
00132     //const size_t deepNode = findNode(0, target);
00133     //const TPoint deepPivot = heap_[deepNode].position();
00134     //const TValue maxSqrRadius = this->squaredDistance(target, deepPivot);
00135     Neighbour result(end_, std::numeric_limits<TValue>::infinity());
00136     doNearestNeighbour(0, target, result);
00137     return result;
00138 }
00139 
00140 
00141 
00142 /** Locates objects within a spherical range around a target position.
00143  *
00144  *  @deprecated USE OVERLOADS WITH ITERATORS INSTEAD
00145  *
00146  *  @param target [in] the center of the spherical range
00147  *  @param maxRadius [in] the radius of the range
00148  *  @param maxCount [in] the maximum number of objects to be returned.
00149  *      @arg If this is zero, then all objects in the range are returned.
00150  *      @arg If this is non-zero, then up to @a maxCount objects are returned.
00151  *          These will be the ones closest to @a target
00152  *  @param neighbourhood [out] a std::vector that will be filled with the found objects.
00153  *          The vector will be @b cleared before use.
00154  *  @return the squared distance between @a target and the furthest found object.
00155  */
00156 template <class O, class OT>
00157 typename KdTree<O, OT>::TValue
00158 KdTree<O, OT>::rangeSearch(
00159         const TPoint& target, TParam maxRadius, size_t maxCount,
00160         TNeighbourhood& neighbourhood) const
00161 {
00162     if (isEmpty())
00163     {
00164         LASS_THROW("can't perform range search in empty KdTree");
00165     }
00166 
00167     if (maxCount == 0)
00168     {
00169         neighbourhood.clear();
00170         rangeSearch(target, maxRadius, std::back_inserter(neighbourhood));
00171         
00172         // neighbourhood is not a heap, find maximum squared distance
00173         TValue maxSquaredDistance = TValue();
00174         const typename TNeighbourhood::const_iterator end = neighbourhood.end();
00175         for (typename TNeighbourhood::const_iterator i = neighbourhood.begin(); i != end; ++i)
00176         {
00177             maxSquaredDistance = std::max(maxSquaredDistance, i->squaredDistance());
00178         }
00179         return maxSquaredDistance;
00180     }
00181 
00182     maxCount = std::min(maxCount, size_);
00183     neighbourhood.resize(maxCount + 1);
00184 
00185     typename TNeighbourhood::iterator last = rangeSearch(
00186         target, maxRadius, maxCount, neighbourhood.begin());
00187     neighbourhood.erase(last, neighbourhood.end());
00188     
00189     if (neighbourhood.empty())
00190     {
00191         return TValue();
00192     }
00193     return neighbourhood.front().squaredDistance();
00194 }
00195 
00196 
00197 
00198 /** Find all objects in a radius of @a maxRadius of @a target.
00199  *  @param target [in] center of range.
00200  *  @param maxRadius [in] radius of range
00201  *  @param first [in] output iterator dereferencable to Neighbour.
00202  *  @return output iterator @e last so that [@a first, last) is the range of all found objects.
00203  *
00204  *  @note
00205  *      The range starting at @a first must be large enough to contain all found objects.  
00206  *      But since this number is probably unknown beforehand, you better use one of those
00207  *      inserter kind of iterators to add the results to a dynamic sized container. 
00208  *
00209  *  @note
00210  *      If you wish to use a fixed sized range, you best use the other rangeSearch overload
00211  *      taking a random access iterator and an extra parameter @a maxCount.
00212  */
00213 template <class O, class OT>
00214 template <typename OutputIterator>
00215 OutputIterator
00216 KdTree<O, OT>::rangeSearch(const TPoint& target, TParam maxRadius, OutputIterator first) const
00217 {
00218     if (isEmpty() || maxRadius == 0)
00219     {
00220         return first;
00221     }
00222     const TValue squaredRadius = maxRadius * maxRadius;
00223     return doRangeSearch(0, target, squaredRadius, first);
00224 }
00225 
00226 
00227 
00228 /** Find up to a fixed number of objects in a radius of @a maxRadius of @a target.
00229  *  @param target [in] center of range.
00230  *  @param maxRadius [in] radius of range
00231  *  @param maxCount [in] maximum number of objects to be found.
00232  *  @param first [in] random access iterator dereferencable to Neighbour, 
00233  *      [@a first, @a first + @a maxCount + 1) must be a valid range.
00234  *  @return output iterator @e last so that [@a first, last) is the range of all found objects.
00235  *
00236  *  @note
00237  *      This overload will search for a maximum number of @a maxCount objects at a maximum
00238  *      distance of @a maxRadius of the center @a target.  When more than @a maxCount objects
00239  *      are within this distance, the closest objects will be selected.
00240  *      To select the closest objects, a heap is constructed on the iterator range, which is
00241  *      why random access iterators are required instead of regular output iterators.  This
00242  *      is also why there's need of an extra position in the range pointed to by @a first:
00243  *      there's need of an extra position to swap in/out new/old objects.  That's why you
00244  *      must make sure [@a first, @a first + @a maxCount + 1) is a valid range.
00245  *
00246  *  @note
00247  *      If you wish to find all points within the range of @a maxRadius, you better use the
00248  *      overload with the regular output iterator and without @a maxCount.
00249  */
00250 template <class O, class OT>
00251 template <typename RandomAccessIterator>
00252 RandomAccessIterator
00253 KdTree<O, OT>::rangeSearch(const TPoint& target, TParam maxRadius, size_t maxCount,
00254         RandomAccessIterator first) const
00255 {
00256     if (isEmpty() || maxRadius == 0)
00257     {
00258         return first;
00259     }
00260     const TValue squaredRadius = maxRadius * maxRadius;
00261     return doRangeSearch(0, target, squaredRadius, maxCount, first, first);
00262 }
00263 
00264 
00265 
00266 /** Swap the representation of two k-d trees.
00267  */
00268 template <class O, class OT>
00269 void KdTree<O, OT>::swap(TSelf& other)
00270 {
00271     std::swap(begin_, other.begin_);
00272     std::swap(end_, other.end_);
00273     heap_.swap(other.heap_);
00274     std::swap(size_, other.size_);
00275 }
00276 
00277 
00278 
00279 /** returns true if there are no objects in the k-d tree
00280  */
00281 template <class O, class OT>
00282 const bool KdTree<O, OT>::isEmpty() const
00283 {
00284     return heap_.empty();
00285 }
00286 
00287 
00288 
00289 /** resest the k-d tree to an empty one.
00290  */
00291 template <class O, class OT>
00292 void KdTree<O, OT>::clear()
00293 {
00294     TSelf temp;
00295     swap(temp);
00296 }
00297 
00298 
00299 
00300 template <class O, class OT> inline
00301 const typename KdTree<O, OT>::TObjectIterator
00302 KdTree<O, OT>::end() const
00303 {
00304     return end_;
00305 }
00306 
00307 
00308 
00309 // --- neighbour -----------------------------------------------------------------------------------
00310 
00311 template <class O, class OT>
00312 KdTree<O, OT>::Neighbour::Neighbour():
00313     object_(),
00314     squaredDistance_(0)
00315 {
00316 }
00317 
00318 
00319 
00320 template <class O, class OT>
00321 KdTree<O, OT>::Neighbour::Neighbour(TObjectIterator object, TValue squaredDistance):
00322     object_(object),
00323     squaredDistance_(squaredDistance)
00324 {
00325 }
00326 
00327 
00328 
00329 template <class O, class OT>
00330 inline typename KdTree<O, OT>::TObjectIterator
00331 KdTree<O, OT>::Neighbour::object() const
00332 {
00333     return object_;
00334 }
00335 
00336 
00337 
00338 template <class O, class OT>
00339 inline typename KdTree<O, OT>::TPoint
00340 KdTree<O, OT>::Neighbour::position() const
00341 {
00342     return TObjectTraits::position(object_);
00343 }
00344 
00345 
00346 
00347 template <class O, class OT>
00348 inline typename KdTree<O, OT>::TValue
00349 KdTree<O, OT>::Neighbour::squaredDistance() const
00350 {
00351     return squaredDistance_;
00352 }
00353 
00354 
00355 
00356 template <class O, class OT>
00357 inline typename KdTree<O, OT>::TObjectReference
00358 KdTree<O, OT>::Neighbour::operator*() const
00359 {
00360     return *object_;
00361 }
00362 
00363 
00364 
00365 template <class O, class OT>
00366 inline typename KdTree<O, OT>::TObjectIterator
00367 KdTree<O, OT>::Neighbour::operator->() const
00368 {
00369     return object_;
00370 }
00371 
00372 
00373 
00374 template <class O, class OT>
00375 inline bool
00376 KdTree<O, OT>::Neighbour::operator<(const Neighbour& other) const
00377 {
00378     return squaredDistance_ < other.squaredDistance_;
00379 }
00380 
00381 
00382 
00383 // --- protected -----------------------------------------------------------------------------------
00384 
00385 
00386 
00387 // --- private -------------------------------------------------------------------------------------
00388 
00389 template <class O, class OT>
00390 void KdTree<O, OT>::balance(size_t index, TIteratorIterator first, TIteratorIterator last)
00391 {
00392     if (last == first)
00393     {
00394         return;
00395     }
00396 
00397     if (last == first + 1)
00398     {
00399         // exactly one element in content
00400         assignNode(index, *first, dummyAxis_);
00401         return;
00402     }
00403 
00404     // more than one, do the balance
00405     //
00406     const TAxis split = findSplitAxis(first, last);
00407     const size_t size = last - first;;
00408     const TIteratorIterator median = first + size / 2;
00409     std::nth_element(first, median, last, LessDim(split));
00410     assignNode(index, *median, split);
00411 
00412     balance(2 * index + 1, first, median);
00413     balance(2 * index + 2, median + 1, last);
00414 }
00415 
00416 
00417 
00418 template <class O, class OT>
00419 typename KdTree<O, OT>::TAxis
00420 KdTree<O, OT>::findSplitAxis(TIteratorIterator first, TIteratorIterator last) const
00421 {
00422     TPoint min = TObjectTraits::position(*first);
00423     TPoint max = min;
00424 
00425     for (TIteratorIterator i = first + 1; i != last; ++i)
00426     {
00427         const TPoint position = TObjectTraits::position(*i);
00428         for (TAxis k = 0; k < dimension; ++k)
00429         {
00430             min[k] = std::min(min[k], position[k]);
00431             max[k] = std::max(max[k], position[k]);
00432         }
00433     }
00434 
00435     TAxis axis = 0;
00436     TValue maxDistance = max[0] - min[0];
00437     for (TAxis k = 1; k < dimension; ++k)
00438     {
00439         const TValue distance = max[k] - min[k];
00440         if (distance > maxDistance)
00441         {
00442             axis = k;
00443             maxDistance = distance;
00444         }
00445     }
00446 
00447     return axis;
00448 }
00449 
00450 
00451 
00452 template <class O, class OT>
00453 inline void KdTree<O, OT>::assignNode(size_t index, TObjectIterator object, TAxis splitAxis)
00454 {
00455     if (heap_.size() <= index)
00456     {
00457         heap_.resize(index + 1, Node(end_));
00458     }
00459     heap_[index] = Node(object, splitAxis);
00460 }
00461 
00462 
00463 
00464 template <class O, class OT>
00465 size_t KdTree<O, OT>::findNode(size_t index, const TPoint& target) const
00466 {
00467     const size_t size = heap_.size();
00468     if (index >= size || heap_[index].object() == end_)
00469     {
00470         return size;
00471     }
00472     const Node& node = heap_[index];
00473 
00474     const TAxis split = node.axis();
00475     if (split == dummyAxis_)
00476     {
00477         return index;
00478     }
00479 
00480     const TPoint pivot = node.position();
00481     const TValue delta = target[split] - pivot[split]; // distance to splitting plane
00482     const bool isLeftSide = delta < 0;
00483     const size_t result = findNode(2 * index + (isLeftSide ? 1 : 2), target);
00484     return result != size ? result : index;
00485 }
00486 
00487 
00488 
00489 template <class O, class OT>
00490 void KdTree<O, OT>::doNearestNeighbour(size_t index, const TPoint& target, Neighbour& best) const
00491 {
00492     if (index >= heap_.size() || heap_[index].object() == end_)
00493     {
00494         return;
00495     }
00496     const Node& node = heap_[index];
00497     const TPoint pivot = node.position();
00498 
00499     const TAxis split = node.axis();
00500     if (split != dummyAxis_)
00501     {
00502         const TValue delta = target[split] - pivot[split]; // distance to splitting plane
00503         if (delta < 0)
00504         {
00505             // we are left of the plane - search left node first
00506             doNearestNeighbour(2 * index + 1, target, best);
00507             if (num::sqr(delta) < best.squaredDistance())
00508             {
00509                 doNearestNeighbour(2 * index + 2, target, best);
00510             }
00511         }
00512         else
00513         {
00514             // we are right of the plane - search right node first
00515             doNearestNeighbour(2 * index + 2, target, best);
00516             if (num::sqr(delta) < best.squaredDistance())
00517             {
00518                 doNearestNeighbour(2 * index + 1, target, best);
00519             }
00520         }
00521     }
00522 
00523     const TValue sqrDistance = this->squaredDistance(pivot, target);
00524     if (sqrDistance < best.squaredDistance())
00525     {
00526         best = Neighbour(node.object(), sqrDistance);
00527     }
00528 }
00529 
00530 
00531 
00532 template <class O, class OT>
00533 template <typename OutputIterator>
00534 OutputIterator KdTree<O, OT>::doRangeSearch(
00535         size_t index, const TPoint& target, TParam squaredDistance,
00536         OutputIterator output) const
00537 {
00538     if (index >= heap_.size() || heap_[index].object() == end_)
00539     {
00540         return output;
00541     }
00542     const Node& node = heap_[index];
00543 
00544     const TPoint pivot = node.position();
00545     const TAxis split = node.axis();
00546     if (split != dummyAxis_)
00547     {
00548         const TValue delta = target[split] - pivot[split]; // distance to splitting plane
00549         if (delta < TValue())
00550         {
00551             // we are left of the plane - search left node first
00552             output = doRangeSearch(2 * index + 1, target, squaredDistance, output);
00553             if (num::sqr(delta) < squaredDistance)
00554             {
00555                 output = doRangeSearch(2 * index + 2, target, squaredDistance, output);
00556             }
00557         }
00558         else
00559         {
00560             // we are right of the plane - search right node first
00561             output = doRangeSearch(2 * index + 2, target, squaredDistance, output);
00562             if (num::sqr(delta) < squaredDistance)
00563             {
00564                 output = doRangeSearch(2 * index + 1, target, squaredDistance, output);
00565             }
00566         }
00567     }
00568 
00569     const TValue sqrDistance = this->squaredDistance(pivot, target);
00570     if (sqrDistance < squaredDistance)
00571     {
00572         *output++ = Neighbour(node.object(), sqrDistance);
00573     }
00574     return output;
00575 }
00576 
00577 
00578 
00579 template <class O, class OT>
00580 template <typename RandomIterator>
00581 RandomIterator KdTree<O, OT>::doRangeSearch(
00582         size_t index, const TPoint& target, TReference squaredRadius, size_t maxCount,
00583         RandomIterator first, RandomIterator last) const
00584 {
00585     if (index >= heap_.size() || heap_[index].object() == end_)
00586     {
00587         return last;
00588     }
00589     const Node& node = heap_[index];
00590 
00591     const TPoint pivot = node.position();
00592     const TAxis split = node.axis();
00593     if (split != dummyAxis_)
00594     {
00595         const TValue delta = target[split] - pivot[split]; // distance to splitting plane
00596         if (delta < TValue())
00597         {
00598             // we are left of the plane - search left node first
00599             last = doRangeSearch(
00600                 2 * index + 1, target, squaredRadius, maxCount, first, last);
00601             if (num::sqr(delta) < squaredRadius)
00602             {
00603                 last = doRangeSearch(
00604                      2 * index + 2, target, squaredRadius, maxCount, first, last);
00605             }
00606         }
00607         else
00608         {
00609             // we are right of the plane - search right node first
00610             last = doRangeSearch(
00611                 2 * index + 2, target, squaredRadius, maxCount, first, last);
00612             if (num::sqr(delta) < squaredRadius)
00613             {
00614                 last = doRangeSearch(
00615                     2 * index + 1, target, squaredRadius, maxCount, first, last);
00616             }
00617         }
00618     }
00619 
00620     const TValue sqrDistance = squaredDistance(pivot, target);
00621     if (sqrDistance < squaredRadius)
00622     {
00623         *last++ = Neighbour(node.object(), sqrDistance);
00624         std::push_heap(first, last);
00625         LASS_ASSERT(last >= first);
00626         if (static_cast<size_t>(last - first) > maxCount)
00627         {
00628             std::pop_heap(first, last);
00629             --last;
00630             squaredRadius = first->squaredDistance();
00631         }
00632     }
00633     return last;
00634 }
00635 
00636 
00637 
00638 template <class O, class OT> inline
00639 typename KdTree<O, OT>::TValue
00640 KdTree<O, OT>::squaredDistance(const TPoint& a, const TPoint& b)
00641 {
00642     TValue result = TValue();
00643     for (unsigned k = 0; k < dimension; ++k)
00644     {
00645         result += num::sqr(a[k] - b[k]);
00646     }
00647     return result;
00648 }
00649 
00650 
00651 
00652 #ifdef LASS_SPAT_KD_TREE_DIAGNOSTICS
00653 template <class O, class OT>
00654 void KdTree<O, OT>::diagnostics()
00655 {
00656     typedef typename meta::Select< meta::Bool<dimension == 2>, prim::Aabb2D<TValue>, prim::Aabb3D<TValue> >::Type TAabb;
00657     class Visitor
00658     {
00659     public:
00660         Visitor(const TNodes& heap, TObjectIterator end):
00661             xml_("kdtree.xml", "diagnostics"),
00662             heap_(heap),
00663             end_(end)
00664         {
00665         }
00666 
00667         void visit(size_t index, const TAabb& aabb)
00668         {
00669 #if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
00670             using lass::prim::operator<<;
00671 #endif
00672             xml_ << aabb;
00673 
00674             if (index >= heap_.size() || heap_[index].object() == end_)
00675             {
00676                 return;
00677             }
00678             const Node& node = heap_[index];
00679 
00680             const typename TObjectTraits::TPoint pivot = node.position();
00681             xml_ << pivot;
00682 
00683             const TAxis split = node.axis();
00684             if (split == dummyAxis_)
00685             {
00686                 return;
00687             }
00688 
00689             TAabb less = aabb;
00690             typename TAabb::TPoint max = less.max();
00691             max[split] = pivot[split];
00692             less.setMax(max);
00693             visit(2 * index + 1, less);
00694 
00695             TAabb greater = aabb;
00696             typename TAabb::TPoint min = greater.min();
00697             min[split] = pivot[split];
00698             greater.setMin(min);
00699             visit(2 * index + 2, greater);
00700         }
00701 
00702     private:
00703         io::XmlOFile xml_;
00704         const TNodes& heap_;
00705         TObjectIterator end_;
00706     };
00707 
00708     TAabb aabb;
00709     for (TObjectIterator i = begin_; i != end_; ++i)
00710     {
00711         aabb += TObjectTraits::position(i);
00712     }
00713 
00714     Visitor visitor(heap_, end_);
00715     visitor.visit(0, aabb);
00716 }
00717 #endif
00718 
00719 
00720 
00721 // --- free ----------------------------------------------------------------------------------------
00722 
00723 
00724 
00725 }
00726 
00727 }
00728 
00729 #endif
00730 
00731 // EOF

Generated on Mon Nov 10 14:20:06 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo