G4EllipticalTube.cc

Go to the documentation of this file.
00001 //
00002 // ********************************************************************
00003 // * License and Disclaimer                                           *
00004 // *                                                                  *
00005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
00006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
00007 // * conditions of the Geant4 Software License,  included in the file *
00008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
00009 // * include a list of copyright holders.                             *
00010 // *                                                                  *
00011 // * Neither the authors of this software system, nor their employing *
00012 // * institutes,nor the agencies providing financial support for this *
00013 // * work  make  any representation or  warranty, express or implied, *
00014 // * regarding  this  software system or assume any liability for its *
00015 // * use.  Please see the license in the file  LICENSE  and URL above *
00016 // * for the full disclaimer and the limitation of liability.         *
00017 // *                                                                  *
00018 // * This  code  implementation is the result of  the  scientific and *
00019 // * technical work of the GEANT4 collaboration.                      *
00020 // * By using,  copying,  modifying or  distributing the software (or *
00021 // * any work based  on the software)  you  agree  to acknowledge its *
00022 // * use  in  resulting  scientific  publications,  and indicate your *
00023 // * acceptance of all terms of the Geant4 Software license.          *
00024 // ********************************************************************
00025 //
00026 //
00027 // $Id: G4EllipticalTube.cc 67011 2013-01-29 16:17:41Z gcosmo $
00028 //
00029 // 
00030 // --------------------------------------------------------------------
00031 // GEANT 4 class source file
00032 //
00033 //
00034 // G4EllipticalTube.cc
00035 //
00036 // Implementation of a CSG volume representing a tube with elliptical cross
00037 // section (geant3 solid 'ELTU')
00038 //
00039 // --------------------------------------------------------------------
00040 
00041 #include "G4EllipticalTube.hh"
00042 
00043 #include "G4ClippablePolygon.hh"
00044 #include "G4AffineTransform.hh"
00045 #include "G4SolidExtentList.hh"
00046 #include "G4VoxelLimits.hh"
00047 #include "meshdefs.hh"
00048 
00049 #include "Randomize.hh"
00050 
00051 #include "G4VGraphicsScene.hh"
00052 #include "G4Polyhedron.hh"
00053 #include "G4VisExtent.hh"
00054 
00055 using namespace CLHEP;
00056 
00057 //
00058 // Constructor
00059 //
00060 G4EllipticalTube::G4EllipticalTube( const G4String &name, 
00061                                           G4double theDx,
00062                                           G4double theDy,
00063                                           G4double theDz )
00064   : G4VSolid( name ), fCubicVolume(0.), fSurfaceArea(0.), fpPolyhedron(0)
00065 {
00066   dx = theDx;
00067   dy = theDy;
00068   dz = theDz;
00069 }
00070 
00071 
00072 //
00073 // Fake default constructor - sets only member data and allocates memory
00074 //                            for usage restricted to object persistency.
00075 //
00076 G4EllipticalTube::G4EllipticalTube( __void__& a )
00077   : G4VSolid(a), dx(0.), dy(0.), dz(0.),
00078     fCubicVolume(0.), fSurfaceArea(0.), fpPolyhedron(0)
00079 {
00080 }
00081 
00082 
00083 //
00084 // Destructor
00085 //
00086 G4EllipticalTube::~G4EllipticalTube()
00087 {
00088   delete fpPolyhedron;
00089 }
00090 
00091 
00092 //
00093 // Copy constructor
00094 //
00095 G4EllipticalTube::G4EllipticalTube(const G4EllipticalTube& rhs)
00096   : G4VSolid(rhs), dx(rhs.dx), dy(rhs.dy), dz(rhs.dz),
00097     fCubicVolume(rhs.fCubicVolume), fSurfaceArea(rhs.fSurfaceArea),
00098     fpPolyhedron(0)
00099 {
00100 }
00101 
00102 
00103 //
00104 // Assignment operator
00105 //
00106 G4EllipticalTube& G4EllipticalTube::operator = (const G4EllipticalTube& rhs) 
00107 {
00108    // Check assignment to self
00109    //
00110    if (this == &rhs)  { return *this; }
00111 
00112    // Copy base class data
00113    //
00114    G4VSolid::operator=(rhs);
00115 
00116    // Copy data
00117    //
00118    dx = rhs.dx; dy = rhs.dy; dz = rhs.dz;
00119    fCubicVolume = rhs.fCubicVolume; fSurfaceArea = rhs.fSurfaceArea;
00120    fpPolyhedron = 0;
00121 
00122    return *this;
00123 }
00124 
00125 
00126 //
00127 // CalculateExtent
00128 //
00129 G4bool
00130 G4EllipticalTube::CalculateExtent( const EAxis axis,
00131                                    const G4VoxelLimits &voxelLimit,
00132                                    const G4AffineTransform &transform,
00133                                          G4double &min, G4double &max ) const
00134 {
00135   G4SolidExtentList  extentList( axis, voxelLimit );
00136   
00137   //
00138   // We are going to divide up our elliptical face into small
00139   // pieces
00140   //
00141   
00142   //
00143   // Choose phi size of our segment(s) based on constants as
00144   // defined in meshdefs.hh
00145   //
00146   G4int numPhi = kMaxMeshSections;
00147   G4double sigPhi = twopi/numPhi;
00148   
00149   //
00150   // We have to be careful to keep our segments completely outside
00151   // of the elliptical surface. To do so we imagine we have
00152   // a simple (unit radius) circular cross section (as in G4Tubs) 
00153   // and then "stretch" the dimensions as necessary to fit the ellipse.
00154   //
00155   G4double rFudge = 1.0/std::cos(0.5*sigPhi);
00156   G4double dxFudge = dx*rFudge,
00157            dyFudge = dy*rFudge;
00158   
00159   //
00160   // As we work around the elliptical surface, we build
00161   // a "phi" segment on the way, and keep track of two
00162   // additional polygons for the two ends.
00163   //
00164   G4ClippablePolygon endPoly1, endPoly2, phiPoly;
00165   
00166   G4double phi = 0, 
00167            cosPhi = std::cos(phi),
00168            sinPhi = std::sin(phi);
00169   G4ThreeVector v0( dxFudge*cosPhi, dyFudge*sinPhi, +dz ),
00170                 v1( dxFudge*cosPhi, dyFudge*sinPhi, -dz ),
00171                 w0, w1;
00172   transform.ApplyPointTransform( v0 );
00173   transform.ApplyPointTransform( v1 );
00174   do
00175   {
00176     phi += sigPhi;
00177     if (numPhi == 1) phi = 0;  // Try to avoid roundoff
00178     cosPhi = std::cos(phi), 
00179     sinPhi = std::sin(phi);
00180     
00181     w0 = G4ThreeVector( dxFudge*cosPhi, dyFudge*sinPhi, +dz );
00182     w1 = G4ThreeVector( dxFudge*cosPhi, dyFudge*sinPhi, -dz );
00183     transform.ApplyPointTransform( w0 );
00184     transform.ApplyPointTransform( w1 );
00185     
00186     //
00187     // Add a point to our z ends
00188     //
00189     endPoly1.AddVertexInOrder( v0 );
00190     endPoly2.AddVertexInOrder( v1 );
00191     
00192     //
00193     // Build phi polygon
00194     //
00195     phiPoly.ClearAllVertices();
00196     
00197     phiPoly.AddVertexInOrder( v0 );
00198     phiPoly.AddVertexInOrder( v1 );
00199     phiPoly.AddVertexInOrder( w1 );
00200     phiPoly.AddVertexInOrder( w0 );
00201     
00202     if (phiPoly.PartialClip( voxelLimit, axis ))
00203     {
00204       //
00205       // Get unit normal
00206       //
00207       phiPoly.SetNormal( (v1-v0).cross(w0-v0).unit() );
00208       
00209       extentList.AddSurface( phiPoly );
00210     }
00211 
00212     //
00213     // Next vertex
00214     //    
00215     v0 = w0;
00216     v1 = w1;
00217   } while( --numPhi > 0 );
00218 
00219   //
00220   // Process the end pieces
00221   //
00222   if (endPoly1.PartialClip( voxelLimit, axis ))
00223   {
00224     static const G4ThreeVector normal(0,0,+1);
00225     endPoly1.SetNormal( transform.TransformAxis(normal) );
00226     extentList.AddSurface( endPoly1 );
00227   }
00228   
00229   if (endPoly2.PartialClip( voxelLimit, axis ))
00230   {
00231     static const G4ThreeVector normal(0,0,-1);
00232     endPoly2.SetNormal( transform.TransformAxis(normal) );
00233     extentList.AddSurface( endPoly2 );
00234   }
00235   
00236   //
00237   // Return min/max value
00238   //
00239   return extentList.GetExtent( min, max );
00240 }
00241 
00242 
00243 //
00244 // Inside
00245 //
00246 // Note that for this solid, we've decided to define the tolerant
00247 // surface as that which is bounded by ellipses with axes
00248 // at +/- 0.5*kCarTolerance.
00249 //
00250 EInside G4EllipticalTube::Inside( const G4ThreeVector& p ) const
00251 {
00252   static const G4double halfTol = 0.5*kCarTolerance;
00253   
00254   //
00255   // Check z extents: are we outside?
00256   //
00257   G4double absZ = std::fabs(p.z());
00258   if (absZ > dz+halfTol) return kOutside;
00259   
00260   //
00261   // Check x,y: are we outside?
00262   //
00263   // G4double x = p.x(), y = p.y();
00264   
00265   if (CheckXY(p.x(), p.y(), +halfTol) > 1.0) return kOutside;
00266   
00267   //
00268   // We are either inside or on the surface: recheck z extents
00269   //
00270   if (absZ > dz-halfTol) return kSurface;
00271   
00272   //
00273   // Recheck x,y
00274   //
00275   if (CheckXY(p.x(), p.y(), -halfTol) > 1.0) return kSurface;
00276   
00277   return kInside;
00278 }
00279 
00280 
00281 //
00282 // SurfaceNormal
00283 //
00284 G4ThreeVector G4EllipticalTube::SurfaceNormal( const G4ThreeVector& p ) const
00285 {
00286   //
00287   // SurfaceNormal for the point On the Surface, sum the normals on the Corners
00288   //
00289   static const G4double halfTol = 0.5*kCarTolerance;
00290 
00291   G4int noSurfaces=0;
00292   G4ThreeVector norm, sumnorm(0.,0.,0.);
00293 
00294   G4double distZ = std::fabs(std::fabs(p.z()) - dz);
00295   
00296   G4double distR1 = CheckXY( p.x(), p.y(),+ halfTol );
00297   G4double distR2 = CheckXY( p.x(), p.y(),- halfTol );
00298  
00299   if (  (distZ  < halfTol ) && ( distR1 <= 1 ) )
00300   {
00301     noSurfaces++;
00302     sumnorm=G4ThreeVector( 0.0, 0.0, p.z() < 0 ? -1.0 : 1.0 );
00303   }
00304   if( (distR1 <= 1 ) && ( distR2 >= 1 ) )
00305   {
00306     noSurfaces++;
00307     norm= G4ThreeVector( p.x()*dy*dy, p.y()*dx*dx, 0.0 ).unit();
00308     sumnorm+=norm;
00309   }
00310   if ( noSurfaces == 0 )
00311   {
00312 #ifdef G4SPECSDEBUG
00313     G4Exception("G4EllipticalTube::SurfaceNormal(p)", "GeomSolids1002",
00314                 JustWarning, "Point p is not on surface !?" );
00315 #endif 
00316     norm = ApproxSurfaceNormal(p);
00317   }
00318   else if ( noSurfaces == 1 )  { norm = sumnorm; }
00319   else                         { norm = sumnorm.unit(); }
00320  
00321   return norm;
00322 }
00323 
00324 
00325 //
00326 // ApproxSurfaceNormal
00327 //
00328 G4ThreeVector
00329 G4EllipticalTube::ApproxSurfaceNormal( const G4ThreeVector& p ) const
00330 {
00331   //
00332   // Which of the three surfaces are we closest to (approximatively)?
00333   //
00334   G4double distZ = std::fabs(p.z()) - dz;
00335   
00336   G4double rxy = CheckXY( p.x(), p.y() );
00337   G4double distR2 = (rxy < DBL_MIN) ? DBL_MAX : 1.0/rxy;
00338 
00339   //
00340   // Closer to z?
00341   //
00342   if (distZ*distZ < distR2)
00343   {
00344     return G4ThreeVector( 0.0, 0.0, p.z() < 0 ? -1.0 : 1.0 );
00345   }
00346 
00347   //
00348   // Closer to x/y
00349   //
00350   return G4ThreeVector( p.x()*dy*dy, p.y()*dx*dx, 0.0 ).unit();
00351 }
00352 
00353 
00354 //
00355 // DistanceToIn(p,v)
00356 //
00357 // Unlike DistanceToOut(p,v), it is possible for the trajectory
00358 // to miss. The geometric calculations here are quite simple.
00359 // More difficult is the logic required to prevent particles
00360 // from sneaking (or leaking) between the elliptical and end
00361 // surfaces.
00362 //
00363 // Keep in mind that the true distance is allowed to be
00364 // negative if the point is currently on the surface. For oblique
00365 // angles, it can be very negative. 
00366 //
00367 G4double G4EllipticalTube::DistanceToIn( const G4ThreeVector& p,
00368                                          const G4ThreeVector& v ) const
00369 {
00370   static const G4double halfTol = 0.5*kCarTolerance;
00371     
00372   //
00373   // Check z = -dz planer surface
00374   //
00375   G4double sigz = p.z()+dz;
00376 
00377   if (sigz < halfTol)
00378   {
00379     //
00380     // We are "behind" the shape in z, and so can
00381     // potentially hit the rear face. Correct direction?
00382     //
00383     if (v.z() <= 0)
00384     {
00385       //
00386       // As long as we are far enough away, we know we
00387       // can't intersect
00388       //
00389       if (sigz < 0) return kInfinity;
00390       
00391       //
00392       // Otherwise, we don't intersect unless we are
00393       // on the surface of the ellipse
00394       //
00395       if (CheckXY(p.x(),p.y(),-halfTol) <= 1.0) return kInfinity;
00396     }
00397     else
00398     {
00399       //
00400       // How far?
00401       //
00402       G4double q = -sigz/v.z();
00403       
00404       //
00405       // Where does that place us?
00406       //
00407       G4double xi = p.x() + q*v.x(),
00408                yi = p.y() + q*v.y();
00409       
00410       //
00411       // Is this on the surface (within ellipse)?
00412       //
00413       if (CheckXY(xi,yi) <= 1.0)
00414       {
00415         //
00416         // Yup. Return q, unless we are on the surface
00417         //
00418         return (sigz < -halfTol) ? q : 0;
00419       }
00420       else if (xi*dy*dy*v.x() + yi*dx*dx*v.y() >= 0)
00421       {
00422         //
00423         // Else, if we are traveling outwards, we know
00424         // we must miss
00425         //
00426         return kInfinity;
00427       }
00428     }
00429   }
00430 
00431   //
00432   // Check z = +dz planer surface
00433   //
00434   sigz = p.z() - dz;
00435   
00436   if (sigz > -halfTol)
00437   {
00438     if (v.z() >= 0)
00439     {
00440       if (sigz > 0) return kInfinity;
00441       if (CheckXY(p.x(),p.y(),-halfTol) <= 1.0) return kInfinity;
00442     }
00443     else {
00444       G4double q = -sigz/v.z();
00445 
00446       G4double xi = p.x() + q*v.x(),
00447                yi = p.y() + q*v.y();
00448       
00449       if (CheckXY(xi,yi) <= 1.0)
00450       {
00451         return (sigz > -halfTol) ? q : 0;
00452       }
00453       else if (xi*dy*dy*v.x() + yi*dx*dx*v.y() >= 0)
00454       {
00455         return kInfinity;
00456       }
00457     }
00458   }
00459   
00460   //
00461   // Check intersection with the elliptical tube
00462   //
00463   G4double q[2];
00464   G4int n = IntersectXY( p, v, q );
00465   
00466   if (n==0) return kInfinity;
00467   
00468   //
00469   // Is the original point on the surface?
00470   //
00471   if (std::fabs(p.z()) < dz+halfTol) {
00472     if (CheckXY( p.x(), p.y(), halfTol ) < 1.0)
00473     {
00474       //
00475       // Well, yes, but are we traveling inwards at this point?
00476       //
00477       if (p.x()*dy*dy*v.x() + p.y()*dx*dx*v.y() < 0) return 0;
00478     }
00479   }
00480   
00481   //
00482   // We are now certain that point p is not on the surface of 
00483   // the solid (and thus std::fabs(q[0]) > halfTol). 
00484   // Return kInfinity if the intersection is "behind" the point.
00485   //
00486   if (q[0] < 0) return kInfinity;
00487   
00488   //
00489   // Check to see if we intersect the tube within
00490   // dz, but only when we know it might miss
00491   //
00492   G4double zi = p.z() + q[0]*v.z();
00493 
00494   if (v.z() < 0)
00495   {
00496     if (zi < -dz) return kInfinity;
00497   }
00498   else if (v.z() > 0)
00499   {
00500     if (zi > +dz) return kInfinity;
00501   }
00502 
00503   return q[0];
00504 }
00505 
00506 
00507 //
00508 // DistanceToIn(p)
00509 //
00510 // The distance from a point to an ellipse (in 2 dimensions) is a
00511 // surprisingly complicated quadric expression (this is easy to
00512 // appreciate once one understands that there may be up to
00513 // four lines normal to the ellipse intersecting any point). To 
00514 // solve it exactly would be rather time consuming. This method, 
00515 // however, is supposed to be a quick check, and is allowed to be an
00516 // underestimate.
00517 //
00518 // So, I will use the following underestimate of the distance
00519 // from an outside point to an ellipse. First: find the intersection "A"
00520 // of the line from the origin to the point with the ellipse.
00521 // Find the line passing through "A" and tangent to the ellipse 
00522 // at A. The distance of the point p from the ellipse will be approximated
00523 // as the distance to this line.
00524 //
00525 G4double G4EllipticalTube::DistanceToIn( const G4ThreeVector& p ) const
00526 {
00527   static const G4double halfTol = 0.5*kCarTolerance;
00528   
00529   if (CheckXY( p.x(), p.y(), +halfTol ) < 1.0)
00530   {
00531     //
00532     // We are inside or on the surface of the
00533     // elliptical cross section in x/y. Check z
00534     //
00535     if (p.z() < -dz-halfTol) 
00536       return -p.z()-dz;
00537     else if (p.z() > dz+halfTol)
00538       return p.z()-dz;
00539     else
00540       return 0;    // On any surface here (or inside)
00541   }
00542   
00543   //
00544   // Find point on ellipse
00545   //
00546   G4double qnorm = CheckXY( p.x(), p.y() );
00547   if (qnorm < DBL_MIN) return 0;  // This should never happen
00548   
00549   G4double q = 1.0/std::sqrt(qnorm);
00550   
00551   G4double xe = q*p.x(), ye = q*p.y();
00552      
00553   //
00554   // Get tangent to ellipse
00555   //
00556   G4double tx = -ye*dx*dx, ty = +xe*dy*dy;
00557   G4double tnorm = std::sqrt( tx*tx + ty*ty );
00558   
00559   //
00560   // Calculate distance
00561   //
00562   G4double distR = ( (p.x()-xe)*ty - (p.y()-ye)*tx )/tnorm;
00563   
00564   //
00565   // Add the result in quadrature if we are, in addition,
00566   // outside the z bounds of the shape
00567   //
00568   // We could save some time by returning the maximum rather
00569   // than the quadrature sum
00570   //
00571   if (p.z() < -dz) 
00572     return std::sqrt( (p.z()+dz)*(p.z()+dz) + distR*distR );
00573   else if (p.z() > dz)
00574     return std::sqrt( (p.z()-dz)*(p.z()-dz) + distR*distR );
00575 
00576   return distR;
00577 }
00578 
00579 
00580 //
00581 // DistanceToOut(p,v)
00582 //
00583 // This method can be somewhat complicated for a general shape.
00584 // For a convex one, like this, there are several simplifications,
00585 // the most important of which is that one can treat the surfaces
00586 // as infinite in extent when deciding if the p is on the surface.
00587 //
00588 G4double G4EllipticalTube::DistanceToOut( const G4ThreeVector& p,
00589                                           const G4ThreeVector& v,
00590                                           const G4bool calcNorm,
00591                                                 G4bool *validNorm,
00592                                                 G4ThreeVector *norm ) const
00593 {
00594   static const G4double halfTol = 0.5*kCarTolerance;
00595   
00596   //
00597   // Our normal is always valid
00598   //
00599   if (calcNorm)  { *validNorm = true; }
00600   
00601   G4double sBest = kInfinity;
00602   G4ThreeVector nBest(0,0,0);
00603   
00604   //
00605   // Might we intersect the -dz surface?
00606   //
00607   if (v.z() < 0)
00608   {
00609     static const G4ThreeVector normHere(0.0,0.0,-1.0);
00610     //
00611     // Yup. What distance?
00612     //
00613     sBest = -(p.z()+dz)/v.z();
00614     
00615     //
00616     // Are we on the surface? If so, return zero
00617     //
00618     if (p.z() < -dz+halfTol)
00619     {
00620       if (calcNorm)  { *norm = normHere; }
00621       return 0;
00622     }
00623     else
00624     {
00625       nBest = normHere;
00626     }
00627   }
00628   
00629   //
00630   // How about the +dz surface?
00631   //
00632   if (v.z() > 0)
00633   {
00634     static const G4ThreeVector normHere(0.0,0.0,+1.0);
00635     //
00636     // Yup. What distance?
00637     //
00638     G4double q = (dz-p.z())/v.z();
00639     
00640     //
00641     // Are we on the surface? If so, return zero
00642     //
00643     if (p.z() > +dz-halfTol)
00644     {
00645       if (calcNorm)  { *norm = normHere; }
00646       return 0;
00647     }
00648     
00649     //
00650     // Best so far?
00651     //
00652     if (q < sBest) { sBest = q; nBest = normHere; }
00653   }
00654   
00655   //
00656   // Check furthest intersection with ellipse 
00657   //
00658   G4double q[2];
00659   G4int n = IntersectXY( p, v, q );
00660 
00661   if (n == 0)
00662   {
00663     if (sBest == kInfinity)
00664     {
00665       DumpInfo();
00666       std::ostringstream message;
00667       G4int oldprc = message.precision(16) ;
00668       message << "Point p is outside !?" << G4endl
00669               << "Position:"  << G4endl
00670               << "   p.x() = "   << p.x()/mm << " mm" << G4endl
00671               << "   p.y() = "   << p.y()/mm << " mm" << G4endl
00672               << "   p.z() = "   << p.z()/mm << " mm" << G4endl
00673               << "Direction:" << G4endl << G4endl
00674               << "   v.x() = "   << v.x() << G4endl
00675               << "   v.y() = "   << v.y() << G4endl
00676               << "   v.z() = "   << v.z() << G4endl
00677               << "Proposed distance :" << G4endl
00678               << "   snxt = "    << sBest/mm << " mm";
00679       message.precision(oldprc) ;
00680       G4Exception( "G4EllipticalTube::DistanceToOut(p,v,...)",
00681                    "GeomSolids1002", JustWarning, message);
00682     }
00683     if (calcNorm)  { *norm = nBest; }
00684     return sBest;
00685   }
00686   else if (q[n-1] > sBest)
00687   {
00688     if (calcNorm)  { *norm = nBest; }
00689     return sBest;
00690   }  
00691   sBest = q[n-1];
00692       
00693   //
00694   // Intersection with ellipse. Get normal at intersection point.
00695   //
00696   if (calcNorm)
00697   {
00698     G4ThreeVector ip = p + sBest*v;
00699     *norm = G4ThreeVector( ip.x()*dy*dy, ip.y()*dx*dx, 0.0 ).unit();
00700   }
00701   
00702   //
00703   // Do we start on the surface?
00704   //
00705   if (CheckXY( p.x(), p.y(), -halfTol ) > 1.0)
00706   {
00707     //
00708     // Well, yes, but are we traveling outwards at this point?
00709     //
00710     if (p.x()*dy*dy*v.x() + p.y()*dx*dx*v.y() > 0) return 0;
00711   }
00712   
00713   return sBest;
00714 }
00715 
00716 
00717 //
00718 // DistanceToOut(p)
00719 //
00720 // See DistanceToIn(p) for notes on the distance from a point
00721 // to an ellipse in two dimensions.
00722 //
00723 // The approximation used here for a point inside the ellipse
00724 // is to find the intersection with the ellipse of the lines 
00725 // through the point and parallel to the x and y axes. The
00726 // distance of the point from the line connecting the two 
00727 // intersecting points is then used.
00728 //
00729 G4double G4EllipticalTube::DistanceToOut( const G4ThreeVector& p ) const
00730 {
00731   static const G4double halfTol = 0.5*kCarTolerance;
00732   
00733   //
00734   // We need to calculate the distances to all surfaces,
00735   // and then return the smallest
00736   //
00737   // Check -dz and +dz surface
00738   //
00739   G4double sBest = dz - std::fabs(p.z());
00740   if (sBest < halfTol) return 0;
00741   
00742   //
00743   // Check elliptical surface: find intersection of
00744   // line through p and parallel to x axis
00745   //
00746   G4double radical = 1.0 - p.y()*p.y()/dy/dy;
00747   if (radical < +DBL_MIN) return 0;
00748   
00749   G4double xi = dx*std::sqrt( radical );
00750   if (p.x() < 0) xi = -xi;
00751   
00752   //
00753   // Do the same with y axis
00754   //
00755   radical = 1.0 - p.x()*p.x()/dx/dx;
00756   if (radical < +DBL_MIN) return 0;
00757   
00758   G4double yi = dy*std::sqrt( radical );
00759   if (p.y() < 0) yi = -yi;
00760   
00761   //
00762   // Get distance from p to the line connecting
00763   // these two points
00764   //
00765   G4double xdi = p.x() - xi,
00766      ydi = yi - p.y();
00767 
00768   G4double normi = std::sqrt( xdi*xdi + ydi*ydi );
00769   if (normi < halfTol) return 0;
00770   xdi /= normi;
00771   ydi /= normi;
00772   
00773   G4double q = 0.5*(xdi*(p.y()-yi) - ydi*(p.x()-xi));
00774   if (xi*yi < 0) q = -q;
00775   
00776   if (q < sBest) sBest = q;
00777   
00778   //
00779   // Return best answer
00780   //
00781   return sBest < halfTol ? 0 : sBest;
00782 }
00783 
00784 
00785 //
00786 // IntersectXY
00787 //
00788 // Decide if and where the x/y trajectory hits the elliptical cross
00789 // section.
00790 //
00791 // Arguments:
00792 //     p     - (in) Point on trajectory
00793 //     v     - (in) Vector along trajectory
00794 //     q     - (out) Up to two points of intersection, where the
00795 //                   intersection point is p + q*v, and if there are
00796 //                   two intersections, q[0] < q[1]. May be negative.
00797 // Returns:
00798 //     The number of intersections. If 0, the trajectory misses. If 1, the 
00799 //     trajectory just grazes the surface.
00800 //
00801 // Solution:
00802 //     One needs to solve: ( (p.x + q*v.x)/dx )**2  + ( (p.y + q*v.y)/dy )**2 = 1
00803 //
00804 //     The solution is quadratic: a*q**2 + b*q + c = 0
00805 //
00806 //           a = (v.x/dx)**2 + (v.y/dy)**2
00807 //           b = 2*p.x*v.x/dx**2 + 2*p.y*v.y/dy**2
00808 //           c = (p.x/dx)**2 + (p.y/dy)**2 - 1
00809 //
00810 G4int G4EllipticalTube::IntersectXY( const G4ThreeVector &p,
00811                                      const G4ThreeVector &v,
00812                                            G4double ss[2] ) const
00813 {
00814   G4double px = p.x(), py = p.y();
00815   G4double vx = v.x(), vy = v.y();
00816   
00817   G4double a = (vx/dx)*(vx/dx) + (vy/dy)*(vy/dy);
00818   G4double b = 2.0*( px*vx/dx/dx + py*vy/dy/dy );
00819   G4double c = (px/dx)*(px/dx) + (py/dy)*(py/dy) - 1.0;
00820   
00821   if (a < DBL_MIN) return 0;      // Trajectory parallel to z axis
00822   
00823   G4double radical = b*b - 4*a*c;
00824   
00825   if (radical < -DBL_MIN) return 0;    // No solution
00826   
00827   if (radical < DBL_MIN)
00828   {
00829     //
00830     // Grazes surface
00831     //
00832     ss[0] = -b/a/2.0;
00833     return 1;
00834   }
00835   
00836   radical = std::sqrt(radical);
00837   
00838   G4double q = -0.5*( b + (b < 0 ? -radical : +radical) );
00839   G4double sa = q/a;
00840   G4double sb = c/q;    
00841   if (sa < sb) { ss[0] = sa; ss[1] = sb; } else { ss[0] = sb; ss[1] = sa; }
00842   return 2;
00843 }
00844 
00845 
00846 //
00847 // GetEntityType
00848 //
00849 G4GeometryType G4EllipticalTube::GetEntityType() const
00850 {
00851   return G4String("G4EllipticalTube");
00852 }
00853 
00854 
00855 //
00856 // Make a clone of the object
00857 //
00858 G4VSolid* G4EllipticalTube::Clone() const
00859 {
00860   return new G4EllipticalTube(*this);
00861 }
00862 
00863 
00864 //
00865 // GetCubicVolume
00866 //
00867 G4double G4EllipticalTube::GetCubicVolume()
00868 {
00869   if(fCubicVolume != 0.) {;}
00870     else { fCubicVolume = G4VSolid::GetCubicVolume(); }
00871   return fCubicVolume;
00872 }
00873 
00874 //
00875 // GetSurfaceArea
00876 //
00877 G4double G4EllipticalTube::GetSurfaceArea()
00878 {
00879   if(fSurfaceArea != 0.) {;}
00880   else   { fSurfaceArea = G4VSolid::GetSurfaceArea(); }
00881   return fSurfaceArea;
00882 }
00883 
00884 //
00885 // Stream object contents to an output stream
00886 //
00887 std::ostream& G4EllipticalTube::StreamInfo(std::ostream& os) const
00888 {
00889   G4int oldprc = os.precision(16);
00890   os << "-----------------------------------------------------------\n"
00891      << "    *** Dump for solid - " << GetName() << " ***\n"
00892      << "    ===================================================\n"
00893      << " Solid type: G4EllipticalTube\n"
00894      << " Parameters: \n"
00895      << "    length Z: " << dz/mm << " mm \n"
00896      << "    surface equation in X and Y: \n"
00897      << "       (X / " << dx << ")^2 + (Y / " << dy << ")^2 = 1 \n"
00898      << "-----------------------------------------------------------\n";
00899   os.precision(oldprc);
00900 
00901   return os;
00902 }
00903 
00904 
00905 //
00906 // GetPointOnSurface
00907 //
00908 // Randomly generates a point on the surface, 
00909 // with ~ uniform distribution across surface.
00910 //
00911 G4ThreeVector G4EllipticalTube::GetPointOnSurface() const
00912 {
00913   G4double xRand, yRand, zRand, phi, cosphi, sinphi, zArea, cArea,p, chose;
00914 
00915   phi    = RandFlat::shoot(0., 2.*pi);
00916   cosphi = std::cos(phi);
00917   sinphi = std::sin(phi);
00918   
00919   // the ellipse perimeter from: "http://mathworld.wolfram.com/Ellipse.html"
00920   //   m = (dx - dy)/(dx + dy);
00921   //   k = 1.+1./4.*m*m+1./64.*sqr(m)*sqr(m)+1./256.*sqr(m)*sqr(m)*sqr(m);
00922   //   p = pi*(a+b)*k;
00923 
00924   // perimeter below from "http://www.efunda.com/math/areas/EllipseGen.cfm"
00925 
00926   p = 2.*pi*std::sqrt(0.5*(dx*dx+dy*dy));
00927 
00928   cArea = 2.*dz*p;
00929   zArea = pi*dx*dy;
00930 
00931   xRand = dx*cosphi;
00932   yRand = dy*sinphi;
00933   zRand = RandFlat::shoot(dz, -1.*dz);
00934     
00935   chose = RandFlat::shoot(0.,2.*zArea+cArea);
00936   
00937   if( (chose>=0) && (chose < cArea) )
00938   {
00939     return G4ThreeVector (xRand,yRand,zRand);
00940   }
00941   else if( (chose >= cArea) && (chose < cArea + zArea) )
00942   {
00943     xRand = RandFlat::shoot(-1.*dx,dx);
00944     yRand = std::sqrt(1.-sqr(xRand/dx));
00945     yRand = RandFlat::shoot(-1.*yRand, yRand);
00946     return G4ThreeVector (xRand,yRand,dz); 
00947   }
00948   else
00949   { 
00950     xRand = RandFlat::shoot(-1.*dx,dx);
00951     yRand = std::sqrt(1.-sqr(xRand/dx));
00952     yRand = RandFlat::shoot(-1.*yRand, yRand);
00953     return G4ThreeVector (xRand,yRand,-1.*dz);
00954   }
00955 }
00956 
00957 
00958 //
00959 // CreatePolyhedron
00960 //
00961 G4Polyhedron* G4EllipticalTube::CreatePolyhedron() const
00962 {
00963   // create cylinder with radius=1...
00964   //
00965   G4Polyhedron* eTube = new G4PolyhedronTube(0.,1.,dz);
00966 
00967   // apply non-uniform scaling...
00968   //
00969   eTube->Transform(G4Scale3D(dx,dy,1.));
00970   return  eTube;
00971 }
00972 
00973 
00974 //
00975 // GetPolyhedron
00976 //
00977 G4Polyhedron* G4EllipticalTube::GetPolyhedron () const
00978 {
00979   if (!fpPolyhedron ||
00980       fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
00981       fpPolyhedron->GetNumberOfRotationSteps())
00982     {
00983       delete fpPolyhedron;
00984       fpPolyhedron = CreatePolyhedron();
00985     }
00986   return fpPolyhedron;
00987 }
00988 
00989 
00990 //
00991 // DescribeYourselfTo
00992 //
00993 void G4EllipticalTube::DescribeYourselfTo( G4VGraphicsScene& scene ) const
00994 {
00995   scene.AddSolid (*this);
00996 }
00997 
00998 
00999 //
01000 // GetExtent
01001 //
01002 G4VisExtent G4EllipticalTube::GetExtent() const
01003 {
01004   return G4VisExtent( -dx, dx, -dy, dy, -dz, dz );
01005 }

Generated on Mon May 27 17:48:08 2013 for Geant4 by  doxygen 1.4.7