2921 lines
81 KiB
C++
2921 lines
81 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. *
|
|
// ********************************************************************
|
|
//
|
|
// Implementation for G4Sphere class
|
|
//
|
|
// 28.03.94 P.Kent: old C++ code converted to tolerant geometry
|
|
// 17.09.96 V.Grichine: final modifications to commit
|
|
// 30.10.03 J.Apostolakis: new algorithm in Inside for SPhi-sections
|
|
// 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
|
|
// 22.07.05 O.Link: Added check for intersection with double cone
|
|
// 26.03.09 G.Cosmo: optimisations and uniform use of local radial tolerance
|
|
// 26.10.16 E.Tcherniaev: re-implemented CalculateExtent() using
|
|
// G4BoundingEnvelope, removed CreateRotatedVertices()
|
|
// --------------------------------------------------------------------
|
|
|
|
#include "G4Sphere.hh"
|
|
|
|
#if !defined(G4GEOM_USE_USPHERE)
|
|
|
|
#include "G4GeomTools.hh"
|
|
#include "G4VoxelLimits.hh"
|
|
#include "G4AffineTransform.hh"
|
|
#include "G4GeometryTolerance.hh"
|
|
#include "G4BoundingEnvelope.hh"
|
|
|
|
#include "G4VPVParameterisation.hh"
|
|
|
|
#include "Randomize.hh"
|
|
|
|
#include "meshdefs.hh"
|
|
|
|
#include "G4VGraphicsScene.hh"
|
|
#include "G4VisExtent.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), fSPhi(0.0), fFullPhiSphere(true), fFullThetaSphere(true)
|
|
{
|
|
kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
|
|
kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
|
|
|
|
halfCarTolerance = 0.5*kCarTolerance;
|
|
halfAngTolerance = 0.5*kAngTolerance;
|
|
|
|
// Check radii and set radial tolerances
|
|
|
|
if ( (pRmin >= pRmax) || (pRmax < 1.1*kRadTolerance) || (pRmin < 0) )
|
|
{
|
|
std::ostringstream message;
|
|
message << "Invalid radii for Solid: " << GetName() << G4endl
|
|
<< " pRmin = " << pRmin << ", pRmax = " << pRmax;
|
|
G4Exception("G4Sphere::G4Sphere()", "GeomSolids0002",
|
|
FatalException, message);
|
|
}
|
|
fRmin=pRmin; fRmax=pRmax;
|
|
fRminTolerance = (fRmin) ? std::max( kRadTolerance, fEpsilon*fRmin ) : 0;
|
|
fRmaxTolerance = std::max( kRadTolerance, fEpsilon*fRmax );
|
|
|
|
// Check angles
|
|
|
|
CheckPhiAngles(pSPhi, pDPhi);
|
|
CheckThetaAngles(pSTheta, pDTheta);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Fake default constructor - sets only member data and allocates memory
|
|
// for usage restricted to object persistency.
|
|
//
|
|
G4Sphere::G4Sphere( __void__& a )
|
|
: G4CSGSolid(a), fRminTolerance(0.), fRmaxTolerance(0.),
|
|
kAngTolerance(0.), kRadTolerance(0.),
|
|
fRmin(0.), fRmax(0.), fSPhi(0.), fDPhi(0.), fSTheta(0.),
|
|
fDTheta(0.), sinCPhi(0.), cosCPhi(0.),
|
|
cosHDPhi(0.), cosHDPhiOT(0.), cosHDPhiIT(0.),
|
|
sinSPhi(0.), cosSPhi(0.), sinEPhi(0.), cosEPhi(0.), hDPhi(0.), cPhi(0.),
|
|
ePhi(0.), sinSTheta(0.), cosSTheta(0.), sinETheta(0.), cosETheta(0.),
|
|
tanSTheta(0.), tanSTheta2(0.), tanETheta(0.), tanETheta2(0.), eTheta(0.),
|
|
halfCarTolerance(0.), halfAngTolerance(0.)
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Destructor
|
|
|
|
G4Sphere::~G4Sphere()
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copy constructor
|
|
|
|
G4Sphere::G4Sphere(const G4Sphere& rhs)
|
|
: G4CSGSolid(rhs), fRminTolerance(rhs.fRminTolerance),
|
|
fRmaxTolerance(rhs.fRmaxTolerance), kAngTolerance(rhs.kAngTolerance),
|
|
kRadTolerance(rhs.kRadTolerance), fEpsilon(rhs.fEpsilon),
|
|
fRmin(rhs.fRmin), fRmax(rhs.fRmax), fSPhi(rhs.fSPhi), fDPhi(rhs.fDPhi),
|
|
fSTheta(rhs.fSTheta), fDTheta(rhs.fDTheta),
|
|
sinCPhi(rhs.sinCPhi), cosCPhi(rhs.cosCPhi), cosHDPhi(rhs.cosHDPhi),
|
|
cosHDPhiOT(rhs.cosHDPhiOT), cosHDPhiIT(rhs.cosHDPhiIT),
|
|
sinSPhi(rhs.sinSPhi), cosSPhi(rhs.cosSPhi),
|
|
sinEPhi(rhs.sinEPhi), cosEPhi(rhs.cosEPhi),
|
|
hDPhi(rhs.hDPhi), cPhi(rhs.cPhi), ePhi(rhs.ePhi),
|
|
sinSTheta(rhs.sinSTheta), cosSTheta(rhs.cosSTheta),
|
|
sinETheta(rhs.sinETheta), cosETheta(rhs.cosETheta),
|
|
tanSTheta(rhs.tanSTheta), tanSTheta2(rhs.tanSTheta2),
|
|
tanETheta(rhs.tanETheta), tanETheta2(rhs.tanETheta2), eTheta(rhs.eTheta),
|
|
fFullPhiSphere(rhs.fFullPhiSphere), fFullThetaSphere(rhs.fFullThetaSphere),
|
|
fFullSphere(rhs.fFullSphere),
|
|
halfCarTolerance(rhs.halfCarTolerance),
|
|
halfAngTolerance(rhs.halfAngTolerance)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Assignment operator
|
|
|
|
G4Sphere& G4Sphere::operator = (const G4Sphere& rhs)
|
|
{
|
|
// Check assignment to self
|
|
//
|
|
if (this == &rhs) { return *this; }
|
|
|
|
// Copy base class data
|
|
//
|
|
G4CSGSolid::operator=(rhs);
|
|
|
|
// Copy data
|
|
//
|
|
fRminTolerance = rhs.fRminTolerance; fRmaxTolerance = rhs.fRmaxTolerance;
|
|
kAngTolerance = rhs.kAngTolerance; kRadTolerance = rhs.kRadTolerance;
|
|
fEpsilon = rhs.fEpsilon; fRmin = rhs.fRmin; fRmax = rhs.fRmax;
|
|
fSPhi = rhs.fSPhi; fDPhi = rhs.fDPhi; fSTheta = rhs.fSTheta;
|
|
fDTheta = rhs.fDTheta; sinCPhi = rhs.sinCPhi; cosCPhi = rhs.cosCPhi;
|
|
cosHDPhi = rhs.cosHDPhi;
|
|
cosHDPhiOT = rhs.cosHDPhiOT; cosHDPhiIT = rhs.cosHDPhiIT;
|
|
sinSPhi = rhs.sinSPhi; cosSPhi = rhs.cosSPhi;
|
|
sinEPhi = rhs.sinEPhi; cosEPhi = rhs.cosEPhi;
|
|
hDPhi = rhs.hDPhi; cPhi = rhs.cPhi; ePhi = rhs.ePhi;
|
|
sinSTheta = rhs.sinSTheta; cosSTheta = rhs.cosSTheta;
|
|
sinETheta = rhs.sinETheta; cosETheta = rhs.cosETheta;
|
|
tanSTheta = rhs.tanSTheta; tanSTheta2 = rhs.tanSTheta2;
|
|
tanETheta = rhs.tanETheta; tanETheta2 = rhs.tanETheta2;
|
|
eTheta = rhs.eTheta; fFullPhiSphere = rhs.fFullPhiSphere;
|
|
fFullThetaSphere = rhs.fFullThetaSphere; fFullSphere = rhs.fFullSphere;
|
|
halfCarTolerance = rhs.halfCarTolerance;
|
|
halfAngTolerance = rhs.halfAngTolerance;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get bounding box
|
|
|
|
void G4Sphere::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const
|
|
{
|
|
G4double rmin = GetInnerRadius();
|
|
G4double rmax = GetOuterRadius();
|
|
|
|
// Find bounding box
|
|
//
|
|
if (GetDeltaThetaAngle() >= pi && GetDeltaPhiAngle() >= twopi)
|
|
{
|
|
pMin.set(-rmax,-rmax,-rmax);
|
|
pMax.set( rmax, rmax, rmax);
|
|
}
|
|
else
|
|
{
|
|
G4double sinStart = GetSinStartTheta();
|
|
G4double cosStart = GetCosStartTheta();
|
|
G4double sinEnd = GetSinEndTheta();
|
|
G4double cosEnd = GetCosEndTheta();
|
|
|
|
G4double stheta = GetStartThetaAngle();
|
|
G4double etheta = stheta + GetDeltaThetaAngle();
|
|
G4double rhomin = rmin*std::min(sinStart,sinEnd);
|
|
G4double rhomax = rmax;
|
|
if (stheta > halfpi) rhomax = rmax*sinStart;
|
|
if (etheta < halfpi) rhomax = rmax*sinEnd;
|
|
|
|
G4TwoVector xymin,xymax;
|
|
G4GeomTools::DiskExtent(rhomin,rhomax,
|
|
GetSinStartPhi(),GetCosStartPhi(),
|
|
GetSinEndPhi(),GetCosEndPhi(),
|
|
xymin,xymax);
|
|
|
|
G4double zmin = std::min(rmin*cosEnd,rmax*cosEnd);
|
|
G4double zmax = std::max(rmin*cosStart,rmax*cosStart);
|
|
pMin.set(xymin.x(),xymin.y(),zmin);
|
|
pMax.set(xymax.x(),xymax.y(),zmax);
|
|
}
|
|
|
|
// Check correctness of the bounding box
|
|
//
|
|
if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
|
|
{
|
|
std::ostringstream message;
|
|
message << "Bad bounding box (min >= max) for solid: "
|
|
<< GetName() << " !"
|
|
<< "\npMin = " << pMin
|
|
<< "\npMax = " << pMax;
|
|
G4Exception("G4Sphere::BoundingLimits()", "GeomMgt0001",
|
|
JustWarning, message);
|
|
DumpInfo();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calculate extent under transform and specified limit
|
|
|
|
G4bool G4Sphere::CalculateExtent( const EAxis pAxis,
|
|
const G4VoxelLimits& pVoxelLimit,
|
|
const G4AffineTransform& pTransform,
|
|
G4double& pMin, G4double& pMax ) const
|
|
{
|
|
G4ThreeVector bmin, bmax;
|
|
|
|
// Get bounding box
|
|
BoundingLimits(bmin,bmax);
|
|
|
|
// Find extent
|
|
G4BoundingEnvelope bbox(bmin,bmax);
|
|
return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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;
|
|
|
|
const G4double halfRmaxTolerance = fRmaxTolerance*0.5;
|
|
const G4double halfRminTolerance = fRminTolerance*0.5;
|
|
const G4double Rmax_minus = fRmax - halfRmaxTolerance;
|
|
const G4double Rmin_plus = (fRmin > 0) ? fRmin+halfRminTolerance : 0;
|
|
|
|
rho2 = p.x()*p.x() + p.y()*p.y() ;
|
|
rad2 = rho2 + p.z()*p.z() ;
|
|
|
|
// Check radial surfaces. Sets 'in'
|
|
|
|
tolRMin = Rmin_plus;
|
|
tolRMax = Rmax_minus;
|
|
|
|
if(rad2 == 0.0)
|
|
{
|
|
if (fRmin > 0.0)
|
|
{
|
|
return in = kOutside;
|
|
}
|
|
if ( (!fFullPhiSphere) || (!fFullThetaSphere) )
|
|
{
|
|
return in = kSurface;
|
|
}
|
|
else
|
|
{
|
|
return in = kInside;
|
|
}
|
|
}
|
|
|
|
if ( (rad2 <= Rmax_minus*Rmax_minus) && (rad2 >= Rmin_plus*Rmin_plus) )
|
|
{
|
|
in = kInside;
|
|
}
|
|
else
|
|
{
|
|
tolRMax = fRmax + halfRmaxTolerance; // outside case
|
|
tolRMin = std::max(fRmin-halfRminTolerance, 0.); // outside case
|
|
if ( (rad2 <= tolRMax*tolRMax) && (rad2 >= tolRMin*tolRMin) )
|
|
{
|
|
in = kSurface;
|
|
}
|
|
else
|
|
{
|
|
return in = kOutside;
|
|
}
|
|
}
|
|
|
|
// Phi boundaries : Do not check if it has no phi boundary!
|
|
|
|
if ( !fFullPhiSphere && rho2 ) // [fDPhi < twopi] and [p.x or p.y]
|
|
{
|
|
pPhi = std::atan2(p.y(),p.x()) ;
|
|
|
|
if ( pPhi < fSPhi - halfAngTolerance ) { pPhi += twopi; }
|
|
else if ( pPhi > ePhi + halfAngTolerance ) { pPhi -= twopi; }
|
|
|
|
if ( (pPhi < fSPhi - halfAngTolerance)
|
|
|| (pPhi > ePhi + halfAngTolerance) ) { return in = kOutside; }
|
|
|
|
else if (in == kInside) // else it's kSurface anyway already
|
|
{
|
|
if ( (pPhi < fSPhi + halfAngTolerance)
|
|
|| (pPhi > ePhi - halfAngTolerance) ) { in = kSurface; }
|
|
}
|
|
}
|
|
|
|
// Theta bondaries
|
|
|
|
if ( (rho2 || p.z()) && (!fFullThetaSphere) )
|
|
{
|
|
rho = std::sqrt(rho2);
|
|
pTheta = std::atan2(rho,p.z());
|
|
|
|
if ( in == kInside )
|
|
{
|
|
if ( ((fSTheta > 0.0) && (pTheta < fSTheta + halfAngTolerance))
|
|
|| ((eTheta < pi) && (pTheta > eTheta - halfAngTolerance)) )
|
|
{
|
|
if ( (( (fSTheta>0.0)&&(pTheta>=fSTheta-halfAngTolerance) )
|
|
|| (fSTheta == 0.0) )
|
|
&& ((eTheta==pi)||(pTheta <= eTheta + halfAngTolerance) ) )
|
|
{
|
|
in = kSurface;
|
|
}
|
|
else
|
|
{
|
|
in = kOutside;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ((fSTheta > 0.0)&&(pTheta < fSTheta - halfAngTolerance))
|
|
||((eTheta < pi )&&(pTheta > eTheta + halfAngTolerance)) )
|
|
{
|
|
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, radius, pTheta, pPhi=0.;
|
|
G4double distRMin = kInfinity;
|
|
G4double distSPhi = kInfinity, distEPhi = kInfinity;
|
|
G4double distSTheta = kInfinity, distETheta = kInfinity;
|
|
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();
|
|
radius = std::sqrt(rho2+p.z()*p.z());
|
|
rho = std::sqrt(rho2);
|
|
|
|
G4double distRMax = std::fabs(radius-fRmax);
|
|
if (fRmin) distRMin = std::fabs(radius-fRmin);
|
|
|
|
if ( rho && !fFullSphere )
|
|
{
|
|
pPhi = std::atan2(p.y(),p.x());
|
|
|
|
if (pPhi < fSPhi-halfAngTolerance) { pPhi += twopi; }
|
|
else if (pPhi > ePhi+halfAngTolerance) { pPhi -= twopi; }
|
|
}
|
|
if ( !fFullPhiSphere )
|
|
{
|
|
if ( rho )
|
|
{
|
|
distSPhi = std::fabs( pPhi-fSPhi );
|
|
distEPhi = std::fabs( pPhi-ePhi );
|
|
}
|
|
else if( !fRmin )
|
|
{
|
|
distSPhi = 0.;
|
|
distEPhi = 0.;
|
|
}
|
|
nPs = G4ThreeVector(sinSPhi,-cosSPhi,0);
|
|
nPe = G4ThreeVector(-sinEPhi,cosEPhi,0);
|
|
}
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
if ( rho )
|
|
{
|
|
pTheta = std::atan2(rho,p.z());
|
|
distSTheta = std::fabs(pTheta-fSTheta);
|
|
distETheta = std::fabs(pTheta-eTheta);
|
|
|
|
nTs = G4ThreeVector(-cosSTheta*p.x()/rho,
|
|
-cosSTheta*p.y()/rho,
|
|
sinSTheta );
|
|
|
|
nTe = G4ThreeVector( cosETheta*p.x()/rho,
|
|
cosETheta*p.y()/rho,
|
|
-sinETheta );
|
|
}
|
|
else if( !fRmin )
|
|
{
|
|
if ( fSTheta )
|
|
{
|
|
distSTheta = 0.;
|
|
nTs = G4ThreeVector(0.,0.,-1.);
|
|
}
|
|
if ( eTheta < pi )
|
|
{
|
|
distETheta = 0.;
|
|
nTe = G4ThreeVector(0.,0.,1.);
|
|
}
|
|
}
|
|
}
|
|
if( radius ) { nR = G4ThreeVector(p.x()/radius,p.y()/radius,p.z()/radius); }
|
|
|
|
if( distRMax <= halfCarTolerance )
|
|
{
|
|
++noSurfaces;
|
|
sumnorm += nR;
|
|
}
|
|
if( fRmin && (distRMin <= halfCarTolerance) )
|
|
{
|
|
++noSurfaces;
|
|
sumnorm -= nR;
|
|
}
|
|
if( !fFullPhiSphere )
|
|
{
|
|
if (distSPhi <= halfAngTolerance)
|
|
{
|
|
++noSurfaces;
|
|
sumnorm += nPs;
|
|
}
|
|
if (distEPhi <= halfAngTolerance)
|
|
{
|
|
++noSurfaces;
|
|
sumnorm += nPe;
|
|
}
|
|
}
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
if ((distSTheta <= halfAngTolerance) && (fSTheta > 0.))
|
|
{
|
|
++noSurfaces;
|
|
if ((radius <= halfCarTolerance) && fFullPhiSphere) { sumnorm += nZ; }
|
|
else { sumnorm += nTs; }
|
|
}
|
|
if ((distETheta <= halfAngTolerance) && (eTheta < pi))
|
|
{
|
|
++noSurfaces;
|
|
if ((radius <= halfCarTolerance) && fFullPhiSphere) { sumnorm -= nZ; }
|
|
else { sumnorm += nTe; }
|
|
if(sumnorm.z() == 0.) { sumnorm += nZ; }
|
|
}
|
|
}
|
|
if ( noSurfaces == 0 )
|
|
{
|
|
#ifdef G4CSGDEBUG
|
|
G4Exception("G4Sphere::SurfaceNormal(p)", "GeomSolids1002",
|
|
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,radius,pPhi,pTheta;
|
|
G4double distRMin,distRMax,distSPhi,distEPhi,
|
|
distSTheta,distETheta,distMin;
|
|
|
|
rho2=p.x()*p.x()+p.y()*p.y();
|
|
radius=std::sqrt(rho2+p.z()*p.z());
|
|
rho=std::sqrt(rho2);
|
|
|
|
//
|
|
// Distance to r shells
|
|
//
|
|
|
|
distRMax=std::fabs(radius-fRmax);
|
|
if (fRmin)
|
|
{
|
|
distRMin=std::fabs(radius-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 (!fFullPhiSphere && 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 (!fFullThetaSphere && radius)
|
|
{
|
|
pTheta=std::atan2(rho,p.z());
|
|
distSTheta=std::fabs(pTheta-fSTheta)*radius;
|
|
distETheta=std::fabs(pTheta-fSTheta-fDTheta)*radius;
|
|
|
|
// 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()/radius,-p.y()/radius,-p.z()/radius);
|
|
break;
|
|
case kNRMax: // Outer radius
|
|
norm=G4ThreeVector(p.x()/radius,p.y()/radius,p.z()/radius);
|
|
break;
|
|
case kNSPhi:
|
|
norm=G4ThreeVector(sinSPhi,-cosSPhi,0);
|
|
break;
|
|
case kNEPhi:
|
|
norm=G4ThreeVector(-sinEPhi,cosEPhi,0);
|
|
break;
|
|
case kNSTheta:
|
|
norm=G4ThreeVector(-cosSTheta*std::cos(pPhi),
|
|
-cosSTheta*std::sin(pPhi),
|
|
sinSTheta );
|
|
break;
|
|
case kNETheta:
|
|
norm=G4ThreeVector( cosETheta*std::cos(pPhi),
|
|
cosETheta*std::sin(pPhi),
|
|
-sinETheta );
|
|
break;
|
|
default: // Should never reach this case ...
|
|
DumpInfo();
|
|
G4Exception("G4Sphere::ApproxSurfaceNormal()",
|
|
"GeomSolids1002", JustWarning,
|
|
"Undefined side for valid surface normal to solid.");
|
|
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
|
|
//
|
|
// -> 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 tolSTheta=0., tolETheta=0. ;
|
|
const G4double dRmax = 100.*fRmax;
|
|
|
|
const G4double halfRmaxTolerance = fRmaxTolerance*0.5;
|
|
const G4double halfRminTolerance = fRminTolerance*0.5;
|
|
const G4double tolORMin2 = (fRmin>halfRminTolerance)
|
|
? (fRmin-halfRminTolerance)*(fRmin-halfRminTolerance) : 0;
|
|
const G4double tolIRMin2 =
|
|
(fRmin+halfRminTolerance)*(fRmin+halfRminTolerance);
|
|
const G4double tolORMax2 =
|
|
(fRmax+halfRmaxTolerance)*(fRmax+halfRmaxTolerance);
|
|
const G4double tolIRMax2 =
|
|
(fRmax-halfRmaxTolerance)*(fRmax-halfRmaxTolerance);
|
|
|
|
// Intersection point
|
|
//
|
|
G4double xi, yi, zi, rhoi, rhoi2, radi2, iTheta ;
|
|
|
|
// Phi intersection
|
|
//
|
|
G4double Comp ;
|
|
|
|
// Phi precalcs
|
|
//
|
|
G4double Dist, cosPsi ;
|
|
|
|
// Theta precalcs
|
|
//
|
|
G4double dist2STheta, dist2ETheta ;
|
|
G4double t1, t2, b, c, d2, d, sd = 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() ;
|
|
|
|
// Theta precalcs
|
|
//
|
|
if (!fFullThetaSphere)
|
|
{
|
|
tolSTheta = fSTheta - halfAngTolerance ;
|
|
tolETheta = eTheta + halfAngTolerance ;
|
|
|
|
// Special case rad2 = 0 comparing with direction
|
|
//
|
|
if ((rad2!=0.0) || (fRmin!=0.0))
|
|
{
|
|
// Keep going for computation of distance...
|
|
}
|
|
else // Positioned on the sphere's origin
|
|
{
|
|
G4double vTheta = std::atan2(std::sqrt(v.x()*v.x()+v.y()*v.y()),v.z()) ;
|
|
if ( (vTheta < tolSTheta) || (vTheta > tolETheta) )
|
|
{
|
|
return snxt ; // kInfinity
|
|
}
|
|
return snxt = 0.0 ;
|
|
}
|
|
}
|
|
|
|
// 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) +2sd(pxvx+pyvy+pzvz)+sd^2(vx^2+vy^2+vz^2)=R^2
|
|
// => rad2 +2sd(pDotV3d) +sd^2 =R^2
|
|
//
|
|
// => sd=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
|
|
|
|
c = rad2 - fRmax*fRmax ;
|
|
|
|
if (c > fRmaxTolerance*fRmax)
|
|
{
|
|
// If outside tolerant boundary of outer G4Sphere
|
|
// [should be std::sqrt(rad2)-fRmax > halfRmaxTolerance]
|
|
|
|
d2 = pDotV3d*pDotV3d - c ;
|
|
|
|
if ( d2 >= 0 )
|
|
{
|
|
sd = -pDotV3d - std::sqrt(d2) ;
|
|
|
|
if (sd >= 0 )
|
|
{
|
|
if ( sd>dRmax ) // Avoid rounding errors due to precision issues seen on
|
|
{ // 64 bits systems. Split long distances and recompute
|
|
G4double fTerm = sd-std::fmod(sd,dRmax);
|
|
sd = fTerm + DistanceToIn(p+fTerm*v,v);
|
|
}
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
rhoi = std::sqrt(xi*xi + yi*yi) ;
|
|
|
|
if (!fFullPhiSphere && rhoi) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
|
|
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
if (!fFullThetaSphere) // Check theta intersection
|
|
{
|
|
zi = p.z() + sd*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 = sd ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return snxt=sd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!fFullThetaSphere) // Check theta intersection
|
|
{
|
|
zi = p.z() + sd*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=sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 > tolIRMax2)
|
|
&& ( (d2 >= fRmaxTolerance*fRmax) && (pDotV3d < 0) ) )
|
|
{
|
|
if (!fFullPhiSphere)
|
|
{
|
|
// 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 ( !fFullThetaSphere )
|
|
{
|
|
if ( (pTheta >= tolSTheta + kAngTolerance)
|
|
&& (pTheta <= tolETheta - kAngTolerance) )
|
|
{
|
|
return snxt=0;
|
|
}
|
|
}
|
|
else // strictly inside Theta in both cases
|
|
{
|
|
return snxt=0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
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 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 > -halfRminTolerance) && (rad2 < tolIRMin2)
|
|
&& ( (d2 < fRmin*kCarTolerance) || (pDotV3d >= 0) ) )
|
|
{
|
|
if ( !fFullPhiSphere )
|
|
{
|
|
// 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 ( !fFullThetaSphere )
|
|
{
|
|
if ( (pTheta >= tolSTheta + kAngTolerance)
|
|
&& (pTheta <= tolETheta - kAngTolerance) )
|
|
{
|
|
return snxt=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return snxt = 0 ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
if ( (pTheta >= tolSTheta + kAngTolerance)
|
|
&& (pTheta <= tolETheta - kAngTolerance) )
|
|
{
|
|
return snxt = 0 ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return snxt=0;
|
|
}
|
|
}
|
|
}
|
|
else // Not special tolerant case
|
|
{
|
|
if (d2 >= 0)
|
|
{
|
|
sd = -pDotV3d + std::sqrt(d2) ;
|
|
if ( sd >= halfRminTolerance ) // It was >= 0 ??
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
rhoi = std::sqrt(xi*xi+yi*yi) ;
|
|
|
|
if ( !fFullPhiSphere && rhoi ) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
|
|
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
if ( !fFullThetaSphere ) // Check theta intersection
|
|
{
|
|
zi = p.z() + sd*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 = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt=sd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !fFullThetaSphere ) // Check theta intersection
|
|
{
|
|
zi = p.z() + sd*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 = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 ( !fFullPhiSphere )
|
|
{
|
|
// First phi surface ('S'tarting phi)
|
|
// 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 < halfCarTolerance)
|
|
{
|
|
sd = Dist/Comp ;
|
|
|
|
if (sd < snxt)
|
|
{
|
|
if ( sd > 0 )
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
}
|
|
else
|
|
{
|
|
sd = 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 ( !fFullThetaSphere )
|
|
{
|
|
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 = sd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Second phi surface ('E'nding phi)
|
|
// Component in outwards normal dirn
|
|
|
|
Comp = -( v.x()*sinEPhi-v.y()*cosEPhi ) ;
|
|
|
|
if (Comp < 0)
|
|
{
|
|
Dist = -(p.y()*cosEPhi-p.x()*sinEPhi) ;
|
|
if ( Dist < halfCarTolerance )
|
|
{
|
|
sd = Dist/Comp ;
|
|
|
|
if ( sd < snxt )
|
|
{
|
|
if (sd > 0)
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
}
|
|
else
|
|
{
|
|
sd = 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 ( !fFullThetaSphere )
|
|
{
|
|
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 = sd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Theta segment intersection
|
|
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
|
|
// 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))+2sd(pxvx+pyvy-pzvztan^2(t))
|
|
// + sd^2(vx^2+vy^2-vz^2tan^2(t)) = 0
|
|
//
|
|
// => sd^2(1-vz^2(1+tan^2(t))+2sd(pdotv2d-pzvztan^2(t))
|
|
// + (rho2-pz^2tan^2(t)) = 0
|
|
|
|
if (fSTheta)
|
|
{
|
|
dist2STheta = rho2 - p.z()*p.z()*tanSTheta2 ;
|
|
}
|
|
else
|
|
{
|
|
dist2STheta = kInfinity ;
|
|
}
|
|
if ( eTheta < pi )
|
|
{
|
|
dist2ETheta=rho2-p.z()*p.z()*tanETheta2;
|
|
}
|
|
else
|
|
{
|
|
dist2ETheta=kInfinity;
|
|
}
|
|
if ( pTheta < tolSTheta )
|
|
{
|
|
// Inside (theta<stheta-tol) stheta 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 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2STheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if ( d2 >= 0 )
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b - d ; // First root
|
|
zi = p.z() + sd*v.z();
|
|
|
|
if ( (sd < 0) || (zi*(fSTheta - halfpi) > 0) )
|
|
{
|
|
sd = -b+d; // Second root
|
|
}
|
|
if ((sd >= 0) && (sd < snxt))
|
|
{
|
|
xi = p.x() + sd*v.x();
|
|
yi = p.y() + sd*v.y();
|
|
zi = p.z() + sd*v.z();
|
|
rhoi2 = xi*xi + yi*yi;
|
|
radi2 = rhoi2 + zi*zi;
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(fSTheta - halfpi) <= 0) )
|
|
{
|
|
if ( !fFullPhiSphere && rhoi2 ) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Possible intersection with ETheta cone.
|
|
// Second >= 0 root should be considered
|
|
|
|
if ( eTheta < pi )
|
|
{
|
|
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
|
|
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2ETheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d ; // Second root
|
|
|
|
if ( (sd >= 0) && (sd < snxt) )
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(eTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2ETheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b - d ; // First root
|
|
zi = p.z() + sd*v.z();
|
|
|
|
if ( (sd < 0) || (zi*(eTheta - halfpi) > 0) )
|
|
{
|
|
sd = -b + d ; // second root
|
|
}
|
|
if ( (sd >= 0) && (sd < snxt) )
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(eTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2STheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d ; // Second root
|
|
|
|
if ( (sd >= 0) && (sd < snxt) )
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(fSTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( (pTheta < tolSTheta + kAngTolerance)
|
|
&& (fSTheta > halfAngTolerance) )
|
|
{
|
|
// 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<halfpi)
|
|
|| (t2<0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta>halfpi)
|
|
|| (v.z()<0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta==halfpi) )
|
|
{
|
|
if (!fFullPhiSphere && 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) ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2STheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d ;
|
|
if ( (sd >= halfCarTolerance) && (sd < snxt) && (fSTheta < halfpi) )
|
|
{ // ^^^^^^^^^^^^^^^^^^^^^ shouldn't it be >=0 instead ?
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(fSTheta - halfpi) <= 0) )
|
|
{
|
|
if ( !fFullPhiSphere && rhoi2 ) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if ( cosPsi >= cosHDPhiOT )
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((pTheta > tolETheta-kAngTolerance) && (eTheta < 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) && (eTheta < halfpi)
|
|
&& (tolIRMin2 < rad2) && (rad2 < tolIRMax2))
|
|
|| ((t2>=0) && (eTheta > halfpi)
|
|
&& (tolIRMin2 < rad2) && (rad2 < tolIRMax2))
|
|
|| ((v.z()>0) && (eTheta == halfpi)
|
|
&& (tolIRMin2 < rad2) && (rad2 < tolIRMax2)) )
|
|
{
|
|
if (!fFullPhiSphere && 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) ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2ETheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d ;
|
|
|
|
if ( (sd >= halfCarTolerance)
|
|
&& (sd < snxt) && (eTheta > halfpi) )
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(eTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1;
|
|
c = dist2STheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d ; // second root
|
|
|
|
if ((sd >= 0) && (sd < snxt))
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(fSTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if (cosPsi >= cosHDPhiOT)
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
|
|
t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
|
|
if (t1)
|
|
{
|
|
b = t2/t1 ;
|
|
c = dist2ETheta/t1 ;
|
|
d2 = b*b - c ;
|
|
|
|
if (d2 >= 0)
|
|
{
|
|
d = std::sqrt(d2) ;
|
|
sd = -b + d; // second root
|
|
|
|
if ((sd >= 0) && (sd < snxt))
|
|
{
|
|
xi = p.x() + sd*v.x() ;
|
|
yi = p.y() + sd*v.y() ;
|
|
zi = p.z() + sd*v.z() ;
|
|
rhoi2 = xi*xi + yi*yi ;
|
|
radi2 = rhoi2 + zi*zi ;
|
|
|
|
if ( (radi2 <= tolORMax2)
|
|
&& (radi2 >= tolORMin2)
|
|
&& (zi*(eTheta - halfpi) <= 0) )
|
|
{
|
|
if (!fFullPhiSphere && rhoi2) // Check phi intersection
|
|
{
|
|
cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
|
|
if ( cosPsi >= cosHDPhiOT )
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt = sd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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,rds,rho;
|
|
G4double cosPsi;
|
|
G4double pTheta,dTheta1,dTheta2;
|
|
rho2=p.x()*p.x()+p.y()*p.y();
|
|
rds=std::sqrt(rho2+p.z()*p.z());
|
|
rho=std::sqrt(rho2);
|
|
|
|
//
|
|
// Distance to r shells
|
|
//
|
|
if (fRmin)
|
|
{
|
|
safeRMin=fRmin-rds;
|
|
safeRMax=rds-fRmax;
|
|
if (safeRMin>safeRMax)
|
|
{
|
|
safe=safeRMin;
|
|
}
|
|
else
|
|
{
|
|
safe=safeRMax;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safe=rds-fRmax;
|
|
}
|
|
|
|
//
|
|
// Distance to phi extent
|
|
//
|
|
if (!fFullPhiSphere && rho)
|
|
{
|
|
// Psi=angle from central phi to point
|
|
//
|
|
cosPsi=(p.x()*cosCPhi+p.y()*sinCPhi)/rho;
|
|
if (cosPsi<cosHDPhi)
|
|
{
|
|
// Point lies outside phi range
|
|
//
|
|
if ((p.y()*cosCPhi-p.x()*sinCPhi)<=0)
|
|
{
|
|
safePhi=std::fabs(p.x()*sinSPhi-p.y()*cosSPhi);
|
|
}
|
|
else
|
|
{
|
|
safePhi=std::fabs(p.x()*sinEPhi-p.y()*cosEPhi);
|
|
}
|
|
if (safePhi>safe) { safe=safePhi; }
|
|
}
|
|
}
|
|
//
|
|
// Distance to Theta extent
|
|
//
|
|
if ((rds!=0.0) && (!fFullThetaSphere))
|
|
{
|
|
pTheta=std::acos(p.z()/rds);
|
|
if (pTheta<0) { pTheta+=pi; }
|
|
dTheta1=fSTheta-pTheta;
|
|
dTheta2=pTheta-eTheta;
|
|
if (dTheta1>dTheta2)
|
|
{
|
|
if (dTheta1>=0) // WHY ???????????
|
|
{
|
|
safeTheta=rds*std::sin(dTheta1);
|
|
if (safe<=safeTheta)
|
|
{
|
|
safe=safeTheta;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dTheta2>=0)
|
|
{
|
|
safeTheta=rds*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;
|
|
|
|
const G4double halfRmaxTolerance = fRmaxTolerance*0.5;
|
|
const G4double halfRminTolerance = fRminTolerance*0.5;
|
|
const G4double Rmax_plus = fRmax + halfRmaxTolerance;
|
|
const G4double Rmin_minus = (fRmin) ? fRmin-halfRminTolerance : 0;
|
|
G4double t1,t2;
|
|
G4double b,c,d;
|
|
|
|
// Variables for phi intersection:
|
|
|
|
G4double pDistS,compS,pDistE,compE,sphi2,vphi;
|
|
|
|
G4double rho2,rad2,pDotV2d,pDotV3d;
|
|
|
|
G4double xi,yi,zi; // Intersection point
|
|
|
|
// Theta precals
|
|
//
|
|
G4double rhoSecTheta;
|
|
G4double dist2STheta, dist2ETheta, distTheta;
|
|
G4double d2,sd;
|
|
|
|
// General Precalcs
|
|
//
|
|
rho2 = p.x()*p.x()+p.y()*p.y();
|
|
rad2 = rho2+p.z()*p.z();
|
|
|
|
pDotV2d = p.x()*v.x()+p.y()*v.y();
|
|
pDotV3d = pDotV2d+p.z()*v.z();
|
|
|
|
// 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) +2sd(pxvx+pyvy+pzvz)+sd^2(vx^2+vy^2+vz^2)=R^2
|
|
// => rad2 +2sd(pDotV3d) +sd^2 =R^2
|
|
//
|
|
// => sd=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
|
|
|
|
if( (rad2 <= Rmax_plus*Rmax_plus) && (rad2 >= Rmin_minus*Rmin_minus) )
|
|
{
|
|
c = rad2 - fRmax*fRmax;
|
|
|
|
if (c < fRmaxTolerance*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 >- 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 ;
|
|
}
|
|
}
|
|
|
|
// 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 >- fRminTolerance*fRmin) // 2.0 * (0.5*kRadTolerance) * fRmin
|
|
{
|
|
if ( (c < fRminTolerance*fRmin) // leaving from Rmin
|
|
&& (d2 >= fRminTolerance*fRmin) && (pDotV3d < 0) )
|
|
{
|
|
if(calcNorm) { *validNorm = false; } // Rmin surface is concave
|
|
return snxt = 0 ;
|
|
}
|
|
else
|
|
{
|
|
if ( d2 >= 0. )
|
|
{
|
|
sd = -pDotV3d-std::sqrt(d2);
|
|
|
|
if ( sd >= 0. ) // Always intersect Rmin first
|
|
{
|
|
snxt = sd ;
|
|
side = kRMin ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Theta segment intersection
|
|
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
// 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))+2sd(pxvx+pyvy-pzvztan^2(t))
|
|
// + sd^2(vx^2+vy^2-vz^2tan^2(t)) = 0
|
|
//
|
|
// => sd^2(1-vz^2(1+tan^2(t))+2sd(pdotv2d-pzvztan^2(t))
|
|
// + (rho2-pz^2tan^2(t)) = 0
|
|
//
|
|
|
|
if(fSTheta) // intersection with first cons
|
|
{
|
|
if( std::fabs(tanSTheta) > 5./kAngTolerance ) // kons is plane z=0
|
|
{
|
|
if( v.z() > 0. )
|
|
{
|
|
if ( std::fabs( p.z() ) <= halfRmaxTolerance )
|
|
{
|
|
if(calcNorm)
|
|
{
|
|
*validNorm = true;
|
|
*n = G4ThreeVector(0.,0.,1.);
|
|
}
|
|
return snxt = 0 ;
|
|
}
|
|
stheta = -p.z()/v.z();
|
|
sidetheta = kSTheta;
|
|
}
|
|
}
|
|
else // kons is not plane
|
|
{
|
|
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(rho2)-p.z()*tanSTheta;
|
|
|
|
if( std::fabs(t1) < halfAngTolerance ) // 1st order equation,
|
|
{ // v parallel to kons
|
|
if( v.z() > 0. )
|
|
{
|
|
if(std::fabs(distTheta) < halfRmaxTolerance) // 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.;
|
|
}
|
|
}
|
|
stheta = -0.5*dist2STheta/t2;
|
|
sidetheta = kSTheta;
|
|
}
|
|
} // 2nd order equation, 1st root of fSTheta cone,
|
|
else // 2nd if 1st root -ve
|
|
{
|
|
if( std::fabs(distTheta) < halfRmaxTolerance )
|
|
{
|
|
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 )
|
|
{
|
|
sd = -b - d; // First root
|
|
|
|
if ( ((std::fabs(s) < halfRmaxTolerance) && (t2 < 0.))
|
|
|| (sd < 0.) || ( (sd > 0.) && (p.z() + sd*v.z() > 0.) ) )
|
|
{
|
|
sd = -b + d ; // 2nd root
|
|
}
|
|
if( (sd > halfRmaxTolerance) && (p.z() + sd*v.z() <= 0.) )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kSTheta;
|
|
}
|
|
}
|
|
else // sTheta < pi/2, concave surface, no normal
|
|
{
|
|
sd = -b - d; // First root
|
|
|
|
if ( ( (std::fabs(sd) < halfRmaxTolerance) && (t2 >= 0.) )
|
|
|| (sd < 0.) || ( (sd > 0.) && (p.z() + sd*v.z() < 0.) ) )
|
|
{
|
|
sd = -b + d ; // 2nd root
|
|
}
|
|
if( (sd > halfRmaxTolerance) && (p.z() + sd*v.z() >= 0.) )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kSTheta;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (eTheta < pi) // intersection with second cons
|
|
{
|
|
if( std::fabs(tanETheta) > 5./kAngTolerance ) // kons is plane z=0
|
|
{
|
|
if( v.z() < 0. )
|
|
{
|
|
if ( std::fabs( p.z() ) <= halfRmaxTolerance )
|
|
{
|
|
if(calcNorm)
|
|
{
|
|
*validNorm = true;
|
|
*n = G4ThreeVector(0.,0.,-1.);
|
|
}
|
|
return snxt = 0 ;
|
|
}
|
|
sd = -p.z()/v.z();
|
|
|
|
if( sd < stheta )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kETheta;
|
|
}
|
|
}
|
|
}
|
|
else // kons is not plane
|
|
{
|
|
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(rho2)-p.z()*tanETheta;
|
|
|
|
if( std::fabs(t1) < halfAngTolerance ) // 1st order equation,
|
|
{ // v parallel to kons
|
|
if( v.z() < 0. )
|
|
{
|
|
if(std::fabs(distTheta) < halfRmaxTolerance) // p on surface
|
|
{
|
|
if( (eTheta > halfpi) && (p.z() < 0.) )
|
|
{
|
|
if( calcNorm ) { *validNorm = false; }
|
|
return snxt = 0.;
|
|
}
|
|
else if ( (eTheta < halfpi) && (p.z() >= 0) )
|
|
{
|
|
if( calcNorm )
|
|
{
|
|
*validNorm = true;
|
|
if (rho2)
|
|
{
|
|
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2));
|
|
*n = G4ThreeVector( p.x()/rhoSecTheta,
|
|
p.y()/rhoSecTheta,
|
|
-sinETheta );
|
|
}
|
|
else { *n = G4ThreeVector(0.,0.,-1.); }
|
|
}
|
|
return snxt = 0.;
|
|
}
|
|
}
|
|
sd = -0.5*dist2ETheta/t2;
|
|
|
|
if( sd < stheta )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kETheta;
|
|
}
|
|
}
|
|
} // 2nd order equation, 1st root of fSTheta cone
|
|
else // 2nd if 1st root -ve
|
|
{
|
|
if ( std::fabs(distTheta) < halfRmaxTolerance )
|
|
{
|
|
if( (eTheta < halfpi) && (t2 >= 0.) ) // leave
|
|
{
|
|
if( calcNorm )
|
|
{
|
|
*validNorm = true;
|
|
if (rho2)
|
|
{
|
|
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2));
|
|
*n = G4ThreeVector( p.x()/rhoSecTheta,
|
|
p.y()/rhoSecTheta,
|
|
-sinETheta );
|
|
}
|
|
else *n = G4ThreeVector(0.,0.,-1.);
|
|
}
|
|
return snxt = 0.;
|
|
}
|
|
else if ( (eTheta > 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 <halfRmaxTolerance) && (d2 > -halfRmaxTolerance) )
|
|
{
|
|
d2 = 0.;
|
|
}
|
|
if ( d2 >= 0. )
|
|
{
|
|
d = std::sqrt(d2);
|
|
|
|
if( eTheta < halfpi )
|
|
{
|
|
sd = -b - d; // First root
|
|
|
|
if( ((std::fabs(sd) < halfRmaxTolerance) && (t2 < 0.))
|
|
|| (sd < 0.) )
|
|
{
|
|
sd = -b + d ; // 2nd root
|
|
}
|
|
if( sd > halfRmaxTolerance )
|
|
{
|
|
if( sd < stheta )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kETheta;
|
|
}
|
|
}
|
|
}
|
|
else // sTheta+fDTheta > pi/2, concave surface, no normal
|
|
{
|
|
sd = -b - d; // First root
|
|
|
|
if ( ((std::fabs(sd) < halfRmaxTolerance) && (t2 >= 0.))
|
|
|| (sd < 0.)
|
|
|| ( (sd > 0.) && (p.z() + sd*v.z() > halfRmaxTolerance) ) )
|
|
{
|
|
sd = -b + d ; // 2nd root
|
|
}
|
|
if ( ( sd>halfRmaxTolerance )
|
|
&& ( p.z()+sd*v.z() <= halfRmaxTolerance ) )
|
|
{
|
|
if( sd < stheta )
|
|
{
|
|
stheta = sd;
|
|
sidetheta = kETheta;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end theta intersections
|
|
|
|
// Phi Intersection
|
|
|
|
if ( !fFullPhiSphere )
|
|
{
|
|
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 intersection with correct half-plane (if not -> no intersect)
|
|
//
|
|
if( (std::fabs(xi)<=kCarTolerance) && (std::fabs(yi)<=kCarTolerance) )
|
|
{
|
|
vphi = std::atan2(v.y(),v.x());
|
|
sidephi = kSPhi;
|
|
if ( ( (fSPhi-halfAngTolerance) <= vphi)
|
|
&& ( (ePhi+halfAngTolerance) >= vphi) )
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
}
|
|
else if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
|
|
{
|
|
sphi=kInfinity;
|
|
}
|
|
else
|
|
{
|
|
sidephi = kSPhi ;
|
|
if ( pDistS > -halfCarTolerance) { 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 intersection with correct half-plane
|
|
//
|
|
if ( (std::fabs(xi)<=kCarTolerance)
|
|
&& (std::fabs(yi)<=kCarTolerance))
|
|
{
|
|
// Leaving via ending phi
|
|
//
|
|
vphi = std::atan2(v.y(),v.x()) ;
|
|
|
|
if( !((fSPhi-halfAngTolerance <= vphi)
|
|
&&(fSPhi+fDPhi+halfAngTolerance >= vphi)) )
|
|
{
|
|
sidephi = kEPhi;
|
|
if ( pDistE <= -halfCarTolerance ) { sphi = sphi2; }
|
|
else { sphi = 0.0; }
|
|
}
|
|
}
|
|
else if ((yi*cosCPhi-xi*sinCPhi)>=0) // Leaving via ending phi
|
|
{
|
|
sidephi = kEPhi ;
|
|
if ( pDistE <= -halfCarTolerance )
|
|
{
|
|
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( (std::fabs(xi)<=kCarTolerance)&&(std::fabs(yi)<=kCarTolerance) )
|
|
{
|
|
vphi = std::atan2(v.y(),v.x());
|
|
sidephi = kSPhi;
|
|
if ( ( (fSPhi-halfAngTolerance) <= vphi)
|
|
&& ( (ePhi+halfAngTolerance) >= vphi) )
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
}
|
|
else if ( ( yi*cosCPhi - xi*sinCPhi ) <= 0 )
|
|
{
|
|
sphi = kInfinity ;
|
|
}
|
|
else // Leaving via Ending phi
|
|
{
|
|
sidephi = kEPhi ;
|
|
if ( pDistE > -halfCarTolerance ) { 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( (std::fabs(xi)<=kCarTolerance)
|
|
&& (std::fabs(yi)<=kCarTolerance) )
|
|
{
|
|
vphi = std::atan2(v.y(),v.x());
|
|
sidephi = kSPhi;
|
|
if ( ( (fSPhi-halfAngTolerance) <= vphi)
|
|
&& ( (ePhi+halfAngTolerance) >= vphi) )
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
}
|
|
else 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( (std::fabs(xi)<=kCarTolerance)&&(std::fabs(yi)<=kCarTolerance) )
|
|
{
|
|
vphi = std::atan2(v.y(),v.x()) ;
|
|
sidephi = kSPhi;
|
|
if ( ( (fSPhi-halfAngTolerance) <= vphi)
|
|
&& ( (ePhi+halfAngTolerance) >= vphi) )
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
}
|
|
else if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
|
|
{
|
|
sphi = kInfinity ;
|
|
}
|
|
else // Leaving via Starting phi
|
|
{
|
|
sidephi = kSPhi ;
|
|
if ( pDistS > -halfCarTolerance ) { 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( (std::fabs(xi)<=kCarTolerance)
|
|
&& (std::fabs(yi)<=kCarTolerance))
|
|
{
|
|
vphi = std::atan2(v.y(),v.x()) ;
|
|
sidephi = kSPhi;
|
|
if ( ( (fSPhi-halfAngTolerance) <= vphi)
|
|
&& ( (ePhi+halfAngTolerance) >= vphi) )
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
}
|
|
else 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-halfAngTolerance < vphi) && (vphi < ePhi+halfAngTolerance))
|
|
{
|
|
sphi = kInfinity;
|
|
}
|
|
else
|
|
{
|
|
sidephi = kSPhi ; // arbitrary
|
|
sphi = 0 ;
|
|
}
|
|
}
|
|
else // travel along z - no phi intersection
|
|
{
|
|
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(sinSPhi,-cosSPhi,0);
|
|
*validNorm=true;
|
|
}
|
|
else { *validNorm=false; }
|
|
break ;
|
|
|
|
case kEPhi:
|
|
if ( fDPhi <= pi ) // Normal to Phi+
|
|
{
|
|
*n=G4ThreeVector(-sinEPhi,cosEPhi,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();
|
|
rho2=xi*xi+yi*yi;
|
|
if (rho2)
|
|
{
|
|
rhoSecTheta = std::sqrt(rho2*(1+tanSTheta2));
|
|
*n = G4ThreeVector( xi/rhoSecTheta, yi/rhoSecTheta,
|
|
-tanSTheta/std::sqrt(1+tanSTheta2));
|
|
}
|
|
else
|
|
{
|
|
*n = G4ThreeVector(0.,0.,1.);
|
|
}
|
|
*validNorm=true;
|
|
}
|
|
else { *validNorm=false; } // Concave STheta cone
|
|
break;
|
|
|
|
case kETheta:
|
|
if( eTheta == halfpi )
|
|
{
|
|
*n = G4ThreeVector(0.,0.,-1.);
|
|
*validNorm = true;
|
|
}
|
|
else if ( eTheta < halfpi )
|
|
{
|
|
xi=p.x()+snxt*v.x();
|
|
yi=p.y()+snxt*v.y();
|
|
rho2=xi*xi+yi*yi;
|
|
if (rho2)
|
|
{
|
|
rhoSecTheta = std::sqrt(rho2*(1+tanETheta2));
|
|
*n = G4ThreeVector( xi/rhoSecTheta, yi/rhoSecTheta,
|
|
-tanETheta/std::sqrt(1+tanETheta2) );
|
|
}
|
|
else
|
|
{
|
|
*n = G4ThreeVector(0.,0.,-1.);
|
|
}
|
|
*validNorm=true;
|
|
}
|
|
else { *validNorm=false; } // Concave ETheta cone
|
|
break;
|
|
|
|
default:
|
|
G4cout << G4endl;
|
|
DumpInfo();
|
|
std::ostringstream message;
|
|
G4int oldprc = message.precision(16);
|
|
message << "Undefined side for valid surface normal to solid."
|
|
<< G4endl
|
|
<< "Position:" << G4endl << G4endl
|
|
<< "p.x() = " << p.x()/mm << " mm" << G4endl
|
|
<< "p.y() = " << p.y()/mm << " mm" << G4endl
|
|
<< "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl
|
|
<< "Direction:" << G4endl << G4endl
|
|
<< "v.x() = " << v.x() << G4endl
|
|
<< "v.y() = " << v.y() << G4endl
|
|
<< "v.z() = " << v.z() << G4endl << G4endl
|
|
<< "Proposed distance :" << G4endl << G4endl
|
|
<< "snxt = " << snxt/mm << " mm" << G4endl;
|
|
message.precision(oldprc);
|
|
G4Exception("G4Sphere::DistanceToOut(p,v,..)",
|
|
"GeomSolids1002", JustWarning, message);
|
|
break;
|
|
}
|
|
}
|
|
if (snxt == kInfinity)
|
|
{
|
|
G4cout << G4endl;
|
|
DumpInfo();
|
|
std::ostringstream message;
|
|
G4int oldprc = message.precision(16);
|
|
message << "Logic error: snxt = kInfinity ???" << G4endl
|
|
<< "Position:" << G4endl << G4endl
|
|
<< "p.x() = " << p.x()/mm << " mm" << G4endl
|
|
<< "p.y() = " << p.y()/mm << " mm" << G4endl
|
|
<< "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl
|
|
<< "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm
|
|
<< " mm" << G4endl << G4endl
|
|
<< "Direction:" << G4endl << G4endl
|
|
<< "v.x() = " << v.x() << G4endl
|
|
<< "v.y() = " << v.y() << G4endl
|
|
<< "v.z() = " << v.z() << G4endl << G4endl
|
|
<< "Proposed distance :" << G4endl << G4endl
|
|
<< "snxt = " << snxt/mm << " mm" << G4endl;
|
|
message.precision(oldprc);
|
|
G4Exception("G4Sphere::DistanceToOut(p,v,..)",
|
|
"GeomSolids1002", JustWarning, message);
|
|
}
|
|
|
|
return snxt;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calculate 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,rds,rho;
|
|
G4double pTheta,dTheta1 = kInfinity,dTheta2 = kInfinity;
|
|
rho2=p.x()*p.x()+p.y()*p.y();
|
|
rds=std::sqrt(rho2+p.z()*p.z());
|
|
rho=std::sqrt(rho2);
|
|
|
|
#ifdef G4CSGDEBUG
|
|
if( Inside(p) == kOutside )
|
|
{
|
|
G4int old_prc = 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.precision(old_prc) ;
|
|
G4Exception("G4Sphere::DistanceToOut(p)",
|
|
"GeomSolids1002", JustWarning, "Point p is outside !?" );
|
|
}
|
|
#endif
|
|
|
|
// Distance to r shells
|
|
//
|
|
safeRMax = fRmax-rds;
|
|
safe = safeRMax;
|
|
if (fRmin)
|
|
{
|
|
safeRMin = rds-fRmin;
|
|
safe = std::min( safeRMin, safeRMax );
|
|
}
|
|
|
|
// Distance to phi extent
|
|
//
|
|
if ( !fFullPhiSphere )
|
|
{
|
|
if (rho>0.0)
|
|
{
|
|
if ((p.y()*cosCPhi-p.x()*sinCPhi)<=0)
|
|
{
|
|
safePhi=-(p.x()*sinSPhi-p.y()*cosSPhi);
|
|
}
|
|
else
|
|
{
|
|
safePhi=(p.x()*sinEPhi-p.y()*cosEPhi);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
safePhi = 0.0; // Distance to both Phi surfaces (extended)
|
|
}
|
|
// Both cases above can be improved - in case fRMin > 0.0
|
|
// although it may be costlier (good for precise, not fast version)
|
|
|
|
safe= std::min(safe, safePhi);
|
|
}
|
|
|
|
// Distance to Theta extent
|
|
//
|
|
if ( !fFullThetaSphere )
|
|
{
|
|
if( rds > 0.0 )
|
|
{
|
|
pTheta=std::acos(p.z()/rds);
|
|
if (pTheta<0) { pTheta+=pi; }
|
|
if(fSTheta>0.)
|
|
{ dTheta1=pTheta-fSTheta;}
|
|
if(eTheta<pi)
|
|
{ dTheta2=eTheta-pTheta;}
|
|
|
|
safeTheta=rds*std::sin(std::min(dTheta1, dTheta2) );
|
|
}
|
|
else
|
|
{
|
|
safeTheta= 0.0;
|
|
// An improvement will be to return negative answer if outside (TODO)
|
|
}
|
|
safe = std::min( safe, safeTheta );
|
|
}
|
|
|
|
if (safe<0.0) { safe=0; }
|
|
// An improvement to return negative answer if outside (TODO)
|
|
|
|
return safe;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// G4EntityType
|
|
|
|
G4GeometryType G4Sphere::GetEntityType() const
|
|
{
|
|
return G4String("G4Sphere");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Make a clone of the object
|
|
//
|
|
G4VSolid* G4Sphere::Clone() const
|
|
{
|
|
return new G4Sphere(*this);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Stream object contents to an output stream
|
|
|
|
std::ostream& G4Sphere::StreamInfo( std::ostream& os ) const
|
|
{
|
|
G4int oldprc = os.precision(16);
|
|
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";
|
|
os.precision(oldprc);
|
|
|
|
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, rRand;
|
|
|
|
height1 = (fRmax-fRmin)*cosSTheta;
|
|
height2 = (fRmax-fRmin)*cosETheta;
|
|
slant1 = std::sqrt(sqr((fRmax - fRmin)*sinSTheta) + height1*height1);
|
|
slant2 = std::sqrt(sqr((fRmax - fRmin)*sinETheta) + height2*height2);
|
|
rRand = GetRadiusInRing(fRmin,fRmax);
|
|
|
|
aOne = fRmax*fRmax*fDPhi*(cosSTheta-cosETheta);
|
|
aTwo = fRmin*fRmin*fDPhi*(cosSTheta-cosETheta);
|
|
aThr = fDPhi*((fRmax + fRmin)*sinSTheta)*slant1;
|
|
aFou = fDPhi*((fRmax + fRmin)*sinETheta)*slant2;
|
|
aFiv = 0.5*fDTheta*(fRmax*fRmax-fRmin*fRmin);
|
|
|
|
phi = G4RandFlat::shoot(fSPhi, ePhi);
|
|
cosphi = std::cos(phi);
|
|
sinphi = std::sin(phi);
|
|
costheta = G4RandFlat::shoot(cosETheta,cosSTheta);
|
|
sintheta = std::sqrt(1.-sqr(costheta));
|
|
|
|
if(fFullPhiSphere) { aFiv = 0; }
|
|
if(fSTheta == 0) { aThr=0; }
|
|
if(eTheta == pi) { aFou = 0; }
|
|
if(fSTheta == halfpi) { aThr = pi*(fRmax*fRmax-fRmin*fRmin); }
|
|
if(eTheta == halfpi) { aFou = pi*(fRmax*fRmax-fRmin*fRmin); }
|
|
|
|
chose = G4RandFlat::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 != halfpi)
|
|
{
|
|
zRand = G4RandFlat::shoot(fRmin*cosSTheta,fRmax*cosSTheta);
|
|
return G4ThreeVector(tanSTheta*zRand*cosphi,
|
|
tanSTheta*zRand*sinphi,zRand);
|
|
}
|
|
else
|
|
{
|
|
return G4ThreeVector(rRand*cosphi, rRand*sinphi, 0.);
|
|
}
|
|
}
|
|
else if( (chose>=aOne+aTwo+aThr) && (chose<aOne+aTwo+aThr+aFou) )
|
|
{
|
|
if(eTheta != halfpi)
|
|
{
|
|
zRand = G4RandFlat::shoot(fRmin*cosETheta, fRmax*cosETheta);
|
|
return G4ThreeVector (tanETheta*zRand*cosphi,
|
|
tanETheta*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*cosSPhi,
|
|
rRand*sintheta*sinSPhi,rRand*costheta);
|
|
}
|
|
else
|
|
{
|
|
return G4ThreeVector(rRand*sintheta*cosEPhi,
|
|
rRand*sintheta*sinEPhi,rRand*costheta);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetSurfaceArea
|
|
|
|
G4double G4Sphere::GetSurfaceArea()
|
|
{
|
|
if(fSurfaceArea != 0.) {;}
|
|
else
|
|
{
|
|
G4double Rsq=fRmax*fRmax;
|
|
G4double rsq=fRmin*fRmin;
|
|
|
|
fSurfaceArea = fDPhi*(rsq+Rsq)*(cosSTheta - cosETheta);
|
|
if(!fFullPhiSphere)
|
|
{
|
|
fSurfaceArea = fSurfaceArea + fDTheta*(Rsq-rsq);
|
|
}
|
|
if(fSTheta >0)
|
|
{
|
|
G4double acos1=std::acos( std::pow(sinSTheta,2) * std::cos(fDPhi)
|
|
+ std::pow(cosSTheta,2));
|
|
if(fDPhi>pi)
|
|
{
|
|
fSurfaceArea = fSurfaceArea + 0.5*(Rsq-rsq)*(twopi-acos1);
|
|
}
|
|
else
|
|
{
|
|
fSurfaceArea = fSurfaceArea + 0.5*(Rsq-rsq)*acos1;
|
|
}
|
|
}
|
|
if(eTheta < pi)
|
|
{
|
|
G4double acos2=std::acos( std::pow(sinETheta,2) * std::cos(fDPhi)
|
|
+ std::pow(cosETheta,2));
|
|
if(fDPhi>pi)
|
|
{
|
|
fSurfaceArea = fSurfaceArea + 0.5*(Rsq-rsq)*(twopi-acos2);
|
|
}
|
|
else
|
|
{
|
|
fSurfaceArea = fSurfaceArea + 0.5*(Rsq-rsq)*acos2;
|
|
}
|
|
}
|
|
}
|
|
return fSurfaceArea;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
#endif
|