ConcentricShells.h

Go to the documentation of this file.
00001 #ifndef ConcentricShells_HEADER
00002 #define ConcentricShells_HEADER
00003 
00004 #ifdef HAVE_CONFIG_H
00005 # include <config.h>
00006 #endif
00007 
00008 #include <boost/utility.hpp>
00009 #include <utilities/SmallList.h>
00010 #include <math.h> // sqrt
00011 #include <utilities/foreach.h>
00012 #include <utilities/bit-hacks.h>
00013 
00014 #if 0
00015 #define dprintf(...) printf(__VA_ARGS__)
00016 #else
00017 #define dprintf(...)
00018 #endif
00019 
00033 template <class Item, class Point>
00034 class ConcentricShells : public boost::noncopyable {
00035   typedef hudson::SmallList<Item*> ItemList;
00036 
00037   struct Annulus {
00038     const int log_radius2;
00039     ItemList items;
00040     Annulus(int log_r2): log_radius2(log_r2) { }
00041     void add(Item *item) {
00042       items.push_front(item);
00043     }
00044   };
00045 
00046   typedef hudson::SmallList<Annulus> Annuli;
00047 
00048   Annuli annuli_;
00049 
00050   void newAnnulus(const typename Annuli::iterator& next, int log_r2, Item *item) {
00051     typename Annuli::iterator it = annuli_.insert(next, Annulus(log_r2));
00052     (*it).add(item);
00053 
00054     dprintf("Inserted annulus with radius %d; next has radius %d\n",
00055         log_r2,
00056         (++it) == annuli_.end()
00057           ? std::numeric_limits<int>::min()
00058           :  (*it).log_radius2
00059         );
00060   }
00061 
00062   public:
00063 
00064   /***************************************************************************
00065    * Add an item to the set.
00066    *
00067    * It will be stored in some annulus, as needed.
00068    * Takes time up to linear in the number of annuli.  For input vertices, this
00069    * is O(lg cfs(center)/lfs(center))).  For Steiner points, this is O(1).
00070    * If we cared about this term, we could make it be O(1) using a hash table
00071    * or an array like in BucketPQ.
00072    *
00073    * We don't store items at distance exactly zero.  TODO: instead, assert that
00074    * distance is non-zero, but first check for pointer equality that the center
00075    * is not the same as the item.
00076    ***************************************************************************/
00077   void add(const Point& center, Item *item) {
00078 
00079     double dist2 = item->toPoint().dist2(center);
00080     if (dist2 == 0.0) { return; }
00081     int log_d2 = bithacks::log2_norm(dist2);
00082 
00083     dprintf("Adding to center %s, item %s, annulus %d\n",
00084         center.toString().c_str(),
00085         item->toString().c_str(), log_d2);
00086 
00087     if (annuli_.empty()) {
00088       /* No annuli?  Better create one. */
00089       dprintf("Annuli empty\n");
00090       newAnnulus(annuli_.begin(), log_d2, item);
00091     }
00092     else if ( annuli_.front().log_radius2 < log_d2 ) {
00093       /* No annulus this big?  Create one. */
00094       dprintf("Annuli all small: %d\n", annuli_.front().log_radius2);
00095       newAnnulus(annuli_.begin(), log_d2, item);
00096     }
00097     else {
00098       /* Search for the proper annulus, or one a bit smaller. */
00099       typename Annuli::iterator it = annuli_.begin();
00100       typename Annuli::iterator end = annuli_.end();
00101       while (it != end && (*it).log_radius2 > log_d2) { ++it; }
00102       if (it != end && (*it).log_radius2 == log_d2) {
00103         // found!
00104         dprintf("Found correct annulus\n");
00105         (*it).add(item);
00106       } else {
00107         // not found; need a new annulus
00108         dprintf("No proper annulus\n");
00109         newAnnulus(it, log_d2, item);
00110       }
00111     }
00112   }
00113 
00114   /************************************************************
00115     * Reassign items *from* this set *to* the other set.
00116     * We look only at annuli that are at least in part closer
00117     * to the other center than to our own center.
00118    ************************************************************/
00119   void reassignTo(const Point& center, ConcentricShells& other, const Point& othercenter) {
00120     // find the midpoint between center and other.
00121     double pq2 = center.dist2(othercenter);
00122     assert(pq2 > 0.0);
00123     double half2 = pq2 / 4.0; // half squared, hence 1/4
00124     int last_annulus = bithacks::log2_norm(half2);
00125 
00126     /* We can't use BOOST_FOREACH since we modify the list in-place. */
00127     typename Annuli::iterator annulus_it = annuli_.begin();
00128 
00129     /* Keep going until all points in the annulus are closer
00130      * to our center than to the other center. */
00131     while (annulus_it != annuli_.end()
00132         && (*annulus_it).log_radius2 >= last_annulus) {
00133       ItemList& l = (*annulus_it).items;
00134       typename ItemList::iterator item_it = l.begin();
00135       while (item_it != l.end() /* end() must be recomputed: we modify the list */) {
00136         /* Reassign u if it's closer to newv than to this. */
00137         Item *item = *item_it;
00138         double up2 = center.dist2(item->toPoint());
00139         double uq2 = othercenter.dist2(item->toPoint());
00140         if (uq2 < up2) {
00141           item_it = l.erase(item_it);
00142           other.add(othercenter, item);
00143         } else {
00144           ++item_it;
00145         }
00146       }
00147 
00148       /* Erase the annulus if it's now empty. */
00149       if (l.empty()) {
00150         annulus_it = annuli_.erase(annulus_it);
00151       } else {
00152         ++annulus_it;
00153       }
00154     }
00155   }
00156 
00157   private:
00173   static int computeSmallestAnnulus(double pq2, double r2) {
00174     if (pq2 <= r2) {
00175       // B includes p, so every annulus intersects.
00176       return std::numeric_limits<int>::min();
00177     } else {
00178       double pB2 = pq2 + r2 - 2.0 * sqrt(pq2 * r2);
00179       return bithacks::log2_norm(pB2);
00180     }
00181   }
00182 
00183   /***************************************************************************
00184    * Find the closest item to q that is also within distance sqrt(r2) of q.
00185    *
00186    * Time is up to linear in the number of items, but it often much less
00187    * because we can avoid looking at any annuli that do not overlap B(q, r).
00188    ***************************************************************************/
00189   public:
00190   std::pair<Item*, double> findClosest(const Point& center, const Point& q, double r2) const {
00191     double pq2 = center.dist2(q);
00192     int smallest_annulus = computeSmallestAnnulus(pq2, r2);
00193 
00194     Item *best = NULL;
00195     // r2 stores the distance from q to best
00196 
00197     /* Keep going until all points in the annulus are outside B(q,r) */
00198     BOOST_FOREACH(const Annulus& annulus, annuli_) {
00199       /* Break if the annulus is too small to matter. */
00200       if (annulus.log_radius2 < smallest_annulus) { break; }
00201 
00202       /* Otherwise, iterate over all the items and keep track of the closest.
00203        * Update r to shrink B(q,r) as we find items. */
00204       BOOST_FOREACH(Item *item, annulus.items) {
00205         double iq2 = item->toPoint().dist2(q);
00206         if (iq2 < r2) {
00207           best = item;
00208           r2 = iq2;
00209           smallest_annulus = computeSmallestAnnulus(pq2, r2);
00210         }
00211       }
00212     }
00213     return make_pair(best, r2);
00214   }
00215 
00216   /***************************************************************************
00217    * Find an item that is within distance sqrt(r2) of q.
00218    *
00219    * Same as above, but short-cut.
00220    ***************************************************************************/
00221   public:
00222   std::pair<Item*, double> findPoint(const Point& center, const Point& q, double r2) const {
00223     double pq2 = center.dist2(q);
00224     int smallest_annulus = computeSmallestAnnulus(pq2, r2);
00225 
00226     // Loop over the annuli in order of size.
00227     BOOST_FOREACH(const Annulus& annulus, annuli_) {
00228       // Stop if the remaining annuli don't intersect B.
00229       if (annulus.log_radius2 < smallest_annulus) { break; }
00230 
00231       // Stop if we find an item in range.
00232       const ItemList& l = annulus.items;
00233       BOOST_FOREACH(Item *item, l) {
00234         double d2 = item->toPoint().dist2(q);
00235         if (d2 < r2) {
00236           return std::make_pair(item, d2);
00237         }
00238       }
00239     }
00240     return std::make_pair((Item*)0, r2);
00241   }
00242 
00243   /***************************************************************************
00244    * Return whether there are any items in this set.
00245    *
00246    * O(1) time.
00247    ***************************************************************************/
00248   bool empty() const {
00249     // in add we only create annuli when they have members; in reassign we
00250     // delete empty ones.  So the existence of an annulus is sufficient
00251     // evidence for there being an item.
00252     return annuli_.empty();
00253   }
00254 
00255   /***************************************************************************
00256    * Return an item in the outermost shell; it is within a factor of two of
00257    * the farthest item.
00258    *
00259    * O(1) time.
00260    ***************************************************************************/
00261   Item *getFarItem() const {
00262     assert(!empty());
00263     return annuli_.front().items.front();
00264   }
00265 };
00266 
00267 #undef dprintf
00268 
00269 #endif

Generated on Thu Mar 27 19:04:14 2008 by  doxygen 1.4.6