Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/mathutil/boundingSphere.cxx

Go to the documentation of this file.
00001 // Filename: boundingSphere.cxx
00002 // Created by:  drose (01Oct99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "boundingSphere.h"
00020 #include "boundingHexahedron.h"
00021 #include "boundingLine.h"
00022 #include "config_mathutil.h"
00023 #include "dcast.h"
00024 
00025 #include <math.h>
00026 #include <algorithm>
00027 
00028 TypeHandle BoundingSphere::_type_handle;
00029 
00030 BoundingVolume *BoundingSphere::
00031 make_copy() const {
00032   return new BoundingSphere(*this);
00033 }
00034 
00035 LPoint3f BoundingSphere::
00036 get_min() const {
00037   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00038   nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00039   return LPoint3f(_center[0] - _radius,
00040                   _center[1] - _radius,
00041                   _center[2] - _radius);
00042 }
00043 
00044 LPoint3f BoundingSphere::
00045 get_max() const {
00046   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00047   nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00048   return LPoint3f(_center[0] + _radius,
00049                   _center[1] + _radius,
00050                   _center[2] + _radius);
00051 }
00052 
00053 LPoint3f BoundingSphere::
00054 get_approx_center() const {
00055   nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00056   nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00057   return get_center();
00058 }
00059 
00060 void BoundingSphere::
00061 xform(const LMatrix4f &mat) {
00062   nassertv(!mat.is_nan());
00063 
00064   if (!is_empty() && !is_infinite()) {
00065     // First, determine the longest axis of the matrix, in case it
00066     // contains a non-proportionate scale.
00067 
00068 /*
00069     LVector3f x,y,z;
00070         mat.get_row3(x,0);
00071         mat.get_row3(y,1);
00072         mat.get_row3(z,2);
00073 
00074     float xd = dot(x, x);
00075     float yd = dot(y, y);
00076     float zd = dot(z, z);
00077 */
00078     float xd,yd,zd,scale;
00079 
00080         #define ROW_DOTTED(mat,ROWNUM)                        \
00081             (mat._m.m._##ROWNUM##0*mat._m.m._##ROWNUM##0 +    \
00082              mat._m.m._##ROWNUM##1*mat._m.m._##ROWNUM##1 +    \
00083              mat._m.m._##ROWNUM##2*mat._m.m._##ROWNUM##2)
00084 
00085     xd = ROW_DOTTED(mat,0);
00086     yd = ROW_DOTTED(mat,1);
00087     zd = ROW_DOTTED(mat,2);
00088 
00089         scale = max(xd,yd);
00090         scale = max(scale,zd);
00091         scale = sqrtf(scale);
00092 
00093     // Transform the radius
00094     _radius *= scale;
00095 
00096     // Transform the center
00097     _center = _center * mat;
00098   }
00099 }
00100 
00101 void BoundingSphere::
00102 output(ostream &out) const {
00103   if (is_empty()) {
00104     out << "bsphere, empty";
00105   } else if (is_infinite()) {
00106     out << "bsphere, infinite";
00107   } else {
00108     out << "bsphere, c (" << _center << "), r " << _radius;
00109   }
00110 }
00111 
00112 bool BoundingSphere::
00113 extend_other(BoundingVolume *other) const {
00114   return other->extend_by_sphere(this);
00115 }
00116 
00117 bool BoundingSphere::
00118 around_other(BoundingVolume *other,
00119              const BoundingVolume **first,
00120              const BoundingVolume **last) const {
00121   return other->around_spheres(first, last);
00122 }
00123 
00124 int BoundingSphere::
00125 contains_other(const BoundingVolume *other) const {
00126   return other->contains_sphere(this);
00127 }
00128 
00129 
00130 bool BoundingSphere::
00131 extend_by_point(const LPoint3f &point) {
00132   nassertr(!point.is_nan(), false);
00133 
00134   if (is_empty()) {
00135     _center = point;
00136     _radius = 0.0f;
00137     _flags = 0;
00138   } else if (!is_infinite()) {
00139     LVector3f v = point - _center;
00140     float dist2 = dot(v, v);
00141     if (dist2 > _radius * _radius) {
00142       _radius = sqrtf(dist2);
00143     }
00144   }
00145   return true;
00146 }
00147 
00148 bool BoundingSphere::
00149 extend_by_sphere(const BoundingSphere *sphere) {
00150   nassertr(!sphere->is_empty() && !sphere->is_infinite(), false);
00151   nassertr(!is_infinite(), false);
00152 
00153   if (is_empty()) {
00154     _center = sphere->_center;
00155     _radius = sphere->_radius;
00156     _flags = 0;
00157   } else {
00158     float dist = length(sphere->_center - _center);
00159 
00160     _radius = max(_radius, dist + sphere->_radius);
00161   }
00162   return true;
00163 }
00164 
00165 bool BoundingSphere::
00166 extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
00167   return extend_by_finite(hexahedron);
00168 }
00169 
00170 bool BoundingSphere::
00171 extend_by_finite(const FiniteBoundingVolume *volume) {
00172   nassertr(!volume->is_empty(), false);
00173 
00174   LVector3f min1 = volume->get_min();
00175   LVector3f max1 = volume->get_max();
00176 
00177   if (is_empty()) {
00178     _center = (min1 + max1) * 0.5f;
00179     _radius = length(LVector3f(max1 - _center));
00180     _flags = 0;
00181   } else {
00182     LVector3f v = max1 - _center;
00183     float dist2 = dot(v, v);
00184 
00185     if (dist2 > _radius * _radius) {
00186       _radius = sqrtf(dist2);
00187     }
00188   }
00189 
00190   return true;
00191 }
00192 
00193 bool BoundingSphere::
00194 around_points(const LPoint3f *first, const LPoint3f *last) {
00195   nassertr(first != last, false);
00196 
00197   // First, get the minmax of all the points to construct a bounding
00198   // box.
00199   const LPoint3f *p = first;
00200 
00201 #ifndef NDEBUG
00202   // Skip any NaN points.
00203   int skipped_nan = 0;
00204   while (p != last && (*p).is_nan()) {
00205     ++p;
00206     ++skipped_nan;
00207   }
00208   if (p == last) {
00209     mathutil_cat.warning()
00210       << "BoundingSphere around NaN\n";
00211     return false;
00212   }
00213 #endif
00214 
00215   LPoint3f min_box = *p;
00216   LPoint3f max_box = *p;
00217   ++p;
00218 
00219 #ifndef NDEBUG
00220   // Skip more NaN points.
00221   while (p != last && (*p).is_nan()) {
00222     ++p;
00223     ++skipped_nan;
00224   }
00225 #endif
00226 
00227   if (p == last) {
00228     // Only one point; we have a radius of zero.  This is not the same
00229     // thing as an empty sphere, because our volume contains one
00230     // point; an empty sphere contains no points.
00231     _center = min_box;
00232     _radius = 0.0f;
00233 
00234   } else {
00235     // More than one point; we have a nonzero radius.
00236     while (p != last) {
00237 #ifndef NDEBUG
00238       // Skip more NaN points.
00239       if ((*p).is_nan()) {
00240         ++skipped_nan;
00241       } else
00242 #endif
00243         {
00244           min_box.set(min(min_box[0], (*p)[0]),
00245                       min(min_box[1], (*p)[1]),
00246                       min(min_box[2], (*p)[2]));
00247           max_box.set(max(max_box[0], (*p)[0]),
00248                       max(max_box[1], (*p)[1]),
00249                       max(max_box[2], (*p)[2]));
00250         }
00251       ++p;
00252     }
00253 
00254     // Now take the center of the bounding box as the center of the sphere.
00255     _center = (min_box + max_box) * 0.5f;
00256 
00257     // Now walk back through to get the max distance from center.
00258     float max_dist2 = 0.0f;
00259     for (p = first; p != last; ++p) {
00260       LVector3f v = (*p) - _center;
00261       float dist2 = dot(v, v);
00262       max_dist2 = max(max_dist2, dist2);
00263     }
00264 
00265     _radius = sqrtf(max_dist2);
00266   }
00267 
00268 #ifndef NDEBUG
00269   if (skipped_nan != 0) {
00270     mathutil_cat.warning()
00271       << "BoundingSphere ignored " << skipped_nan << " NaN points of "
00272       << (last - first) << " total.\n";
00273   }
00274 #endif
00275 
00276   _flags = 0;
00277 
00278   return true;
00279 }
00280 
00281 bool BoundingSphere::
00282 around_spheres(const BoundingVolume **first,
00283                const BoundingVolume **last) {
00284   return around_finite(first, last);
00285 }
00286 
00287 bool BoundingSphere::
00288 around_hexahedrons(const BoundingVolume **first,
00289                    const BoundingVolume **last) {
00290   return around_finite(first, last);
00291 }
00292 
00293 bool BoundingSphere::
00294 around_finite(const BoundingVolume **first,
00295               const BoundingVolume **last) {
00296   nassertr(first != last, false);
00297 
00298   // We're given a set of bounding volumes, at least the first one of
00299   // which is guaranteed to be finite and nonempty.  Some others may
00300   // not be.
00301 
00302   // First, get the minmax of all the points to construct a bounding
00303   // box.
00304   const BoundingVolume **p = first;
00305   nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
00306   nassertr((*p)->is_of_type(FiniteBoundingVolume::get_class_type()), false);
00307   const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
00308   LPoint3f min_box = vol->get_min();
00309   LPoint3f max_box = vol->get_max();
00310 
00311   bool any_unknown = false;
00312 
00313   for (++p; p != last; ++p) {
00314     nassertr(!(*p)->is_infinite(), false);
00315     if (!(*p)->is_empty() &&
00316         (*p)->is_of_type(FiniteBoundingVolume::get_class_type())) {
00317       const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
00318       LPoint3f min1 = vol->get_min();
00319       LPoint3f max1 = vol->get_max();
00320       min_box.set(min(min_box[0], min1[0]),
00321                   min(min_box[1], min1[1]),
00322                   min(min_box[2], min1[2]));
00323       max_box.set(max(max_box[0], max1[0]),
00324                   max(max_box[1], max1[1]),
00325                   max(max_box[2], max1[2]));
00326 
00327       if (!(*p)->is_of_type(BoundingSphere::get_class_type())) {
00328         any_unknown = true;
00329       }
00330     }
00331   }
00332 
00333   // Now take the center of the bounding box as the center of the sphere.
00334   _center = (min_box + max_box) * 0.5f;
00335 
00336   if (any_unknown) {
00337     // If we have any volumes in the list that we don't know what to
00338     // do with, we'll have to make the bounding sphere large enough to
00339     // enclose the bounding box.  Less than ideal, but too bad.
00340     _radius = length(max_box - _center);
00341 
00342   } else {
00343     // Otherwise, we do understand all the volumes in the list; make
00344     // the sphere as tight as we can.
00345     _radius = 0.0f;
00346     for (p = first; p != last; ++p) {
00347       if (!(*p)->is_empty()) {
00348         if ((*p)->is_of_type(BoundingSphere::get_class_type())) {
00349           const BoundingSphere *sphere = DCAST(BoundingSphere, *p);
00350           float dist = length(sphere->_center - _center);
00351           _radius = max(_radius, dist + sphere->_radius);
00352         } else {
00353           // Shouldn't get here, unless we missed a type from above.
00354           mathutil_cat.error()
00355             << "Unexpected type in BoundingSphere::around_finite()\n";
00356           nassertr(false, false);
00357         }
00358       }
00359     }
00360   }
00361 
00362   _flags = 0;
00363   return true;
00364 }
00365 
00366 int BoundingSphere::
00367 contains_point(const LPoint3f &point) const {
00368   nassertr(!point.is_nan(), IF_no_intersection);
00369 
00370   if (is_empty()) {
00371     return IF_no_intersection;
00372 
00373   } else if (is_infinite()) {
00374     return IF_possible | IF_some | IF_all;
00375 
00376   } else {
00377     LVector3f v = point - _center;
00378     float dist2 = dot(v, v);
00379     return (dist2 <= _radius * _radius) ?
00380       IF_possible | IF_some | IF_all : IF_no_intersection;
00381   }
00382 }
00383 
00384 int BoundingSphere::
00385 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
00386   nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
00387 
00388   if (a == b) {
00389     return contains_point(a);
00390   }
00391   if (is_empty()) {
00392     return IF_no_intersection;
00393 
00394   } else if (is_infinite()) {
00395     return IF_possible | IF_some | IF_all;
00396 
00397   } else {
00398     LPoint3f from = a;
00399     LVector3f delta = b - a;
00400     float t1, t2;
00401 
00402     // Solve the equation for the intersection of a line with a sphere
00403     // using the quadratic equation.
00404     float A = dot(delta, delta);
00405 
00406     nassertr(A != 0.0f, 0);    // Trivial line segment.
00407 
00408     LVector3f fc = from - _center;
00409     float B = 2.0f * dot(delta, fc);
00410     float C = dot(fc, fc) - _radius * _radius;
00411 
00412     float radical = B*B - 4.0f*A*C;
00413 
00414     if (IS_NEARLY_ZERO(radical)) {
00415       // Tangent.
00416       t1 = t2 = -B / (2.0f*A);
00417       return (t1 >= 0.0f && t1 <= 1.0f) ?
00418                  IF_possible | IF_some : IF_no_intersection;
00419     }
00420 
00421     if (radical < 0.0f) {
00422       // No real roots: no intersection with the line.
00423       return IF_no_intersection;
00424     }
00425 
00426         float reciprocal_2A = 1.0f/(2.0f*A);
00427     float sqrt_radical = sqrtf(radical);
00428 
00429     t1 = ( -B - sqrt_radical ) * reciprocal_2A;
00430     t2 = ( -B + sqrt_radical ) * reciprocal_2A;
00431 
00432     if (t1 >= 0.0f && t2 <= 1.0f) {
00433       return IF_possible | IF_some | IF_all;
00434     } else if (t1 <= 1.0f && t2 >= 0.0f) {
00435       return IF_possible | IF_some;
00436     } else {
00437       return IF_no_intersection;
00438     }
00439   }
00440 }
00441 
00442 int BoundingSphere::
00443 contains_sphere(const BoundingSphere *sphere) const {
00444   nassertr(!is_empty() && !is_infinite(), 0);
00445   nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0);
00446 
00447   LVector3f v = sphere->_center - _center;
00448   float dist2 = dot(v, v);
00449 
00450   if (_radius >= sphere->_radius &&
00451       dist2 <= (_radius - sphere->_radius) * (_radius - sphere->_radius)) {
00452     // The other sphere is completely within this sphere.
00453     return IF_possible | IF_some | IF_all;
00454 
00455   } else if (dist2 > (_radius + sphere->_radius) * (_radius + sphere->_radius)) {
00456     // The other sphere is completely outside this sphere.
00457     return IF_no_intersection;
00458 
00459   } else {
00460     // The other sphere is partially within this sphere.
00461     return IF_possible | IF_some;
00462   }
00463 }
00464 
00465 int BoundingSphere::
00466 contains_hexahedron(const BoundingHexahedron *hexahedron) const {
00467   return hexahedron->contains_sphere(this) & ~IF_all;
00468 }
00469 
00470 int BoundingSphere::
00471 contains_line(const BoundingLine *line) const {
00472   return line->contains_sphere(this) & ~IF_all;
00473 }

Generated on Fri May 2 00:40:23 2003 for Panda by doxygen1.3