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

Volume.cxx

Go to the documentation of this file.
00001 // $Id: Volume.cxx,v 1.3 2001/09/04 19:27:40 atwood Exp $
00002 //
00003 //
00004 
00005 #include "geometry/Volume.h"
00006 #include "geometry/Ray.h"
00007 #include "geometry/Intersection.h"
00008 #include <cassert>
00009 # include <strstream>
00010 
00011 static unsigned  s_count=0;
00012 //THB 13-Aug-96 const double Surface_EPSILON= 2e-6; //(100*DBL_EPSILON);
00013 const double Volume::Surface_EPSILON= 2e-5; //(100*DBL_EPSILON);
00014 
00015 Volume::Volume(unsigned n)
00016 : SurfaceList(n)
00017 , max_dimension(FLT_MAX)
00018 {
00019     s_count=0;
00020 }
00021 
00022 void Volume::addSurface(Surface* s)
00023 {
00024     if( s_count<size() )
00025         operator[](s_count++)=s;
00026     else {
00027         push_back(s);
00028         s_count++;
00029     }
00030 }
00031 
00032 Volume::~Volume()
00033 {  //  destructor: kill the surfaces
00034     deleteSurfaces();
00035 }
00036 
00037 void Volume::deleteSurfaces()
00038 {  //  Removes all Surfaces from its list and deletes them
00039     for( iterator it=begin(); it !=end(); ++it) {
00040         delete *it; *it = 0;
00041     }
00042     s_count=0;
00043 }
00044 
00045 GeomObject&
00046 Volume::transform(const CoordTransform& T)
00047 {
00048    Shape::transform(T);
00049    for(unsigned i=0; i<surfaceCount(); i++)
00050          surface(i).transform(T);
00051    return *this;
00052 }
00053 
00054 void Volume::printOn( std::ostream& os ) const
00055 {
00056     Shape::printOn(os);
00057     os  << " maximum dimension   " << max_dimension   << "\n"
00058         << "bounded by the surfaces:\n";
00059     for(unsigned i=0; i<surfaceCount(); i++)
00060         os << surface(i);
00061 }
00062 
00063 static unsigned isurf;   // local boundary index
00064 int
00065 Volume::getBoundaryIndex(const Point& p)const
00066 {
00067     double closest = FLT_MAX;
00068     int found = -1;
00069     for( const_iterator it=begin(); it!=end(); ++it) {
00070         double d = fabs( (*it)->how_near(p) );
00071         if( d < closest ) {
00072             found = it-begin();
00073             closest = d;
00074         }
00075     }
00076     if( closest< Surface_EPSILON)
00077         return found;
00078     WARNING("Volume::associate: suface to tag not found");
00079     return -1;
00080 }
00081 int
00082 Volume::lastBoundaryIndex()const
00083 {
00084     // distanceToLeave sets index of left boundary: client can use
00085 
00086     return isurf;
00087 }
00088 // status of Ray::position() rel. to Surface
00089 enum Surface_Status {Surface_INSIDE, Surface_AMBIGOUS, Surface_OUTSIDE} ;
00090 /*
00091 typedef int Surface_Status;
00092 #define Surface_INSIDE      1
00093 #define Surface_AMBIGOUS    2
00094 #define Surface_OUTSIDE     3
00095 */
00096 // type of solution of Intersection of Ray with Surface
00097 enum Surface_Solution {Surface_SOLUTION, Surface_UNREACHABLE, Surface_NO_SOLUTION} ;
00098 /*
00099 typedef int Surface_Solution;
00100 #define Surface_SOLUTION    1
00101 #define Surface_UNREACHABLE 2
00102 #define Surface_NO_SOLUTION 3
00103 */
00104 
00105 static double Surface_distanceToLeave( const Surface &s, const Ray& r, double maxStep, Surface_Status& status, Surface_Solution& solution )
00106 {
00107     double d = s.how_near( r.position() );
00108 
00109     if ( d > maxStep ) {
00110         // no solution due to max. step
00111                 
00112         status   = Surface_INSIDE;
00113         solution = Surface_UNREACHABLE;
00114         return maxStep; //FLT_MAX/2;
00115     }
00116 
00117 
00118     if ( d < -Volume::Surface_EPSILON ) {
00119         // the outside case for s
00120                 std::ostrstream error_text;
00121                 error_text << "Volume--bad geometry for surface \n\t" << s
00122                     << "\n\t" << r.position() << " is " << d << "cm outside\n";
00123                 error_text << '\0';
00124                 WARNING(error_text.str());
00125                 status   = Surface_OUTSIDE;
00126                 solution = Surface_NO_SOLUTION;
00127                 return FLT_MAX;
00128     }
00129     else if (d < Volume::Surface_EPSILON ) {
00130                 // the ambigous case for s --> take first leaving intersection
00131                 
00132         status   = Surface_AMBIGOUS;
00133         solution = Surface_SOLUTION;
00134 
00135 #if 0 // THB: this screws me up
00136         if (s.normal( r.position() ) * r.direction() > 0)
00137             return 0;
00138 #endif
00139 
00140         Intersection inter(r,s);
00141         d = inter.distance(maxStep, 1);
00142 
00143         solution = (d == FLT_MAX) ? Surface_NO_SOLUTION : Surface_SOLUTION;
00144         return d;
00145     }
00146     else {
00147         // the inside case for s --> take first leaving intersection
00148 
00149         status   = Surface_INSIDE;
00150 
00151         Intersection inter(r,s);
00152         d = inter.distance(maxStep, 1);
00153 
00154         solution = (d == FLT_MAX) ? Surface_NO_SOLUTION : Surface_SOLUTION;
00155         return d;
00156     }
00157 }
00158 
00159 
00160 double
00161 Volume::distanceToLeave( const Ray& r,  double maxStep ) const
00162 {
00163         //  Distance along the Ray r or any of its derived classes
00164         //  to the nearest Surface to leave the Volume, constrained to be
00165         //  less than maxStep.
00166         //  If no intersection point is found, returns a large number (FLT_MAX).
00167         //  Loop over each Surface in the Volume's list.
00168         //  Use the how_near function to find the closest distance between the
00169         //  origin of the Ray (or its derived class) and the Surface.
00170         //  Only if that distance is non-negative, less than maxStep,
00171         //  and less than the distance along the Ray found for the previous Surface,
00172         //  find the distance along the Ray (or its derived class) to that Surface.
00173         //  This will get the shortest distance along the Ray to leave the Volume.
00174         //  The distanceToLeaveSurface member function of Ray will make sure
00175         //  that the Ray or its derived class is leaving the Surface (defined by
00176         //  the direction of the normal to the Surface) at the point of intersection.
00177 
00178     double             t = FLT_MAX;
00179     Surface_Status     statusVolume   = Surface_INSIDE;
00180     Surface_Solution   solutionVolume = Surface_NO_SOLUTION;
00181 
00182     isurf = 99999;
00183     for (unsigned i=0; i< surfaceCount(); i++)  {
00184         const Surface& s = surface(i);
00185         Surface_Status   status;
00186         Surface_Solution solution;
00187         double d = Surface_distanceToLeave( s, r, maxStep, status, solution );
00188 
00189         if ( status == Surface_OUTSIDE )
00190         {
00191             statusVolume   = Surface_OUTSIDE;
00192             solutionVolume = Surface_NO_SOLUTION;
00193             return FLT_MAX;
00194         }
00195 
00196         // special speedup: when one surface finds the current point to be on-surface and the ray
00197         // leaving that surface at the current point, then there is no need to test all surfaces
00198         // again for the inside condition, because the current point has been tested against all
00199         // other surfaces already and is really inside or on-surface
00200         if (t == 0)
00201         {
00202             statusVolume   = Surface_AMBIGOUS;
00203             solutionVolume = Surface_SOLUTION;
00204             return 0;
00205         }
00206 
00207         if ( status > statusVolume )
00208                 statusVolume = status;
00209 
00210         if ( solution < solutionVolume )
00211                 solutionVolume = solution;
00212 
00213         if (d < t)
00214         {
00215             isurf = i;
00216             t = d;
00217         }
00218     }
00219 
00220 
00221     // at this point, i know that i'm INSIDE or ON-SURFACE relativ to ALL surfaces !!!
00222     // It might be, that there was no intersection found, though. there may be 3 reasons:
00223     //   (a) the surface which i would have hit was unreachable
00224     //   (b) the ray is circling
00225     //   (c) Volume is has no closed surface (e.g. a box with one side missing)
00226     //       this is clearly an error
00227     if (solutionVolume == Surface_UNREACHABLE)
00228         // (a) there still might be an error in the geometry of Volume, but
00229         //     there is no easy way to find out !!
00230         return t; //FLT_MAX/2;
00231 
00232    if (solutionVolume == Surface_NO_SOLUTION) {
00233         // cases (b) and (c), but how am i supposed to find out, which one ?
00234 #if 0 // do not need anymore? def _DEBUG
00235        std::cerr << "Volume::distanceToLeave, can't leave!\n";
00236        std::cerr << "\tVolume=" << *this << '\n';
00237        std::cerr << "\tRay= " << r << '\n';
00238 #endif
00239         return FLT_MAX;
00240    }
00241 
00242     // maybe another speedup (?):
00243     // we already know we're inside. so, the closest point p where the ray leaves MUST be
00244     // inside (or rather on-surface), because if it would be outside the ray must have
00245     // left the volume earlier (if we have a contigous ray that is), so we should have
00246     // found a smaller solution in contradiction to the assumption of p being the closest
00247     // solution.
00248     //
00249     // PROBLEM:  what about the right surface, which would give the real, closest
00250     // point being unreachable?
00251     //  there are examples, where the surface
00252     //
00253     // so maybe it's safe to return here. NO !
00254     // return t;
00255 
00256 
00257     // check for r.position(t) to be inside or on-surface
00258     Point p = r.position(t);
00259     int bad=0;
00260     for (unsigned j=0; j<surfaceCount(); j++)
00261     {   
00262         if (j==isurf)
00263                 continue;
00264 
00265         bad = (surface(j).how_near(p) < -Surface_EPSILON);
00266 
00267         if ( bad )
00268         {
00269             std::ostrstream cerr;
00270             cerr << "Volume::distanceToLeave, position bad by "
00271                 << surface(j).how_near(p) << '\n';
00272             cerr << "\tVolume=" << *this << '\n';
00273             cerr << "\tRay= " << r << '\n';
00274             cerr << "\tPoint" << p << '\n';
00275             cerr << '\0'; WARNING(cerr.str());
00276             solutionVolume = Surface_NO_SOLUTION;
00277             return FLT_MAX;
00278         }
00279     }
00280 
00281     return t;
00282 }
00283 
00284 
00285 double
00286 Volume::distanceToEnter( const Ray& r, double maxStep ) const
00287 { //  Distance along the Ray r or any of its derived classes
00288   //  to the nearest Surface of the Volume, constrained to be < maxStep.
00289   //  If the Ray's origin is inside and no entering solution found: return -1.
00290   //  If no intersection point is found: return a large number (FLT_MAX).
00291   //  Quick test to see if the Ray can possibly enter this Volume:
00292   //  if the distance from the origin of the Ray to the center of the Volume
00293   //  minus the maximum dimension (which is the radius of a sphere which
00294   //  circumscribes the Volume) is greater than maxStep, then the Ray cannot
00295   //  enter the Volume.
00296 
00297     Point startPos = r.position();
00298 
00299     if( maxStep < FLT_MAX ) {
00300         Vector oc = center() - startPos;
00301         if ( ( oc.mag() - max_dimension ) > maxStep ) return FLT_MAX;
00302     }
00303 
00304     double t = FLT_MAX;             // value to be returned
00305 
00306     for(unsigned i=0; i<surfaceCount(); i++) {
00307 
00308         //  Check that we can reach surface in this step: examine signed
00309         //  closeness of the surface
00310         double d = surface(i).how_near(startPos);
00311         if( d> Surface_EPSILON      // already inside: can't enter thru this surface
00312             ||  fabs(d) > t         // alredy have a closer surface
00313             ||  fabs(d) > maxStep   // no point in trying
00314          )      continue;
00315 
00316     // Now check distance to the surface: create an Intersection object
00317         Intersection inter(r,surface(i));
00318         d = inter.distance(maxStep, -1 );
00319         if( d< 0 && d > -Surface_EPSILON)
00320                 d=0;  // right on this surface!!
00321                 
00322         if( d < t && d >= 0 ){
00323 
00324     // It's the closest, so far, but it must be inside the others
00325             Point p = r.position(d);
00326             int bad=0;
00327             for(unsigned j=0; j<surfaceCount(); j++) {
00328                 if(j==i) continue;
00329                 if( (bad = !surface(j).inside(p))!=0)break;
00330                     }
00331                 if( !bad ) {
00332                     t = d; // must be the one.
00333                         break;
00334             }
00335         }
00336     }
00337     return t;
00338 }
00339 
00340 int
00341 Volume::inside( const Point& x ) const
00342 {
00343     //  Return 0 if a point x is outside the Volume, 1 if inside.
00344     //  The point is inside the Volume if it is inside all the Surfaces
00345     //  which define the Volume.
00346     //  Surfaces provide an outward-pointing unit normal for this purpose.
00347     for(unsigned i=0; i<surfaceCount(); i++)
00348     {
00349         if( !surface(i).inside( x ))
00350            return 0;
00351     }
00352     return 1;
00353 }
00354 

Generated at Mon Nov 26 18:18:22 2001 by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000