Files
geant4/source/geometry/solids/CSG/src/G4Orb.cc
T
2016-06-09 14:55:03 +02:00

652 lines
19 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
// $Id: G4Orb.cc,v 1.23 2006/06/29 18:45:12 gunter Exp $
// GEANT4 tag $Name: geant4-08-02 $
//
// class G4Orb
//
// Implementation for G4Orb class
//
// History:
//
// 30.06.04 V.Grichine - bug fixed in DistanceToIn(p,v) on Rmax surface
// 20.08.03 V.Grichine - created
//
//////////////////////////////////////////////////////////////
#include <assert.h>
#include "G4Orb.hh"
#include "G4VoxelLimits.hh"
#include "G4AffineTransform.hh"
#include "G4VPVParameterisation.hh"
#include "Randomize.hh"
#include "meshdefs.hh"
#include "G4VGraphicsScene.hh"
#include "G4Polyhedron.hh"
#include "G4NURBS.hh"
#include "G4NURBSbox.hh"
using namespace CLHEP;
// Private enum: Not for external use - used by distanceToOut
enum ESide {kNull,kRMax};
// used by normal
enum ENorm {kNRMax};
const G4double G4Orb::fEpsilon = 2.e-11; // relative tolerance of fRmax
////////////////////////////////////////////////////////////////////////
//
// constructor - check positive radius
//
G4Orb::G4Orb( const G4String& pName,G4double pRmax )
: G4CSGSolid(pName)
{
// Check radius
if (pRmax >= 10*kCarTolerance ) fRmax = pRmax;
else
{
G4Exception("G4Orb::G4Orb()", "InvalidSetup", FatalException,
"Invalid radius > 10*kCarTolerance.");
}
fRmaxTolerance = std::max( kRadTolerance, fEpsilon*fRmax);
}
///////////////////////////////////////////////////////////////////////
//
// Fake default constructor - sets only member data and allocates memory
// for usage restricted to object persistency.
//
G4Orb::G4Orb( __void__& a )
: G4CSGSolid(a)
{
}
/////////////////////////////////////////////////////////////////////
//
// Destructor
G4Orb::~G4Orb()
{
}
//////////////////////////////////////////////////////////////////////////
//
// Dispatch to parameterisation for replication mechanism dimension
// computation & modification.
void G4Orb::ComputeDimensions( G4VPVParameterisation* p,
const G4int n,
const G4VPhysicalVolume* pRep)
{
p->ComputeDimensions(*this,n,pRep);
}
////////////////////////////////////////////////////////////////////////////
//
// Calculate extent under transform and specified limit
G4bool G4Orb::CalculateExtent( const EAxis pAxis,
const G4VoxelLimits& pVoxelLimit,
const G4AffineTransform& pTransform,
G4double& pMin, G4double& pMax ) const
{
// Compute x/y/z mins and maxs for bounding box respecting limits,
// with early returns if outside limits. Then switch() on pAxis,
// and compute exact x and y limit for x/y case
G4double xoffset,xMin,xMax;
G4double yoffset,yMin,yMax;
G4double zoffset,zMin,zMax;
G4double diff1,diff2,maxDiff,newMin,newMax;
G4double xoff1,xoff2,yoff1,yoff2;
xoffset=pTransform.NetTranslation().x();
xMin=xoffset-fRmax;
xMax=xoffset+fRmax;
if (pVoxelLimit.IsXLimited())
{
if ( (xMin>pVoxelLimit.GetMaxXExtent()+kCarTolerance)
|| (xMax<pVoxelLimit.GetMinXExtent()-kCarTolerance) )
{
return false;
}
else
{
if (xMin<pVoxelLimit.GetMinXExtent())
{
xMin=pVoxelLimit.GetMinXExtent();
}
if (xMax>pVoxelLimit.GetMaxXExtent())
{
xMax=pVoxelLimit.GetMaxXExtent();
}
}
}
yoffset=pTransform.NetTranslation().y();
yMin=yoffset-fRmax;
yMax=yoffset+fRmax;
if (pVoxelLimit.IsYLimited())
{
if ( (yMin>pVoxelLimit.GetMaxYExtent()+kCarTolerance)
|| (yMax<pVoxelLimit.GetMinYExtent()-kCarTolerance) )
{
return false;
}
else
{
if (yMin<pVoxelLimit.GetMinYExtent())
{
yMin=pVoxelLimit.GetMinYExtent();
}
if (yMax>pVoxelLimit.GetMaxYExtent())
{
yMax=pVoxelLimit.GetMaxYExtent();
}
}
}
zoffset=pTransform.NetTranslation().z();
zMin=zoffset-fRmax;
zMax=zoffset+fRmax;
if (pVoxelLimit.IsZLimited())
{
if ( (zMin>pVoxelLimit.GetMaxZExtent()+kCarTolerance)
|| (zMax<pVoxelLimit.GetMinZExtent()-kCarTolerance) )
{
return false;
}
else
{
if (zMin<pVoxelLimit.GetMinZExtent())
{
zMin=pVoxelLimit.GetMinZExtent();
}
if (zMax>pVoxelLimit.GetMaxZExtent())
{
zMax=pVoxelLimit.GetMaxZExtent();
}
}
}
// Known to cut sphere
switch (pAxis)
{
case kXAxis:
yoff1=yoffset-yMin;
yoff2=yMax-yoffset;
if ( yoff1 >= 0 && yoff2 >= 0 )
{
// Y limits cross max/min x => no change
//
pMin=xMin;
pMax=xMax;
}
else
{
// Y limits don't cross max/min x => compute max delta x,
// hence new mins/maxs
//
diff1=std::sqrt(fRmax*fRmax-yoff1*yoff1);
diff2=std::sqrt(fRmax*fRmax-yoff2*yoff2);
maxDiff=(diff1>diff2) ? diff1:diff2;
newMin=xoffset-maxDiff;
newMax=xoffset+maxDiff;
pMin=(newMin<xMin) ? xMin : newMin;
pMax=(newMax>xMax) ? xMax : newMax;
}
break;
case kYAxis:
xoff1=xoffset-xMin;
xoff2=xMax-xoffset;
if (xoff1>=0&&xoff2>=0)
{
// X limits cross max/min y => no change
//
pMin=yMin;
pMax=yMax;
}
else
{
// X limits don't cross max/min y => compute max delta y,
// hence new mins/maxs
//
diff1=std::sqrt(fRmax*fRmax-xoff1*xoff1);
diff2=std::sqrt(fRmax*fRmax-xoff2*xoff2);
maxDiff=(diff1>diff2) ? diff1:diff2;
newMin=yoffset-maxDiff;
newMax=yoffset+maxDiff;
pMin=(newMin<yMin) ? yMin : newMin;
pMax=(newMax>yMax) ? yMax : newMax;
}
break;
case kZAxis:
pMin=zMin;
pMax=zMax;
break;
default:
break;
}
pMin -= fRmaxTolerance;
pMax += fRmaxTolerance;
return true;
}
///////////////////////////////////////////////////////////////////////////
//
// Return whether point inside/outside/on surface
// Split into radius checks
//
EInside G4Orb::Inside( const G4ThreeVector& p ) const
{
G4double rad2,tolRMax;
EInside in;
rad2 = p.x()*p.x()+p.y()*p.y()+p.z()*p.z() ;
// G4double rad = std::sqrt(rad2);
// Check radial surface
// sets `in'
tolRMax = fRmax - fRmaxTolerance*0.5 ;
if ( rad2 <= tolRMax*tolRMax ) in = kInside ;
else
{
tolRMax = fRmax + fRmaxTolerance*0.5 ;
if ( rad2 <= tolRMax*tolRMax ) in = kSurface ;
else in = kOutside ;
}
return in;
}
/////////////////////////////////////////////////////////////////////
//
// Return unit normal of surface closest to p
// - note if point on z axis, ignore phi divided sides
// - unsafe if point close to z axis a rmin=0 - no explicit checks
G4ThreeVector G4Orb::SurfaceNormal( const G4ThreeVector& p ) const
{
ENorm side = kNRMax;
G4ThreeVector norm;
G4double rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
switch (side)
{
case kNRMax:
norm = G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
break;
default:
DumpInfo();
#ifdef G4CSGDEBUG
G4Exception("G4Orb::SurfaceNormal()", "Notification", JustWarning,
"Undefined side for valid surface normal to solid.");
#endif
break;
}
return norm;
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate distance to shape from outside, along normalised vector
// - return kInfinity if no intersection, or intersection distance <= tolerance
//
// -> If point is outside outer radius, compute intersection with rmax
// - if no intersection return
// - if valid phi,theta return intersection Dist
G4double G4Orb::DistanceToIn( const G4ThreeVector& p,
const G4ThreeVector& v ) const
{
G4double snxt = kInfinity ; // snxt = default return value
G4double rad2, pDotV3d, tolORMax2, tolIRMax2 ;
G4double c, d2, s = kInfinity ;
// General Precalcs
rad2 = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() ;
pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ;
// Radial Precalcs
tolORMax2 = (fRmax+fRmaxTolerance*0.5)*(fRmax+fRmaxTolerance*0.5) ;
tolIRMax2 = (fRmax-fRmaxTolerance*0.5)*(fRmax-fRmaxTolerance*0.5) ;
// Outer spherical shell intersection
// - Only if outside tolerant fRmax
// - Check for if inside and outer G4Orb heading through solid (-> 0)
// - No intersect -> no intersection with G4Orb
//
// Shell eqn: x^2+y^2+z^2 = RSPH^2
//
// => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
//
// => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
// => rad2 +2s(pDotV3d) +s^2 =R^2
//
// => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
c = rad2 - fRmax*fRmax ;
if ( c > fRmaxTolerance*fRmax )
{
// If outside tolerant boundary of outer G4Orb
// [ should be std::sqrt(rad2) - fRmax > fRmaxTolerance*0.5 ]
d2 = pDotV3d*pDotV3d - c ;
if ( d2 >= 0 )
{
s = -pDotV3d - std::sqrt(d2) ;
if (s >= 0 ) return snxt = s;
}
else // No intersection with G4Orb
{
return snxt = kInfinity;
}
}
else
{
if ( c > -fRmaxTolerance*fRmax ) // on surface
{
d2 = pDotV3d*pDotV3d - c ;
// if ( pDotV3d >= 0 ) return snxt = kInfinity;
if ( d2 < fRmaxTolerance*fRmax || pDotV3d >= 0 ) return snxt = kInfinity;
else return snxt = 0.;
}
else // inside ???
{
G4Exception("G4Orb::DistanceToIn(p,v)", "Notification",
JustWarning, "Point p is inside !?");
}
}
return snxt;
}
//////////////////////////////////////////////////////////////////////
//
// Calculate distance (<= actual) to closest surface of shape from outside
// - Calculate distance to radial plane
// - Return 0 if point inside
G4double G4Orb::DistanceToIn( const G4ThreeVector& p ) const
{
G4double safe=0.0, rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
safe = rad - fRmax;
if( safe < 0 ) safe = 0. ;
return safe;
}
/////////////////////////////////////////////////////////////////////
//
// Calculate distance to surface of shape from `inside', allowing for tolerance
//
G4double G4Orb::DistanceToOut( const G4ThreeVector& p,
const G4ThreeVector& v,
const G4bool calcNorm,
G4bool *validNorm,
G4ThreeVector *n ) const
{
G4double snxt = kInfinity; // ??? snxt is default return value
ESide side = kNull;
G4double rad2,pDotV3d;
G4double xi,yi,zi; // Intersection point
G4double c,d2;
rad2 = p.x()*p.x() + p.y()*p.y() + p.z()*p.z();
pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z();
// Radial Intersection from G4Orb::DistanceToIn
//
// Outer spherical shell intersection
// - Only if outside tolerant fRmax
// - Check for if inside and outer G4Orb heading through solid (-> 0)
// - No intersect -> no intersection with G4Orb
//
// Shell eqn: x^2+y^2+z^2=RSPH^2
//
// => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
//
// => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
// => rad2 +2s(pDotV3d) +s^2 =R^2
//
// => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
const G4double Rmax_plus = fRmax + fRmaxTolerance*0.5;
if( rad2 <= Rmax_plus*Rmax_plus )
{
c = rad2-fRmax*fRmax ;
if ( c < fRmaxTolerance*fRmax)
{
// Within tolerant Outer radius
//
// The test is
// rad - fRmax < 0.5*fRmaxTolerance
// => rad < fRmax + 0.5*kRadTol
// => rad2 < (fRmax + 0.5*kRadTol)^2
// => rad2 < fRmax^2 + 2.*0.5*fRmax*kRadTol + 0.25*kRadTol*kRadTol
// => rad2 - fRmax^2 <~ fRmax*kRadTol
d2 = pDotV3d*pDotV3d - c;
if( ( c > -fRmaxTolerance*fRmax) && // on tolerant surface
( ( pDotV3d >= 0 ) || ( d2 < 0 )) ) // leaving outside from Rmax
// not re-entering
{
if(calcNorm)
{
*validNorm = true ;
*n = G4ThreeVector(p.x()/fRmax,p.y()/fRmax,p.z()/fRmax) ;
}
return snxt = 0;
}
else
{
snxt = -pDotV3d + std::sqrt(d2); // second root since inside Rmax
side = kRMax ;
}
}
}
else // p is outside ???
{
G4cout.precision(16);
G4cout << G4endl;
DumpInfo();
G4cout << "Position:" << G4endl << G4endl;
G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl;
G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl;
G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl;
G4cout << "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm << " mm"
<< G4endl << G4endl;
G4cout << "Direction:" << G4endl << G4endl;
G4cout << "v.x() = " << v.x() << G4endl;
G4cout << "v.y() = " << v.y() << G4endl;
G4cout << "v.z() = " << v.z() << G4endl << G4endl;
G4cout << "Proposed distance :" << G4endl << G4endl;
G4cout << "snxt = " << snxt/mm << " mm" << G4endl << G4endl;
G4Exception("G4Orb::DistanceToOut(p,v,..)", "Notification",
JustWarning, "Logic error: snxt = kInfinity ???");
}
if (calcNorm) // Output switch operator
{
switch( side )
{
case kRMax:
xi=p.x()+snxt*v.x();
yi=p.y()+snxt*v.y();
zi=p.z()+snxt*v.z();
*n=G4ThreeVector(xi/fRmax,yi/fRmax,zi/fRmax);
*validNorm=true;
break;
default:
G4cout.precision(16);
G4cout << G4endl;
DumpInfo();
G4cout << "Position:" << G4endl << G4endl;
G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl;
G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl;
G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl;
G4cout << "Direction:" << G4endl << G4endl;
G4cout << "v.x() = " << v.x() << G4endl;
G4cout << "v.y() = " << v.y() << G4endl;
G4cout << "v.z() = " << v.z() << G4endl << G4endl;
G4cout << "Proposed distance :" << G4endl << G4endl;
G4cout << "snxt = " << snxt/mm << " mm" << G4endl << G4endl;
G4Exception("G4Orb::DistanceToOut(p,v,..)","Notification",JustWarning,
"Undefined side for valid surface normal to solid.");
break;
}
}
return snxt;
}
/////////////////////////////////////////////////////////////////////////
//
// Calculate distance (<=actual) to closest surface of shape from inside
G4double G4Orb::DistanceToOut( const G4ThreeVector& p ) const
{
G4double safe=0.0,rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
#ifdef G4CSGDEBUG
if( Inside(p) == kOutside )
{
G4cout.precision(16) ;
G4cout << G4endl ;
DumpInfo();
G4cout << "Position:" << G4endl << G4endl ;
G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl ;
G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl ;
G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl ;
G4Exception("G4Orb::DistanceToOut(p)", "Notification", JustWarning,
"Point p is outside !?" );
}
#endif
safe = fRmax - rad;
if ( safe < 0. ) safe = 0.;
return safe;
}
//////////////////////////////////////////////////////////////////////////
//
// G4EntityType
G4GeometryType G4Orb::GetEntityType() const
{
return G4String("G4Orb");
}
//////////////////////////////////////////////////////////////////////////
//
// Stream object contents to an output stream
std::ostream& G4Orb::StreamInfo( std::ostream& os ) const
{
os << "-----------------------------------------------------------\n"
<< " *** Dump for solid - " << GetName() << " ***\n"
<< " ===================================================\n"
<< " Solid type: G4Orb\n"
<< " Parameters: \n"
<< " outer radius: " << fRmax/mm << " mm \n"
<< "-----------------------------------------------------------\n";
return os;
}
/////////////////////////////////////////////////////////////////////////
//
// GetPointOnSurface
G4ThreeVector G4Orb::GetPointOnSurface() const
{
// generate a random number from zero to 2pi...
//
G4double phi = RandFlat::shoot(0.,2.*pi);
G4double cosphi = std::cos(phi);
G4double sinphi = std::sin(phi);
G4double theta = RandFlat::shoot(0.,pi);
G4double costheta = std::cos(theta);
G4double sintheta = std::sqrt(1.-sqr(costheta));
return G4ThreeVector (fRmax*sintheta*cosphi,
fRmax*sintheta*sinphi, fRmax*costheta);
}
////////////////////////////////////////////////////////////////////////
//
// Methods for visualisation
void G4Orb::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
{
scene.AddSolid (*this);
}
G4Polyhedron* G4Orb::CreatePolyhedron () const
{
return new G4PolyhedronSphere (0., fRmax, 0., 2*pi, 0., pi);
}
G4NURBS* G4Orb::CreateNURBS () const
{
return new G4NURBSbox (fRmax, fRmax, fRmax); // Box for now!!!
}