miniball.H

00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00020 
00021 
00022 //    Copright (C) 1999
00023 //    $Revision: 1.1 $
00024 //    $Date: 2003/03/15 16:26:26 $
00025 //
00026 //    This program is free software; you can redistribute it and/or modify
00027 //    it under the terms of the GNU General Public License as published by
00028 //    the Free Software Foundation; either version 2 of the License, or
00029 //    (at your option) any later version.
00030 //
00031 //    This program is distributed in the hope that it will be useful,
00032 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00033 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00034 //    GNU General Public License for more details.
00035 //
00036 //    You should have received a copy of the GNU General Public License
00037 //    along with this program; if not, write to the Free Software
00038 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA,
00039 //    or download the License terms from prep.ai.mit.edu/pub/gnu/COPYING-2.0.
00040 //
00041 //    Contact:
00042 //    --------
00043 //    Bernd Gaertner
00044 //    Institut f. Informatik
00045 //    ETH Zuerich
00046 //    ETH-Zentrum
00047 //    CH-8092 Zuerich, Switzerland
00048 //    http://www.inf.ethz.ch/personal/gaertner
00049 //
00050 
00051 
00052 #include <list>
00053 
00054 #include <archon/math/vector.H>
00055 
00056 namespace Archon
00057 {
00058   using namespace std;
00059 
00060   namespace Utilities
00061   {
00062     template<int N>
00063     class MinimalBoundingBall
00064     {
00065     public:
00066       // types
00067       typedef Math::BasicVector<double, N>           Point;
00068       typedef typename list<Point>::iterator         It;
00069       typedef typename list<Point>::const_iterator   Cit;
00070 
00071       class Basis
00072       {
00073       private:
00074         // data members
00075         int    m, s;   // size and number of support points
00076         double q0[N];
00077     
00078         double z[N+1];
00079         double f[N+1];
00080         double v[N+1][N];
00081         double a[N+1][N];
00082     
00083         double c[N+1][N];
00084         double sqr_r[N+1];
00085 
00086         double *current_c;      // points to some c[j]
00087         double current_sqr_r;
00088     
00089         double sqr(double r) const { return r*r; }
00090     
00091       public:
00092         Basis();
00093 
00094         // access
00095         const double *center() const;
00096         double       squared_radius() const;
00097         int          size() const;
00098         int          support_size() const;
00099         double       excess(const Point &) const;
00100 
00101         // modification
00102         void reset(); // generates empty sphere with m=s=0
00103         bool push(const Point &);
00104         void pop();
00105 
00106         // checking
00107         double slack() const;
00108       };
00109 
00110     private:
00111       // data members
00112       list<Point> L;              // STL list keeping the points
00113       Basis       B;              // basis keeping the current ball
00114       It          support_end;    // past-the-end iterator of support set
00115     
00116       // private methods
00117       void   mtf_mb(It k);
00118       void   pivot_mb(It k);
00119       void   move_to_front(It j);
00120       double max_excess(It t, It i, It &pivot) const;
00121       double abs(double r) const { return r>0 ? r : -r; }
00122       double sqr(double r) const { return r*r; }
00123 
00124     public:
00125       // construction
00126       MinimalBoundingBall() {}
00127       void check_in(const Point &);
00128       void build(bool pivoting = true);
00129     
00130       // access
00131       Point  center() const;
00132       double squared_radius() const;
00133       int    nr_points() const;
00134       Cit    points_begin() const;
00135       Cit    points_end() const;
00136       int    nr_support_points() const;
00137       Cit    support_points_begin() const;
00138       Cit    support_points_end() const;
00139     
00140       // checking
00141       double accuracy(double &slack) const;
00142       bool   is_valid(double tolerance = 1e-15) const;
00143     };
00144 
00145 
00146 
00147     template <int N>
00148     void MinimalBoundingBall<N>::check_in(const Point &p)
00149     {
00150       L.push_back(p);
00151     }
00152 
00153     template<int N>
00154     void MinimalBoundingBall<N>::build(bool pivoting)
00155     {
00156       B.reset();
00157       support_end = L.begin();
00158       if(pivoting) pivot_mb(L.end());
00159       else mtf_mb(L.end());
00160     }
00161 
00162     template<int N>
00163     void MinimalBoundingBall<N>::mtf_mb(It i)
00164     {
00165       support_end = L.begin();
00166       if((B.size())==N+1) return;
00167       for(It k=L.begin(); k!=i;)
00168       {
00169         It j=k++;
00170         if(B.excess(*j) > 0)
00171         {
00172           if(B.push(*j))
00173           {
00174             mtf_mb (j);
00175             B.pop();
00176             move_to_front(j);
00177           }
00178         }
00179       }
00180     }
00181 
00182     template<int N>
00183     void MinimalBoundingBall<N>::move_to_front(It j)
00184     {
00185       if(support_end == j) support_end++;
00186       L.splice(L.begin(), L, j);
00187     }
00188 
00189     template<int N>
00190     void MinimalBoundingBall<N>::pivot_mb(It i)
00191     {
00192       It t = ++L.begin();
00193       mtf_mb(t);
00194       double max_e, old_sqr_r = 0;
00195       do
00196       {
00197         It pivot;
00198         max_e = max_excess(t, i, pivot);
00199         if(max_e > 0)
00200         {
00201           t = support_end;
00202           if(t==pivot) ++t;
00203           old_sqr_r = B.squared_radius();
00204           B.push(*pivot);
00205           mtf_mb(support_end);
00206           B.pop();
00207           move_to_front(pivot);
00208         }
00209       }
00210       while(max_e > 0 && B.squared_radius() > old_sqr_r);
00211     }
00212 
00213     template<int N>
00214     double MinimalBoundingBall<N>::max_excess(It t, It i, It &pivot) const
00215     {
00216       const double *c = B.center(), sqr_r = B.squared_radius();
00217       double e, max_e = 0;
00218       for(It k=t; k!=i; ++k)
00219       {
00220         const Point &p = *k;
00221         e = -sqr_r;
00222         for(int j=0; j<N; ++j) e += sqr(p[j]-c[j]);
00223         if(e > max_e)
00224         {
00225           max_e = e;
00226           pivot = k;
00227         }
00228       }
00229       return max_e;
00230     }
00231 
00232     template<int N>
00233     typename MinimalBoundingBall<N>::Point MinimalBoundingBall<N>::center() const
00234     {
00235       return Point(B.center());
00236     }
00237 
00238     template<int N>
00239     double MinimalBoundingBall<N>::squared_radius() const
00240     {
00241       return B.squared_radius();
00242     }
00243 
00244     template<int N>
00245     int MinimalBoundingBall<N>::nr_points() const
00246     {
00247       return L.size();
00248     }
00249 
00250     template<int N>
00251     typename MinimalBoundingBall<N>::Cit MinimalBoundingBall<N>::points_begin() const
00252     {
00253       return L.begin();
00254     }
00255 
00256     template<int N>
00257     typename MinimalBoundingBall<N>::Cit MinimalBoundingBall<N>::points_end() const
00258     {
00259       return L.end();
00260     }
00261 
00262     template<int N>
00263     int MinimalBoundingBall<N>::nr_support_points() const
00264     {
00265       return B.support_size();
00266     }
00267 
00268     template<int N>
00269     typename MinimalBoundingBall<N>::Cit MinimalBoundingBall<N>::support_points_begin() const
00270     {
00271       return L.begin();
00272     }
00273 
00274     template<int N>
00275     typename MinimalBoundingBall<N>::Cit MinimalBoundingBall<N>::support_points_end() const
00276     {
00277       return support_end;
00278     }
00279 
00280     template<int N>
00281     double MinimalBoundingBall<N>::accuracy(double &slack) const
00282     {
00283       double e, max_e = 0;
00284       int n_supp=0;
00285       Cit i;
00286       for(i=L.begin(); i!=support_end; ++i,++n_supp)
00287         if((e = abs(B.excess(*i))) > max_e)
00288           max_e = e;
00289    
00290       // you've found a non-numerical problem if the following ever fails
00291       assert(n_supp == nr_support_points());
00292    
00293       for(i=support_end; i!=L.end(); ++i)
00294         if((e = B.excess(*i)) > max_e)
00295           max_e = e;
00296    
00297       slack = B.slack();
00298       return max_e/squared_radius();
00299     }
00300 
00301     template<int N>
00302     bool MinimalBoundingBall<N>::is_valid(double tolerance) const
00303     {
00304       double slack;
00305       return accuracy(slack) < tolerance && slack == 0;
00306     }
00307 
00308 
00309     // MinimalBoundingBall<N>::Basis
00310 
00311     template<int N>
00312     const double *MinimalBoundingBall<N>::Basis::center() const
00313     {
00314       return current_c;
00315     }
00316 
00317     template<int N>
00318     double MinimalBoundingBall<N>::Basis::squared_radius() const
00319     {
00320       return current_sqr_r;
00321     }
00322 
00323     template<int N>
00324     int MinimalBoundingBall<N>::Basis::size() const
00325     {
00326       return m;
00327     }
00328 
00329     template<int N>
00330     int MinimalBoundingBall<N>::Basis::support_size() const
00331     {
00332       return s;
00333     }
00334 
00335     template<int N>
00336     double MinimalBoundingBall<N>::Basis::excess(const Point &p) const
00337     {
00338       double e = -current_sqr_r;
00339       for(int k=0; k<N; ++k) e += sqr(p[k]-current_c[k]);
00340       return e;
00341     }
00342 
00343     template<int N>
00344     void MinimalBoundingBall<N>::Basis::reset()
00345     {
00346       m = s = 0;
00347       // we misuse c[0] for the center of the empty sphere
00348       for(int j=0; j<N; ++j) c[0][j]=0;
00349       current_c = c[0];
00350       current_sqr_r = -1;
00351     }
00352 
00353     template<int N>
00354     MinimalBoundingBall<N>::Basis::Basis()
00355     {
00356       reset();
00357     }
00358 
00359     template<int N>
00360     void MinimalBoundingBall<N>::Basis::pop()
00361     {
00362       --m;
00363     }
00364 
00365     template<int N>
00366     bool MinimalBoundingBall<N>::Basis::push(const Point &p)
00367     {
00368       int i, j;
00369       double eps = 1e-32;
00370       if(m==0)
00371       {
00372         for(i=0; i<N; ++i) q0[i] = p[i];
00373         for(i=0; i<N; ++i) c[0][i] = q0[i];
00374         sqr_r[0] = 0;
00375       }
00376       else
00377       {
00378         // set v_m to Q_m
00379         for(i=0; i<N; ++i) v[m][i] = p[i]-q0[i];
00380 
00381         // compute the a_{m,i}, i< m
00382         for(i=1; i<m; ++i)
00383         {
00384           a[m][i] = 0;
00385           for(j=0; j<N; ++j) a[m][i] += v[i][j] * v[m][j];
00386           a[m][i]*=(2/z[i]);
00387         }
00388    
00389         // update v_m to Q_m-\bar{Q}_m
00390         for(i=1; i<m; ++i)
00391           for(j=0; j<N; ++j)
00392             v[m][j] -= a[m][i]*v[i][j];
00393    
00394         // compute z_m
00395         z[m]=0;
00396         for(j=0; j<N; ++j) z[m] += sqr(v[m][j]);
00397         z[m]*=2;
00398    
00399         // reject push if z_m too small
00400         if(z[m]<eps*current_sqr_r) return false;
00401    
00402         // update c, sqr_r
00403         double e = -sqr_r[m-1];
00404         for(i=0; i<N; ++i) e += sqr(p[i]-c[m-1][i]);
00405         f[m]=e/z[m];
00406    
00407         for(i=0; i<N; ++i) c[m][i] = c[m-1][i]+f[m]*v[m][i];
00408         sqr_r[m] = sqr_r[m-1] + e*f[m]/2;
00409       }
00410       current_c = c[m];
00411       current_sqr_r = sqr_r[m];
00412       s = ++m;
00413       return true;
00414     }
00415 
00416     template<int N>
00417     double MinimalBoundingBall<N>::Basis::slack() const
00418     {
00419       double l[N+1], min_l=0;
00420       l[0] = 1;
00421       for(int i=s-1; i>0; --i)
00422       {
00423         l[i] = f[i];
00424         for(int k=s-1; k>i; --k) l[i]-=a[k][i]*l[k];
00425         if(l[i] < min_l) min_l = l[i];
00426         l[0] -= l[i];
00427       }
00428       if(l[0] < min_l) min_l = l[0];
00429       return min_l < 0 ? -min_l : 0;
00430     }
00431   }
00432 }

Generated on Sun Jul 30 22:55:44 2006 for Archon by  doxygen 1.4.4