llsphere.cpp

Go to the documentation of this file.
00001 
00023 #include "llsphere.h"
00024 
00025 LLSphere::LLSphere()
00026 :       mCenter(0.f, 0.f, 0.f),
00027         mRadius(0.f)
00028 { }
00029 
00030 LLSphere::LLSphere( const LLVector3& center, F32 radius)
00031 {
00032         set(center, radius);
00033 }
00034 
00035 void LLSphere::set( const LLVector3& center, F32 radius )
00036 {
00037         mCenter = center;
00038         setRadius(radius);
00039 }
00040 
00041 void LLSphere::setCenter( const LLVector3& center)
00042 {
00043         mCenter = center;
00044 }
00045 
00046 void LLSphere::setRadius( F32 radius)
00047 {
00048         if (radius < 0.f)
00049         {
00050                 radius = -radius;
00051         }
00052         mRadius = radius;
00053 }
00054         
00055 const LLVector3& LLSphere::getCenter() const
00056 {
00057         return mCenter;
00058 }
00059 
00060 F32 LLSphere::getRadius() const
00061 {
00062         return mRadius;
00063 }
00064 
00065 // returns 'TRUE' if this sphere completely contains other_sphere
00066 BOOL LLSphere::contains(const LLSphere& other_sphere) const
00067 {
00068         F32 separation = (mCenter - other_sphere.mCenter).length();
00069         return (mRadius >= separation + other_sphere.mRadius) ? TRUE : FALSE;
00070 }
00071 
00072 // returns 'TRUE' if this sphere completely contains other_sphere
00073 BOOL LLSphere::overlaps(const LLSphere& other_sphere) const
00074 {
00075         F32 separation = (mCenter - other_sphere.mCenter).length();
00076         return (separation <= mRadius + other_sphere.mRadius) ? TRUE : FALSE;
00077 }
00078 
00079 // returns overlap
00080 // negative overlap is closest approach
00081 F32 LLSphere::getOverlap(const LLSphere& other_sphere) const
00082 {
00083         // separation is distance from other_sphere's edge and this center
00084         return (mCenter - other_sphere.mCenter).length() - mRadius - other_sphere.mRadius;
00085 }
00086 
00087 bool LLSphere::operator==(const LLSphere& rhs) const
00088 {
00089         // TODO? -- use approximate equality for centers?
00090         return (mRadius == rhs.mRadius
00091                         && mCenter == rhs.mCenter);
00092 }
00093 
00094 std::ostream& operator<<( std::ostream& output_stream, const LLSphere& sphere)
00095 {
00096         output_stream << "{center=" << sphere.mCenter << "," << "radius=" << sphere.mRadius << "}";
00097         return output_stream;
00098 }
00099 
00100 // static 
00101 // removes any spheres that are contained in others
00102 void LLSphere::collapse(std::vector<LLSphere>& sphere_list)
00103 {
00104         std::vector<LLSphere>::iterator first_itr = sphere_list.begin();
00105         while (first_itr != sphere_list.end())
00106         {
00107                 bool delete_from_front = false;
00108 
00109                 std::vector<LLSphere>::iterator second_itr = first_itr;
00110                 ++second_itr;
00111                 while (second_itr != sphere_list.end())
00112                 {
00113                         if (second_itr->contains(*first_itr))
00114                         {
00115                                 delete_from_front = true;
00116                                 break;
00117                         }
00118                         else if (first_itr->contains(*second_itr))
00119                         {
00120                                 sphere_list.erase(second_itr++);
00121                         }
00122                         else
00123                         {
00124                                 ++second_itr;
00125                         }
00126                 }
00127 
00128                 if (delete_from_front)
00129                 {
00130                         sphere_list.erase(first_itr++);
00131                 }
00132                 else
00133                 {
00134                         ++first_itr;
00135                 }
00136         }
00137 }
00138 
00139 // static
00140 // returns the bounding sphere that contains both spheres
00141 LLSphere LLSphere::getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere)
00142 {
00143         LLVector3 direction = second_sphere.mCenter - first_sphere.mCenter;
00144 
00145         // HACK -- it is possible to get enough floating point error in the 
00146         // other getBoundingSphere() method that we have to add some slop
00147         // at the end.  Unfortunately, this breaks the link-order invarience
00148         // for the linkability tests... unless we also apply the same slop
00149         // here.
00150         F32 half_milimeter = 0.0005f;
00151 
00152         F32 distance = direction.length();
00153         if (0.f == distance)
00154         {
00155                 direction.setVec(1.f, 0.f, 0.f);
00156         }
00157         else
00158         {
00159                 direction.normVec();
00160         }
00161         // the 'edge' is measured from the first_sphere's center
00162         F32 max_edge = 0.f;
00163         F32 min_edge = 0.f;
00164 
00165         max_edge = llmax(max_edge + first_sphere.getRadius(), max_edge + distance + second_sphere.getRadius() + half_milimeter);
00166         min_edge = llmin(min_edge - first_sphere.getRadius(), min_edge + distance - second_sphere.getRadius() - half_milimeter);
00167         F32 radius = 0.5f * (max_edge - min_edge);
00168         LLVector3 center = first_sphere.mCenter + (0.5f * (max_edge + min_edge)) * direction;
00169         return LLSphere(center, radius);
00170 }
00171 
00172 // static
00173 // returns the bounding sphere that contains an arbitrary set of spheres
00174 LLSphere LLSphere::getBoundingSphere(const std::vector<LLSphere>& sphere_list)
00175 {
00176         // this algorithm can get relatively inaccurate when the sphere 
00177         // collection is 'small' (contained within a bounding sphere of about 
00178         // 2 meters or less)
00179         // TODO -- improve the accuracy for small collections of spheres
00180 
00181         LLSphere bounding_sphere( LLVector3(0.f, 0.f, 0.f), 0.f );
00182         S32 sphere_count = sphere_list.size();
00183         if (1 == sphere_count)
00184         {
00185                 // trivial case -- single sphere
00186                 std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
00187                 bounding_sphere = *sphere_itr;
00188         }
00189         else if (2 == sphere_count)
00190         {
00191                 // trivial case -- two spheres
00192                 std::vector<LLSphere>::const_iterator first_sphere = sphere_list.begin();
00193                 std::vector<LLSphere>::const_iterator second_sphere = first_sphere;
00194                 ++second_sphere;
00195                 bounding_sphere = LLSphere::getBoundingSphere(*first_sphere, *second_sphere);
00196         }
00197         else if (sphere_count > 0)
00198         {
00199                 // non-trivial case -- we will approximate the solution
00200                 //
00201                 // NOTE -- there is a fancy/fast way to do this for large 
00202                 // numbers of arbirary N-dimensional spheres -- you can look it
00203                 // up on the net.  We're dealing with 3D spheres at collection
00204                 // sizes of 256 spheres or smaller, so we just use this
00205                 // brute force method.
00206 
00207                 // TODO -- perhaps would be worthwile to test for the solution where
00208                 // the largest spanning radius just happens to work.  That is, where
00209                 // there are really two spheres that determine the bounding sphere,
00210                 // and all others are contained therein.
00211 
00212                 // compute the AABB
00213                 std::vector<LLSphere>::const_iterator first_itr = sphere_list.begin();
00214                 LLVector3 max_corner = first_itr->getCenter() + first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
00215                 LLVector3 min_corner = first_itr->getCenter() - first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
00216                 {
00217                         std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
00218                         for (++sphere_itr; sphere_itr != sphere_list.end(); ++sphere_itr)
00219                         {
00220                                 LLVector3 center = sphere_itr->getCenter();
00221                                 F32 radius = sphere_itr->getRadius();
00222                                 for (S32 i=0; i<3; ++i)
00223                                 {
00224                                         if (center.mV[i] + radius > max_corner.mV[i])
00225                                         {
00226                                                 max_corner.mV[i] = center.mV[i] + radius;
00227                                         }
00228                                         if (center.mV[i] - radius < min_corner.mV[i])
00229                                         {
00230                                                 min_corner.mV[i] = center.mV[i] - radius;
00231                                         }
00232                                 }
00233                         }
00234                 }
00235 
00236                 // get the starting center and radius from the AABB
00237                 LLVector3 diagonal = max_corner - min_corner;
00238                 F32 bounding_radius = 0.5f * diagonal.length();
00239                 LLVector3 bounding_center = 0.5f * (max_corner + min_corner);
00240 
00241                 // compute the starting step-size
00242                 F32 minimum_radius = 0.5f * llmin(diagonal.mV[VX], llmin(diagonal.mV[VY], diagonal.mV[VZ]));
00243                 F32 step_length = bounding_radius - minimum_radius;
00244                 S32 step_count = 0;
00245                 S32 max_step_count = 12;
00246                 F32 half_milimeter = 0.0005f;
00247 
00248                 // wander the center around in search of tighter solutions
00249                 S32 last_dx = 2;        // 2 is out of bounds --> no match
00250                 S32 last_dy = 2;
00251                 S32 last_dz = 2;
00252 
00253                 while (step_length > half_milimeter
00254                                 && step_count < max_step_count)
00255                 {
00256                         // the algorithm for testing the maximum radius could be expensive enough
00257                         // that it makes sense to NOT duplicate testing when possible, so we keep
00258                         // track of where we last tested, and only test the new points
00259 
00260                         S32 best_dx = 0;
00261                         S32 best_dy = 0;
00262                         S32 best_dz = 0;
00263 
00264                         // sample near the center of the box
00265                         bool found_better_center = false;
00266                         for (S32 dx = -1; dx < 2; ++dx)
00267                         {
00268                                 for (S32 dy = -1; dy < 2; ++dy)
00269                                 {
00270                                         for (S32 dz = -1; dz < 2; ++dz)
00271                                         {
00272                                                 if (dx == 0 && dy == 0 && dz == 0)
00273                                                 {
00274                                                         continue;
00275                                                 }
00276 
00277                                                 // count the number of indecies that match the last_*'s
00278                                                 S32 match_count = 0;
00279                                                 if (last_dx == dx) ++match_count;
00280                                                 if (last_dy == dy) ++match_count;
00281                                                 if (last_dz == dz) ++match_count;
00282                                                 if (match_count == 2)
00283                                                 {
00284                                                         // we've already tested this point
00285                                                         continue;
00286                                                 }
00287 
00288                                                 LLVector3 center = bounding_center;
00289                                                 center.mV[VX] += (F32) dx * step_length;
00290                                                 center.mV[VY] += (F32) dy * step_length;
00291                                                 center.mV[VZ] += (F32) dz * step_length;
00292 
00293                                                 // compute the radius of the bounding sphere
00294                                                 F32 max_radius = 0.f;
00295                                                 std::vector<LLSphere>::const_iterator sphere_itr;
00296                                                 for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
00297                                                 {
00298                                                         F32 radius = (sphere_itr->getCenter() - center).length() + sphere_itr->getRadius();
00299                                                         if (radius > max_radius)
00300                                                         {
00301                                                                 max_radius = radius;
00302                                                         }
00303                                                 }
00304                                                 if (max_radius < bounding_radius)
00305                                                 {
00306                                                         best_dx = dx;
00307                                                         best_dy = dy;
00308                                                         best_dz = dz;
00309                                                         bounding_center = center;
00310                                                         bounding_radius = max_radius;
00311                                                         found_better_center = true;
00312                                                 }
00313                                         }
00314                                 }
00315                         }
00316                         if (found_better_center)
00317                         {
00318                                 // remember where we came from so we can avoid retesting
00319                                 last_dx = -best_dx;
00320                                 last_dy = -best_dy;
00321                                 last_dz = -best_dz;
00322                         }
00323                         else
00324                         {
00325                                 // reduce the step size
00326                                 step_length *= 0.5f;
00327                                 //++step_count;
00328                                 // reset the last_*'s
00329                                 last_dx = 2;    // 2 is out of bounds --> no match
00330                                 last_dy = 2;
00331                                 last_dz = 2;
00332                         }
00333                 }
00334 
00335                 // HACK -- it is possible to get enough floating point error for the
00336                 // bounding sphere to too small on the order of 10e-6, but we only need
00337                 // it to be accurate to within about half a millimeter
00338                 bounding_radius += half_milimeter;
00339 
00340                 // this algorithm can get relatively inaccurate when the sphere 
00341                 // collection is 'small' (contained within a bounding sphere of about 
00342                 // 2 meters or less)
00343                 // TODO -- fix this
00344                 /* debug code
00345                 {
00346                         std::vector<LLSphere>::const_iterator sphere_itr;
00347                         for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
00348                         {
00349                                 F32 radius = (sphere_itr->getCenter() - bounding_center).length() + sphere_itr->getRadius();
00350                                 if (radius + 0.1f > bounding_radius)
00351                                 {
00352                                         std::cout << " rad = " << radius << "  bounding - rad = " << (bounding_radius - radius) << std::endl;
00353                                 }
00354                         }
00355                         std::cout << "\n" << std::endl;
00356                 }
00357                 */ 
00358 
00359                 bounding_sphere.set(bounding_center, bounding_radius);
00360         }
00361         return bounding_sphere;
00362 }
00363 
00364 

Generated on Fri May 16 08:32:16 2008 for SecondLife by  doxygen 1.5.5