Files
geant4/source/geometry/solids/specific/src/G4Ellipsoid.cc
T
2016-06-10 12:08:39 +02:00

1108 lines
34 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: G4Ellipsoid.cc 83572 2014-09-01 15:23:27Z gcosmo $
//
// class G4Ellipsoid
//
// Implementation for G4Ellipsoid class
//
// History:
//
// 10.11.99 G.Horton-Smith -- first writing, based on G4Sphere class
// 25.02.05 G.Guerrieri -- Modified for future Geant4 release
//
// --------------------------------------------------------------------
#include "globals.hh"
#include "G4Ellipsoid.hh"
#include "G4VoxelLimits.hh"
#include "G4AffineTransform.hh"
#include "G4GeometryTolerance.hh"
#include "meshdefs.hh"
#include "Randomize.hh"
#include "G4VPVParameterisation.hh"
#include "G4VGraphicsScene.hh"
#include "G4VisExtent.hh"
#include "G4AutoLock.hh"
namespace
{
G4Mutex polyhedronMutex = G4MUTEX_INITIALIZER;
}
using namespace CLHEP;
///////////////////////////////////////////////////////////////////////////////
//
// constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
// - note if pDPhi>2PI then reset to 2PI
G4Ellipsoid::G4Ellipsoid(const G4String& pName,
G4double pxSemiAxis,
G4double pySemiAxis,
G4double pzSemiAxis,
G4double pzBottomCut,
G4double pzTopCut)
: G4VSolid(pName), fRebuildPolyhedron(false), fpPolyhedron(0),
fCubicVolume(0.), fSurfaceArea(0.), zBottomCut(0.), zTopCut(0.)
{
// note: for users that want to use the full ellipsoid it is useful
// to include a default for the cuts
kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
halfCarTolerance = kCarTolerance*0.5;
halfRadTolerance = kRadTolerance*0.5;
// Check Semi-Axis
if ( (pxSemiAxis<=0.) || (pySemiAxis<=0.) || (pzSemiAxis<=0.) )
{
std::ostringstream message;
message << "Invalid semi-axis - " << GetName();
G4Exception("G4Ellipsoid::G4Ellipsoid()", "GeomSolids0002",
FatalErrorInArgument, message);
}
SetSemiAxis(pxSemiAxis, pySemiAxis, pzSemiAxis);
if ( pzBottomCut == 0 && pzTopCut == 0 )
{
SetZCuts(-pzSemiAxis, pzSemiAxis);
}
else if ( (pzBottomCut < pzSemiAxis) && (pzTopCut > -pzSemiAxis)
&& (pzBottomCut < pzTopCut) )
{
SetZCuts(pzBottomCut, pzTopCut);
}
else
{
std::ostringstream message;
message << "Invalid z-coordinate for cutting plane - " << GetName();
G4Exception("G4Ellipsoid::G4Ellipsoid()", "GeomSolids0002",
FatalErrorInArgument, message);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Fake default constructor - sets only member data and allocates memory
// for usage restricted to object persistency.
//
G4Ellipsoid::G4Ellipsoid( __void__& a )
: G4VSolid(a), fRebuildPolyhedron(false), fpPolyhedron(0), kRadTolerance(0.),
halfCarTolerance(0.), halfRadTolerance(0.), fCubicVolume(0.),
fSurfaceArea(0.), xSemiAxis(0.), ySemiAxis(0.), zSemiAxis(0.),
semiAxisMax(0.), zBottomCut(0.), zTopCut(0.)
{
}
///////////////////////////////////////////////////////////////////////////////
//
// Destructor
G4Ellipsoid::~G4Ellipsoid()
{
delete fpPolyhedron; fpPolyhedron = 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// Copy constructor
G4Ellipsoid::G4Ellipsoid(const G4Ellipsoid& rhs)
: G4VSolid(rhs),
fRebuildPolyhedron(false), fpPolyhedron(0),
kRadTolerance(rhs.kRadTolerance),
halfCarTolerance(rhs.halfCarTolerance),
halfRadTolerance(rhs.halfRadTolerance),
fCubicVolume(rhs.fCubicVolume), fSurfaceArea(rhs.fSurfaceArea),
xSemiAxis(rhs.xSemiAxis), ySemiAxis(rhs.ySemiAxis),
zSemiAxis(rhs.zSemiAxis), semiAxisMax(rhs.semiAxisMax),
zBottomCut(rhs.zBottomCut), zTopCut(rhs.zTopCut)
{
}
///////////////////////////////////////////////////////////////////////////////
//
// Assignment operator
G4Ellipsoid& G4Ellipsoid::operator = (const G4Ellipsoid& rhs)
{
// Check assignment to self
//
if (this == &rhs) { return *this; }
// Copy base class data
//
G4VSolid::operator=(rhs);
// Copy data
//
kRadTolerance = rhs.kRadTolerance;
halfCarTolerance = rhs.halfCarTolerance;
halfRadTolerance = rhs.halfRadTolerance;
fCubicVolume = rhs.fCubicVolume; fSurfaceArea = rhs.fSurfaceArea;
xSemiAxis = rhs.xSemiAxis; ySemiAxis = rhs.ySemiAxis;
zSemiAxis = rhs.zSemiAxis; semiAxisMax = rhs.semiAxisMax;
zBottomCut = rhs.zBottomCut; zTopCut = rhs.zTopCut;
fRebuildPolyhedron = false;
delete fpPolyhedron; fpPolyhedron = 0;
return *this;
}
////////////////////////////////////////////////////////////////////////
//
// Dispatch to parameterisation for replication mechanism dimension
// computation & modification.
void G4Ellipsoid::ComputeDimensions(G4VPVParameterisation* p,
const G4int n,
const G4VPhysicalVolume* pRep)
{
p->ComputeDimensions(*this,n,pRep);
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate extent under transform and specified limit
G4bool
G4Ellipsoid::CalculateExtent(const EAxis pAxis,
const G4VoxelLimits& pVoxelLimit,
const G4AffineTransform& pTransform,
G4double& pMin, G4double& pMax) const
{
if (!pTransform.IsRotated())
{
// Special case handling for unrotated solid ellipsoid
// 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 maxDiff,newMin,newMax;
G4double xoff,yoff;
xoffset=pTransform.NetTranslation().x();
xMin=xoffset - xSemiAxis;
xMax=xoffset + xSemiAxis;
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 - ySemiAxis;
yMax=yoffset + ySemiAxis;
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 + (-zSemiAxis > zBottomCut ? -zSemiAxis : zBottomCut);
zMax=zoffset + ( zSemiAxis < zTopCut ? zSemiAxis : zTopCut);
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();
}
}
}
// if here, then known to cut bounding box around ellipsoid
//
xoff = (xoffset < xMin) ? (xMin-xoffset)
: (xoffset > xMax) ? (xoffset-xMax) : 0.0;
yoff = (yoffset < yMin) ? (yMin-yoffset)
: (yoffset > yMax) ? (yoffset-yMax) : 0.0;
// detailed calculations
// NOTE: does not use X or Y offsets to adjust Z range,
// and does not use Z offset to adjust X or Y range,
// which is consistent with G4Sphere::CalculateExtent behavior
//
switch (pAxis)
{
case kXAxis:
if (yoff==0.)
{
// YZ limits cross max/min x => no change
//
pMin=xMin;
pMax=xMax;
}
else
{
// YZ limits don't cross max/min x => compute max delta x,
// hence new mins/maxs
//
maxDiff= 1.0-sqr(yoff/ySemiAxis);
if (maxDiff < 0.0) { return false; }
maxDiff= xSemiAxis * std::sqrt(maxDiff);
newMin=xoffset-maxDiff;
newMax=xoffset+maxDiff;
pMin=(newMin<xMin) ? xMin : newMin;
pMax=(newMax>xMax) ? xMax : newMax;
}
break;
case kYAxis:
if (xoff==0.)
{
// XZ limits cross max/min y => no change
//
pMin=yMin;
pMax=yMax;
}
else
{
// XZ limits don't cross max/min y => compute max delta y,
// hence new mins/maxs
//
maxDiff= 1.0-sqr(xoff/xSemiAxis);
if (maxDiff < 0.0) { return false; }
maxDiff= ySemiAxis * std::sqrt(maxDiff);
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 // not rotated
{
G4int i,j,noEntries,noBetweenSections;
G4bool existsAfterClip=false;
// Calculate rotated vertex coordinates
G4int noPolygonVertices=0;
G4ThreeVectorList* 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 G4Ellipsoid::Inside(const G4ThreeVector& p) const
{
G4double rad2oo, // outside surface outer tolerance
rad2oi; // outside surface inner tolerance
EInside in;
// check this side of z cut first, because that's fast
//
if (p.z() < zBottomCut-halfRadTolerance) { return in=kOutside; }
if (p.z() > zTopCut+halfRadTolerance) { return in=kOutside; }
rad2oo= sqr(p.x()/(xSemiAxis+halfRadTolerance))
+ sqr(p.y()/(ySemiAxis+halfRadTolerance))
+ sqr(p.z()/(zSemiAxis+halfRadTolerance));
if (rad2oo > 1.0) { return in=kOutside; }
rad2oi= sqr(p.x()*(1.0+halfRadTolerance/xSemiAxis)/xSemiAxis)
+ sqr(p.y()*(1.0+halfRadTolerance/ySemiAxis)/ySemiAxis)
+ sqr(p.z()*(1.0+halfRadTolerance/zSemiAxis)/zSemiAxis);
// Check radial surfaces
// sets `in' (already checked for rad2oo > 1.0)
//
if (rad2oi < 1.0)
{
in = ( (p.z() < zBottomCut+halfRadTolerance)
|| (p.z() > zTopCut-halfRadTolerance) ) ? kSurface : kInside;
if ( rad2oi > 1.0-halfRadTolerance ) { in=kSurface; }
}
else
{
in = kSurface;
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
//
// Return unit normal of surface closest to p not protected against p=0
G4ThreeVector G4Ellipsoid::SurfaceNormal( const G4ThreeVector& p) const
{
G4double distR, distZBottom, distZTop;
// normal vector with special magnitude: parallel to normal, units 1/length
// norm*p == 1.0 if on surface, >1.0 if outside, <1.0 if inside
//
G4ThreeVector norm(p.x()/(xSemiAxis*xSemiAxis),
p.y()/(ySemiAxis*ySemiAxis),
p.z()/(zSemiAxis*zSemiAxis));
G4double radius = 1.0/norm.mag();
// approximate distance to curved surface
//
distR = std::fabs( (p*norm - 1.0) * radius ) / 2.0;
// Distance to z-cut plane
//
distZBottom = std::fabs( p.z() - zBottomCut );
distZTop = std::fabs( p.z() - zTopCut );
if ( (distZBottom < distR) || (distZTop < distR) )
{
return G4ThreeVector(0.,0.,(distZBottom < distZTop) ? -1.0 : 1.0);
}
return ( norm *= radius );
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate distance to shape from outside, along normalised vector
// - return kInfinity if no intersection, or intersection distance <= tolerance
//
G4double G4Ellipsoid::DistanceToIn( const G4ThreeVector& p,
const G4ThreeVector& v ) const
{
G4double distMin = std::min(xSemiAxis,ySemiAxis);
const G4double dRmax = 100.*std::min(distMin,zSemiAxis);
distMin= kInfinity;
// check to see if Z plane is relevant
if (p.z() <= zBottomCut+halfCarTolerance)
{
if (v.z() <= 0.0) { return distMin; }
G4double distZ = (zBottomCut - p.z()) / v.z();
if ( (distZ > -halfRadTolerance) && (Inside(p+distZ*v) != kOutside) )
{
// early exit since can't intercept curved surface if we reach here
if ( std::fabs(distZ) < halfRadTolerance ) { distZ=0.; }
return distMin= distZ;
}
}
if (p.z() >= zTopCut-halfCarTolerance)
{
if (v.z() >= 0.0) { return distMin;}
G4double distZ = (zTopCut - p.z()) / v.z();
if ( (distZ > -halfRadTolerance) && (Inside(p+distZ*v) != kOutside) )
{
// early exit since can't intercept curved surface if we reach here
if ( std::fabs(distZ) < halfRadTolerance ) { distZ=0.; }
return distMin= distZ;
}
}
// if fZCut1 <= p.z() <= fZCut2, then must hit curved surface
// now check curved surface intercept
G4double A,B,C;
A= sqr(v.x()/xSemiAxis) + sqr(v.y()/ySemiAxis) + sqr(v.z()/zSemiAxis);
C= sqr(p.x()/xSemiAxis) + sqr(p.y()/ySemiAxis) + sqr(p.z()/zSemiAxis) - 1.0;
B= 2.0 * ( p.x()*v.x()/(xSemiAxis*xSemiAxis)
+ p.y()*v.y()/(ySemiAxis*ySemiAxis)
+ p.z()*v.z()/(zSemiAxis*zSemiAxis) );
C= B*B - 4.0*A*C;
if (C > 0.0)
{
G4double distR= (-B - std::sqrt(C)) / (2.0*A);
G4double intZ = p.z()+distR*v.z();
if ( (distR > halfRadTolerance)
&& (intZ >= zBottomCut-halfRadTolerance)
&& (intZ <= zTopCut+halfRadTolerance) )
{
distMin = distR;
}
else if( (distR >- halfRadTolerance)
&& (intZ >= zBottomCut-halfRadTolerance)
&& (intZ <= zTopCut+halfRadTolerance) )
{
// p is on the curved surface, DistanceToIn returns 0 or kInfinity:
// DistanceToIn returns 0, if second root is positive (means going inside)
// If second root is negative, DistanceToIn returns kInfinity (outside)
//
distR = (-B + std::sqrt(C) ) / (2.0*A);
if(distR>0.) { distMin=0.; }
}
else
{
distR= (-B + std::sqrt(C)) / (2.0*A);
intZ = p.z()+distR*v.z();
if ( (distR > halfRadTolerance)
&& (intZ >= zBottomCut-halfRadTolerance)
&& (intZ <= zTopCut+halfRadTolerance) )
{
G4ThreeVector norm=SurfaceNormal(p);
if (norm.dot(v)<0.) { distMin = distR; }
}
}
if ( (distMin!=kInfinity) && (distMin>dRmax) )
{ // Avoid rounding errors due to precision issues on
// 64 bits systems. Split long distances and recompute
G4double fTerm = distMin-std::fmod(distMin,dRmax);
distMin = fTerm + DistanceToIn(p+fTerm*v,v);
}
}
if (std::fabs(distMin)<halfRadTolerance) { distMin=0.; }
return distMin;
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate distance (<= actual) to closest surface of shape from outside
// - Return 0 if point inside
G4double G4Ellipsoid::DistanceToIn(const G4ThreeVector& p) const
{
G4double distR, distZ;
// normal vector: parallel to normal, magnitude 1/(characteristic radius)
//
G4ThreeVector norm(p.x()/(xSemiAxis*xSemiAxis),
p.y()/(ySemiAxis*ySemiAxis),
p.z()/(zSemiAxis*zSemiAxis));
G4double radius= 1.0/norm.mag();
// approximate distance to curved surface ( <= actual distance )
//
distR= (p*norm - 1.0) * radius / 2.0;
// Distance to z-cut plane
//
distZ= zBottomCut - p.z();
if (distZ < 0.0)
{
distZ = p.z() - zTopCut;
}
// Distance to closest surface from outside
//
if (distZ < 0.0)
{
return (distR < 0.0) ? 0.0 : distR;
}
else if (distR < 0.0)
{
return distZ;
}
else
{
return (distZ < distR) ? distZ : distR;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate distance to surface of shape from `inside', allowing for tolerance
G4double G4Ellipsoid::DistanceToOut(const G4ThreeVector& p,
const G4ThreeVector& v,
const G4bool calcNorm,
G4bool *validNorm,
G4ThreeVector *n ) const
{
G4double distMin;
enum surface_e {kPlaneSurf, kCurvedSurf, kNoSurf} surface;
distMin= kInfinity;
surface= kNoSurf;
// check to see if Z plane is relevant
//
if (v.z() < 0.0)
{
G4double distZ = (zBottomCut - p.z()) / v.z();
if (distZ < 0.0)
{
distZ= 0.0;
if (!calcNorm) {return 0.0;}
}
distMin= distZ;
surface= kPlaneSurf;
}
if (v.z() > 0.0)
{
G4double distZ = (zTopCut - p.z()) / v.z();
if (distZ < 0.0)
{
distZ= 0.0;
if (!calcNorm) {return 0.0;}
}
distMin= distZ;
surface= kPlaneSurf;
}
// normal vector: parallel to normal, magnitude 1/(characteristic radius)
//
G4ThreeVector nearnorm(p.x()/(xSemiAxis*xSemiAxis),
p.y()/(ySemiAxis*ySemiAxis),
p.z()/(zSemiAxis*zSemiAxis));
// now check curved surface intercept
//
G4double A,B,C;
A= sqr(v.x()/xSemiAxis) + sqr(v.y()/ySemiAxis) + sqr(v.z()/zSemiAxis);
C= (p * nearnorm) - 1.0;
B= 2.0 * (v * nearnorm);
C= B*B - 4.0*A*C;
if (C > 0.0)
{
G4double distR= (-B + std::sqrt(C) ) / (2.0*A);
if (distR < 0.0)
{
distR= 0.0;
if (!calcNorm) {return 0.0;}
}
if (distR < distMin)
{
distMin= distR;
surface= kCurvedSurf;
}
}
// set normal if requested
//
if (calcNorm)
{
if (surface == kNoSurf)
{
*validNorm = false;
}
else
{
*validNorm = true;
switch (surface)
{
case kPlaneSurf:
*n= G4ThreeVector(0.,0.,(v.z() > 0.0 ? 1. : -1.));
break;
case kCurvedSurf:
{
G4ThreeVector pexit= p + distMin*v;
G4ThreeVector truenorm(pexit.x()/(xSemiAxis*xSemiAxis),
pexit.y()/(ySemiAxis*ySemiAxis),
pexit.z()/(zSemiAxis*zSemiAxis));
truenorm *= 1.0/truenorm.mag();
*n= truenorm;
} break;
default: // Should never reach this case ...
DumpInfo();
std::ostringstream message;
G4int oldprc = message.precision(16);
message << "Undefined side for valid surface normal to solid."
<< G4endl
<< "Position:" << G4endl
<< " p.x() = " << p.x()/mm << " mm" << G4endl
<< " p.y() = " << p.y()/mm << " mm" << G4endl
<< " p.z() = " << p.z()/mm << " mm" << G4endl
<< "Direction:" << G4endl << G4endl
<< " v.x() = " << v.x() << G4endl
<< " v.y() = " << v.y() << G4endl
<< " v.z() = " << v.z() << G4endl
<< "Proposed distance :" << G4endl
<< " distMin = " << distMin/mm << " mm";
message.precision(oldprc);
G4Exception("G4Ellipsoid::DistanceToOut(p,v,..)",
"GeomSolids1002", JustWarning, message);
break;
}
}
}
return distMin;
}
///////////////////////////////////////////////////////////////////////////////
//
// Calculate distance (<=actual) to closest surface of shape from inside
G4double G4Ellipsoid::DistanceToOut(const G4ThreeVector& p) const
{
G4double distR, distZ;
#ifdef G4SPECSDEBUG
if( Inside(p) == kOutside )
{
DumpInfo();
std::ostringstream message;
G4int oldprc = message.precision(16);
message << "Point p is outside !?" << G4endl
<< "Position:" << G4endl
<< " p.x() = " << p.x()/mm << " mm" << G4endl
<< " p.y() = " << p.y()/mm << " mm" << G4endl
<< " p.z() = " << p.z()/mm << " mm";
message.precision(oldprc) ;
G4Exception("G4Ellipsoid::DistanceToOut(p)", "GeomSolids1002",
JustWarning, message);
}
#endif
// Normal vector: parallel to normal, magnitude 1/(characteristic radius)
//
G4ThreeVector norm(p.x()/(xSemiAxis*xSemiAxis),
p.y()/(ySemiAxis*ySemiAxis),
p.z()/(zSemiAxis*zSemiAxis));
// the following is a safe inlined "radius= min(1.0/norm.mag(),p.mag())
//
G4double radius= p.mag();
G4double tmp= norm.mag();
if ( (tmp > 0.0) && (1.0 < radius*tmp) ) {radius = 1.0/tmp;}
// Approximate distance to curved surface ( <= actual distance )
//
distR = (1.0 - p*norm) * radius / 2.0;
// Distance to z-cut plane
//
distZ = p.z() - zBottomCut;
if (distZ < 0.0) {distZ= zTopCut - p.z();}
// Distance to closest surface from inside
//
if ( (distZ < 0.0) || (distR < 0.0) )
{
return 0.0;
}
else
{
return (distZ < distR) ? distZ : distR;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// 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*
G4Ellipsoid::CreateRotatedVertices(const G4AffineTransform& pTransform,
G4int& noPolygonVertices) const
{
G4ThreeVectorList *vertices;
G4ThreeVector vertex;
G4double meshAnglePhi, meshRMaxFactor,
crossAnglePhi, coscrossAnglePhi, sincrossAnglePhi, sAnglePhi;
G4double meshTheta, crossTheta, startTheta;
G4double rMaxX, rMaxY, rMaxZ, rMaxMax, rx, ry, rz;
G4int crossSectionPhi, noPhiCrossSections, crossSectionTheta, noThetaSections;
// Phi cross sections
//
noPhiCrossSections=G4int (twopi/kMeshAngleDefault)+1; // = 9!
/*
if (noPhiCrossSections<kMinMeshSections) // <3
{
noPhiCrossSections=kMinMeshSections;
}
else if (noPhiCrossSections>kMaxMeshSections) // >37
{
noPhiCrossSections=kMaxMeshSections;
}
*/
meshAnglePhi=twopi/(noPhiCrossSections-1);
// Set start angle such that mesh will be at fRMax
// on the x axis. Will give better extent calculations when not rotated.
sAnglePhi = -meshAnglePhi*0.5;
// Theta cross sections
noThetaSections = G4int(pi/kMeshAngleDefault)+3; // = 7!
/*
if (noThetaSections<kMinMeshSections) // <3
{
noThetaSections=kMinMeshSections;
}
else if (noThetaSections>kMaxMeshSections) // >37
{
noThetaSections=kMaxMeshSections;
}
*/
meshTheta= pi/(noThetaSections-2);
// Set start angle such that mesh will be at fRMax
// on the z axis. Will give better extent calculations when not rotated.
startTheta = -meshTheta*0.5;
meshRMaxFactor = 1.0/std::cos(0.5*
std::sqrt(meshAnglePhi*meshAnglePhi+meshTheta*meshTheta));
rMaxMax= (xSemiAxis > ySemiAxis ? xSemiAxis : ySemiAxis);
if (zSemiAxis > rMaxMax) rMaxMax= zSemiAxis;
rMaxX= xSemiAxis + rMaxMax*(meshRMaxFactor-1.0);
rMaxY= ySemiAxis + rMaxMax*(meshRMaxFactor-1.0);
rMaxZ= zSemiAxis + rMaxMax*(meshRMaxFactor-1.0);
G4double* cosCrossTheta = new G4double[noThetaSections];
G4double* sinCrossTheta = new G4double[noThetaSections];
vertices=new G4ThreeVectorList(noPhiCrossSections*noThetaSections);
if (vertices && cosCrossTheta && sinCrossTheta)
{
for (crossSectionTheta=0; crossSectionTheta<noThetaSections;
crossSectionTheta++)
{
// Compute sine and cosine table (for historical reasons)
//
crossTheta=startTheta+crossSectionTheta*meshTheta;
cosCrossTheta[crossSectionTheta]=std::cos(crossTheta);
sinCrossTheta[crossSectionTheta]=std::sin(crossTheta);
}
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
//
rx= sinCrossTheta[crossSectionTheta]*coscrossAnglePhi*rMaxX;
ry= sinCrossTheta[crossSectionTheta]*sincrossAnglePhi*rMaxY;
rz= cosCrossTheta[crossSectionTheta]*rMaxZ;
if (rz < zBottomCut)
{ rz= zBottomCut; }
if (rz > zTopCut)
{ rz= zTopCut; }
vertex= G4ThreeVector(rx,ry,rz);
vertices->push_back(pTransform.TransformPoint(vertex));
} // Theta forward
} // Phi
noPolygonVertices = noThetaSections ;
}
else
{
DumpInfo();
G4Exception("G4Ellipsoid::CreateRotatedVertices()",
"GeomSolids0003", FatalException,
"Error in allocation of vertices. Out of memory !");
}
delete[] cosCrossTheta;
delete[] sinCrossTheta;
return vertices;
}
//////////////////////////////////////////////////////////////////////////
//
// G4EntityType
G4GeometryType G4Ellipsoid::GetEntityType() const
{
return G4String("G4Ellipsoid");
}
//////////////////////////////////////////////////////////////////////////
//
// Make a clone of the object
G4VSolid* G4Ellipsoid::Clone() const
{
return new G4Ellipsoid(*this);
}
//////////////////////////////////////////////////////////////////////////
//
// Stream object contents to an output stream
std::ostream& G4Ellipsoid::StreamInfo( std::ostream& os ) const
{
G4int oldprc = os.precision(16);
os << "-----------------------------------------------------------\n"
<< " *** Dump for solid - " << GetName() << " ***\n"
<< " ===================================================\n"
<< " Solid type: G4Ellipsoid\n"
<< " Parameters: \n"
<< " semi-axis x: " << xSemiAxis/mm << " mm \n"
<< " semi-axis y: " << ySemiAxis/mm << " mm \n"
<< " semi-axis z: " << zSemiAxis/mm << " mm \n"
<< " max semi-axis: " << semiAxisMax/mm << " mm \n"
<< " lower cut plane level z: " << zBottomCut/mm << " mm \n"
<< " upper cut plane level z: " << zTopCut/mm << " mm \n"
<< "-----------------------------------------------------------\n";
os.precision(oldprc);
return os;
}
////////////////////////////////////////////////////////////////////
//
// GetPointOnSurface
G4ThreeVector G4Ellipsoid::GetPointOnSurface() const
{
G4double aTop, aBottom, aCurved, chose, xRand, yRand, zRand, phi;
G4double cosphi, sinphi, costheta, sintheta, alpha, beta, max1, max2, max3;
max1 = xSemiAxis > ySemiAxis ? xSemiAxis : ySemiAxis;
max1 = max1 > zSemiAxis ? max1 : zSemiAxis;
if (max1 == xSemiAxis) { max2 = ySemiAxis; max3 = zSemiAxis; }
else if (max1 == ySemiAxis) { max2 = xSemiAxis; max3 = zSemiAxis; }
else { max2 = xSemiAxis; max3 = ySemiAxis; }
phi = RandFlat::shoot(0.,twopi);
cosphi = std::cos(phi); sinphi = std::sin(phi);
costheta = RandFlat::shoot(zBottomCut,zTopCut)/zSemiAxis;
sintheta = std::sqrt(1.-sqr(costheta));
alpha = 1.-sqr(max2/max1); beta = 1.-sqr(max3/max1);
aTop = pi*xSemiAxis*ySemiAxis*(1 - sqr(zTopCut/zSemiAxis));
aBottom = pi*xSemiAxis*ySemiAxis*(1 - sqr(zBottomCut/zSemiAxis));
// approximation
// from:" http://www.citr.auckland.ac.nz/techreports/2004/CITR-TR-139.pdf"
aCurved = 4.*pi*max1*max2*(1.-1./6.*(alpha+beta)-
1./120.*(3.*sqr(alpha)+2.*alpha*beta+3.*sqr(beta)));
aCurved *= 0.5*(1.2*zTopCut/zSemiAxis - 1.2*zBottomCut/zSemiAxis);
if( ( zTopCut >= zSemiAxis && zBottomCut <= -1.*zSemiAxis )
|| ( zTopCut == 0 && zBottomCut ==0 ) )
{
aTop = 0; aBottom = 0;
}
chose = RandFlat::shoot(0.,aTop + aBottom + aCurved);
if(chose < aCurved)
{
xRand = xSemiAxis*sintheta*cosphi;
yRand = ySemiAxis*sintheta*sinphi;
zRand = zSemiAxis*costheta;
return G4ThreeVector (xRand,yRand,zRand);
}
else if(chose >= aCurved && chose < aCurved + aTop)
{
xRand = RandFlat::shoot(-1.,1.)*xSemiAxis
* std::sqrt(1-sqr(zTopCut/zSemiAxis));
yRand = RandFlat::shoot(-1.,1.)*ySemiAxis
* std::sqrt(1.-sqr(zTopCut/zSemiAxis)-sqr(xRand/xSemiAxis));
zRand = zTopCut;
return G4ThreeVector (xRand,yRand,zRand);
}
else
{
xRand = RandFlat::shoot(-1.,1.)*xSemiAxis
* std::sqrt(1-sqr(zBottomCut/zSemiAxis));
yRand = RandFlat::shoot(-1.,1.)*ySemiAxis
* std::sqrt(1.-sqr(zBottomCut/zSemiAxis)-sqr(xRand/xSemiAxis));
zRand = zBottomCut;
return G4ThreeVector (xRand,yRand,zRand);
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Methods for visualisation
void G4Ellipsoid::DescribeYourselfTo (G4VGraphicsScene& scene) const
{
scene.AddSolid(*this);
}
G4VisExtent G4Ellipsoid::GetExtent() const
{
// Define the sides of the box into which the G4Ellipsoid instance would fit.
//
return G4VisExtent (-semiAxisMax, semiAxisMax,
-semiAxisMax, semiAxisMax,
-semiAxisMax, semiAxisMax);
}
G4Polyhedron* G4Ellipsoid::CreatePolyhedron () const
{
return new G4PolyhedronEllipsoid(xSemiAxis, ySemiAxis, zSemiAxis,
zBottomCut, zTopCut);
}
G4Polyhedron* G4Ellipsoid::GetPolyhedron () const
{
if (!fpPolyhedron ||
fRebuildPolyhedron ||
fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
fpPolyhedron->GetNumberOfRotationSteps())
{
G4AutoLock l(&polyhedronMutex);
delete fpPolyhedron;
fpPolyhedron = CreatePolyhedron();
fRebuildPolyhedron = false;
l.unlock();
}
return fpPolyhedron;
}