Files
geant4/source/geometry/solids/CSG/src/G4Sphere.cc
T
2016-06-09 15:58:43 +02:00

3372 lines
92 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: G4Sphere.cc,v 1.68 2008/07/07 09:35:16 grichine Exp $
// GEANT4 tag $Name: geant4-09-02 $
//
// class G4Sphere
//
// Implementation for G4Sphere class
//
// History:
//
// 12.06.08 V.Grichine: fix for theta intersections in DistanceToOut(p,v,...)
// 22.07.05 O.Link : Added check for intersection with double cone
// 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
// 16.09.04 V.Grichine: bug fixed in SurfaceNormal(p), theta normals
// 16.07.04 V.Grichine: bug fixed in DistanceToOut(p,v), Rmin go outside
// 02.06.04 V.Grichine: bug fixed in DistanceToIn(p,v), on Rmax,Rmin go inside
// 30.10.03 J.Apostolakis: new algorithm in Inside for SPhi-sections
// 29.10.03 J.Apostolakis: fix in Inside for SPhi-0.5*kAngTol < phi < SPhi, SPhi<0
// 19.06.02 V.Grichine: bug fixed in Inside(p), && -> && fDTheta - kAngTolerance
// 30.01.02 V.Grichine: bug fixed in Inside(p), && -> || at l.451
// 06.03.00 V.Grichine: modifications in Distance ToOut(p,v,...)
// 18.11.99 V.Grichine: side = kNull in Distance ToOut(p,v,...)
// 25.11.98 V.Grichine: bug fixed in DistanceToIn(p,v), phi intersections
// 12.11.98 V.Grichine: bug fixed in DistanceToIn(p,v), theta intersections
// 09.10.98 V.Grichine: modifications in DistanceToOut(p,v,...)
// 17.09.96 V.Grichine: final modifications to commit
// 28.03.94 P.Kent: old C++ code converted to tolerant geometry
// --------------------------------------------------------------------
#include <assert.h>
#include "G4Sphere.hh"
#include "G4VoxelLimits.hh"
#include "G4AffineTransform.hh"
#include "G4GeometryTolerance.hh"
#include "G4VPVParameterisation.hh"
#include "Randomize.hh"
#include "meshdefs.hh"
#include "G4VGraphicsScene.hh"
#include "G4VisExtent.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,kRMin,kRMax,kSPhi,kEPhi,kSTheta,kETheta};
// used by normal
enum ENorm {kNRMin,kNRMax,kNSPhi,kNEPhi,kNSTheta,kNETheta};
////////////////////////////////////////////////////////////////////////
//
// constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
// - note if pDPhi>2PI then reset to 2PI
G4Sphere::G4Sphere( const G4String& pName,
G4double pRmin, G4double pRmax,
G4double pSPhi, G4double pDPhi,
G4double pSTheta, G4double pDTheta )
: G4CSGSolid(pName)
{
fEpsilon = 1.0e-14;
kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
// Check radii
if (pRmin<pRmax&&pRmin>=0)
{
fRmin=pRmin; fRmax=pRmax;
}
else
{
G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
<< " Invalide values for radii ! - "
<< " pRmin = " << pRmin << ", pRmax = " << pRmax << G4endl;
G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
"Invalid radii");
}
// Check angles
if (pDPhi>=twopi)
{
fDPhi=twopi;
}
else if (pDPhi>0)
{
fDPhi=pDPhi;
}
else
{
G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
<< " Negative Z delta-Phi ! - "
<< pDPhi << G4endl;
G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
"Invalid DPhi.");
}
// Convert fSPhi to 0-2PI
if (pSPhi<0)
{
fSPhi=twopi-std::fmod(std::fabs(pSPhi),twopi);
}
else
{
fSPhi=std::fmod(pSPhi,twopi);
}
// Sphere is placed such that fSPhi+fDPhi>twopi !
// fSPhi could be < 0 !!?
//
if (fSPhi+fDPhi>twopi) fSPhi-=twopi;
// Check theta angles
if (pSTheta<0 || pSTheta>pi)
{
G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl;
G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
"stheta outside 0-PI range.");
}
else
{
fSTheta=pSTheta;
}
if (pDTheta+pSTheta>=pi)
{
fDTheta=pi-pSTheta;
}
else if (pDTheta>0)
{
fDTheta=pDTheta;
}
else
{
G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
<< " Negative delta-Theta ! - "
<< pDTheta << G4endl;
G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
"Invalid pDTheta.");
}
}
///////////////////////////////////////////////////////////////////////
//
// Fake default constructor - sets only member data and allocates memory
// for usage restricted to object persistency.
//
G4Sphere::G4Sphere( __void__& a )
: G4CSGSolid(a)
{
}
/////////////////////////////////////////////////////////////////////
//
// Destructor
G4Sphere::~G4Sphere()
{
}
//////////////////////////////////////////////////////////////////////////
//
// Dispatch to parameterisation for replication mechanism dimension
// computation & modification.
void G4Sphere::ComputeDimensions( G4VPVParameterisation* p,
const G4int n,
const G4VPhysicalVolume* pRep)
{
p->ComputeDimensions(*this,n,pRep);
}
////////////////////////////////////////////////////////////////////////////
//
// Calculate extent under transform and specified limit
G4bool G4Sphere::CalculateExtent( const EAxis pAxis,
const G4VoxelLimits& pVoxelLimit,
const G4AffineTransform& pTransform,
G4double& pMin, G4double& pMax ) const
{
if ( fDPhi==twopi && fDTheta==pi) // !pTransform.IsRotated() &&
{
// Special case handling for solid spheres-shells
// (rotation doesn't influence).
// 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-=kCarTolerance;
pMax+=kCarTolerance;
return true;
}
else // Transformed cutted sphere
{
G4int i,j,noEntries,noBetweenSections;
G4bool existsAfterClip=false;
// Calculate rotated vertex coordinates
G4ThreeVectorList* vertices;
G4int noPolygonVertices ;
vertices=CreateRotatedVertices(pTransform,noPolygonVertices);
pMin=+kInfinity;
pMax=-kInfinity;
noEntries=vertices->size(); // noPolygonVertices*noPhiCrossSections
noBetweenSections=noEntries-noPolygonVertices;
G4ThreeVectorList ThetaPolygon ;
for (i=0;i<noEntries;i+=noPolygonVertices)
{
for(j=0;j<(noPolygonVertices/2)-1;j++)
{
ThetaPolygon.push_back((*vertices)[i+j]) ;
ThetaPolygon.push_back((*vertices)[i+j+1]) ;
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-2-j]) ;
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-1-j]) ;
CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
ThetaPolygon.clear() ;
}
}
for (i=0;i<noBetweenSections;i+=noPolygonVertices)
{
for(j=0;j<noPolygonVertices-1;j++)
{
ThetaPolygon.push_back((*vertices)[i+j]) ;
ThetaPolygon.push_back((*vertices)[i+j+1]) ;
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices+j+1]) ;
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices+j]) ;
CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
ThetaPolygon.clear() ;
}
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-1]) ;
ThetaPolygon.push_back((*vertices)[i]) ;
ThetaPolygon.push_back((*vertices)[i+noPolygonVertices]) ;
ThetaPolygon.push_back((*vertices)[i+2*noPolygonVertices-1]) ;
CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
ThetaPolygon.clear() ;
}
if (pMin!=kInfinity || pMax!=-kInfinity)
{
existsAfterClip=true;
// Add 2*tolerance to avoid precision troubles
//
pMin-=kCarTolerance;
pMax+=kCarTolerance;
}
else
{
// Check for case where completely enveloping clipping volume
// If point inside then we are confident that the solid completely
// envelopes the clipping volume. Hence set min/max extents according
// to clipping volume extents along the specified axis.
G4ThreeVector clipCentre(
(pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
(pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
(pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5);
if (Inside(pTransform.Inverse().TransformPoint(clipCentre))!=kOutside)
{
existsAfterClip=true;
pMin=pVoxelLimit.GetMinExtent(pAxis);
pMax=pVoxelLimit.GetMaxExtent(pAxis);
}
}
delete vertices;
return existsAfterClip;
}
}
///////////////////////////////////////////////////////////////////////////
//
// Return whether point inside/outside/on surface
// Split into radius, phi, theta checks
// Each check modifies `in', or returns as approprate
EInside G4Sphere::Inside( const G4ThreeVector& p ) const
{
G4double rho,rho2,rad2,tolRMin,tolRMax;
G4double pPhi,pTheta;
EInside in=kOutside;
rho2 = p.x()*p.x() + p.y()*p.y() ;
rad2 = rho2 + p.z()*p.z() ;
// if(rad2 >= 1.369e+19) DBG();
// G4double rad = std::sqrt(rad2);
// Check radial surfaces
// sets `in'
if ( fRmin ) tolRMin = fRmin + kRadTolerance*0.5;
else tolRMin = 0 ;
tolRMax = fRmax - kRadTolerance*0.5 ;
// const G4double fractionTolerance = 1.0e-12;
const G4double flexRadMaxTolerance = // kRadTolerance;
std::max(kRadTolerance, fEpsilon * fRmax);
const G4double Rmax_minus = fRmax - flexRadMaxTolerance*0.5;
const G4double flexRadMinTolerance = std::max(kRadTolerance,
fEpsilon * fRmin);
const G4double Rmin_plus = (fRmin > 0) ? fRmin + flexRadMinTolerance*0.5 : 0 ;
if(rad2 <= Rmax_minus*Rmax_minus && rad2 >= Rmin_plus*Rmin_plus) in = kInside ;
// if ( rad2 <= tolRMax*tolRMax && rad2 >= tolRMin*tolRMin ) in = kInside ;
// if ( rad <= tolRMax && rad >= tolRMin ) in = kInside ;
else
{
tolRMax = fRmax + kRadTolerance*0.5 ;
tolRMin = fRmin - kRadTolerance*0.5 ;
if ( tolRMin < 0.0 ) tolRMin = 0.0 ;
if ( rad2 <= tolRMax*tolRMax && rad2 >= tolRMin*tolRMin ) in = kSurface ;
// if ( rad <= tolRMax && rad >= tolRMin ) in = kSurface ;
else return in = kOutside ;
}
// Phi boundaries : Do not check if it has no phi boundary!
// (in != kOutside). It is new J.Apostolakis proposal of 30.10.03
if ( ( fDPhi < twopi - kAngTolerance ) &&
( (p.x() != 0.0 ) || (p.y() != 0.0) ) )
{
pPhi = std::atan2(p.y(),p.x()) ;
if ( pPhi < fSPhi - kAngTolerance*0.5 ) pPhi += twopi ;
else if ( pPhi > fSPhi + fDPhi + kAngTolerance*0.5 ) pPhi -= twopi;
if ((pPhi < fSPhi - kAngTolerance*0.5) ||
(pPhi > fSPhi + fDPhi + kAngTolerance*0.5) ) return in = kOutside ;
else if (in == kInside) // else it's kSurface anyway already
{
if ( (pPhi < fSPhi + kAngTolerance*0.5) ||
(pPhi > fSPhi + fDPhi - kAngTolerance*0.5) ) in = kSurface ;
}
}
// Theta bondaries
// (in!=kOutside)
if ( (rho2 || p.z()) && fDTheta < pi - kAngTolerance*0.5 )
{
rho = std::sqrt(rho2);
pTheta = std::atan2(rho,p.z());
if ( in == kInside )
{
if ( (pTheta < fSTheta + kAngTolerance*0.5)
|| (pTheta > fSTheta + fDTheta - kAngTolerance*0.5) )
{
if ( (pTheta >= fSTheta - kAngTolerance*0.5)
&& (pTheta <= fSTheta + fDTheta + kAngTolerance*0.5) )
{
in = kSurface ;
}
else
{
in = kOutside ;
}
}
}
else
{
if ( (pTheta < fSTheta - kAngTolerance*0.5)
|| (pTheta > fSTheta + fDTheta + kAngTolerance*0.5) )
{
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 G4Sphere::SurfaceNormal( const G4ThreeVector& p ) const
{
G4int noSurfaces = 0;
G4double rho, rho2, rad, pTheta, pPhi=0.;
G4double distRMin = kInfinity;
G4double distSPhi = kInfinity, distEPhi = kInfinity;
G4double distSTheta = kInfinity, distETheta = kInfinity;
G4double delta = 0.5*kCarTolerance, dAngle = 0.5*kAngTolerance;
G4ThreeVector nR, nPs, nPe, nTs, nTe, nZ(0.,0.,1.);
G4ThreeVector norm, sumnorm(0.,0.,0.);
rho2 = p.x()*p.x()+p.y()*p.y();
rad = std::sqrt(rho2+p.z()*p.z());
rho = std::sqrt(rho2);
G4double distRMax = std::fabs(rad-fRmax);
if (fRmin) distRMin = std::fabs(rad-fRmin);
if ( rho && (fDPhi < twopi || fDTheta < pi) )
{
pPhi = std::atan2(p.y(),p.x());
if(pPhi < fSPhi-dAngle) pPhi += twopi;
else if(pPhi > fSPhi+fDPhi+dAngle) pPhi -= twopi;
}
if ( fDPhi < twopi ) // && rho ) // old limitation against (0,0,z)
{
if ( rho )
{
distSPhi = std::fabs( pPhi - fSPhi );
distEPhi = std::fabs(pPhi-fSPhi-fDPhi);
}
else if( !fRmin )
{
distSPhi = 0.;
distEPhi = 0.;
}
nPs = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
}
if ( fDTheta < pi ) // && rad ) // old limitation against (0,0,0)
{
if ( rho )
{
pTheta = std::atan2(rho,p.z());
distSTheta = std::fabs(pTheta-fSTheta);
distETheta = std::fabs(pTheta-fSTheta-fDTheta);
nTs = G4ThreeVector(-std::cos(fSTheta)*p.x()/rho, // *std::cos(pPhi),
-std::cos(fSTheta)*p.y()/rho, // *std::sin(pPhi),
std::sin(fSTheta) );
nTe = G4ThreeVector( std::cos(fSTheta+fDTheta)*p.x()/rho, // *std::cos(pPhi),
std::cos(fSTheta+fDTheta)*p.y()/rho, // *std::sin(pPhi),
-std::sin(fSTheta+fDTheta) );
}
else if( !fRmin )
{
if ( fSTheta )
{
distSTheta = 0.;
nTs = G4ThreeVector(0.,0.,-1.);
}
if ( fSTheta + fDTheta < pi ) // distETheta = 0.;
{
distETheta = 0.;
nTe = G4ThreeVector(0.,0.,1.);
}
}
}
if( rad ) nR = G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
if( distRMax <= delta )
{
noSurfaces ++;
sumnorm += nR;
}
if( fRmin && distRMin <= delta )
{
noSurfaces ++;
sumnorm -= nR;
}
if( fDPhi < twopi )
{
if (distSPhi <= dAngle)
{
noSurfaces ++;
sumnorm += nPs;
}
if (distEPhi <= dAngle)
{
noSurfaces ++;
sumnorm += nPe;
}
}
if ( fDTheta < pi )
{
if (distSTheta <= dAngle && fSTheta > 0.)
{
noSurfaces ++;
if( rad <= delta && fDPhi >= twopi) sumnorm += nZ;
else sumnorm += nTs;
}
if (distETheta <= dAngle && fSTheta+fDTheta < pi)
{
noSurfaces ++;
if( rad <= delta && fDPhi >= twopi) sumnorm -= nZ;
else sumnorm += nTe;
if(sumnorm.z() == 0.) sumnorm += nZ;
}
}
if ( noSurfaces == 0 )
{
#ifdef G4CSGDEBUG
G4Exception("G4Sphere::SurfaceNormal(p)", "Notification", JustWarning,
"Point p is not on surface !?" );
#endif
norm = ApproxSurfaceNormal(p);
}
else if ( noSurfaces == 1 ) norm = sumnorm;
else norm = sumnorm.unit();
return norm;
}
/////////////////////////////////////////////////////////////////////////////////////////////
//
// Algorithm for SurfaceNormal() following the original specification
// for points not on the surface
G4ThreeVector G4Sphere::ApproxSurfaceNormal( const G4ThreeVector& p ) const
{
ENorm side;
G4ThreeVector norm;
G4double rho,rho2,rad,pPhi,pTheta;
G4double distRMin,distRMax,distSPhi,distEPhi,
distSTheta,distETheta,distMin;
rho2=p.x()*p.x()+p.y()*p.y();
rad=std::sqrt(rho2+p.z()*p.z());
rho=std::sqrt(rho2);
//
// Distance to r shells
//
distRMax=std::fabs(rad-fRmax);
if (fRmin)
{
distRMin=std::fabs(rad-fRmin);
if (distRMin<distRMax)
{
distMin=distRMin;
side=kNRMin;
}
else
{
distMin=distRMax;
side=kNRMax;
}
}
else
{
distMin=distRMax;
side=kNRMax;
}
//
// Distance to phi planes
//
// Protected against (0,0,z)
pPhi = std::atan2(p.y(),p.x());
if (pPhi<0) pPhi += twopi;
if (fDPhi<twopi&&rho)
{
if (fSPhi<0)
{
distSPhi=std::fabs(pPhi-(fSPhi+twopi))*rho;
}
else
{
distSPhi=std::fabs(pPhi-fSPhi)*rho;
}
distEPhi=std::fabs(pPhi-fSPhi-fDPhi)*rho;
// Find new minimum
//
if (distSPhi<distEPhi)
{
if (distSPhi<distMin)
{
distMin=distSPhi;
side=kNSPhi;
}
}
else
{
if (distEPhi<distMin)
{
distMin=distEPhi;
side=kNEPhi;
}
}
}
//
// Distance to theta planes
//
if (fDTheta<pi&&rad)
{
pTheta=std::atan2(rho,p.z());
distSTheta=std::fabs(pTheta-fSTheta)*rad;
distETheta=std::fabs(pTheta-fSTheta-fDTheta)*rad;
// Find new minimum
//
if (distSTheta<distETheta)
{
if (distSTheta<distMin)
{
distMin = distSTheta ;
side = kNSTheta ;
}
}
else
{
if (distETheta<distMin)
{
distMin = distETheta ;
side = kNETheta ;
}
}
}
switch (side)
{
case kNRMin: // Inner radius
norm=G4ThreeVector(-p.x()/rad,-p.y()/rad,-p.z()/rad);
break;
case kNRMax: // Outer radius
norm=G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
break;
case kNSPhi:
norm=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
break;
case kNEPhi:
norm=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
break;
case kNSTheta:
norm=G4ThreeVector(-std::cos(fSTheta)*std::cos(pPhi),
-std::cos(fSTheta)*std::sin(pPhi),
std::sin(fSTheta) );
// G4cout<<G4endl<<" case kNSTheta:"<<G4endl;
// G4cout<<"pPhi = "<<pPhi<<G4endl;
// G4cout<<"rad = "<<rad<<G4endl;
// G4cout<<"pho = "<<rho<<G4endl;
// G4cout<<"p: "<<p.x()<<"; "<<p.y()<<"; "<<p.z()<<G4endl;
// G4cout<<"norm: "<<norm.x()<<"; "<<norm.y()<<"; "<<norm.z()<<G4endl;
break;
case kNETheta:
norm=G4ThreeVector( std::cos(fSTheta+fDTheta)*std::cos(pPhi),
std::cos(fSTheta+fDTheta)*std::sin(pPhi),
-std::sin(fSTheta+fDTheta) );
// G4cout<<G4endl<<" case kNETheta:"<<G4endl;
// G4cout<<"pPhi = "<<pPhi<<G4endl;
// G4cout<<"rad = "<<rad<<G4endl;
// G4cout<<"pho = "<<rho<<G4endl;
// G4cout<<"p: "<<p.x()<<"; "<<p.y()<<"; "<<p.z()<<G4endl;
// G4cout<<"norm: "<<norm.x()<<"; "<<norm.y()<<"; "<<norm.z()<<G4endl;
break;
default:
DumpInfo();
G4Exception("G4Sphere::ApproxSurfaceNormal()", "Notification", JustWarning,
"Undefined side for valid surface normal to solid.");
break;
} // end case
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
//
// -> If shell, compute intersection with inner radius, taking largest +ve root
// - if valid phi,theta, save intersection
//
// -> If phi segmented, compute intersection with phi half planes
// - if valid intersection(r,theta), return smallest intersection of
// inner shell & phi intersection
//
// -> If theta segmented, compute intersection with theta cones
// - if valid intersection(r,phi), return smallest intersection of
// inner shell & theta intersection
//
//
// NOTE:
// - `if valid' (above) implies tolerant checking of intersection points
//
// OPT:
// Move tolIO/ORmin/RMax2 precalcs to where they are needed -
// not required for most cases.
// Avoid atan2 for non theta cut G4Sphere.
G4double G4Sphere::DistanceToIn( const G4ThreeVector& p,
const G4ThreeVector& v ) const
{
G4double snxt = kInfinity ; // snxt = default return value
G4double rho2, rad2, pDotV2d, pDotV3d, pTheta ;
G4double tolIRMin2, tolORMin2, tolORMax2, tolIRMax2 ;
G4double tolSTheta=0., tolETheta=0. ;
// Intersection point
G4double xi, yi, zi, rhoi, rhoi2, radi2, iTheta ;
// Phi intersection
G4double sinSPhi, cosSPhi, ePhi, sinEPhi, cosEPhi , Comp ;
// Phi flag and precalcs
G4bool segPhi ;
G4double hDPhi, hDPhiOT, hDPhiIT, cPhi, sinCPhi=0., cosCPhi=0. ;
G4double cosHDPhiOT=0., cosHDPhiIT=0. ;
G4double Dist, cosPsi ;
G4bool segTheta ; // Theta flag and precals
G4double tanSTheta, tanETheta ;
G4double tanSTheta2, tanETheta2 ;
G4double dist2STheta, dist2ETheta ;
G4double t1, t2, b, c, d2, d, s = kInfinity ;
// General Precalcs
rho2 = p.x()*p.x() + p.y()*p.y() ;
rad2 = rho2 + p.z()*p.z() ;
pTheta = std::atan2(std::sqrt(rho2),p.z()) ;
pDotV2d = p.x()*v.x() + p.y()*v.y() ;
pDotV3d = pDotV2d + p.z()*v.z() ;
// Radial Precalcs
if (fRmin > kRadTolerance*0.5)
{
tolORMin2=(fRmin-kRadTolerance*0.5)*(fRmin-kRadTolerance*0.5);
}
else
{
tolORMin2 = 0 ;
}
tolIRMin2 = (fRmin+kRadTolerance*0.5)*(fRmin+kRadTolerance*0.5) ;
tolORMax2 = (fRmax+kRadTolerance*0.5)*(fRmax+kRadTolerance*0.5) ;
tolIRMax2 = (fRmax-kRadTolerance*0.5)*(fRmax-kRadTolerance*0.5) ;
// Set phi divided flag and precalcs
if (fDPhi < twopi)
{
segPhi = true ;
hDPhi = 0.5*fDPhi ; // half delta phi
cPhi = fSPhi + hDPhi ;
hDPhiOT = hDPhi+0.5*kAngTolerance; // Outer Tolerant half delta phi
hDPhiIT = hDPhi-0.5*kAngTolerance;
sinCPhi = std::sin(cPhi) ;
cosCPhi = std::cos(cPhi) ;
cosHDPhiOT = std::cos(hDPhiOT) ;
cosHDPhiIT = std::cos(hDPhiIT) ;
}
else
{
segPhi = false ;
}
// Theta precalcs
if (fDTheta < pi )
{
segTheta = true ;
tolSTheta = fSTheta - kAngTolerance*0.5 ;
tolETheta = fSTheta + fDTheta + kAngTolerance*0.5 ;
}
else
{
segTheta = false ;
}
// Outer spherical shell intersection
// - Only if outside tolerant fRmax
// - Check for if inside and outer G4Sphere heading through solid (-> 0)
// - No intersect -> no intersection with G4Sphere
//
// 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 ;
const G4double flexRadMaxTolerance = // kRadTolerance;
std::max(kRadTolerance, fEpsilon * fRmax);
// if (c > kRadTolerance*fRmax)
if (c > flexRadMaxTolerance*fRmax)
{
// If outside toleranct boundary of outer G4Sphere
// [should be std::sqrt(rad2)-fRmax > kRadTolerance*0.5]
d2 = pDotV3d*pDotV3d - c ;
if ( d2 >= 0 )
{
s = -pDotV3d - std::sqrt(d2) ;
if (s >= 0 )
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
rhoi = std::sqrt(xi*xi + yi*yi) ;
if (segPhi && rhoi) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
if (cosPsi >= cosHDPhiOT)
{
if (segTheta) // Check theta intersection
{
zi = p.z() + s*v.z() ;
// rhoi & zi can never both be 0
// (=>intersect at origin =>fRmax=0)
//
iTheta = std::atan2(rhoi,zi) ;
if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
{
return snxt = s ;
}
}
else
{
return snxt=s;
}
}
}
else
{
if (segTheta) // Check theta intersection
{
zi = p.z() + s*v.z() ;
// rhoi & zi can never both be 0
// (=>intersect at origin => fRmax=0 !)
//
iTheta = std::atan2(rhoi,zi) ;
if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
{
return snxt=s;
}
}
else
{
return snxt = s ;
}
}
}
}
else // No intersection with G4Sphere
{
return snxt=kInfinity;
}
}
else
{
// Inside outer radius
// check not inside, and heading through G4Sphere (-> 0 to in)
d2 = pDotV3d*pDotV3d - c ;
// if (rad2 > tolIRMin2 && pDotV3d < 0 )
if (rad2 > tolIRMax2 && ( d2 >= flexRadMaxTolerance*fRmax && pDotV3d < 0 ) )
{
if (segPhi)
{
// Use inner phi tolerant boundary -> if on tolerant
// phi boundaries, phi intersect code handles leaving/entering checks
cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
if (cosPsi>=cosHDPhiIT)
{
// inside radii, delta r -ve, inside phi
if (segTheta)
{
if ( (pTheta >= tolSTheta + kAngTolerance)
&& (pTheta <= tolETheta - kAngTolerance) )
{
return snxt=0;
}
}
else // strictly inside Theta in both cases
{
return snxt=0;
}
}
}
else
{
if ( segTheta )
{
if ( (pTheta >= tolSTheta + kAngTolerance)
&& (pTheta <= tolETheta - kAngTolerance) )
{
return snxt=0;
}
}
else // strictly inside Theta in both cases
{
return snxt=0;
}
}
}
}
// Inner spherical shell intersection
// - Always farthest root, because would have passed through outer
// surface first.
// - Tolerant check for if travelling through solid
if (fRmin)
{
c = rad2 - fRmin*fRmin ;
d2 = pDotV3d*pDotV3d - c ;
// Within tolerance inner radius of inner G4Sphere
// Check for immediate entry/already inside and travelling outwards
// if (c >- kRadTolerance*0.5 && pDotV3d >= 0 && rad2 < tolIRMin2 )
if ( c > -kRadTolerance*0.5 && rad2 < tolIRMin2 &&
( d2 < fRmin*kCarTolerance || pDotV3d >= 0 ) )
{
if (segPhi)
{
// Use inner phi tolerant boundary -> if on tolerant
// phi boundaries, phi intersect code handles leaving/entering checks
cosPsi = (p.x()*cosCPhi+p.y()*sinCPhi)/std::sqrt(rho2) ;
if (cosPsi >= cosHDPhiIT)
{
// inside radii, delta r -ve, inside phi
//
if (segTheta)
{
if ( (pTheta >= tolSTheta + kAngTolerance)
&& (pTheta <= tolETheta - kAngTolerance) )
{
return snxt=0;
}
}
else
{
return snxt = 0 ;
}
}
}
else
{
if (segTheta)
{
if ( (pTheta >= tolSTheta + kAngTolerance)
&& (pTheta <= tolETheta - kAngTolerance) )
{
return snxt = 0 ;
}
}
else
{
return snxt=0;
}
}
}
else // Not special tolerant case
{
// d2 = pDotV3d*pDotV3d - c ;
if (d2 >= 0)
{
s = -pDotV3d + std::sqrt(d2) ;
if ( s >= kRadTolerance*0.5 ) // It was >= 0 ??
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
rhoi = std::sqrt(xi*xi+yi*yi) ;
if ( segPhi && rhoi ) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
if (cosPsi >= cosHDPhiOT)
{
if (segTheta) // Check theta intersection
{
zi = p.z() + s*v.z() ;
// rhoi & zi can never both be 0
// (=>intersect at origin =>fRmax=0)
//
iTheta = std::atan2(rhoi,zi) ;
if ( (iTheta >= tolSTheta) && (iTheta<=tolETheta) )
{
snxt = s ;
}
}
else
{
snxt=s;
}
}
}
else
{
if (segTheta) // Check theta intersection
{
zi = p.z() + s*v.z() ;
// rhoi & zi can never both be 0
// (=>intersect at origin => fRmax=0 !)
//
iTheta = std::atan2(rhoi,zi) ;
if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
{
snxt = s ;
}
}
else
{
snxt=s;
}
}
}
}
}
}
// Phi segment intersection
//
// o Tolerant of points inside phi planes by up to kCarTolerance*0.5
//
// o NOTE: Large duplication of code between sphi & ephi checks
// -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
// intersection check <=0 -> >=0
// -> Should use some form of loop Construct
//
if ( segPhi )
{
// First phi surface (`S'tarting phi)
sinSPhi = std::sin(fSPhi) ;
cosSPhi = std::cos(fSPhi) ;
// Comp = Component in outwards normal dirn
//
Comp = v.x()*sinSPhi - v.y()*cosSPhi ;
if ( Comp < 0 )
{
Dist = p.y()*cosSPhi - p.x()*sinSPhi ;
if (Dist < kCarTolerance*0.5)
{
s = Dist/Comp ;
if (s < snxt)
{
if ( s > 0 )
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
}
else
{
s = 0 ;
xi = p.x() ;
yi = p.y() ;
zi = p.z() ;
rhoi2 = rho2 ;
radi2 = rad2 ;
}
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& ((yi*cosCPhi-xi*sinCPhi) <= 0) )
{
// Check theta intersection
// rhoi & zi can never both be 0
// (=>intersect at origin =>fRmax=0)
//
if ( segTheta )
{
iTheta = std::atan2(std::sqrt(rhoi2),zi) ;
if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
{
// r and theta intersections good
// - check intersecting with correct half-plane
if ((yi*cosCPhi-xi*sinCPhi) <= 0)
{
snxt = s ;
}
}
}
else
{
snxt = s ;
}
}
}
}
}
// Second phi surface (`E'nding phi)
ePhi = fSPhi + fDPhi ;
sinEPhi = std::sin(ePhi) ;
cosEPhi = std::cos(ePhi) ;
// Compnent in outwards normal dirn
Comp = -( v.x()*sinEPhi-v.y()*cosEPhi ) ;
if (Comp < 0)
{
Dist = -(p.y()*cosEPhi-p.x()*sinEPhi) ;
if ( Dist < kCarTolerance*0.5 )
{
s = Dist/Comp ;
if ( s < snxt )
{
if (s > 0)
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
}
else
{
s = 0 ;
xi = p.x() ;
yi = p.y() ;
zi = p.z() ;
rhoi2 = rho2 ;
radi2 = rad2 ;
} if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& ((yi*cosCPhi-xi*sinCPhi) >= 0) )
{
// Check theta intersection
// rhoi & zi can never both be 0
// (=>intersect at origin =>fRmax=0)
//
if ( segTheta )
{
iTheta = std::atan2(std::sqrt(rhoi2),zi) ;
if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
{
// r and theta intersections good
// - check intersecting with correct half-plane
if ((yi*cosCPhi-xi*sinCPhi) >= 0)
{
snxt = s ;
}
}
}
else
{
snxt = s ;
}
}
}
}
}
}
// Theta segment intersection
if ( segTheta )
{
// Intersection with theta surfaces
// Known failure cases:
// o Inside tolerance of stheta surface, skim
// ~parallel to cone and Hit & enter etheta surface [& visa versa]
//
// To solve: Check 2nd root of etheta surface in addition to stheta
//
// o start/end theta is exactly pi/2
// Intersections with cones
//
// Cone equation: x^2+y^2=z^2tan^2(t)
//
// => (px+svx)^2+(py+svy)^2=(pz+svz)^2tan^2(t)
//
// => (px^2+py^2-pz^2tan^2(t))+2s(pxvx+pyvy-pzvztan^2(t))
// + s^2(vx^2+vy^2-vz^2tan^2(t)) = 0
//
// => s^2(1-vz^2(1+tan^2(t))+2s(pdotv2d-pzvztan^2(t))+(rho2-pz^2tan^2(t))=0
tanSTheta = std::tan(fSTheta) ;
tanSTheta2 = tanSTheta*tanSTheta ;
tanETheta = std::tan(fSTheta+fDTheta) ;
tanETheta2 = tanETheta*tanETheta ;
if (fSTheta)
{
dist2STheta = rho2 - p.z()*p.z()*tanSTheta2 ;
}
else
{
dist2STheta = kInfinity ;
}
if ( fSTheta + fDTheta < pi )
{
dist2ETheta=rho2-p.z()*p.z()*tanETheta2;
}
else
{
dist2ETheta=kInfinity;
}
if ( pTheta < tolSTheta) // dist2STheta<-kRadTolerance*0.5 && dist2ETheta>0)
{
// Inside (theta<stheta-tol) s theta cone
// First root of stheta cone, second if first root -ve
t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
b = t2/t1 ;
c = dist2STheta/t1 ;
d2 = b*b - c ;
if ( d2 >= 0 )
{
d = std::sqrt(d2) ;
s = -b - d ; // First root
zi = p.z() + s*v.z();
if ( s < 0 || zi*(fSTheta - halfpi) > 0 )
{
s = -b+d; // Second root
}
if (s >= 0 && s < snxt)
{
xi = p.x() + s*v.x();
yi = p.y() + s*v.y();
zi = p.z() + s*v.z();
rhoi2 = xi*xi + yi*yi;
radi2 = rhoi2 + zi*zi;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta - halfpi) <= 0) )
{
if ( segPhi && rhoi2 ) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi >= cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
// Possible intersection with ETheta cone.
// Second >= 0 root should be considered
if ( fSTheta + fDTheta < pi )
{
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
b = t2/t1 ;
c = dist2ETheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d ; // Second root
if (s >= 0 && s < snxt)
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta + fDTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi >= cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
}
}
else if ( pTheta > tolETheta )
{
// dist2ETheta<-kRadTolerance*0.5 && dist2STheta>0)
// Inside (theta > etheta+tol) e-theta cone
// First root of etheta cone, second if first root `imaginary'
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
b = t2/t1 ;
c = dist2ETheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b - d ; // First root
zi = p.z() + s*v.z();
if (s < 0 || zi*(fSTheta + fDTheta - halfpi) > 0)
{
s = -b + d ; // second root
}
if (s >= 0 && s < snxt)
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta + fDTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi >= cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
// Possible intersection with STheta cone.
// Second >= 0 root should be considered
if ( fSTheta )
{
t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
b = t2/t1 ;
c = dist2STheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d ; // Second root
if ( (s >= 0) && (s < snxt) )
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi >= cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
}
}
else if ( (pTheta <tolSTheta + kAngTolerance)
&& (fSTheta > kAngTolerance) )
{
// In tolerance of stheta
// If entering through solid [r,phi] => 0 to in
// else try 2nd root
t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
if ( (t2>=0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta<pi*.5)
|| (t2<0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta>pi*.5)
|| (v.z()<0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta==pi*.5) )
{
if (segPhi && rho2) // Check phi intersection
{
cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
if (cosPsi >= cosHDPhiIT)
{
return 0 ;
}
}
else
{
return 0 ;
}
}
// Not entering immediately/travelling through
t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
b = t2/t1 ;
c = dist2STheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d ;
if ( (s >= kCarTolerance*0.5) && (s < snxt) && (fSTheta < pi*0.5) )
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta - halfpi) <= 0) )
{
if ( segPhi && rhoi2 ) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if ( cosPsi >= cosHDPhiOT )
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
}
else if ( (pTheta > tolETheta - kAngTolerance)
&& ((fSTheta + fDTheta) < pi-kAngTolerance) )
{
// In tolerance of etheta
// If entering through solid [r,phi] => 0 to in
// else try 2nd root
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
if (
(t2<0 && (fSTheta+fDTheta) <pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
|| (t2>=0 && (fSTheta+fDTheta) >pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
|| (v.z()>0 && (fSTheta+fDTheta)==pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
)
{
if (segPhi && rho2) // Check phi intersection
{
cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
if (cosPsi >= cosHDPhiIT)
{
return 0 ;
}
}
else
{
return 0 ;
}
}
// Not entering immediately/travelling through
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
b = t2/t1 ;
c = dist2ETheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d ;
if ( (s >= kCarTolerance*0.5)
&& (s < snxt) && ((fSTheta + fDTheta) > pi*0.5) )
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta + fDTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi>=cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
}
else
{
// stheta+tol<theta<etheta-tol
// For BOTH stheta & etheta check 2nd root for validity [r,phi]
t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
b = t2/t1;
c = dist2STheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d ; // second root
if (s >= 0 && s < snxt)
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if (cosPsi >= cosHDPhiOT)
{
snxt = s ;
}
}
else
{
snxt = s ;
}
}
}
}
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
b = t2/t1 ;
c = dist2ETheta/t1 ;
d2 = b*b - c ;
if (d2 >= 0)
{
d = std::sqrt(d2) ;
s = -b + d; // second root
if (s >= 0 && s < snxt)
{
xi = p.x() + s*v.x() ;
yi = p.y() + s*v.y() ;
zi = p.z() + s*v.z() ;
rhoi2 = xi*xi + yi*yi ;
radi2 = rhoi2 + zi*zi ;
if ( (radi2 <= tolORMax2)
&& (radi2 >= tolORMin2)
&& (zi*(fSTheta + fDTheta - halfpi) <= 0) )
{
if (segPhi && rhoi2) // Check phi intersection
{
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
if ( cosPsi >= cosHDPhiOT )
{
snxt=s;
}
}
else
{
snxt = s ;
}
}
}
}
}
}
return snxt;
}
//////////////////////////////////////////////////////////////////////
//
// Calculate distance (<= actual) to closest surface of shape from outside
// - Calculate distance to radial planes
// - Only to phi planes if outside phi extent
// - Only to theta planes if outside theta extent
// - Return 0 if point inside
G4double G4Sphere::DistanceToIn( const G4ThreeVector& p ) const
{
G4double safe=0.0,safeRMin,safeRMax,safePhi,safeTheta;
G4double rho2,rad,rho;
G4double phiC,cosPhiC,sinPhiC,cosPsi,ePhi;
G4double pTheta,dTheta1,dTheta2;
rho2=p.x()*p.x()+p.y()*p.y();
rad=std::sqrt(rho2+p.z()*p.z());
rho=std::sqrt(rho2);
//
// Distance to r shells
//
if (fRmin)
{
safeRMin=fRmin-rad;
safeRMax=rad-fRmax;
if (safeRMin>safeRMax)
{
safe=safeRMin;
}
else
{
safe=safeRMax;
}
}
else
{
safe=rad-fRmax;
}
//
// Distance to phi extent
//
if (fDPhi<twopi&&rho)
{
phiC=fSPhi+fDPhi*0.5;
cosPhiC=std::cos(phiC);
sinPhiC=std::sin(phiC);
// Psi=angle from central phi to point
//
cosPsi=(p.x()*cosPhiC+p.y()*sinPhiC)/rho;
if (cosPsi<std::cos(fDPhi*0.5))
{
// Point lies outside phi range
//
if ((p.y()*cosPhiC-p.x()*sinPhiC)<=0)
{
safePhi=std::fabs(p.x()*std::sin(fSPhi)-p.y()*std::cos(fSPhi));
}
else
{
ePhi=fSPhi+fDPhi;
safePhi=std::fabs(p.x()*std::sin(ePhi)-p.y()*std::cos(ePhi));
}
if (safePhi>safe) safe=safePhi;
}
}
//
// Distance to Theta extent
//
if ((rad!=0.0) && (fDTheta<pi))
{
pTheta=std::acos(p.z()/rad);
if (pTheta<0) pTheta+=pi;
dTheta1=fSTheta-pTheta;
dTheta2=pTheta-(fSTheta+fDTheta);
if (dTheta1>dTheta2)
{
if (dTheta1>=0) // WHY ???????????
{
safeTheta=rad*std::sin(dTheta1);
if (safe<=safeTheta)
{
safe=safeTheta;
}
}
}
else
{
if (dTheta2>=0)
{
safeTheta=rad*std::sin(dTheta2);
if (safe<=safeTheta)
{
safe=safeTheta;
}
}
}
}
if (safe<0) safe=0;
return safe;
}
/////////////////////////////////////////////////////////////////////
//
// Calculate distance to surface of shape from `inside', allowing for tolerance
// - Only Calc rmax intersection if no valid rmin intersection
G4double G4Sphere::DistanceToOut( const G4ThreeVector& p,
const G4ThreeVector& v,
const G4bool calcNorm,
G4bool *validNorm,
G4ThreeVector *n ) const
{
G4double snxt = kInfinity; // snxt is default return value
G4double sphi= kInfinity,stheta= kInfinity;
ESide side=kNull,sidephi=kNull,sidetheta=kNull;
G4double t1,t2;
G4double b,c,d;
// Variables for phi intersection:
G4double sinSPhi,cosSPhi,ePhi,sinEPhi,cosEPhi;
G4double cPhi,sinCPhi,cosCPhi;
G4double pDistS,compS,pDistE,compE,sphi2,vphi;
G4double rho2,rad2,pDotV2d,pDotV3d,pTheta;
G4double tolSTheta=0.,tolETheta=0.;
G4double xi,yi,zi; // Intersection point
// G4double Comp; // Phi intersection
G4bool segPhi; // Phi flag and precalcs
G4double hDPhi,hDPhiOT,hDPhiIT;
G4double cosHDPhiOT,cosHDPhiIT;
G4bool segTheta; // Theta flag and precals
G4double tanSTheta=0.,tanETheta=0., rhoSecTheta;
G4double tanSTheta2=0.,tanETheta2=0.;
G4double dist2STheta, dist2ETheta, distTheta;
G4double d2,s;
// General Precalcs
rho2 = p.x()*p.x()+p.y()*p.y();
rad2 = rho2+p.z()*p.z();
// G4double rad=std::sqrt(rad2);
pTheta = std::atan2(std::sqrt(rho2),p.z());
pDotV2d = p.x()*v.x()+p.y()*v.y();
pDotV3d = pDotV2d+p.z()*v.z();
// Set phi divided flag and precalcs
if( fDPhi < twopi )
{
segPhi=true;
hDPhi=0.5*fDPhi; // half delta phi
cPhi=fSPhi+hDPhi;;
hDPhiOT=hDPhi+0.5*kAngTolerance; // Outer Tolerant half delta phi
hDPhiIT=hDPhi-0.5*kAngTolerance;
sinCPhi=std::sin(cPhi);
cosCPhi=std::cos(cPhi);
cosHDPhiOT=std::cos(hDPhiOT);
cosHDPhiIT=std::cos(hDPhiIT);
}
else
{
segPhi=false;
}
// Theta precalcs
if ( fDTheta < pi )
{
segTheta = true;
tolSTheta = fSTheta - kAngTolerance*0.5;
tolETheta = fSTheta + fDTheta + kAngTolerance*0.5;
}
else segTheta = false;
// Radial Intersections from G4Sphere::DistanceToIn
//
// Outer spherical shell intersection
// - Only if outside tolerant fRmax
// - Check for if inside and outer G4Sphere heading through solid (-> 0)
// - No intersect -> no intersection with G4Sphere
//
// 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 fractionTolerance = 1.0e-12;
const G4double flexRadMaxTolerance = // kRadTolerance;
std::max(kRadTolerance, fEpsilon * fRmax);
const G4double Rmax_plus = fRmax + flexRadMaxTolerance*0.5;
const G4double flexRadMinTolerance = std::max(kRadTolerance,
fEpsilon * fRmin);
const G4double Rmin_minus= (fRmin > 0) ? fRmin-flexRadMinTolerance*0.5 : 0 ;
if(rad2 <= Rmax_plus*Rmax_plus && rad2 >= Rmin_minus*Rmin_minus)
// if(rad <= Rmax_plus && rad >= Rmin_minus)
{
c = rad2 - fRmax*fRmax;
if (c < flexRadMaxTolerance*fRmax)
{
// Within tolerant Outer radius
//
// The test is
// rad - fRmax < 0.5*kRadTolerance
// => 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 >- flexRadMaxTolerance*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 ;
}
}
// Inner spherical shell intersection:
// Always first >=0 root, because would have passed
// from outside of Rmin surface .
if (fRmin)
{
c = rad2 - fRmin*fRmin;
d2 = pDotV3d*pDotV3d - c;
if ( c >- flexRadMinTolerance*fRmin ) // 2.0 * (0.5*kRadTolerance) * fRmin
{
if( c < flexRadMinTolerance*fRmin &&
d2 >= flexRadMinTolerance*fRmin && pDotV3d < 0 ) // leaving from Rmin
{
if(calcNorm) *validNorm = false ; // Rmin surface is concave
return snxt = 0 ;
}
else
{
if ( d2 >= 0. )
{
s = -pDotV3d-std::sqrt(d2);
if ( s >= 0. ) // Always intersect Rmin first
{
snxt = s ;
side = kRMin ;
}
}
}
}
}
}
// Theta segment intersection
if (segTheta)
{
// Intersection with theta surfaces
//
// Known failure cases:
// o Inside tolerance of stheta surface, skim
// ~parallel to cone and Hit & enter etheta surface [& visa versa]
//
// To solve: Check 2nd root of etheta surface in addition to stheta
//
// o start/end theta is exactly pi/2
//
// Intersections with cones
//
// Cone equation: x^2+y^2=z^2tan^2(t)
//
// => (px+svx)^2+(py+svy)^2=(pz+svz)^2tan^2(t)
//
// => (px^2+py^2-pz^2tan^2(t))+2s(pxvx+pyvy-pzvztan^2(t))
// + s^2(vx^2+vy^2-vz^2tan^2(t)) = 0
//
// => s^2(1-vz^2(1+tan^2(t))+2s(pdotv2d-pzvztan^2(t))+(rho2-pz^2tan^2(t))=0
//
/* ////////////////////////////////////////////////////////
tanSTheta=std::tan(fSTheta);
tanSTheta2=tanSTheta*tanSTheta;
tanETheta=std::tan(fSTheta+fDTheta);
tanETheta2=tanETheta*tanETheta;
if (fSTheta)
{
dist2STheta=rho2-p.z()*p.z()*tanSTheta2;
}
else
{
dist2STheta = kInfinity;
}
if (fSTheta + fDTheta < pi)
{
dist2ETheta = rho2-p.z()*p.z()*tanETheta2;
}
else
{
dist2ETheta = kInfinity ;
}
if (pTheta > tolSTheta && pTheta < tolETheta) // Inside theta
{
// In tolerance of STheta and possible leaving out to small thetas N-
if(pTheta < tolSTheta + kAngTolerance && fSTheta > kAngTolerance)
{
t2=pDotV2d-p.z()*v.z()*tanSTheta2 ; // =(VdotN+)*rhoSecSTheta
if( fSTheta < pi*0.5 && t2 < 0)
{
if(calcNorm) *validNorm = false ;
return snxt = 0 ;
}
else if(fSTheta > pi*0.5 && t2 >= 0)
{
if(calcNorm)
{
rhoSecTheta = std::sqrt(rho2*(1+tanSTheta2)) ;
*validNorm = true ;
*n = G4ThreeVector(-p.x()/rhoSecTheta, // N-
-p.y()/rhoSecTheta,
tanSTheta/std::sqrt(1+tanSTheta2) ) ;
}
return snxt = 0 ;
}
else if( fSTheta == pi*0.5 && v.z() > 0)
{
if(calcNorm)
{
*validNorm = true ;
*n = G4ThreeVector(0,0,1) ;
}
return snxt = 0 ;
}
}
// In tolerance of ETheta and possible leaving out to larger thetas N+
if ( (pTheta > tolETheta - kAngTolerance)
&& (( fSTheta + fDTheta) < pi - kAngTolerance) )
{
t2=pDotV2d-p.z()*v.z()*tanETheta2 ;
if((fSTheta+fDTheta)>pi*0.5 && t2<0)
{
if(calcNorm) *validNorm = false ;
return snxt = 0 ;
}
else if( (fSTheta+fDTheta) < pi*0.5 && t2 >= 0 )
{
if(calcNorm)
{
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2)) ;
*validNorm = true ;
*n = G4ThreeVector( p.x()/rhoSecTheta, // N+
p.y()/rhoSecTheta,
-tanETheta/std::sqrt(1+tanETheta2) ) ;
}
return snxt = 0 ;
}
else if( ( fSTheta+fDTheta) == pi*0.5 && v.z() < 0 )
{
if(calcNorm)
{
*validNorm = true ;
*n = G4ThreeVector(0,0,-1) ;
}
return snxt = 0 ;
}
}
if( fSTheta > 0 )
{
// First root of fSTheta cone, second if first root -ve
t1 = 1-v.z()*v.z()*(1+tanSTheta2);
t2 = pDotV2d-p.z()*v.z()*tanSTheta2;
b = t2/t1;
c = dist2STheta/t1;
d2 = b*b - c ;
if ( d2 >= 0 )
{
d = std::sqrt(d2) ;
s = -b - d ; // First root
if ( s < 0 )
{
s = -b + d ; // Second root
}
if (s > flexRadMaxTolerance*0.5 ) // && s<sr)
{
// check against double cone solution
zi=p.z()+s*v.z();
if (fSTheta<pi*0.5 && zi<0)
{
s = kInfinity ; // wrong cone
}
if (fSTheta>pi*0.5 && zi>0)
{
s = kInfinity ; // wrong cone
}
stheta = s ;
sidetheta = kSTheta ;
}
}
}
// Possible intersection with ETheta cone
if (fSTheta + fDTheta < pi)
{
t1 = 1-v.z()*v.z()*(1+tanETheta2);
t2 = pDotV2d-p.z()*v.z()*tanETheta2;
b = t2/t1;
c = dist2ETheta/t1;
d2 = b*b-c ;
if ( d2 >= 0 )
{
d = std::sqrt(d2);
s = -b - d ; // First root
if ( s < 0 )
{
s=-b+d; // Second root
}
if (s > flexRadMaxTolerance*0.5 && s < stheta )
{
// check against double cone solution
zi=p.z()+s*v.z();
if (fSTheta+fDTheta<pi*0.5 && zi<0)
{
s = kInfinity ; // wrong cone
}
if (fSTheta+fDTheta>pi*0.5 && zi>0)
{
s = kInfinity ; // wrong cone
}
}
if (s < stheta)
{
stheta = s ;
sidetheta = kETheta ;
}
}
}
}
*/ ////////////////////////////////////////////////////////////
if(fSTheta) // intersection with first cons
{
tanSTheta = std::tan(fSTheta);
if( std::fabs(tanSTheta) > 5./kAngTolerance ) // kons is plane z=0
{
if( v.z() > 0. )
{
if ( std::fabs( p.z() ) <= flexRadMaxTolerance*0.5 )
{
if(calcNorm)
{
*validNorm = true;
*n = G4ThreeVector(0.,0.,1.);
}
return snxt = 0 ;
}
// s = -p.z()/v.z();
stheta = -p.z()/v.z();
sidetheta = kSTheta;
}
}
else // kons is not plane
{
tanSTheta2 = tanSTheta*tanSTheta;
t1 = 1-v.z()*v.z()*(1+tanSTheta2);
t2 = pDotV2d-p.z()*v.z()*tanSTheta2; // ~vDotN if p on cons
dist2STheta = rho2-p.z()*p.z()*tanSTheta2; // t3
// distTheta = std::sqrt(std::fabs(dist2STheta/(1+tanSTheta2)));
distTheta = std::sqrt(rho2)-p.z()*tanSTheta;
if( std::fabs(t1) < 0.5*kAngTolerance ) // 1st order equation, v parallel to kons
{
if( v.z() > 0. )
{
if(std::fabs(distTheta) < flexRadMaxTolerance*0.5) // p on surface
{
if( fSTheta < halfpi && p.z() > 0. )
{
if( calcNorm ) *validNorm = false;
return snxt = 0.;
}
else if( fSTheta > halfpi && p.z() <= 0)
{
if( calcNorm )
{
*validNorm = true;
if (rho2)
{
rhoSecTheta = std::sqrt(rho2*(1+tanSTheta2));
*n = G4ThreeVector( p.x()/rhoSecTheta,
p.y()/rhoSecTheta,
std::sin(fSTheta) );
}
else *n = G4ThreeVector(0.,0.,1.);
}
return snxt = 0.;
}
}
// s = -0.5*dist2STheta/t2;
stheta = -0.5*dist2STheta/t2;
sidetheta = kSTheta;
}
}
else // 2nd order equation, 1st root of fSTheta cone, 2nd if 1st root -ve
{
if( std::fabs(distTheta) < flexRadMaxTolerance*0.5) // && t2 >= 0.) surface
{
if( fSTheta > halfpi && t2 >= 0. ) // leave
{
if( calcNorm )
{
*validNorm = true;
if (rho2)
{
rhoSecTheta = std::sqrt(rho2*(1+tanSTheta2));
*n = G4ThreeVector( p.x()/rhoSecTheta,
p.y()/rhoSecTheta,
std::sin(fSTheta) );
}
else *n = G4ThreeVector(0.,0.,1.);
}
return snxt = 0.;
}
else if( fSTheta < halfpi && t2 < 0. && p.z() >=0. ) // leave
{
if( calcNorm ) *validNorm = false;
return snxt = 0.;
}
}
b = t2/t1;
c = dist2STheta/t1;
d2 = b*b - c ;
if ( d2 >= 0. )
{
d = std::sqrt(d2);
if( fSTheta > halfpi )
{
s = -b - d; // First root
if( (std::fabs(s) < flexRadMaxTolerance*0.5 && t2 < 0.) ||
s < 0. ||
( s > 0. && p.z() + s*v.z() > 0.) )
{
s = -b + d ; // 2nd root
}
if( s > flexRadMaxTolerance*0.5 && p.z() + s*v.z() <= 0.)
{
stheta = s;
sidetheta = kSTheta;
}
}
else // sTheta < pi/2, concave surface, no normal
{
s = -b - d; // First root
if( (std::fabs(s) < flexRadMaxTolerance*0.5 && t2 >= 0.) ||
s < 0. ||
( s > 0. && p.z() + s*v.z() < 0.) )
{
s = -b + d ; // 2nd root
}
if( s > flexRadMaxTolerance*0.5 && p.z() + s*v.z() >= 0.)
{
stheta = s;
sidetheta = kSTheta;
}
}
}
}
}
}
if (fSTheta + fDTheta < pi) // intersection with second cons
{
tanETheta = std::tan(fSTheta+fDTheta);
if( std::fabs(tanETheta) > 5./kAngTolerance ) // kons is plane z=0
{
if( v.z() < 0. )
{
if ( std::fabs( p.z() ) <= flexRadMaxTolerance*0.5 )
{
if(calcNorm)
{
*validNorm = true;
*n = G4ThreeVector(0.,0.,-1.);
}
return snxt = 0 ;
}
s = -p.z()/v.z();
if( s < stheta)
{
stheta = s;
sidetheta = kETheta;
}
}
}
else // kons is not plane
{
tanETheta2 = tanETheta*tanETheta;
t1 = 1-v.z()*v.z()*(1+tanETheta2);
t2 = pDotV2d-p.z()*v.z()*tanETheta2; // ~vDotN if p on cons
dist2ETheta = rho2-p.z()*p.z()*tanETheta2; // t3
// distTheta = std::sqrt(std::fabs(dist2ETheta/(1+tanETheta2)));
distTheta = std::sqrt(rho2)-p.z()*tanETheta;
if( std::fabs(t1) < 0.5*kAngTolerance ) // 1st order equation, v parallel to kons
{
if( v.z() < 0. )
{
if(std::fabs(distTheta) < flexRadMaxTolerance*0.5) // p on surface
{
if( fSTheta+fDTheta > halfpi && p.z() < 0. )
{
if( calcNorm ) *validNorm = false;
return snxt = 0.;
}
else if( fSTheta+fDTheta < halfpi && p.z() >= 0)
{
if( calcNorm )
{
*validNorm = true;
if (rho2)
{
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2));
*n = G4ThreeVector( p.x()/rhoSecTheta,
p.y()/rhoSecTheta,
-std::sin(fSTheta+fDTheta) );
}
else *n = G4ThreeVector(0.,0.,-1.);
}
return snxt = 0.;
}
}
s = -0.5*dist2ETheta/t2;
if( s < stheta)
{
stheta = s;
sidetheta = kETheta;
}
}
}
else // 2nd order equation, 1st root of fSTheta cone, 2nd if 1st root -ve
{
if( std::fabs(distTheta) < flexRadMaxTolerance*0.5) // && t2 >= 0.) surface
{
if( fSTheta+fDTheta < halfpi && t2 >= 0. ) // leave
{
if( calcNorm )
{
*validNorm = true;
if (rho2)
{
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2));
*n = G4ThreeVector( p.x()/rhoSecTheta,
p.y()/rhoSecTheta,
-std::sin(fSTheta+fDTheta) );
}
else *n = G4ThreeVector(0.,0.,-1.);
}
return snxt = 0.;
}
else if( fSTheta+fDTheta > halfpi && t2 < 0. && p.z() <=0. ) // leave
{
if( calcNorm ) *validNorm = false;
return snxt = 0.;
}
}
b = t2/t1;
c = dist2ETheta/t1;
d2 = b*b - c ;
if ( d2 >= 0. )
{
d = std::sqrt(d2);
if( fSTheta+fDTheta < halfpi )
{
s = -b - d; // First root
if( (std::fabs(s) < flexRadMaxTolerance*0.5 && t2 < 0.) ||
s < 0. )
{
s = -b + d ; // 2nd root
}
if( s > flexRadMaxTolerance*0.5 )
{
if( s < stheta )
{
stheta = s;
sidetheta = kETheta;
}
}
}
else // sTheta+fDTheta > pi/2, concave surface, no normal
{
s = -b - d; // First root
if( (std::fabs(s) < flexRadMaxTolerance*0.5 && t2 >= 0.) ||
s < 0. ||
( s > 0. && p.z() + s*v.z() > 0.) )
{
s = -b + d ; // 2nd root
}
if( s > flexRadMaxTolerance*0.5 && p.z() + s*v.z() <= 0.)
{
if( s < stheta )
{
stheta = s;
sidetheta = kETheta;
}
}
}
}
}
}
}
} // end theta intersections
// Phi Intersection
if ( fDPhi < twopi)
{
sinSPhi=std::sin(fSPhi);
cosSPhi=std::cos(fSPhi);
ePhi=fSPhi+fDPhi;
sinEPhi=std::sin(ePhi);
cosEPhi=std::cos(ePhi);
cPhi=fSPhi+fDPhi*0.5;
sinCPhi=std::sin(cPhi);
cosCPhi=std::cos(cPhi);
if ( p.x()||p.y() ) // Check if on z axis (rho not needed later)
{
// pDist -ve when inside
pDistS=p.x()*sinSPhi-p.y()*cosSPhi;
pDistE=-p.x()*sinEPhi+p.y()*cosEPhi;
// Comp -ve when in direction of outwards normal
compS = -sinSPhi*v.x()+cosSPhi*v.y() ;
compE = sinEPhi*v.x()-cosEPhi*v.y() ;
sidephi = kNull ;
if ( pDistS <= 0 && pDistE <= 0 )
{
// Inside both phi *full* planes
if ( compS < 0 )
{
sphi = pDistS/compS ;
xi = p.x()+sphi*v.x() ;
yi = p.y()+sphi*v.y() ;
// Check intersecting with correct half-plane
// (if not -> no intersect)
if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
{
sphi=kInfinity;
}
else
{
sidephi = kSPhi ;
if ( pDistS > -0.5*kCarTolerance) sphi =0 ; // Leave by sphi
}
}
else sphi = kInfinity ;
if ( compE < 0 )
{
sphi2=pDistE/compE ;
if (sphi2 < sphi) // Only check further if < starting phi intersection
{
xi = p.x()+sphi2*v.x() ;
yi = p.y()+sphi2*v.y() ;
// Check intersecting with correct half-plane
if ((yi*cosCPhi-xi*sinCPhi)>=0) // Leaving via ending phi
{
sidephi = kEPhi ;
if ( pDistE <= -0.5*kCarTolerance )
{
sphi=sphi2;
}
else
{
sphi = 0 ;
}
}
}
}
}
else if ( pDistS >= 0 && pDistE >= 0 ) // Outside both *full* phi planes
{
if ( pDistS <= pDistE )
{
sidephi = kSPhi ;
}
else
{
sidephi = kEPhi ;
}
if ( fDPhi > pi )
{
if ( compS < 0 && compE < 0 ) sphi = 0 ;
else sphi = kInfinity ;
}
else
{
// if towards both >=0 then once inside (after error)
// will remain inside
if ( compS >= 0 && compE >= 0 )
{
sphi=kInfinity;
}
else
{
sphi=0;
}
}
}
else if ( pDistS > 0 && pDistE < 0 )
{
// Outside full starting plane, inside full ending plane
if ( fDPhi > pi )
{
if ( compE < 0 )
{
sphi = pDistE/compE ;
xi = p.x() + sphi*v.x() ;
yi = p.y() + sphi*v.y() ;
// Check intersection in correct half-plane
// (if not -> not leaving phi extent)
//
if ( ( yi*cosCPhi - xi*sinCPhi ) <= 0 )
{
sphi = kInfinity ;
}
else // Leaving via Ending phi
{
sidephi = kEPhi ;
if ( pDistE > -0.5*kCarTolerance ) sphi = 0. ;
}
}
else
{
sphi = kInfinity ;
}
}
else
{
if ( compS >= 0 )
{
if ( compE < 0 )
{
sphi = pDistE/compE ;
xi = p.x() + sphi*v.x() ;
yi = p.y() + sphi*v.y() ;
// Check intersection in correct half-plane
// (if not -> remain in extent)
//
if ( ( yi*cosCPhi - xi*sinCPhi) <= 0 )
{
sphi=kInfinity;
}
else // otherwise leaving via Ending phi
{
sidephi = kEPhi ;
}
}
else sphi=kInfinity;
}
else // leaving immediately by starting phi
{
sidephi = kSPhi ;
sphi = 0 ;
}
}
}
else
{
// Must be pDistS < 0 && pDistE > 0
// Inside full starting plane, outside full ending plane
if ( fDPhi > pi )
{
if ( compS < 0 )
{
sphi=pDistS/compS;
xi=p.x()+sphi*v.x();
yi=p.y()+sphi*v.y();
// Check intersection in correct half-plane
// (if not -> not leaving phi extent)
//
if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
{
sphi = kInfinity ;
}
else // Leaving via Starting phi
{
sidephi = kSPhi ;
if ( pDistS > -0.5*kCarTolerance ) sphi = 0 ;
}
}
else
{
sphi = kInfinity ;
}
}
else
{
if ( compE >= 0 )
{
if ( compS < 0 )
{
sphi = pDistS/compS ;
xi = p.x()+sphi*v.x() ;
yi = p.y()+sphi*v.y() ;
// Check intersection in correct half-plane
// (if not -> remain in extent)
//
if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
{
sphi = kInfinity ;
}
else // otherwise leaving via Starting phi
{
sidephi = kSPhi ;
}
}
else
{
sphi = kInfinity ;
}
}
else // leaving immediately by ending
{
sidephi = kEPhi ;
sphi = 0 ;
}
}
}
}
else
{
// On z axis + travel not || to z axis -> if phi of vector direction
// within phi of shape, Step limited by rmax, else Step =0
if ( v.x() || v.y() )
{
vphi = std::atan2(v.y(),v.x()) ;
if ( fSPhi < vphi && vphi < fSPhi + fDPhi )
{
sphi=kInfinity;
}
else
{
sidephi = kSPhi ; // arbitrary
sphi = 0 ;
}
}
else // travel along z - no phi intersaction
{
sphi = kInfinity ;
}
}
if ( sphi < snxt ) // Order intersecttions
{
snxt = sphi ;
side = sidephi ;
}
}
if (stheta < snxt ) // Order intersections
{
snxt = stheta ;
side = sidetheta ;
}
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;
case kRMin:
*validNorm=false; // Rmin is concave
break;
case kSPhi:
if ( fDPhi <= pi ) // Normal to Phi-
{
*n=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
*validNorm=true;
}
else *validNorm=false;
break ;
case kEPhi:
if ( fDPhi <= pi ) // Normal to Phi+
{
*n=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
*validNorm=true;
}
else *validNorm=false;
break;
case kSTheta:
if( fSTheta == halfpi )
{
*n=G4ThreeVector(0.,0.,1.);
*validNorm=true;
}
else if ( fSTheta > halfpi )
{
xi = p.x() + snxt*v.x();
yi = p.y() + snxt*v.y();
rhoSecTheta = std::sqrt((xi*xi+yi*yi)*(1+tanSTheta2));
*n = G4ThreeVector( xi/rhoSecTheta, // N-
yi/rhoSecTheta,
-tanSTheta/std::sqrt(1+tanSTheta2));
*validNorm=true;
}
else *validNorm=false; // Concave STheta cone
break;
case kETheta:
if( ( fSTheta + fDTheta ) == halfpi )
{
*n = G4ThreeVector(0.,0.,-1.);
*validNorm = true;
}
else if ( ( fSTheta + fDTheta ) < halfpi)
{
xi=p.x()+snxt*v.x();
yi=p.y()+snxt*v.y();
rhoSecTheta = std::sqrt((xi*xi+yi*yi)*(1+tanETheta2));
*n = G4ThreeVector( xi/rhoSecTheta, // N+
yi/rhoSecTheta,
-tanETheta/std::sqrt(1+tanETheta2) );
*validNorm=true;
}
else *validNorm=false; // Concave ETheta cone
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("G4Sphere::DistanceToOut(p,v,..)",
"Notification", JustWarning,
"Undefined side for valid surface normal to solid.");
break;
}
}
if (snxt == kInfinity)
{
G4cout.precision(24);
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("G4Sphere::DistanceToOut(p,v,..)",
"Notification", JustWarning,
"Logic error: snxt = kInfinity ???");
}
return snxt;
}
/////////////////////////////////////////////////////////////////////////
//
// Calcluate distance (<=actual) to closest surface of shape from inside
G4double G4Sphere::DistanceToOut( const G4ThreeVector& p ) const
{
G4double safe=0.0,safeRMin,safeRMax,safePhi,safeTheta;
G4double rho2,rad,rho;
G4double phiC,cosPhiC,sinPhiC,ePhi;
G4double pTheta,dTheta1,dTheta2;
rho2=p.x()*p.x()+p.y()*p.y();
rad=std::sqrt(rho2+p.z()*p.z());
rho=std::sqrt(rho2);
#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("G4Sphere::DistanceToOut(p)",
"Notification", JustWarning, "Point p is outside !?" );
}
#endif
//
// Distance to r shells
//
if (fRmin)
{
safeRMin=rad-fRmin;
safeRMax=fRmax-rad;
if (safeRMin<safeRMax)
{
safe=safeRMin;
}
else
{
safe=safeRMax;
}
}
else
{
safe=fRmax-rad;
}
//
// Distance to phi extent
//
if (fDPhi<twopi && rho)
{
phiC=fSPhi+fDPhi*0.5;
cosPhiC=std::cos(phiC);
sinPhiC=std::sin(phiC);
if ((p.y()*cosPhiC-p.x()*sinPhiC)<=0)
{
safePhi=-(p.x()*std::sin(fSPhi)-p.y()*std::cos(fSPhi));
}
else
{
ePhi=fSPhi+fDPhi;
safePhi=(p.x()*std::sin(ePhi)-p.y()*std::cos(ePhi));
}
if (safePhi<safe) safe=safePhi;
}
//
// Distance to Theta extent
//
if (rad)
{
pTheta=std::acos(p.z()/rad);
if (pTheta<0) pTheta+=pi;
dTheta1=pTheta-fSTheta;
dTheta2=(fSTheta+fDTheta)-pTheta;
if (dTheta1<dTheta2)
{
safeTheta=rad*std::sin(dTheta1);
if (safe>safeTheta)
{
safe=safeTheta;
}
}
else
{
safeTheta=rad*std::sin(dTheta2);
if (safe>safeTheta)
{
safe=safeTheta;
}
}
}
if (safe<0) safe=0;
return safe;
}
//////////////////////////////////////////////////////////////////////////
//
// Create a List containing the transformed vertices
// Ordering [0-3] -fDz cross section
// [4-7] +fDz cross section such that [0] is below [4],
// [1] below [5] etc.
// Note:
// Caller has deletion resposibility
// Potential improvement: For last slice, use actual ending angle
// to avoid rounding error problems.
G4ThreeVectorList*
G4Sphere::CreateRotatedVertices( const G4AffineTransform& pTransform,
G4int& noPolygonVertices ) const
{
G4ThreeVectorList *vertices;
G4ThreeVector vertex;
G4double meshAnglePhi,meshRMax,crossAnglePhi,
coscrossAnglePhi,sincrossAnglePhi,sAnglePhi;
G4double meshTheta,crossTheta,startTheta;
G4double rMaxX,rMaxY,rMinX,rMinY,rMinZ,rMaxZ;
G4int crossSectionPhi,noPhiCrossSections,crossSectionTheta,noThetaSections;
// Phi cross sections
noPhiCrossSections=G4int (fDPhi/kMeshAngleDefault)+1;
if (noPhiCrossSections<kMinMeshSections)
{
noPhiCrossSections=kMinMeshSections;
}
else if (noPhiCrossSections>kMaxMeshSections)
{
noPhiCrossSections=kMaxMeshSections;
}
meshAnglePhi=fDPhi/(noPhiCrossSections-1);
// If complete in phi, set start angle such that mesh will be at fRMax
// on the x axis. Will give better extent calculations when not rotated.
if (fDPhi==pi*2.0 && fSPhi==0)
{
sAnglePhi = -meshAnglePhi*0.5;
}
else
{
sAnglePhi=fSPhi;
}
// Theta cross sections
noThetaSections = G4int(fDTheta/kMeshAngleDefault)+1;
if (noThetaSections<kMinMeshSections)
{
noThetaSections=kMinMeshSections;
}
else if (noThetaSections>kMaxMeshSections)
{
noThetaSections=kMaxMeshSections;
}
meshTheta=fDTheta/(noThetaSections-1);
// If complete in Theta, set start angle such that mesh will be at fRMax
// on the z axis. Will give better extent calculations when not rotated.
if (fDTheta==pi && fSTheta==0)
{
startTheta = -meshTheta*0.5;
}
else
{
startTheta=fSTheta;
}
meshRMax = (meshAnglePhi >= meshTheta) ?
fRmax/std::cos(meshAnglePhi*0.5) : fRmax/std::cos(meshTheta*0.5);
G4double* cosCrossTheta = new G4double[noThetaSections];
G4double* sinCrossTheta = new G4double[noThetaSections];
vertices=new G4ThreeVectorList();
vertices->reserve(noPhiCrossSections*(noThetaSections*2));
if (vertices && cosCrossTheta && sinCrossTheta)
{
for (crossSectionPhi=0;
crossSectionPhi<noPhiCrossSections; crossSectionPhi++)
{
crossAnglePhi=sAnglePhi+crossSectionPhi*meshAnglePhi;
coscrossAnglePhi=std::cos(crossAnglePhi);
sincrossAnglePhi=std::sin(crossAnglePhi);
for (crossSectionTheta=0;
crossSectionTheta<noThetaSections;crossSectionTheta++)
{
// Compute coordinates of cross section at section crossSectionPhi
//
crossTheta=startTheta+crossSectionTheta*meshTheta;
cosCrossTheta[crossSectionTheta]=std::cos(crossTheta);
sinCrossTheta[crossSectionTheta]=std::sin(crossTheta);
rMinX=fRmin*sinCrossTheta[crossSectionTheta]*coscrossAnglePhi;
rMinY=fRmin*sinCrossTheta[crossSectionTheta]*sincrossAnglePhi;
rMinZ=fRmin*cosCrossTheta[crossSectionTheta];
vertex=G4ThreeVector(rMinX,rMinY,rMinZ);
vertices->push_back(pTransform.TransformPoint(vertex));
} // Theta forward
for (crossSectionTheta=noThetaSections-1;
crossSectionTheta>=0; crossSectionTheta--)
{
rMaxX=meshRMax*sinCrossTheta[crossSectionTheta]*coscrossAnglePhi;
rMaxY=meshRMax*sinCrossTheta[crossSectionTheta]*sincrossAnglePhi;
rMaxZ=meshRMax*cosCrossTheta[crossSectionTheta];
vertex=G4ThreeVector(rMaxX,rMaxY,rMaxZ);
vertices->push_back(pTransform.TransformPoint(vertex));
} // Theta back
} // Phi
noPolygonVertices = noThetaSections*2 ;
}
else
{
DumpInfo();
G4Exception("G4Sphere::CreateRotatedVertices()",
"FatalError", FatalException,
"Error in allocation of vertices. Out of memory !");
}
delete[] cosCrossTheta;
delete[] sinCrossTheta;
return vertices;
}
//////////////////////////////////////////////////////////////////////////
//
// G4EntityType
G4GeometryType G4Sphere::GetEntityType() const
{
return G4String("G4Sphere");
}
//////////////////////////////////////////////////////////////////////////
//
// Stream object contents to an output stream
std::ostream& G4Sphere::StreamInfo( std::ostream& os ) const
{
os << "-----------------------------------------------------------\n"
<< " *** Dump for solid - " << GetName() << " ***\n"
<< " ===================================================\n"
<< " Solid type: G4Sphere\n"
<< " Parameters: \n"
<< " inner radius: " << fRmin/mm << " mm \n"
<< " outer radius: " << fRmax/mm << " mm \n"
<< " starting phi of segment : " << fSPhi/degree << " degrees \n"
<< " delta phi of segment : " << fDPhi/degree << " degrees \n"
<< " starting theta of segment: " << fSTheta/degree << " degrees \n"
<< " delta theta of segment : " << fDTheta/degree << " degrees \n"
<< "-----------------------------------------------------------\n";
return os;
}
////////////////////////////////////////////////////////////////////////////////
//
// GetPointOnSurface
G4ThreeVector G4Sphere::GetPointOnSurface() const
{
G4double zRand, aOne, aTwo, aThr, aFou, aFiv, chose, phi, sinphi, cosphi;
G4double height1, height2, slant1, slant2, costheta, sintheta,theta,rRand;
height1 = (fRmax-fRmin)*std::cos(fSTheta);
height2 = (fRmax-fRmin)*std::cos(fSTheta+fDTheta);
slant1 = std::sqrt(sqr((fRmax - fRmin)*std::sin(fSTheta))
+ height1*height1);
slant2 = std::sqrt(sqr((fRmax - fRmin)*std::sin(fSTheta+fDTheta))
+ height2*height2);
rRand = RandFlat::shoot(fRmin,fRmax);
aOne = fRmax*fRmax*fDPhi*(std::cos(fSTheta)-std::cos(fSTheta+fDTheta));
aTwo = fRmin*fRmin*fDPhi*(std::cos(fSTheta)-std::cos(fSTheta+fDTheta));
aThr = fDPhi*((fRmax + fRmin)*std::sin(fSTheta))*slant1;
aFou = fDPhi*((fRmax + fRmin)*std::sin(fSTheta+fDTheta))*slant2;
aFiv = 0.5*fDTheta*(fRmax*fRmax-fRmin*fRmin);
phi = RandFlat::shoot(fSPhi, fSPhi + fDPhi);
cosphi = std::cos(phi);
sinphi = std::sin(phi);
theta = RandFlat::shoot(fSTheta,fSTheta+fDTheta);
costheta = std::cos(theta);
sintheta = std::sqrt(1.-sqr(costheta));
if( ((fSPhi==0) && (fDPhi==2.*pi)) || (fDPhi==2.*pi) ) {aFiv = 0;}
if(fSTheta == 0) {aThr=0;}
if(fDTheta + fSTheta == pi) {aFou = 0;}
if(fSTheta == 0.5*pi) {aThr = pi*(fRmax*fRmax-fRmin*fRmin);}
if(fSTheta + fDTheta == 0.5*pi) { aFou = pi*(fRmax*fRmax-fRmin*fRmin);}
chose = RandFlat::shoot(0.,aOne+aTwo+aThr+aFou+2.*aFiv);
if( (chose>=0.) && (chose<aOne) )
{
return G4ThreeVector(fRmax*sintheta*cosphi,
fRmax*sintheta*sinphi, fRmax*costheta);
}
else if( (chose>=aOne) && (chose<aOne+aTwo) )
{
return G4ThreeVector(fRmin*sintheta*cosphi,
fRmin*sintheta*sinphi, fRmin*costheta);
}
else if( (chose>=aOne+aTwo) && (chose<aOne+aTwo+aThr) )
{
if (fSTheta != 0.5*pi)
{
zRand = RandFlat::shoot(fRmin*std::cos(fSTheta),fRmax*std::cos(fSTheta));
return G4ThreeVector(std::tan(fSTheta)*zRand*cosphi,
std::tan(fSTheta)*zRand*sinphi,zRand);
}
else
{
return G4ThreeVector(rRand*cosphi, rRand*sinphi, 0.);
}
}
else if( (chose>=aOne+aTwo+aThr) && (chose<aOne+aTwo+aThr+aFou) )
{
if(fSTheta + fDTheta != 0.5*pi)
{
zRand = RandFlat::shoot(fRmin*std::cos(fSTheta+fDTheta),
fRmax*std::cos(fSTheta+fDTheta));
return G4ThreeVector (std::tan(fSTheta+fDTheta)*zRand*cosphi,
std::tan(fSTheta+fDTheta)*zRand*sinphi,zRand);
}
else
{
return G4ThreeVector(rRand*cosphi, rRand*sinphi, 0.);
}
}
else if( (chose>=aOne+aTwo+aThr+aFou) && (chose<aOne+aTwo+aThr+aFou+aFiv) )
{
return G4ThreeVector(rRand*sintheta*std::cos(fSPhi),
rRand*sintheta*std::sin(fSPhi),rRand*costheta);
}
else
{
return G4ThreeVector(rRand*sintheta*std::cos(fSPhi+fDPhi),
rRand*sintheta*std::sin(fSPhi+fDPhi),rRand*costheta);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Methods for visualisation
G4VisExtent G4Sphere::GetExtent() const
{
return G4VisExtent(-fRmax, fRmax,-fRmax, fRmax,-fRmax, fRmax );
}
void G4Sphere::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
{
scene.AddSolid (*this);
}
G4Polyhedron* G4Sphere::CreatePolyhedron () const
{
return new G4PolyhedronSphere (fRmin, fRmax, fSPhi, fDPhi, fSTheta, fDTheta);
}
G4NURBS* G4Sphere::CreateNURBS () const
{
return new G4NURBSbox (fRmax, fRmax, fRmax); // Box for now!!!
}