Circles.h

Go to the documentation of this file.
00001 /* $Id$ */
00002 #ifndef Circles_HEADER
00003 #define Circles_HEADER
00004 
00005 #include <boost/array.hpp>
00006 
00007 #ifdef HAVE_LAPACK
00008 #  include <clapack.h>
00009 #endif
00010 
00011 #include <math.h>
00012 
00013 #include "Point.h"
00014 #include "PointConvert.h"
00015 #include "predicates.h"
00016 
00017 namespace Geometry {
00018 using namespace std;
00019 using namespace boost;
00020 
00021 template <size_t d> struct CenterRadius {
00022   Point<d> center;
00023   double radius2;
00024   CenterRadius(Point<d> c, double r2)
00025     : center(c), radius2(r2) { }
00026   CenterRadius()
00027     : center(0.0), radius2(0.0) { }
00028 
00029   double dist2(const CenterRadius& other) {
00030     return center.dist2(other.center);
00031   }
00032 
00037   bool approximatelyIntersects(const CenterRadius& other) {
00038     double d2 = dist2(other);
00039     // using branches to avoid the 3 sqrts here might be useful, depending
00040     // on architecture.
00041     double r = sqrt(radius2) + sqrt(other.radius2);
00042     return sqrt(d2) < r;
00043   }
00044 
00045   std::string toString() const {
00046     char buffer[4096];
00047     snprintf(buffer, sizeof(buffer), "%s // %g", center.toString().c_str(), radius2);
00048     return std::string(buffer);
00049   }
00050 };
00051 
00052 #ifdef HAVE_LAPACK
00053 /***************************************************************************
00054  * Compute the circumcenter of the simplex.  If we live in a lower dimension
00055  * than the ambient, compute the center of the diametral ball.
00056  *
00057  * This is a general-dimensional algorithm, fairly accurate except on slivers,
00058  * but amazingly slow because of all the marshalling / unmarshalling code
00059  * talking to LAPACK.
00060  *
00061  * First, translate the simplex by q so that it includes the origin.
00062  *
00063  * We want a point x that is equidistant to all the points:
00064  *   (x-pi)^2 = (x-pj)^2  for all i, any j != i
00065  * Let pj be the origin; that yields:
00066  *   (x-pi)^2 = x^2       for all i
00067  * Algebra then gives the system:
00068  *   pi^T x = 0.5 pi^2    for all i
00069  * Rewrite as:
00070  *   P^T x = 0.5 b        where P has pi as its columns, b = {pi^2}
00071  *
00072  * If P is a lower-dimensional space, then we also want to impose that
00073  * x be on the affine plane of P (which goes through the origin):
00074  *   x = P alpha
00075  * Substitute in to see:
00076  *   P^T P alpha = 0.5 b
00077  * Solve for alpha, then substitute for x.
00078  *
00079  * Having found x, we translate back by q.
00080  ***************************************************************************/
00081 template <unsigned ambient, unsigned topological>
00082 CenterRadius<ambient>
00083 circumcenter(const array<Point<ambient>, topological+1>& points) {
00084   typedef Point<topological> PPoint;
00085   typedef Point<ambient> Point;
00086 
00087   // Compute P.
00088   array<Point, topological> P;
00089   const Point& q = points[topological];
00090   for(unsigned i = 0; i < topological; i++) {
00091     P[i] = ( points[i] - q );
00092   }
00093 
00094   // Compute b.
00095   PPoint b;
00096   for(unsigned i = 0; i < topological; i++) {
00097     b[i] = P[i].norm2() / 2.0;
00098   }
00099 
00100   // Compute A, which is P^T or P^T P depending on dimension.
00101   // A is column-major.
00102   array<double, topological * topological> A;
00103   for(unsigned i = 0; i < topological; i++) {
00104     for(unsigned j = 0; j < topological; j++) {
00105       if (ambient == topological) {
00106         A[topological * j + i] = P[i][j];
00107       } else {
00108         A[topological * j + i] = P[i].dot(P[j]);
00109       }
00110     }
00111   }
00112 
00113   // Call LAPACK.  This is a bit ugly.
00114   {
00115     integer n = topological, nrhs = 1, lda = topological,
00116         pivots[topological], ldb = topological, info;
00117     dgesv_(&n, &nrhs, A.c_array(), &lda, pivots, b.c_array(), &ldb, &info);
00118     assert(info == 0);
00119   }
00120 
00121   // Now, A and b have been clobbered.  b is our answer.
00122   // We have to turn the answer into the circumcenter.
00123   Point c; // uninitialized
00124   if (ambient == topological) {
00125     c = pointCast<ambient,topological>(PPoint(b));
00126   } else {
00127     // b is now alpha; need to compute P alpha
00128     c.assign(0.0); // init to zero and sum up
00129     for (unsigned i = 0; i < topological; i++) {
00130       c += P[i] * b[i];
00131     }
00132   }
00133 
00134   // For basically free, we can get the radius along with the center, so
00135   // return it too.
00136   double r2 = c.norm2();
00137 
00138   // Don't forget to translate back by q.
00139   c += q;
00140 
00141   //printf("center %s, radius %g\n", c.toString().c_str(), sqrt(r2));
00142   return CenterRadius<ambient>(c, r2);
00143 }
00144 #endif
00145 
00146 /***************************************************************************
00147  * typedef the CenterRadius and Point properly.
00148  * assumes we want 'CenterRadius' to mean CenterRadius<ambient>
00149  * Use a macro in case gcc decides to change how this is supposed to be
00150  * written.  I undef the macro at the end of the file.
00151  ***************************************************************************/
00152 #define typedef_ambient_types(ambient) \
00153   typedef Geometry::Point< (ambient) > Point; \
00154   typedef Geometry::CenterRadius< (ambient) > CenterRadius /* no ; */
00155 
00156 /***************************************************************************
00157   * Special-dimension circumcenter code: for segments.
00158   *
00159   * This is very cheap, very robust.
00160  ***************************************************************************/
00161 template <unsigned ambient>
00162 CenterRadius<ambient>
00163 circumcenter_segment(const Point<ambient>& a, const Point<ambient>& b) {
00164   return CenterRadius<ambient>(
00165       (a + b) * 0.5, /* center is average of the points */
00166       (a - b).norm2() * 0.25 /* radius is |a-b|/2; we return radius^2 */);
00167 }
00168 
00169 /***************************************************************************
00170   * Special-dimension circumcenter code: for topological triangles.
00171   *
00172   * For ambient dimensions 2 and 3, I have special-dimensional code which is
00173   * much faster than calling the LAPACK-based circumcenter() routine above.
00174   * The special-dimensional code solves the same linear system using Cramer's
00175   * Rule.  For larger ambient dimensions, the code calls the LAPACK-based
00176   * routine (of course, HAVE_LAPACK must be defined then).
00177   *
00178   * I define cctr_tri_struct and specialize it because at the moment (2007),
00179   * C++ doesn't allow template specialization of functions.
00180   * Call circumcenter_triangle() and ignore the structures.
00181  ***************************************************************************/
00182 template <unsigned ambient> struct cctr_tri_struct
00183 #ifdef HAVE_LAPACK
00184 {
00185   typedef_ambient_types(ambient);
00186   CenterRadius operator() (const Point& a, const Point& b, const Point& c)
00187   {
00188     array<Point, 3> points;
00189     points[0] = a;
00190     points[1] = b;
00191     points[2] = c;
00192     return circumcenter<ambient, 2>(points);
00193   }
00194 }
00195 #endif
00196 ;
00197 
00198 template <> struct cctr_tri_struct<2> {
00199   static const unsigned ambient = 2;
00200   typedef_ambient_types(ambient);
00201   CenterRadius operator() (const Point& a, const Point& b, const Point& c)
00202   {
00203     /* Straight out of Shewchuk's robnotes */
00204     double denom = 2.0 * orient2d(a.data(), b.data(), c.data());
00205     Point r = a - c;
00206     Point s = b - c;
00207     double r2 = r.norm2();
00208     double s2 = s.norm2();
00209     double numx = r2 * s[1] - s2 * r[1];
00210     double numy = s2 * r[0] - r2 * s[0];
00211     Point q;
00212     q[0] = numx / denom;
00213     q[1] = numy / denom;
00214     return CenterRadius(c + q, q.norm2());
00215   }
00216 };
00217 
00218 template <> struct cctr_tri_struct<3> {
00219   static const unsigned ambient = 3;
00220   typedef_ambient_types(ambient);
00221   CenterRadius operator() (const Point& a, const Point& b, const Point& c)
00222   {
00223     /* Straight out of Shewchuk's robnotes */
00224     Point r = a - c;
00225     Point s = b - c;
00226     double r2 = r.norm2();
00227     double s2 = s.norm2();
00228     Point rs = crossProduct(r, s);
00229     Point r2s_s2r = (s * r2 - r * s2);
00230     Point numerator = crossProduct(r2s_s2r, rs);
00231     double denominator = 2.0 * rs.norm2();
00232     Point q = numerator / denominator;
00233     return CenterRadius(c + q, q.norm2());
00234   }
00235 };
00236 
00237 template <unsigned ambient>
00238 CenterRadius<ambient>
00239 circumcenter_triangle(const Point<ambient>& a, const Point<ambient>& b, const Point<ambient>& c) {
00240   return cctr_tri_struct<ambient>()(a,b,c);
00241 }
00242 
00243 /***************************************************************************
00244   * Special-dimension circumcenter code: for topological tetrahedra.
00245   *
00246   * The code in ambient dimension 3 is both faster, and on sliver is far more
00247   * numerically stable than the general-dimension code.
00248   * TODO: it is terrible on skinny tets -- this code is solving a linear system
00249   * Ax = b, where A is degenerate on slivers.
00250   *
00251   * For higher ambient dimension, HAVE_LAPACK must be defined.
00252   *
00253   * I define cctr_tet_struct and specialize it because at the moment (2007),
00254   * C++ doesn't allow template specialization of functions.
00255   * Call circumcenter_tetrahedron() and ignore the structures.
00256   *
00257  ***************************************************************************/
00258 template <unsigned ambient> struct cctr_tet_struct
00259 #ifdef HAVE_LAPACK
00260 {
00261   typedef_ambient_types(ambient);
00262   CenterRadius operator() (const Point& a, const Point& b, const Point& c, const Point& d)
00263   {
00264     array<Point, 4> points;
00265     points[0] = a;
00266     points[1] = b;
00267     points[2] = c;
00268     points[3] = d;
00269     return circumcenter<ambient, 3>(points);
00270   }
00271 }
00272 #endif
00273 ;
00274 
00275 template <> struct cctr_tet_struct<3> {
00276   static const unsigned ambient = 3;
00277   typedef_ambient_types(ambient);
00278   CenterRadius operator() (const Point& a, const Point& b, const Point& c, const Point& d)
00279   {
00280     /* Code from jrs' robnotes. */
00281     double denominator = 2.0 * orient3d(a.data(), b.data(), c.data(), d.data());
00282 
00283     /* Denominator is related to the volume; if the volume is small,
00284      * and we have a sliver, then all four points are almost cocircular,
00285      * so an arbitrary face's circumcenter is the global circumcenter.
00286      *
00287      * TODO: What if it's not a sliver?
00288      * TODO: What is small?  We've seen problems at 10^-15, but is 10^-10 too big?
00289      */
00290     if (fabs(denominator) < 1e-10) {
00291       return circumcenter_triangle<ambient>(a, b, c);
00292     }
00293 
00294     Point t = d - a; double t2 = t.norm2();
00295     Point u = d - b; double u2 = u.norm2();
00296     Point v = d - c; double v2 = v.norm2();
00297 
00298     Point uv = crossProduct(u,v);
00299     Point vt = crossProduct(v,t);
00300     Point tu = crossProduct(t,u);
00301     Point numerator = uv*t2 + vt*u2 + tu*v2;
00302     Point q = numerator / denominator;
00303     return CenterRadius(d + q, q.norm2());
00304   }
00305 };
00306 
00307 template <unsigned ambient>
00308 CenterRadius<ambient>
00309 circumcenter_tetrahedron(const Point<ambient>& a, const Point<ambient>& b, const Point<ambient>& c, const Point<ambient>& d) {
00310   return cctr_tet_struct<ambient>()(a,b,c,d);
00311 }
00312 
00313 #undef typedef_ambient_types
00314 
00315 }; // namespace Geometry
00316 
00317 #endif

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