Import Geant4 0.0.0 source tree
This commit is contained in:
@@ -0,0 +1,732 @@
|
||||
// This code implementation is the intellectual property of
|
||||
// the RD44 GEANT4 collaboration.
|
||||
//
|
||||
// By copying, distributing or modifying the Program (or any work
|
||||
// based on the Program) you indicate your acceptance of this statement,
|
||||
// and all its terms.
|
||||
//
|
||||
// $Id: G4Box.cc,v 2.3 1998/10/09 13:24:45 japost Exp $
|
||||
// GEANT4 tag $Name: geant4-00 $
|
||||
//
|
||||
//
|
||||
//
|
||||
// Implementation for G4Box class
|
||||
//
|
||||
// 24.06.98 - V. Grichine: insideEdge in DistanceToIn(p,v)
|
||||
// 20.09.98 - V.Grichine: new algorithm of DistanceToIn(p,v)
|
||||
|
||||
#include "G4Box.hh"
|
||||
|
||||
#include "G4VoxelLimits.hh"
|
||||
#include "G4AffineTransform.hh"
|
||||
|
||||
#include "G4VPVParameterisation.hh"
|
||||
|
||||
#include "G4VGraphicsScene.hh"
|
||||
#include "G4Polyhedron.hh"
|
||||
#include "G4NURBS.hh"
|
||||
#include "G4NURBSbox.hh"
|
||||
#include "G4VisExtent.hh"
|
||||
|
||||
// Constructor - check & set half widths
|
||||
G4Box::G4Box(const G4String& pName, G4double pX,
|
||||
G4double pY, G4double pZ) : G4CSGSolid(pName)
|
||||
{
|
||||
if (pX>0&&pY>0&&pZ>0)
|
||||
{
|
||||
fDx=pX; fDy=pY; fDz=pZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
G4Exception("Error in G4Box::Box - negative parameters");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Destructor
|
||||
G4Box::~G4Box()
|
||||
{}
|
||||
|
||||
// Dispatch to parameterisation for replication mechanism dimension
|
||||
// computation & modification.
|
||||
void G4Box::ComputeDimensions(G4VPVParameterisation* p,
|
||||
const G4int n,
|
||||
const G4VPhysicalVolume* pRep)
|
||||
{
|
||||
p->ComputeDimensions(*this,n,pRep);
|
||||
}
|
||||
|
||||
|
||||
// Calculate extent under transform and specified limit
|
||||
G4bool G4Box::CalculateExtent(const EAxis pAxis,
|
||||
const G4VoxelLimits& pVoxelLimit,
|
||||
const G4AffineTransform& pTransform,
|
||||
G4double& pMin, G4double& pMax) const
|
||||
{
|
||||
if (!pTransform.IsRotated())
|
||||
{
|
||||
// Special case handling for unrotated boxes
|
||||
// Compute x/y/z mins and maxs respecting limits, with early returns
|
||||
// if outside limits. Then switch() on pAxis
|
||||
G4double xoffset,xMin,xMax;
|
||||
G4double yoffset,yMin,yMax;
|
||||
G4double zoffset,zMin,zMax;
|
||||
|
||||
xoffset=pTransform.NetTranslation().x();
|
||||
xMin=xoffset-fDx;
|
||||
xMax=xoffset+fDx;
|
||||
if (pVoxelLimit.IsXLimited())
|
||||
{
|
||||
if (xMin>pVoxelLimit.GetMaxXExtent()
|
||||
||xMax<pVoxelLimit.GetMinXExtent())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xMin<pVoxelLimit.GetMinXExtent())
|
||||
{
|
||||
xMin=pVoxelLimit.GetMinXExtent();
|
||||
}
|
||||
if (xMax>pVoxelLimit.GetMaxXExtent())
|
||||
{
|
||||
xMax=pVoxelLimit.GetMaxXExtent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
yoffset=pTransform.NetTranslation().y();
|
||||
yMin=yoffset-fDy;
|
||||
yMax=yoffset+fDy;
|
||||
if (pVoxelLimit.IsYLimited())
|
||||
{
|
||||
if (yMin>pVoxelLimit.GetMaxYExtent()
|
||||
||yMax<pVoxelLimit.GetMinYExtent())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (yMin<pVoxelLimit.GetMinYExtent())
|
||||
{
|
||||
yMin=pVoxelLimit.GetMinYExtent();
|
||||
}
|
||||
if (yMax>pVoxelLimit.GetMaxYExtent())
|
||||
{
|
||||
yMax=pVoxelLimit.GetMaxYExtent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
zoffset=pTransform.NetTranslation().z();
|
||||
zMin=zoffset-fDz;
|
||||
zMax=zoffset+fDz;
|
||||
if (pVoxelLimit.IsZLimited())
|
||||
{
|
||||
if (zMin>pVoxelLimit.GetMaxZExtent()
|
||||
||zMax<pVoxelLimit.GetMinZExtent())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zMin<pVoxelLimit.GetMinZExtent())
|
||||
{
|
||||
zMin=pVoxelLimit.GetMinZExtent();
|
||||
}
|
||||
if (zMax>pVoxelLimit.GetMaxZExtent())
|
||||
{
|
||||
zMax=pVoxelLimit.GetMaxZExtent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch (pAxis)
|
||||
{
|
||||
case kXAxis:
|
||||
pMin=xMin;
|
||||
pMax=xMax;
|
||||
break;
|
||||
case kYAxis:
|
||||
pMin=yMin;
|
||||
pMax=yMax;
|
||||
break;
|
||||
case kZAxis:
|
||||
pMin=zMin;
|
||||
pMax=zMax;
|
||||
break;
|
||||
}
|
||||
|
||||
pMin-=kCarTolerance;
|
||||
pMax+=kCarTolerance;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// General rotated case - create and clip mesh to boundaries
|
||||
|
||||
G4bool existsAfterClip=false;
|
||||
G4ThreeVectorList *vertices;
|
||||
|
||||
pMin=+kInfinity;
|
||||
pMax=-kInfinity;
|
||||
// Calculate rotated vertex coordinates
|
||||
|
||||
vertices=CreateRotatedVertices(pTransform);
|
||||
ClipCrossSection(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
|
||||
ClipCrossSection(vertices,4,pVoxelLimit,pAxis,pMin,pMax);
|
||||
ClipBetweenSections(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
|
||||
|
||||
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, using tolerance
|
||||
EInside G4Box::Inside(const G4ThreeVector& p) const
|
||||
{
|
||||
EInside in=kOutside;
|
||||
|
||||
if (fabs(p.x())<=fDx-kCarTolerance*0.5)
|
||||
{
|
||||
if (fabs(p.y())<=fDy-kCarTolerance*0.5)
|
||||
{
|
||||
if (fabs(p.z())<=fDz-kCarTolerance*0.5)
|
||||
{
|
||||
in=kInside;
|
||||
}
|
||||
else if (fabs(p.z())<=fDz+kCarTolerance*0.5)
|
||||
{
|
||||
in=kSurface;
|
||||
}
|
||||
}
|
||||
else if (fabs(p.y())<=fDy+kCarTolerance*0.5)
|
||||
{
|
||||
if (fabs(p.z())<=fDz+kCarTolerance*0.5)
|
||||
{
|
||||
in=kSurface;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fabs(p.x())<=fDx+kCarTolerance*0.5)
|
||||
{
|
||||
if (fabs(p.y())<=fDy+kCarTolerance*0.5)
|
||||
{
|
||||
if (fabs(p.z())<=fDz+kCarTolerance*0.5)
|
||||
{
|
||||
in=kSurface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
// Calculate side nearest to p, and return normal
|
||||
// If two sides are equidistant, normal of first side (x/y/z)
|
||||
// encountered returned
|
||||
G4ThreeVector G4Box::SurfaceNormal( const G4ThreeVector& p) const
|
||||
{
|
||||
G4double distx,disty,distz;
|
||||
G4ThreeVector norm;
|
||||
// Calculate distances as if in 1st octant
|
||||
distx=fabs(fabs(p.x())-fDx);
|
||||
disty=fabs(fabs(p.y())-fDy);
|
||||
distz=fabs(fabs(p.z())-fDz);
|
||||
|
||||
if (distx<=disty)
|
||||
{
|
||||
if (distx<=distz)
|
||||
{
|
||||
// Closest to X
|
||||
if (p.x()<0) norm=G4ThreeVector(-1.0,0,0);
|
||||
else norm=G4ThreeVector(1.0,0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Closest to Z
|
||||
if (p.z()<0) norm=G4ThreeVector(0,0,-1.0);
|
||||
else norm=G4ThreeVector(0,0,1.0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (disty<=distz)
|
||||
{
|
||||
// Closest to Y
|
||||
if (p.y()<0) norm=G4ThreeVector(0,-1.0,0);
|
||||
else norm=G4ThreeVector(0,1.0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Closest to Z
|
||||
if (p.z()<0) norm=G4ThreeVector(0,0,-1.0);
|
||||
else norm=G4ThreeVector(0,0,1.0);
|
||||
}
|
||||
}
|
||||
|
||||
return norm;
|
||||
}
|
||||
|
||||
// Calculate distance to box from an outside point
|
||||
// - return kInfinity if no intersection.
|
||||
//
|
||||
// ALGORITHM:
|
||||
//
|
||||
// Check that if point lies outside x/y/z extent of box, travel is towards
|
||||
// the box (ie. there is a possibility of an intersection)
|
||||
//
|
||||
// Calculate pairs of minimum and maximum distances for x/y/z travel for
|
||||
// intersection with the box's x/y/z extent.
|
||||
// If there is a valid intersection, it is given by the maximum min distance
|
||||
// (ie. distance to satisfy x/y/z intersections) *if* <= minimum max distance
|
||||
// (ie. distance after which 1+ of x/y/z intersections not satisfied)
|
||||
//
|
||||
// NOTE:
|
||||
//
|
||||
// `Inside' safe - meaningful answers given if point is inside the exact
|
||||
// shape.
|
||||
|
||||
G4double G4Box::DistanceToIn(const G4ThreeVector& p,const G4ThreeVector& v) const
|
||||
{
|
||||
G4double safx, safy, safz ;
|
||||
G4double smin=0.0, sminy, sminz ; // , sminx ;
|
||||
G4double smax=kInfinity, smaxy, smaxz ; // , smaxx ; // they always > 0
|
||||
G4double stmp ;
|
||||
G4double sOut=kInfinity, sOuty=kInfinity, sOutz=kInfinity ;
|
||||
|
||||
safx = fabs(p.x()) - fDx ; // minimum distance to x surface of shape
|
||||
safy = fabs(p.y()) - fDy ;
|
||||
safz = fabs(p.z()) - fDz ;
|
||||
|
||||
// Will we intersect?
|
||||
// If safx/y/z is >-tol/2 the point is outside/on the box's x/y/z extent.
|
||||
// If both p.x/y/z and v.x/y/z repectively are both positive/negative,
|
||||
// travel is in a direction away from the shape.
|
||||
|
||||
if ( ((p.x()*v.x() >= 0.0) && safx > -kCarTolerance*0.5)
|
||||
|| ((p.y()*v.y() >= 0.0) && safy > -kCarTolerance*0.5)
|
||||
|| ((p.z()*v.z() >= 0.0) && safz > -kCarTolerance*0.5) )
|
||||
{
|
||||
return kInfinity ; // travel away or parallel within tolerance
|
||||
}
|
||||
|
||||
// Compute min / max distances for x/y/z travel:
|
||||
// X Planes
|
||||
|
||||
if ( v.x())
|
||||
{
|
||||
stmp = 1.0/fabs(v.x()) ;
|
||||
|
||||
if (safx >= 0.0)
|
||||
{
|
||||
smin = safx*stmp ;
|
||||
smax = (fDx+fabs(p.x()))*stmp ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.x() > 0)
|
||||
{
|
||||
sOut = (fDx - p.x())*stmp ;
|
||||
}
|
||||
if (v.x() < 0)
|
||||
{
|
||||
sOut = (fDx + p.x())*stmp ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Y Planes
|
||||
|
||||
if ( v.y())
|
||||
{
|
||||
stmp = 1.0/fabs(v.y()) ;
|
||||
|
||||
if (safy >= 0.0)
|
||||
{
|
||||
sminy = safy*stmp ;
|
||||
smaxy = (fDy+fabs(p.y()))*stmp ;
|
||||
|
||||
if (sminy > smin) smin=sminy ;
|
||||
|
||||
if (smaxy < smax) smax=smaxy ;
|
||||
|
||||
if (smin >= smax-kCarTolerance*0.5)
|
||||
{
|
||||
return kInfinity ; // touch XY corner
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.y() > 0)
|
||||
{
|
||||
sOuty = (fDy - p.y())*stmp ;
|
||||
}
|
||||
if (v.y() < 0)
|
||||
{
|
||||
sOuty = (fDy + p.y())*stmp ;
|
||||
}
|
||||
if( sOuty < sOut ) sOut = sOuty ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Z planes
|
||||
|
||||
if ( v.z() )
|
||||
{
|
||||
stmp = 1.0/fabs(v.z()) ;
|
||||
|
||||
if ( safz >= 0.0)
|
||||
{
|
||||
sminz = safz*stmp ;
|
||||
smaxz = (fDz+fabs(p.z()))*stmp ;
|
||||
|
||||
if (sminz > smin) smin = sminz ;
|
||||
|
||||
if (smaxz < smax) smax = smaxz ;
|
||||
|
||||
if (smin >= smax-kCarTolerance*0.5)
|
||||
{
|
||||
return kInfinity ; // touch ZX or ZY corners
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.z() > 0)
|
||||
{
|
||||
sOutz = (fDz - p.z())*stmp ;
|
||||
}
|
||||
if (v.z() < 0)
|
||||
{
|
||||
sOutz = (fDz + p.z())*stmp ;
|
||||
}
|
||||
if( sOutz < sOut ) sOut = sOutz ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sOut <= smin + 0.5*kCarTolerance) // travel over edge
|
||||
{
|
||||
return kInfinity ;
|
||||
}
|
||||
|
||||
if (smin < 0)
|
||||
{
|
||||
smin= 0.0;
|
||||
}
|
||||
return smin ;
|
||||
}
|
||||
|
||||
// Appoximate distance to box.
|
||||
// Returns largest perpendicular distance to the closest x/y/z sides of
|
||||
// the box, which is the most fast estimation of the shortest distance to box
|
||||
// - If inside return 0
|
||||
|
||||
G4double G4Box::DistanceToIn(const G4ThreeVector& p) const
|
||||
{
|
||||
G4double safex,safey,safez,safe=0.0;
|
||||
safex=fabs(p.x())-fDx;
|
||||
safey=fabs(p.y())-fDy;
|
||||
safez=fabs(p.z())-fDz;
|
||||
|
||||
if (safex>safe) safe=safex;
|
||||
if (safey>safe) safe=safey;
|
||||
if (safez>safe) safe=safez;
|
||||
return safe;
|
||||
}
|
||||
|
||||
// Calcluate distance to surface of box from inside
|
||||
// by calculating distances to box's x/y/z planes.
|
||||
// Smallest distance is exact distance to exiting.
|
||||
// - Eliminate one side of each pair by considering direction of v
|
||||
// - when leaving a surface & v.close, return 0
|
||||
|
||||
G4double G4Box::DistanceToOut(const G4ThreeVector& p,const G4ThreeVector& v,
|
||||
const G4bool calcNorm,
|
||||
G4bool *validNorm,G4ThreeVector *n) const
|
||||
{
|
||||
ESide side;
|
||||
G4double pdist,stmp,snxt;
|
||||
|
||||
if (calcNorm) *validNorm = true ; // All normals are valid
|
||||
|
||||
// X planes --------------------------------------------
|
||||
|
||||
if (v.x() > 0)
|
||||
{
|
||||
pdist = fDx-p.x() ;
|
||||
|
||||
if (pdist > kCarTolerance*0.5)
|
||||
{
|
||||
snxt=pdist/v.x();
|
||||
side=kPX;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(1,0,0);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
else if (v.x() < 0)
|
||||
{
|
||||
pdist = fDx + p.x() ;
|
||||
|
||||
if (pdist > kCarTolerance*0.5)
|
||||
{
|
||||
snxt=-pdist/v.x();
|
||||
side=kMX;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(-1,0,0);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snxt=kInfinity;
|
||||
}
|
||||
|
||||
// Y planes ------------------------------------------
|
||||
|
||||
if (v.y()>0)
|
||||
{
|
||||
pdist=fDy-p.y();
|
||||
|
||||
if (pdist>kCarTolerance*0.5)
|
||||
{
|
||||
stmp=pdist/v.y();
|
||||
|
||||
if (stmp<snxt)
|
||||
{
|
||||
snxt=stmp;
|
||||
side=kPY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(0,1,0);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
else if (v.y()<0)
|
||||
{
|
||||
pdist=fDy+p.y();
|
||||
|
||||
if (pdist>kCarTolerance*0.5)
|
||||
{
|
||||
stmp=-pdist/v.y();
|
||||
|
||||
if (stmp<snxt)
|
||||
{
|
||||
snxt=stmp;
|
||||
side=kMY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(0,-1,0);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
|
||||
// Z planes -----------------------------------------------
|
||||
|
||||
if (v.z()>0)
|
||||
{
|
||||
pdist=fDz-p.z();
|
||||
|
||||
if (pdist>kCarTolerance*0.5)
|
||||
{
|
||||
stmp=pdist/v.z();
|
||||
|
||||
if (stmp<snxt)
|
||||
{
|
||||
snxt=stmp;
|
||||
side=kPZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(0,0,1);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
else if (v.z()<0)
|
||||
{
|
||||
pdist=fDz+p.z();
|
||||
|
||||
if (pdist>kCarTolerance*0.5)
|
||||
{
|
||||
stmp=-pdist/v.z();
|
||||
|
||||
if (stmp<snxt)
|
||||
{
|
||||
snxt=stmp;
|
||||
side=kMZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (calcNorm)
|
||||
{
|
||||
*n=G4ThreeVector(0,0,-1);
|
||||
}
|
||||
return snxt=0;
|
||||
}
|
||||
}
|
||||
if (calcNorm)
|
||||
{
|
||||
switch (side)
|
||||
{
|
||||
case kPX:
|
||||
*n=G4ThreeVector(1,0,0);
|
||||
break;
|
||||
case kMX:
|
||||
*n=G4ThreeVector(-1,0,0);
|
||||
break;
|
||||
case kPY:
|
||||
*n=G4ThreeVector(0,1,0);
|
||||
break;
|
||||
case kMY:
|
||||
*n=G4ThreeVector(0,-1,0);
|
||||
break;
|
||||
case kPZ:
|
||||
*n=G4ThreeVector(0,0,1);
|
||||
break;
|
||||
case kMZ:
|
||||
*n=G4ThreeVector(0,0,-1);
|
||||
break;
|
||||
default:
|
||||
G4Exception("Invalid enum in G4Box::CalcNormal");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return snxt;
|
||||
}
|
||||
|
||||
// Calculate exact shortest distance to any boundary from inside
|
||||
// - If outside return 0
|
||||
G4double G4Box::DistanceToOut(const G4ThreeVector& p) const
|
||||
{
|
||||
G4double safx1,safx2,safy1,safy2,safz1,safz2,safe;
|
||||
|
||||
safx1=fDx-p.x();
|
||||
safx2=fDx+p.x();
|
||||
safy1=fDy-p.y();
|
||||
safy2=fDy+p.y();
|
||||
safz1=fDz-p.z();
|
||||
safz2=fDz+p.z();
|
||||
|
||||
// shortest Dist to any boundary now MIN(safx1,safx2,safy1..)
|
||||
if (safx2<safx1) safe=safx2;
|
||||
else safe=safx1;
|
||||
if (safy1<safe) safe=safy1;
|
||||
if (safy2<safe) safe=safy2;
|
||||
if (safz1<safe) safe=safz1;
|
||||
if (safz2<safe) safe=safz2;
|
||||
|
||||
if (safe<0) safe=0;
|
||||
return safe;
|
||||
}
|
||||
|
||||
// Create a List containing the transformed vertices
|
||||
// Ordering [0-3] -fDz cross section
|
||||
// [4-7] +fDz cross section such that [0] is below [4],
|
||||
// [1] below [5] etc.
|
||||
// Note:
|
||||
// Caller has deletion resposibility
|
||||
|
||||
G4ThreeVectorList*
|
||||
G4Box::CreateRotatedVertices(const G4AffineTransform& pTransform) const
|
||||
{
|
||||
G4ThreeVectorList *vertices;
|
||||
vertices=new G4ThreeVectorList(8);
|
||||
if (vertices)
|
||||
{
|
||||
G4ThreeVector vertex0(-fDx,-fDy,-fDz);
|
||||
G4ThreeVector vertex1(fDx,-fDy,-fDz);
|
||||
G4ThreeVector vertex2(fDx,fDy,-fDz);
|
||||
G4ThreeVector vertex3(-fDx,fDy,-fDz);
|
||||
G4ThreeVector vertex4(-fDx,-fDy,fDz);
|
||||
G4ThreeVector vertex5(fDx,-fDy,fDz);
|
||||
G4ThreeVector vertex6(fDx,fDy,fDz);
|
||||
G4ThreeVector vertex7(-fDx,fDy,fDz);
|
||||
|
||||
vertices->insert(pTransform.TransformPoint(vertex0));
|
||||
vertices->insert(pTransform.TransformPoint(vertex1));
|
||||
vertices->insert(pTransform.TransformPoint(vertex2));
|
||||
vertices->insert(pTransform.TransformPoint(vertex3));
|
||||
vertices->insert(pTransform.TransformPoint(vertex4));
|
||||
vertices->insert(pTransform.TransformPoint(vertex5));
|
||||
vertices->insert(pTransform.TransformPoint(vertex6));
|
||||
vertices->insert(pTransform.TransformPoint(vertex7));
|
||||
}
|
||||
else
|
||||
{
|
||||
G4Exception("G4Box::CreateRotatedVertices Out of memory - Cannot alloc vertices");
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
void G4Box::DescribeYourselfTo (G4VGraphicsScene& scene) const {
|
||||
scene.AddThis (*this);
|
||||
}
|
||||
|
||||
G4VisExtent G4Box::GetExtent() const {
|
||||
return G4VisExtent (-fDx, fDx, -fDy, fDy, -fDz, fDz);
|
||||
}
|
||||
|
||||
G4Polyhedron* G4Box::CreatePolyhedron () const {
|
||||
return new G4PolyhedronBox (fDx, fDy, fDz);
|
||||
}
|
||||
|
||||
G4NURBS* G4Box::CreateNURBS () const {
|
||||
return new G4NURBSbox (fDx, fDy, fDz);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// This code implementation is the intellectual property of
|
||||
// the RD44 GEANT4 collaboration.
|
||||
//
|
||||
// By copying, distributing or modifying the Program (or any work
|
||||
// based on the Program) you indicate your acceptance of this statement,
|
||||
// and all its terms.
|
||||
//
|
||||
// $Id: G4CSGSolid.cc,v 2.0 1998/07/02 17:02:11 gunter Exp $
|
||||
// GEANT4 tag $Name: geant4-00 $
|
||||
//
|
||||
#include "G4CSGSolid.hh"
|
||||
|
||||
// Constructor
|
||||
// - Base class constructor
|
||||
|
||||
G4CSGSolid::G4CSGSolid(const G4String& name) :
|
||||
G4VSolid(name)
|
||||
{
|
||||
}
|
||||
|
||||
G4CSGSolid::~G4CSGSolid()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// G4ClippablePolygon.cc
|
||||
//
|
||||
// Based on code from G4VSolid (P. Kent, V. Grichine, J. Allison)
|
||||
//
|
||||
|
||||
#include "G4ClippablePolygon.hh"
|
||||
|
||||
#include "G4VoxelLimits.hh"
|
||||
|
||||
//
|
||||
// AddVertexInOrder
|
||||
//
|
||||
void G4ClippablePolygon::AddVertexInOrder( const G4ThreeVector vertex )
|
||||
{
|
||||
vertices.append( vertex );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ClearAllVertices
|
||||
//
|
||||
void G4ClippablePolygon::ClearAllVertices()
|
||||
{
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Clip
|
||||
//
|
||||
void G4ClippablePolygon::Clip( const G4VoxelLimits &voxelLimit )
|
||||
{
|
||||
//
|
||||
// Heh. Do we have anything to do?
|
||||
//
|
||||
if (!voxelLimit.IsLimited()) return;
|
||||
|
||||
//
|
||||
// Loop over all axes
|
||||
//
|
||||
static EAxis axes[3] = { kXAxis, kYAxis, kZAxis };
|
||||
|
||||
EAxis *axis = axes;
|
||||
do {
|
||||
if (voxelLimit.IsLimited(*axis)) {
|
||||
G4ThreeVectorList tempPolygon;
|
||||
|
||||
//
|
||||
// Build a "simple" voxelLimit that includes only the min extent
|
||||
// and apply this to our vertices, producing result in tempPolygon
|
||||
//
|
||||
G4VoxelLimits simpleLimit1;
|
||||
simpleLimit1.AddLimit( *axis, voxelLimit.GetMinExtent(*axis), kInfinity );
|
||||
ClipToSimpleLimits( vertices, tempPolygon, simpleLimit1 );
|
||||
|
||||
//
|
||||
// If nothing is left from the above clip, we might as well return now
|
||||
// (but with an empty vertices)
|
||||
//
|
||||
if (tempPolygon.entries() == 0) {
|
||||
vertices.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Now do the same, but using a "simple" limit that includes only the max extent.
|
||||
// Apply this to out tempPolygon, producing result in vertices.
|
||||
//
|
||||
G4VoxelLimits simpleLimit2;
|
||||
simpleLimit2.AddLimit( *axis, -kInfinity, voxelLimit.GetMaxExtent(*axis) );
|
||||
ClipToSimpleLimits( tempPolygon, vertices, simpleLimit2 );
|
||||
|
||||
//
|
||||
// If nothing is left, return now
|
||||
//
|
||||
if (vertices.entries() == 0) return;
|
||||
}
|
||||
} while( ++axis < axes + sizeof(axes)/sizeof(EAxis) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// GetExtent
|
||||
//
|
||||
void G4ClippablePolygon::GetExtent( const EAxis axis,
|
||||
G4double &min, G4double &max )
|
||||
{
|
||||
G4int noLeft = vertices.entries();
|
||||
|
||||
G4int i;
|
||||
for( i=0; i<noLeft; i++ ) {
|
||||
G4double component = vertices(i).operator()( axis );
|
||||
if (component < min )
|
||||
min = component;
|
||||
else if (component > max )
|
||||
max = component;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pVoxelLimits must be only limited along one axis, and either the maximum
|
||||
// along the axis must be +kInfinity, or the minimum -kInfinity
|
||||
void G4ClippablePolygon::ClipToSimpleLimits( G4ThreeVectorList& pPolygon,
|
||||
G4ThreeVectorList& outputPolygon,
|
||||
const G4VoxelLimits& pVoxelLimit )
|
||||
{
|
||||
G4int i;
|
||||
G4int noVertices=pPolygon.entries();
|
||||
G4ThreeVector vEnd,vStart;
|
||||
|
||||
outputPolygon.clear();
|
||||
|
||||
for (i=0;i<noVertices;i++)
|
||||
{
|
||||
vStart=pPolygon(i);
|
||||
if (i==noVertices-1)
|
||||
{
|
||||
vEnd=pPolygon(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vEnd=pPolygon(i+1);
|
||||
}
|
||||
|
||||
if (pVoxelLimit.Inside(vStart))
|
||||
{
|
||||
if (pVoxelLimit.Inside(vEnd))
|
||||
{
|
||||
// vStart and vEnd inside -> output end point
|
||||
outputPolygon.insert(vEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// vStart inside, vEnd outside -> output crossing point
|
||||
pVoxelLimit.ClipToLimits(vStart,vEnd);
|
||||
outputPolygon.insert(vEnd);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pVoxelLimit.Inside(vEnd))
|
||||
{
|
||||
// vStart outside, vEnd inside -> output inside section
|
||||
pVoxelLimit.ClipToLimits(vStart,vEnd);
|
||||
outputPolygon.insert(vStart);
|
||||
outputPolygon.insert(vEnd);
|
||||
}
|
||||
else
|
||||
// Both point outside -> no output
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// G4EnclosingCylinder.cc
|
||||
//
|
||||
// Implementation of a utility class for a quick check of geometry
|
||||
//
|
||||
|
||||
#include "G4EnclosingCylinder.hh"
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
G4EnclosingCylinder::G4EnclosingCylinder( const G4double r[], const G4double z[], const G4int n,
|
||||
const G4bool thePhiIsOpen,
|
||||
const G4double theStartPhi, const G4double theTotalPhi )
|
||||
{
|
||||
//
|
||||
// Obtain larges r and smallest and larges z
|
||||
//
|
||||
radius = r[0];
|
||||
zLo = zHi = z[0];
|
||||
const G4double *rr = r, *zz = z;
|
||||
while( ++zz, ++rr < r+n ) {
|
||||
if (*rr > radius) radius = *rr;
|
||||
if (*zz > zHi ) zHi = *zz;
|
||||
if (*zz < zLo ) zLo = *zz;
|
||||
}
|
||||
|
||||
//
|
||||
// Save phi info
|
||||
//
|
||||
if ( phiIsOpen = thePhiIsOpen ) {
|
||||
startPhi = theStartPhi;
|
||||
totalPhi = theTotalPhi;
|
||||
|
||||
rx1 = cos(startPhi);
|
||||
ry1 = sin(startPhi);
|
||||
dx1 = +ry1*10*kCarTolerance;
|
||||
dy1 = -rx1*10*kCarTolerance;
|
||||
|
||||
rx2 = cos(startPhi+totalPhi);
|
||||
ry2 = sin(startPhi+totalPhi);
|
||||
dx2 = -ry2*10*kCarTolerance;
|
||||
dy2 = +rx2*10*kCarTolerance;
|
||||
}
|
||||
|
||||
//
|
||||
// Add safety
|
||||
//
|
||||
radius += 10*kCarTolerance;
|
||||
zLo -= 10*kCarTolerance;
|
||||
zHi += 10*kCarTolerance;
|
||||
}
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4EnclosingCylinder::~G4EnclosingCylinder() {;}
|
||||
|
||||
|
||||
//
|
||||
// Outside
|
||||
//
|
||||
// Decide very rapidly if the point is outside the cylinder
|
||||
//
|
||||
// If one is not certain, return false
|
||||
//
|
||||
G4bool G4EnclosingCylinder::Outside( const G4ThreeVector &p ) const
|
||||
{
|
||||
if (p.perp() > radius) return true;
|
||||
if (p.z() < zLo) return true;
|
||||
if (p.z() > zHi) return true;
|
||||
|
||||
if (phiIsOpen) {
|
||||
if ( ((p.x()-dx1)*ry1 - (p.y()-dy1)*rx1) > 0) return false;
|
||||
if ( ((p.x()-dx2)*ry2 - (p.y()-dy2)*rx2) < 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Misses
|
||||
//
|
||||
// Decide very rapidly if the trajectory is going to miss the cylinder
|
||||
//
|
||||
// If one is not sure, return false
|
||||
//
|
||||
G4bool G4EnclosingCylinder::Misses( const G4ThreeVector &p, const G4ThreeVector &v ) const
|
||||
{
|
||||
if (!Outside(p)) return false;
|
||||
|
||||
G4double cross = p.x()*v.y() - p.y()*v.x();
|
||||
if (cross > radius) return true;
|
||||
|
||||
if (p.perp() > radius) {
|
||||
G4double dot = p.x()*v.x() + p.y()*v.y();
|
||||
if (dot > 0) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,289 @@
|
||||
//
|
||||
// G4IntersectingCone.cc
|
||||
//
|
||||
// Implementation of a utility class which calculates the intersection
|
||||
// of an arbitrary line with a fixed cone
|
||||
//
|
||||
#include "G4IntersectingCone.hh"
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
G4IntersectingCone::G4IntersectingCone( const G4double r[2], const G4double z[2] )
|
||||
{
|
||||
//
|
||||
// What type of cone are we?
|
||||
//
|
||||
type1 = (fabs(z[1]-z[0]) > fabs(r[1]-r[0]));
|
||||
|
||||
if (type1) {
|
||||
B = (r[1]-r[0])/(z[1]-z[0]); // tube like
|
||||
A = 0.5*( r[1]+r[0] - B*(z[1]+z[0]) );
|
||||
}
|
||||
else {
|
||||
B = (z[1]-z[0])/(r[1]-r[0]); // disk like
|
||||
A = 0.5*( z[1]+z[0] - B*(r[1]+r[0]) );
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate extent
|
||||
//
|
||||
if (r[0] < r[1]) {
|
||||
rLo = r[0]; rHi = r[1];
|
||||
}
|
||||
else {
|
||||
rLo = r[1]; rHi = r[0];
|
||||
}
|
||||
|
||||
if (z[0] < z[1]) {
|
||||
zLo = z[0]; zHi = z[1];
|
||||
}
|
||||
else {
|
||||
zLo = z[1]; zHi = z[0];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4IntersectingCone::~G4IntersectingCone()
|
||||
{;}
|
||||
|
||||
|
||||
//
|
||||
// HitOn
|
||||
//
|
||||
// Check r or z extent, as appropriate, to see if the point is possibly
|
||||
// on the cone.
|
||||
//
|
||||
G4bool G4IntersectingCone::HitOn( const G4double r, const G4double z )
|
||||
{
|
||||
//
|
||||
// Be careful! The inequalities cannot be "<=" and ">=" here without
|
||||
// punching a tiny hole in our shape!
|
||||
//
|
||||
if (type1) {
|
||||
if (z < zLo || z > zHi) return false;
|
||||
}
|
||||
else {
|
||||
if (r < rLo || r > rHi) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LineHitsCone
|
||||
//
|
||||
// Calculate the intersection of a line with our conical surface, ignoring
|
||||
// any phi division
|
||||
//
|
||||
G4int G4IntersectingCone::LineHitsCone( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
G4double *s1, G4double *s2 )
|
||||
{
|
||||
if (type1) {
|
||||
return LineHitsCone1( p, v, s1, s2 );
|
||||
}
|
||||
else {
|
||||
return LineHitsCone2( p, v, s1, s2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LineHitsCone1
|
||||
//
|
||||
// Calculate the intersections of a line with a conical surface. Only
|
||||
// suitable if zPlane[0] != zPlane[1].
|
||||
//
|
||||
// Equation of a line:
|
||||
//
|
||||
// x = x0 + s*tx y = y0 + s*ty z = z0 + s*tz
|
||||
//
|
||||
// Equation of a conical surface:
|
||||
//
|
||||
// x**2 + y**2 = (A + B*z)**2
|
||||
//
|
||||
// Solution is quadratic:
|
||||
//
|
||||
// a*s**2 + b*s + c = 0
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// a = x0**2 + y0**2 - (A + B*z0)**2
|
||||
//
|
||||
// b = 2*( x0*tx + y0*ty - (A*B - B*B*z0)*tz)
|
||||
//
|
||||
// c = tx**2 + ty**2 - (B*tz)**2
|
||||
//
|
||||
// Notice, that if a < 0, this indicates that the two solutions (assuming
|
||||
// they exist) are in opposite cones (that is, given z0 = -A/B, one z < z0
|
||||
// and the other z > z0). For our shapes, the invalid solution is one
|
||||
// which produces A + Bz < 0, or the one where Bz is smallest (most negative).
|
||||
// Since Bz = B*s*tz, if B*tz > 0, we want the largest s, otherwise,
|
||||
// the smaller.
|
||||
//
|
||||
// If there are two solutions on one side of the cone, we want to make
|
||||
// sure that they are on the "correct" side, that is A + B*z0 + s*B*tz >= 0.
|
||||
//
|
||||
// If a = 0, we have a linear problem: s = c/b, which again gives one solution.
|
||||
// This should be rare.
|
||||
//
|
||||
// For b*b - 4*a*c = 0, we also have one solution, which is almost always
|
||||
// a line just grazing the surface of a the cone, which we want to ignore.
|
||||
// However, there are two other, very rare, possibilities:
|
||||
// a line intersecting the z axis and either:
|
||||
// 1. At the same angle atan(B) to just miss one side of the cone, or
|
||||
// 2. Intersecting the cone apex (0,0,-A/B)
|
||||
// We *don't* want to miss these! How do we identify them? Well, since
|
||||
// this case is rare, we can at least swallow a little more CPU than we would
|
||||
// normally be comfortable with. Intersection with the z axis means
|
||||
// x0*tx + y0*ty = 0. Case (1) means a==0, and we've already dealt with that
|
||||
// above. Case (2) means a < 0.
|
||||
//
|
||||
// Now: x0*tx + y0*ty = 0 in terms of roundoff error. We can write:
|
||||
// Delta = x0*tx + y0*ty
|
||||
// b = 2*( Delta - (A*B + B*B*z0)*tz )
|
||||
// For:
|
||||
// b*b - 4*a*c = epsilon
|
||||
// where epsilon is small, then:
|
||||
// Delta = epsilon/2/B
|
||||
//
|
||||
G4int G4IntersectingCone::LineHitsCone1( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
G4double *s1, G4double *s2 )
|
||||
{
|
||||
G4double x0 = p.x(), y0 = p.y(), z0 = p.z();
|
||||
G4double tx = v.x(), ty = v.y(), tz = v.z();
|
||||
|
||||
G4double a = tx*tx + ty*ty - sqr(B*tz);
|
||||
G4double b = 2*( x0*tx + y0*ty - (A*B + B*B*z0)*tz);
|
||||
G4double c = x0*x0 + y0*y0 - sqr(A + B*z0);
|
||||
|
||||
G4double radical = b*b - 4*a*c;
|
||||
|
||||
if (radical < -1E-6) return 0; // No solution
|
||||
|
||||
if (radical < 1E-6) {
|
||||
//
|
||||
// The radical is roughly zero: check for special, very rare, cases
|
||||
//
|
||||
if ( (fabs(x0*tx + y0*ty) < fabs(1E-6/B)) && (a < 1/kInfinity) ) {
|
||||
*s1 = -0.5*b/a;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
radical = sqrt(radical);
|
||||
|
||||
if (a > 1/kInfinity) {
|
||||
G4double sa, sb, q = -0.5*( b + (b < 0 ? -radical : +radical) );
|
||||
sa = q/a;
|
||||
sb = c/q;
|
||||
if (sa < sb) { *s1 = sa; *s2 = sb; } else { *s1 = sb; *s2 = sa; }
|
||||
if (B*(z0+(*s1)*tz) < -A) return 0;
|
||||
return 2;
|
||||
}
|
||||
else if (a < -1/kInfinity) {
|
||||
G4double sa, sb, q = -0.5*( b + (b < 0 ? -radical : +radical) );
|
||||
sa = q/a;
|
||||
sb = c/q;
|
||||
*s1 = (B*tz > 0)^(sa > sb) ? sb : sa;
|
||||
return 1;
|
||||
}
|
||||
else if (fabs(b) < 1/kInfinity) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
*s1 = -c/b;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LineHitsCone2
|
||||
//
|
||||
// See comments under LineHitsCone1. In this routine, case2, we have:
|
||||
//
|
||||
// Z = A + B*R
|
||||
//
|
||||
// The solution is still quadratic:
|
||||
//
|
||||
// a = tz**2 - B*B*(tx**2 + ty**2)
|
||||
//
|
||||
// b = 2*( (z0-A)*tz - B*B*(x0*tx+y0*ty) )
|
||||
//
|
||||
// c = ( (z0-A)**2 - B*B*(x0**2 + y0**2) )
|
||||
//
|
||||
// The rest is much the same, except some details.
|
||||
//
|
||||
// a > 0 now means we intersect only once in the correct hemisphere.
|
||||
//
|
||||
// a > 0 ? We only want solution which produces R > 0.
|
||||
// since R = (z0+s*tz-A)/B, for tz/B > 0, this is the largest s
|
||||
// for tz/B < 0, this is the smallest s
|
||||
// thus, same as in case 1 ( since sign(tz/B) = sign(tz*B) )
|
||||
//
|
||||
G4int G4IntersectingCone::LineHitsCone2( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
G4double *s1, G4double *s2 )
|
||||
{
|
||||
G4double x0 = p.x(), y0 = p.y(), z0 = p.z();
|
||||
G4double tx = v.x(), ty = v.y(), tz = v.z();
|
||||
|
||||
//
|
||||
// Special case which might not be so rare: B = 0 (precisely)
|
||||
//
|
||||
if (B==0) {
|
||||
if (fabs(tz) < 1/kInfinity) return 0;
|
||||
|
||||
*s1 = (A-z0)/tz;
|
||||
return 1;
|
||||
}
|
||||
|
||||
G4double B2 = B*B;
|
||||
|
||||
G4double a = tz*tz - B2*(tx*tx + ty*ty);
|
||||
G4double b = 2*( (z0-A)*tz - B2*(x0*tx + y0*ty) );
|
||||
G4double c = sqr(z0-A) - B2*( x0*x0 + y0*y0 );
|
||||
|
||||
G4double radical = b*b - 4*a*c;
|
||||
|
||||
if (radical < -1E-6) return 0; // No solution
|
||||
|
||||
if (radical < 1E-6) {
|
||||
//
|
||||
// The radical is roughly zero: check for special, very rare, cases
|
||||
//
|
||||
if ( (fabs(x0*tx + y0*ty) < fabs(1E-6*B)) && (a < 1/kInfinity) ) {
|
||||
*s1 = -0.5*b/a;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
radical = sqrt(radical);
|
||||
|
||||
if (a < 1/kInfinity) {
|
||||
G4double sa, sb, q = -0.5*( b + (b < 0 ? -radical : +radical) );
|
||||
sa = q/a;
|
||||
sb = c/q;
|
||||
if (sa < sb) { *s1 = sa; *s2 = sb; } else { *s1 = sb; *s2 = sa; }
|
||||
if ((z0 + (*s1)*tz - A)/B < 0) return 0;
|
||||
return 2;
|
||||
}
|
||||
else if (a > -1/kInfinity) {
|
||||
G4double sa, sb, q = -0.5*( b + (b < 0 ? -radical : +radical) );
|
||||
sa = q/a;
|
||||
sb = c/q;
|
||||
*s1 = (tz*B > 0)^(sa > sb) ? sb : sa;
|
||||
return 1;
|
||||
}
|
||||
else if (fabs(b) < 1/kInfinity) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
*s1 = -c/b;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,534 @@
|
||||
//
|
||||
// G4PolyPhiFace.cc
|
||||
//
|
||||
// Implementation of the face that bounds a polycone or polyhedra at
|
||||
// its phi opening.
|
||||
//
|
||||
|
||||
#include "G4PolyPhiFace.hh"
|
||||
#include "G4ClippablePolygon.hh"
|
||||
#include "G4AffineTransform.hh"
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
// Points r,z should be supplied in clockwise order in r,z. For example:
|
||||
//
|
||||
// [1]---------[2] ^ R
|
||||
// | | |
|
||||
// | | +--> z
|
||||
// [0]---------[3]
|
||||
//
|
||||
G4PolyPhiFace::G4PolyPhiFace( const G4double *r, const G4double *z,
|
||||
const G4int n, const G4double phi,
|
||||
const G4double deltaPhi, const G4bool start )
|
||||
{
|
||||
//
|
||||
// Build radial vector
|
||||
//
|
||||
radial = G4ThreeVector( cos(phi), sin(phi), 0.0 );
|
||||
|
||||
//
|
||||
// Build normal
|
||||
//
|
||||
G4double zSign = start ? 1 : -1;
|
||||
normal = G4ThreeVector( zSign*radial.y(), -zSign*radial.x(), 0 );
|
||||
|
||||
//
|
||||
// Allocate corners
|
||||
//
|
||||
corners = new G4PolyPhiFaceVertex[n];
|
||||
|
||||
//
|
||||
// Fill their positions, avoiding duplicates
|
||||
//
|
||||
rMin = kInfinity; rMax = -kInfinity;
|
||||
zMin = kInfinity; zMax = -kInfinity;
|
||||
|
||||
const G4double *rOne = r, *zOne = z,
|
||||
*rNext, *zNext;
|
||||
G4PolyPhiFaceVertex *corn = corners;
|
||||
do {
|
||||
rNext = rOne + 1;
|
||||
zNext = zOne + 1;
|
||||
|
||||
if (rNext == r+n) {rNext = r; zNext = z;}
|
||||
|
||||
if (*rNext == *rOne && *zNext == *zOne) continue;
|
||||
|
||||
corn->r = *rOne;
|
||||
corn->z = *zOne;
|
||||
|
||||
corn++;
|
||||
|
||||
if (*rOne < rMin) rMin = *rOne;
|
||||
if (*rOne > rMax) rMax = *rOne;
|
||||
if (*zOne < zMin) zMin = *zOne;
|
||||
if (*zOne > zMax) zMax = *zOne;
|
||||
} while( rOne=rNext, zOne=zNext, rOne != r );
|
||||
|
||||
numEdges = corn-corners;
|
||||
|
||||
//
|
||||
// Allocate edges
|
||||
//
|
||||
edges = new G4PolyPhiFaceEdge[numEdges];
|
||||
|
||||
//
|
||||
// Fill them
|
||||
//
|
||||
G4double midPhi = phi + (start ? +0.5 : -0.5)*deltaPhi;
|
||||
G4double cosMid = cos(midPhi),
|
||||
sinMid = sin(midPhi);
|
||||
G4double rFact = cos(0.5*deltaPhi);
|
||||
G4ThreeVector sideNorm;
|
||||
|
||||
G4PolyPhiFaceVertex *prev = corners+numEdges-1,
|
||||
*here = corners;
|
||||
G4PolyPhiFaceEdge *edge = edges;
|
||||
do {
|
||||
edge->v0 = prev;
|
||||
edge->v1 = here;
|
||||
|
||||
G4double dr = here->r - prev->r,
|
||||
dz = here->z - prev->z;
|
||||
|
||||
edge->length = sqrt( dr*dr + dz*dz );
|
||||
|
||||
edge->tr = dr/edge->length;
|
||||
edge->tz = dz/edge->length;
|
||||
|
||||
sideNorm = G4ThreeVector( dz*rFact*cosMid, dz*rFact*sinMid, -dr );
|
||||
sideNorm = sideNorm.unit();
|
||||
sideNorm += normal;
|
||||
edge->norm3D = sideNorm.unit();
|
||||
} while( edge++, prev=here, ++here < corners+numEdges );
|
||||
|
||||
//
|
||||
// Go back an fill in corner "normals", which are just the
|
||||
// average of the normals of the ajoining edges
|
||||
//
|
||||
G4PolyPhiFaceEdge *prevEdge = edges+numEdges-1;
|
||||
edge = edges;
|
||||
do {
|
||||
G4double rPart = prevEdge->tr + edge->tr;
|
||||
G4double zPart = prevEdge->tz + edge->tz;
|
||||
G4double norm = sqrt( rPart*rPart + zPart*zPart );
|
||||
edge->v0->rNorm = +zPart/norm;
|
||||
edge->v0->zNorm = -rPart/norm;
|
||||
|
||||
//
|
||||
// Corner normal should be average of normals of connecting edges,
|
||||
// or, equivalently, the average of all connecting faces.
|
||||
//
|
||||
// prevEdge->norm3D = normal + side1.normal = A
|
||||
// edge->norm3D = normal + side2.normal = B
|
||||
// A + B - normal = normal + side1.normal + side2.normal
|
||||
//
|
||||
|
||||
G4ThreeVector norm3D = prevEdge->norm3D + edge->norm3D - normal;
|
||||
edge->v0->norm3D = norm3D.unit();
|
||||
} while( prevEdge=edge, ++edge < edges+numEdges );
|
||||
|
||||
//
|
||||
// Complain if something is obviously wrong
|
||||
//
|
||||
if (numEdges <= 2)
|
||||
G4Exception( "G4PolyPhiFace: more than two unique corners must be specified" );
|
||||
|
||||
//
|
||||
// Build point on surface
|
||||
//
|
||||
G4double rAve = 0.5*(rMax-rMin),
|
||||
zAve = 0.5*(zMax-zMin);
|
||||
surface = G4ThreeVector( rAve*radial.x(), rAve*radial.y(), zAve );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4PolyPhiFace::~G4PolyPhiFace()
|
||||
{
|
||||
delete [] edges;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Intersect
|
||||
//
|
||||
G4bool G4PolyPhiFace::Intersect( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
const G4bool outgoing, const G4double surfTolerance,
|
||||
G4double &distance, G4double &distFromSurface,
|
||||
G4ThreeVector &aNormal, G4bool &allBehind )
|
||||
{
|
||||
G4double normSign = outgoing ? +1 : -1;
|
||||
|
||||
//
|
||||
// These don't change
|
||||
//
|
||||
allBehind = true;
|
||||
aNormal = normal;
|
||||
|
||||
//
|
||||
// Correct normal? Here we have straight sides, and can safely ignore
|
||||
// intersections where the dot product with the normal is zero.
|
||||
//
|
||||
G4double dotProd = normSign*normal.dot(v);
|
||||
|
||||
if (dotProd <= 0) return false;
|
||||
|
||||
//
|
||||
// Calculate distance to surface. If the side is too far
|
||||
// behind the point, we must reject it.
|
||||
//
|
||||
G4ThreeVector ps = p - surface;
|
||||
distFromSurface = -normSign*ps.dot(normal);
|
||||
|
||||
if (distFromSurface < surfTolerance) return false;
|
||||
|
||||
//
|
||||
// Calculate precise distance to intersection with the side
|
||||
// (along the trajectory, not normal to the surface)
|
||||
//
|
||||
distance = distFromSurface/dotProd;
|
||||
|
||||
//
|
||||
// Calculate intersection point in r,z
|
||||
//
|
||||
G4ThreeVector ip = p + distance*v;
|
||||
|
||||
G4double r = radial.dot(ip);
|
||||
|
||||
//
|
||||
// And is it inside the r/z extent?
|
||||
//
|
||||
return InsideEdges( r, ip.z() );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Distance
|
||||
//
|
||||
G4double G4PolyPhiFace::Distance( const G4ThreeVector &p, const G4bool outgoing )
|
||||
{
|
||||
G4double normSign = outgoing ? +1 : -1;
|
||||
//
|
||||
// Correct normal?
|
||||
//
|
||||
G4ThreeVector ps = p - surface;
|
||||
G4double distPhi = -normSign*normal.dot(ps);
|
||||
|
||||
if (distPhi <= 0) return kInfinity;
|
||||
|
||||
//
|
||||
// Calculate projected point in r,z
|
||||
//
|
||||
G4double r = radial.dot(p);
|
||||
|
||||
//
|
||||
// Are we inside the face?
|
||||
//
|
||||
G4double distRZ2;
|
||||
|
||||
if (InsideEdges( r, p.z(), &distRZ2, 0 )) {
|
||||
//
|
||||
// Yup, answer is just distPhi
|
||||
//
|
||||
return distPhi;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Nope. Penalize by distance out
|
||||
//
|
||||
return sqrt( distPhi*distPhi + distRZ2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inside
|
||||
//
|
||||
EInside G4PolyPhiFace::Inside( const G4ThreeVector &p, const G4double tolerance,
|
||||
G4double *bestDistance )
|
||||
{
|
||||
//
|
||||
// Get distance along phi, which if negative means the point
|
||||
// is nominally inside the shape.
|
||||
//
|
||||
G4ThreeVector ps = p - surface;
|
||||
G4double distPhi = normal.dot(ps);
|
||||
|
||||
//
|
||||
// Calculate projected point in r,z
|
||||
//
|
||||
G4double r = radial.dot(p);
|
||||
|
||||
//
|
||||
// Are we inside the face?
|
||||
//
|
||||
G4double distRZ2;
|
||||
G4PolyPhiFaceVertex *base3Dnorm;
|
||||
G4ThreeVector *head3Dnorm;
|
||||
G4bool wereIn = InsideEdges( r, p.z(), &distRZ2, &base3Dnorm, &head3Dnorm );
|
||||
|
||||
if (wereIn) {
|
||||
//
|
||||
// Looks like we're inside. Distance is distance in phi.
|
||||
//
|
||||
*bestDistance = fabs(distPhi);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// We're outside the extent of the face,
|
||||
// so the distance is penalized by distance from edges in RZ
|
||||
//
|
||||
*bestDistance = sqrt( distPhi*distPhi + distRZ2 );
|
||||
}
|
||||
|
||||
//
|
||||
// Can we be on the surface? Yes, but only if we're inside, or
|
||||
// close to inside by tolerance
|
||||
//
|
||||
if (wereIn || distRZ2 < tolerance*tolerance ) {
|
||||
//
|
||||
// Yup, answer depends on distPhi, and we can use tolerance
|
||||
// to decide if we are on the surface
|
||||
//
|
||||
if (distPhi < -tolerance) return kInside;
|
||||
if (distPhi < tolerance) return kSurface;
|
||||
return kOutside;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Nope. we can only be in or out, and we must
|
||||
// used the edge normal to decide
|
||||
//
|
||||
G4ThreeVector cc( base3Dnorm->r*radial.x(),
|
||||
base3Dnorm->r*radial.y(),
|
||||
base3Dnorm->z );
|
||||
cc = p - cc;
|
||||
return head3Dnorm->dot(cc) < 0 ? kInside : kOutside;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Normal
|
||||
//
|
||||
// This virtual member is simple for our planer shape, which has only one normal
|
||||
//
|
||||
G4ThreeVector G4PolyPhiFace::Normal( const G4ThreeVector &p, G4double *bestDistance )
|
||||
{
|
||||
//
|
||||
// Get distance along phi, which if negative means the point
|
||||
// is nominally inside the shape.
|
||||
//
|
||||
G4double distPhi = normal.dot(p);
|
||||
|
||||
//
|
||||
// Calculate projected point in r,z
|
||||
//
|
||||
G4double r = radial.dot(p);
|
||||
|
||||
//
|
||||
// Are we inside the face?
|
||||
//
|
||||
G4double distRZ2;
|
||||
|
||||
if (InsideEdges( r, p.z(), &distRZ2, 0 )) {
|
||||
//
|
||||
// Yup, answer is just distPhi
|
||||
//
|
||||
*bestDistance = fabs(distPhi);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Nope. Penalize by distance out
|
||||
//
|
||||
*bestDistance = sqrt( distPhi*distPhi + distRZ2 );
|
||||
}
|
||||
|
||||
return normal;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Extent
|
||||
//
|
||||
// This actually isn't needed by polycone or polyhedra...
|
||||
//
|
||||
G4double G4PolyPhiFace::Extent( const G4ThreeVector axis )
|
||||
{
|
||||
G4double max = -kInfinity;
|
||||
|
||||
G4PolyPhiFaceVertex *corner = corners;
|
||||
do {
|
||||
G4double here = axis.x()*corner->r*radial.x()
|
||||
+ axis.y()*corner->r*radial.y()
|
||||
+ axis.z()*corner->z;
|
||||
if (here > max) max = here;
|
||||
} while( ++corner < corners + numEdges );
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CalculateExtent
|
||||
//
|
||||
// See notes in G4VCSGface
|
||||
//
|
||||
void G4PolyPhiFace::CalculateExtent( const EAxis axis,
|
||||
const G4VoxelLimits &voxelLimit,
|
||||
const G4AffineTransform &transform,
|
||||
G4double &min, G4double &max )
|
||||
{
|
||||
//
|
||||
// Construct a (sometimes big) clippable polygon,
|
||||
//
|
||||
// Perform the necessary transformations while doing so
|
||||
//
|
||||
G4ClippablePolygon polygon;
|
||||
|
||||
G4PolyPhiFaceVertex *corner = corners;
|
||||
do {
|
||||
G4ThreeVector point( 0, 0, corner->z );
|
||||
point += radial*corner->r;
|
||||
|
||||
polygon.AddVertexInOrder( transform.TransformPoint( point ) );
|
||||
} while( ++corner < corners + numEdges );
|
||||
|
||||
//
|
||||
// Clip away
|
||||
//
|
||||
polygon.Clip( voxelLimit );
|
||||
|
||||
//
|
||||
// Get extent
|
||||
//
|
||||
polygon.GetExtent( axis, min, max );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//-------------------------------------------------------
|
||||
|
||||
//
|
||||
// InsideEdges (don't care aboud distance)
|
||||
//
|
||||
// Decide if the point in r,z is inside the edges of our face
|
||||
//
|
||||
// This routine can be made a zillion times quicker by implementing
|
||||
// better code, for example:
|
||||
//
|
||||
// int pnpoly(int npol, float *xp, float *yp, float x, float y)
|
||||
// {
|
||||
// int i, j, c = 0;
|
||||
// for (i = 0, j = npol-1; i < npol; j = i++) {
|
||||
// if ((((yp[i]<=y) && (y<yp[j])) ||
|
||||
// ((yp[j]<=y) && (y<yp[i]))) &&
|
||||
// (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))
|
||||
//
|
||||
// c = !c;
|
||||
// }
|
||||
// return c;
|
||||
// }
|
||||
//
|
||||
// See "Point in Polyon Strategies", Eric Haines [Graphic Gems IV] pp. 24-46
|
||||
//
|
||||
// My algorithm below is rather unique, but is based on code needed to
|
||||
// calculate the distance to the shape. I left it in here because ...
|
||||
// well ... to test it better.
|
||||
//
|
||||
G4bool G4PolyPhiFace::InsideEdges( const G4double r, const G4double z )
|
||||
{
|
||||
//
|
||||
// Quick check of extent
|
||||
//
|
||||
if ( r < rMin || r > rMax ) return false;
|
||||
if ( z < zMin || z > zMax ) return false;
|
||||
|
||||
//
|
||||
// More thorough check
|
||||
//
|
||||
G4double notUsed;
|
||||
|
||||
return InsideEdges( r, z, ¬Used, 0 );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// InsideEdges (care about distance)
|
||||
//
|
||||
// Decide if the point in r,z is inside the edges of our face
|
||||
//
|
||||
G4bool G4PolyPhiFace::InsideEdges( const G4double r, const G4double z,
|
||||
G4double *bestDist2,
|
||||
G4PolyPhiFaceVertex **base3Dnorm,
|
||||
G4ThreeVector **head3Dnorm )
|
||||
{
|
||||
G4double bestDistance2 = kInfinity;
|
||||
G4bool answer;
|
||||
|
||||
G4PolyPhiFaceEdge *edge = edges;
|
||||
do {
|
||||
G4PolyPhiFaceVertex *testMe;
|
||||
//
|
||||
// Get distance perpendicular to the edge
|
||||
//
|
||||
G4double dr = (r-edge->v0->r), dz = (z-edge->v0->z);
|
||||
|
||||
G4double distOut = dr*edge->tz - dz*edge->tr;
|
||||
G4double distance2 = distOut*distOut;
|
||||
if (distance2 > bestDistance2) continue; // No hope!
|
||||
|
||||
//
|
||||
// Check to see if normal intersects edge within the edge's boundary
|
||||
//
|
||||
G4double s = dr*edge->tr + dz*edge->tz;
|
||||
|
||||
//
|
||||
// If it doesn't, penalize distance2 appropriately
|
||||
//
|
||||
if (s < 0) {
|
||||
distance2 += s*s;
|
||||
testMe = edge->v0;
|
||||
}
|
||||
else if (s > edge->length) {
|
||||
G4double s2 = s-edge->length;
|
||||
distance2 += s2*s2;
|
||||
testMe = edge->v1;
|
||||
}
|
||||
else {
|
||||
testMe = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Closest edge so far?
|
||||
//
|
||||
if (distance2 < bestDistance2) {
|
||||
bestDistance2 = distance2;
|
||||
if (testMe) {
|
||||
G4double distNorm = dr*testMe->rNorm + dz*testMe->zNorm;
|
||||
answer = (distNorm <= 0);
|
||||
if (base3Dnorm) {
|
||||
*base3Dnorm = testMe;
|
||||
*head3Dnorm = &testMe->norm3D;
|
||||
}
|
||||
}
|
||||
else {
|
||||
answer = (distOut <= 0);
|
||||
if (base3Dnorm) {
|
||||
*base3Dnorm = edge->v0;
|
||||
*head3Dnorm = &edge->norm3D;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while( ++edge < edges + numEdges );
|
||||
|
||||
*bestDist2 = bestDistance2;
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// G4Polycone.cc
|
||||
//
|
||||
// Implementation of a CSG polycone
|
||||
//
|
||||
#include "G4Polycone.hh"
|
||||
#include "G4PolyconeSide.hh"
|
||||
#include "G4PolyPhiFace.hh"
|
||||
|
||||
#include "G4Polyhedron.hh"
|
||||
|
||||
|
||||
//
|
||||
// Constructor (GEANT3 style parameters)
|
||||
//
|
||||
G4Polycone::G4Polycone( G4String name,
|
||||
const G4double phiStart,
|
||||
const G4double phiTotal,
|
||||
const G4int numZPlanes,
|
||||
const G4double zPlane[],
|
||||
const G4double rInner[],
|
||||
const G4double rOuter[] ) : G4VCSGfaceted( name )
|
||||
{
|
||||
//
|
||||
// Real ugly
|
||||
//
|
||||
original_parameters.exist = true;
|
||||
|
||||
original_parameters.Start_angle = phiStart;
|
||||
original_parameters.Opening_angle = phiTotal;
|
||||
original_parameters.Num_z_planes = numZPlanes;
|
||||
original_parameters.Z_values = new G4double[numZPlanes];
|
||||
original_parameters.Rmin = new G4double[numZPlanes];
|
||||
original_parameters.Rmax = new G4double[numZPlanes];
|
||||
G4int i;
|
||||
for (i=0; i<numZPlanes; i++) {
|
||||
original_parameters.Z_values[i] = zPlane[i];
|
||||
original_parameters.Rmin[i] = rInner[i];
|
||||
original_parameters.Rmax[i] = rOuter[i];
|
||||
}
|
||||
|
||||
//
|
||||
// Translate GEANT3 into generic parameters
|
||||
// Duplicate vertices and divided surfaces are (or should be) dealt with
|
||||
// by routine "Create."
|
||||
//
|
||||
G4double *r = new G4double[numZPlanes*2];
|
||||
G4double *z = new G4double[numZPlanes*2];
|
||||
|
||||
G4double *rOut = r + numZPlanes,
|
||||
*zOut = z + numZPlanes,
|
||||
*rIn = rOut-1,
|
||||
*zIn = zOut-1;
|
||||
|
||||
for( i=0; i < numZPlanes; i++, rOut++, zOut++, rIn--, zIn-- ) {
|
||||
*rOut = rOuter[i];
|
||||
*rIn = rInner[i];
|
||||
*zOut = *zIn = zPlane[i];
|
||||
}
|
||||
|
||||
Create( phiStart, phiTotal, numZPlanes*2, r, z );
|
||||
|
||||
delete [] r;
|
||||
delete [] z;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Constructor (generic parameters)
|
||||
//
|
||||
G4Polycone::G4Polycone( G4String name,
|
||||
const G4double phiStart,
|
||||
const G4double phiTotal,
|
||||
const G4int numRZ,
|
||||
const G4double r[],
|
||||
const G4double z[] ) : G4VCSGfaceted( name )
|
||||
{
|
||||
original_parameters.exist = false;
|
||||
|
||||
Create( phiStart, phiTotal, numRZ, r, z );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create
|
||||
//
|
||||
// Generic create routine, called by each constructor after conversion of arguments
|
||||
//
|
||||
void G4Polycone::Create( const G4double phiStart,
|
||||
const G4double phiTotal,
|
||||
const G4int numRZ,
|
||||
const G4double r[],
|
||||
const G4double z[] )
|
||||
{
|
||||
//
|
||||
// Phi opening? Account for some possible roundoff, and interpret
|
||||
// nonsense value as representing no phi opening
|
||||
//
|
||||
if (phiTotal <= 0 || phiTotal > 2.0*M_PI-1E-10) {
|
||||
phiIsOpen = false;
|
||||
startPhi = 0;
|
||||
endPhi = 2*M_PI;
|
||||
}
|
||||
else {
|
||||
phiIsOpen = true;
|
||||
|
||||
//
|
||||
// Convert phi into our convention
|
||||
//
|
||||
startPhi = phiStart;
|
||||
while( startPhi < 0 ) startPhi += 2*M_PI;
|
||||
|
||||
endPhi = phiStart+phiTotal;
|
||||
while( endPhi < startPhi ) endPhi += 2*M_PI;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate corner array. We may not end up using all of this array,
|
||||
// since we delete duplicate corners, but that's not so bad
|
||||
//
|
||||
corners = new G4PolyconeSideRZ[numRZ];
|
||||
|
||||
//
|
||||
// Copy corners, avoiding duplicates on the way
|
||||
//
|
||||
// We should also look for divided conical surfaces...
|
||||
// We must also look for overlapping surfaces...
|
||||
//
|
||||
G4PolyconeSideRZ *next = corners;
|
||||
const G4double *rOne = r;
|
||||
const G4double *zOne = z;
|
||||
const G4double *rNext, *zNext;
|
||||
G4bool notFinished;
|
||||
do {
|
||||
rNext = rOne + 1;
|
||||
zNext = zOne + 1;
|
||||
if (notFinished = (rNext < r+numRZ)) {
|
||||
if (*rNext == *rOne && *zNext == *zOne) continue;
|
||||
}
|
||||
|
||||
next->r = *rOne;
|
||||
next->z = *zOne;
|
||||
next++;
|
||||
} while( rOne=rNext, zOne=zNext, notFinished );
|
||||
|
||||
numCorner = next - corners;
|
||||
|
||||
//
|
||||
// Allocate face pointer array
|
||||
//
|
||||
numFace = phiIsOpen ? numCorner+2 : numCorner;
|
||||
faces = new G4VCSGface*[numFace];
|
||||
|
||||
//
|
||||
// Construct conical faces
|
||||
//
|
||||
// But! Don't construct a face if both points are at zero radius!
|
||||
//
|
||||
G4PolyconeSideRZ *corner = corners,
|
||||
*prev = corners + numCorner-1,
|
||||
*nextNext;
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
next = corner+1;
|
||||
if (next >= corners+numCorner) next = corners;
|
||||
nextNext = next+1;
|
||||
if (nextNext >= corners+numCorner) nextNext = corners;
|
||||
|
||||
if (corner->r < 1/kInfinity && next->r < 1/kInfinity) continue;
|
||||
|
||||
*face++ = new G4PolyconeSide( prev, corner, next, nextNext,
|
||||
startPhi, endPhi-startPhi, phiIsOpen );
|
||||
} while( prev=corner, corner=next, corner > corners );
|
||||
|
||||
if (phiIsOpen) {
|
||||
//
|
||||
// Construct phi open edges
|
||||
//
|
||||
*face++ = new G4PolyPhiFace( r, z, numRZ, startPhi, 0, true );
|
||||
*face++ = new G4PolyPhiFace( r, z, numRZ, endPhi, 0, false );
|
||||
}
|
||||
|
||||
//
|
||||
// We might have dropped a face or two: recalculate numFace
|
||||
//
|
||||
numFace = face-faces;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4Polycone::~G4Polycone()
|
||||
{
|
||||
delete [] corners;
|
||||
|
||||
if (original_parameters.exist) {
|
||||
delete [] original_parameters.Z_values;
|
||||
delete [] original_parameters.Rmin;
|
||||
delete [] original_parameters.Rmax;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ComputeDimensions
|
||||
//
|
||||
void G4Polycone::ComputeDimensions( G4VPVParameterisation* p,
|
||||
const G4int n,
|
||||
const G4VPhysicalVolume* pRep)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CreatePolyhedron
|
||||
//
|
||||
G4Polyhedron *G4Polycone::CreatePolyhedron() const
|
||||
{
|
||||
//
|
||||
// It is *really* unfortunate how the design in /graphics_reps is
|
||||
// written to parallel the design in /geometry/solids. Ugly, ugly, ugly.
|
||||
//
|
||||
// This has to be fixed, but I won't do it now. Fake it for the moment.
|
||||
//
|
||||
if (original_parameters.exist) {
|
||||
|
||||
return new G4PolyhedronPcon( original_parameters.Start_angle,
|
||||
original_parameters.Opening_angle,
|
||||
original_parameters.Num_z_planes,
|
||||
original_parameters.Z_values,
|
||||
original_parameters.Rmin,
|
||||
original_parameters.Rmax);
|
||||
}
|
||||
else {
|
||||
G4Exception( "G4Polycone: waiting for graphics_reps to catch up" );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CreateNURBS
|
||||
//
|
||||
G4NURBS *G4Polycone::CreateNURBS() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
//
|
||||
// G4PolyconeSide.cc
|
||||
//
|
||||
// Implemenation of the face representing one conical side of a polycone
|
||||
//
|
||||
|
||||
#include "G4PolyconeSide.hh"
|
||||
#include "G4IntersectingCone.hh"
|
||||
#include "G4ClippablePolygon.hh"
|
||||
#include "G4AffineTransform.hh"
|
||||
#include "meshdefs.hh"
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
// Values for r1,z1 and r2,z2 should be specified in clockwise
|
||||
// order in (r,z).
|
||||
//
|
||||
G4PolyconeSide::G4PolyconeSide( const G4PolyconeSideRZ *prevRZ,
|
||||
const G4PolyconeSideRZ *tail,
|
||||
const G4PolyconeSideRZ *head,
|
||||
const G4PolyconeSideRZ *nextRZ,
|
||||
const G4double thePhiStart,
|
||||
const G4double theDeltaPhi,
|
||||
const G4bool thePhiIsOpen )
|
||||
{
|
||||
//
|
||||
// Record values
|
||||
//
|
||||
r[0] = tail->r; z[0] = tail->z;
|
||||
r[1] = head->r; z[1] = head->z;
|
||||
|
||||
phiIsOpen = thePhiIsOpen;
|
||||
if (phiIsOpen) {
|
||||
deltaPhi = theDeltaPhi;
|
||||
startPhi = thePhiStart;
|
||||
|
||||
//
|
||||
// Set phi values to our conventions
|
||||
//
|
||||
while (deltaPhi < 0.0) deltaPhi += 2.0*M_PI;
|
||||
while (startPhi < 0.0) startPhi += 2.0*M_PI;
|
||||
}
|
||||
else {
|
||||
deltaPhi = 2*M_PI;
|
||||
startPhi = 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
// Make our intersecting cone
|
||||
//
|
||||
cone = new G4IntersectingCone( r, z );
|
||||
|
||||
//
|
||||
// Calculate vectors in r,z space
|
||||
//
|
||||
rS = r[1]-r[0]; zS = z[1]-z[0];
|
||||
length = sqrt( rS*rS + zS*zS);
|
||||
rS /= length; zS /= length;
|
||||
|
||||
rNorm = +zS;
|
||||
zNorm = -rS;
|
||||
|
||||
G4double rAdj = r[0]-prevRZ->r, zAdj = z[0]-prevRZ->z;
|
||||
G4double lAdj = sqrt( rAdj*rAdj + zAdj*zAdj );
|
||||
rAdj /= lAdj;
|
||||
zAdj /= lAdj;
|
||||
|
||||
rNormEdge[0] = rNorm + zAdj;
|
||||
zNormEdge[0] = zNorm - rAdj;
|
||||
lAdj = sqrt( rNormEdge[0]*rNormEdge[0] + zNormEdge[0]*zNormEdge[0] );
|
||||
rNormEdge[0] /= lAdj;
|
||||
zNormEdge[0] /= lAdj;
|
||||
|
||||
rAdj = nextRZ->r-r[1], zAdj = nextRZ->z-z[1];
|
||||
lAdj = sqrt( rAdj*rAdj + zAdj*zAdj );
|
||||
rAdj /= lAdj;
|
||||
zAdj /= lAdj;
|
||||
|
||||
rNormEdge[1] = rNorm + zAdj;
|
||||
zNormEdge[1] = zNorm - rAdj;
|
||||
lAdj = sqrt( rNormEdge[1]*rNormEdge[1] + zNormEdge[1]*zNormEdge[1] );
|
||||
rNormEdge[1] /= lAdj;
|
||||
zNormEdge[1] /= lAdj;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4PolyconeSide::~G4PolyconeSide()
|
||||
{
|
||||
delete cone;
|
||||
}
|
||||
|
||||
|
||||
G4bool G4PolyconeSide::Intersect( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
const G4bool outgoing, const G4double surfTolerance,
|
||||
G4double &distance, G4double &distFromSurface,
|
||||
G4ThreeVector &normal, G4bool &allBehind )
|
||||
{
|
||||
G4double s1, s2;
|
||||
G4double normSign = outgoing ? +1 : -1;
|
||||
|
||||
allBehind = true;
|
||||
|
||||
//
|
||||
// Check for two possible intersections
|
||||
//
|
||||
G4int nside = cone->LineHitsCone( p, v, &s1, &s2 );
|
||||
if (nside == 0) return false;
|
||||
|
||||
//
|
||||
// Check the first side first, since it is (supposed to be) closest
|
||||
//
|
||||
G4ThreeVector hit = p + s1*v;
|
||||
|
||||
if (PointOnCone( hit, normal )) {
|
||||
//
|
||||
// Good intersection! What about the normal?
|
||||
//
|
||||
G4double vdotN = v.dot(normal);
|
||||
if (normSign*vdotN > 0) {
|
||||
//
|
||||
// Good normal! Check distance
|
||||
//
|
||||
// We want to apply an addition surface tolerance
|
||||
// if the point p is on the surface. Is it?
|
||||
//
|
||||
G4bool opposite = (p.x()*hit.x()+p.y()*hit.y() < 0);
|
||||
G4double distOutside2;
|
||||
G4double notUsed = -normSign*DistanceAway( p, opposite, distOutside2, 0 );
|
||||
|
||||
//
|
||||
// The distance from the surface is defined along the
|
||||
// normal at the intersection point.
|
||||
//
|
||||
// I think.
|
||||
//
|
||||
distFromSurface = normSign*s1*vdotN;
|
||||
|
||||
//
|
||||
// Apply tolerance, but only if the point is outside
|
||||
// the edges of the cone
|
||||
//
|
||||
if (distFromSurface > (distOutside2 > 0 ? 0 : surfTolerance)) {
|
||||
//
|
||||
// Good intersection. Return now, since it is the closest.
|
||||
//
|
||||
distance = s1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nside==1) return false;
|
||||
|
||||
//
|
||||
// Well, try the second hit
|
||||
//
|
||||
hit = p + s2*v;
|
||||
|
||||
if (PointOnCone( hit, normal )) {
|
||||
//
|
||||
// Good intersection! What about the normal?
|
||||
//
|
||||
G4double vdotN = v.dot(normal);
|
||||
if (normSign*vdotN > 0) {
|
||||
//
|
||||
// Good normal! Check distance
|
||||
//
|
||||
// We want to apply an addition surface tolerance
|
||||
// if the point p is on the surface. Is it?
|
||||
//
|
||||
G4bool opposite = (p.x()*hit.x()+p.y()*hit.y() < 0);
|
||||
G4double distOutside2;
|
||||
G4double notUsed = -normSign*DistanceAway( p, opposite, distOutside2, 0 );
|
||||
|
||||
//
|
||||
// The distance from the surface is defined along the
|
||||
// normal at the intersection point.
|
||||
//
|
||||
// I think.
|
||||
//
|
||||
distFromSurface = normSign*s2*vdotN;
|
||||
|
||||
//
|
||||
// Apply tolerance, but only if the point is outside
|
||||
// the edges of the cone
|
||||
//
|
||||
if (distFromSurface > (distOutside2 > 0 ? 0 : surfTolerance)) {
|
||||
//
|
||||
// Good intersection. Return now, since it is the closest.
|
||||
//
|
||||
distance = s2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Better luck next time
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
G4double G4PolyconeSide::Distance( const G4ThreeVector &p, const G4bool outgoing )
|
||||
{
|
||||
G4double normSign = outgoing ? -1 : +1;
|
||||
G4double distFrom, distOut2;
|
||||
|
||||
//
|
||||
// We have two tries for each hemisphere. Try the closest first.
|
||||
//
|
||||
distFrom = DistanceAway( p, false, distOut2, 0 );
|
||||
if (distFrom*normSign > 0) {
|
||||
//
|
||||
// Good answer
|
||||
//
|
||||
if (distOut2 > 0)
|
||||
return sqrt( distFrom*distFrom + distOut2 );
|
||||
else
|
||||
return fabs(distFrom);
|
||||
}
|
||||
|
||||
//
|
||||
// Try second side.
|
||||
//
|
||||
distFrom = DistanceAway( p, true, distOut2, 0 );
|
||||
if (distFrom*normSign > 0) {
|
||||
|
||||
if (distOut2 > 0)
|
||||
return sqrt( distFrom*distFrom + distOut2 );
|
||||
else
|
||||
return fabs(distFrom);
|
||||
}
|
||||
|
||||
return kInfinity;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inside
|
||||
//
|
||||
EInside G4PolyconeSide::Inside( const G4ThreeVector &p, const G4double tolerance,
|
||||
G4double *bestDistance )
|
||||
{
|
||||
//
|
||||
// Check both sides
|
||||
//
|
||||
G4double distFrom[2], distOut2[2], dist2[2];
|
||||
G4double edgeRZnorm[2];
|
||||
|
||||
distFrom[0] = DistanceAway( p, false, distOut2[0], edgeRZnorm );
|
||||
distFrom[1] = DistanceAway( p, true, distOut2[1], edgeRZnorm+1 );
|
||||
|
||||
dist2[0] = distFrom[0]*distFrom[0] + distOut2[0];
|
||||
dist2[1] = distFrom[1]*distFrom[1] + distOut2[1];
|
||||
|
||||
//
|
||||
// Who's closest?
|
||||
//
|
||||
G4int i = fabs(dist2[0]) < fabs(dist2[1]) ? 0 : 1;
|
||||
|
||||
*bestDistance = sqrt( dist2[i] );
|
||||
|
||||
//
|
||||
// Okay then, inside or out?
|
||||
//
|
||||
if ( (fabs(edgeRZnorm[i]) < tolerance) && (distOut2[i] < tolerance*tolerance) )
|
||||
return kSurface;
|
||||
else if (edgeRZnorm[i] < 0)
|
||||
return kInside;
|
||||
else
|
||||
return kOutside;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Normal
|
||||
//
|
||||
G4ThreeVector G4PolyconeSide::Normal( const G4ThreeVector &p, G4double *bestDistance )
|
||||
{
|
||||
G4ThreeVector dFrom;
|
||||
G4double dOut2;
|
||||
|
||||
dFrom = DistanceAway( p, false, dOut2, 0 );
|
||||
|
||||
*bestDistance = sqrt( dFrom*dFrom + dOut2 );
|
||||
|
||||
G4double rad = p.perp();
|
||||
return G4ThreeVector( rNorm*p.x()/rad, rNorm*p.y()/rad, zNorm );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Extent
|
||||
//
|
||||
G4double G4PolyconeSide::Extent( const G4ThreeVector axis )
|
||||
{
|
||||
if (axis.perp2() < 1.0/kInfinity) {
|
||||
//
|
||||
// Special case
|
||||
//
|
||||
return axis.z() < 0 ? -cone->ZLo() : cone->ZHi();
|
||||
}
|
||||
|
||||
//
|
||||
// Is the axis pointing inside our phi gap?
|
||||
//
|
||||
if (phiIsOpen) {
|
||||
G4double phi = axis.phi();
|
||||
while( phi < startPhi ) phi += 2*M_PI;
|
||||
|
||||
if (phi > deltaPhi+startPhi) {
|
||||
//
|
||||
// Yeah, looks so. Make four three vectors defining the phi
|
||||
// opening
|
||||
//
|
||||
G4double cosP = cos(startPhi), sinP = sin(startPhi);
|
||||
G4ThreeVector a( r[0]*cosP, r[0]*sinP, z[0] );
|
||||
G4ThreeVector b( r[1]*cosP, r[1]*sinP, z[1] );
|
||||
cosP = cos(startPhi+deltaPhi); sinP = sin(startPhi+deltaPhi);
|
||||
G4ThreeVector c( r[0]*cosP, r[0]*sinP, z[0] );
|
||||
G4ThreeVector d( r[1]*cosP, r[1]*sinP, z[1] );
|
||||
|
||||
G4double ad = axis.dot(a),
|
||||
bd = axis.dot(b),
|
||||
cd = axis.dot(c),
|
||||
dd = axis.dot(d);
|
||||
|
||||
if (bd > ad) ad = bd;
|
||||
if (cd > ad) ad = cd;
|
||||
if (dd > ad) ad = dd;
|
||||
|
||||
return ad;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Check either end
|
||||
//
|
||||
G4double aPerp = axis.perp();
|
||||
|
||||
G4double a = aPerp*r[0] + axis.z()*z[0];
|
||||
G4double b = aPerp*r[1] + axis.z()*z[1];
|
||||
|
||||
if (b > a) a = b;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CalculateExtent
|
||||
//
|
||||
// See notes in G4VCSGface
|
||||
//
|
||||
void G4PolyconeSide::CalculateExtent( const EAxis axis,
|
||||
const G4VoxelLimits &voxelLimit,
|
||||
const G4AffineTransform &transform,
|
||||
G4double &min, G4double &max )
|
||||
{
|
||||
G4ClippablePolygon polygon;
|
||||
|
||||
//
|
||||
// The following code does not work correctly and needs to be
|
||||
// fixed... DCW 12/10/98
|
||||
//
|
||||
|
||||
//
|
||||
// Here we will cheat (ala G4Cons) and divide our conical section
|
||||
// into segments, like G4Polyhedra. When doing so, the radius
|
||||
// is extented far enough such that the segments always lie
|
||||
// just outside the surface of the conical section we are
|
||||
// approximating.
|
||||
//
|
||||
|
||||
//
|
||||
// Choose phi size of our segment(s) based on constants as
|
||||
// defined in meshdefs.hh
|
||||
//
|
||||
G4int numPhi = deltaPhi/kMeshAngleDefault + 1;
|
||||
if (numPhi < kMinMeshSections)
|
||||
numPhi = kMinMeshSections;
|
||||
else if (numPhi > kMaxMeshSections)
|
||||
numPhi = kMaxMeshSections;
|
||||
|
||||
G4double sigPhi = deltaPhi/numPhi;
|
||||
|
||||
//
|
||||
// Determine radius factor to keep segments outside
|
||||
//
|
||||
G4double rFudge = 1.0/cos(sigPhi);
|
||||
|
||||
//
|
||||
// Loop
|
||||
//
|
||||
G4double phi = startPhi,
|
||||
cosPhi = rFudge*cos(phi),
|
||||
sinPhi = rFudge*sin(phi);
|
||||
|
||||
G4ThreeVector v0( r[0]*cosPhi, r[0]*sinPhi, z[0] ),
|
||||
v1( r[1]*cosPhi, r[1]*sinPhi, z[1] ),
|
||||
w0, w1;
|
||||
transform.ApplyPointTransform( v0 );
|
||||
transform.ApplyPointTransform( v1 );
|
||||
|
||||
do {
|
||||
phi += sigPhi;
|
||||
cosPhi = rFudge*cos(phi),
|
||||
sinPhi = rFudge*sin(phi);
|
||||
|
||||
w0 = G4ThreeVector( r[0]*cosPhi, r[0]*sinPhi, z[0] );
|
||||
w1 = G4ThreeVector( r[1]*cosPhi, r[1]*sinPhi, z[1] );
|
||||
transform.ApplyPointTransform( w0 );
|
||||
transform.ApplyPointTransform( w1 );
|
||||
|
||||
//
|
||||
// Build polygon, taking special care to keep the vertices
|
||||
// in order
|
||||
//
|
||||
polygon.ClearAllVertices();
|
||||
|
||||
polygon.AddVertexInOrder( v0 );
|
||||
polygon.AddVertexInOrder( v1 );
|
||||
polygon.AddVertexInOrder( w1 );
|
||||
polygon.AddVertexInOrder( w0 );
|
||||
|
||||
//
|
||||
// Get extent
|
||||
//
|
||||
polygon.Clip( voxelLimit );
|
||||
polygon.GetExtent( axis, min, max );
|
||||
|
||||
//
|
||||
// Next vertex
|
||||
//
|
||||
v0 = w0;
|
||||
v1 = w1;
|
||||
} while( --numPhi > 0 );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// -------------------------------------------------------
|
||||
|
||||
//
|
||||
// DistanceAway
|
||||
//
|
||||
// Calculate distance of a point from our conical surface, including the effect
|
||||
// of any phi segmentation
|
||||
//
|
||||
// Arguments:
|
||||
// p - (in) Point to check
|
||||
// opposite - (in) If true, check opposite hemisphere (see below)
|
||||
// distOutside - (out) Additional distance outside the edges of the
|
||||
// surface
|
||||
// edgeNorm - (out) Edge Status (belowRZ, aboveRZ, inRZ)
|
||||
// return value = distance from the conical plane, if extrapolated beyond edges,
|
||||
// signed by whether the point is in inside or outside the shape
|
||||
//
|
||||
// Notes:
|
||||
// * There are two answers, depending on which hemisphere is considered.
|
||||
//
|
||||
G4double G4PolyconeSide::DistanceAway( const G4ThreeVector &p, const G4bool opposite,
|
||||
G4double &distOutside2, G4double *edgeRZnorm )
|
||||
{
|
||||
//
|
||||
// Convert our point to r and z
|
||||
//
|
||||
G4double rx = p.perp(), zx = p.z();
|
||||
|
||||
//
|
||||
// Change sign of r if opposite says we should
|
||||
//
|
||||
if (opposite) rx = -rx;
|
||||
|
||||
//
|
||||
// Calculate return value
|
||||
//
|
||||
G4double deltaR = rx - r[0], deltaZ = zx - z[0];
|
||||
G4double answer = deltaR*rNorm + deltaZ*zNorm;
|
||||
|
||||
//
|
||||
// Are we off the surface in r,z space?
|
||||
//
|
||||
G4double s = deltaR*rS + deltaZ*zS;
|
||||
if (s < 0) {
|
||||
distOutside2 = s*s;
|
||||
if (edgeRZnorm) *edgeRZnorm = deltaR*rNormEdge[0] + deltaZ*zNormEdge[0];
|
||||
}
|
||||
else if (s > length) {
|
||||
distOutside2 = sqr( s-length );
|
||||
if (edgeRZnorm) *edgeRZnorm = deltaR*rNormEdge[1] + deltaZ*zNormEdge[1];
|
||||
}
|
||||
else {
|
||||
distOutside2 = 0;
|
||||
if (edgeRZnorm) *edgeRZnorm = answer;
|
||||
}
|
||||
|
||||
if (phiIsOpen) {
|
||||
//
|
||||
// Finally, check phi
|
||||
//
|
||||
G4double phi = p.phi();
|
||||
while( phi < startPhi ) phi += 2*M_PI;
|
||||
|
||||
if (phi > startPhi+deltaPhi) {
|
||||
//
|
||||
// Oops. Are we closer to the start phi or end phi?
|
||||
//
|
||||
G4double d1 = phi-startPhi-deltaPhi;
|
||||
while( phi > startPhi ) phi -= 2*M_PI;
|
||||
G4double d2 = startPhi-phi;
|
||||
|
||||
if (d2 < d1) d1 = d2;
|
||||
|
||||
//
|
||||
// Add result to our distance
|
||||
//
|
||||
distOutside2 += d1*d1*p.perp2();
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// PointOnCone
|
||||
//
|
||||
// Decide if a point is on a cone and return normal if it is
|
||||
//
|
||||
G4bool G4PolyconeSide::PointOnCone( const G4ThreeVector &p, G4ThreeVector &normal )
|
||||
{
|
||||
G4double rx = p.perp();
|
||||
//
|
||||
// Check radial/z extent, as appropriate
|
||||
//
|
||||
if (!cone->HitOn( rx, p.z() )) return false;
|
||||
|
||||
if (phiIsOpen) {
|
||||
//
|
||||
// Check phi segment
|
||||
//
|
||||
G4double phi = p.phi();
|
||||
while( phi < startPhi ) phi += 2*M_PI;
|
||||
|
||||
if (phi > startPhi+deltaPhi) return false;
|
||||
}
|
||||
|
||||
//
|
||||
// We have a good hit! Calculate normal
|
||||
//
|
||||
if (rx<0) rx = p.perp();
|
||||
|
||||
if (rx < -1.0/kInfinity)
|
||||
normal = G4ThreeVector( 0, 0, zNorm < 0 ? -1 : 1 );
|
||||
else
|
||||
normal = G4ThreeVector( rNorm*p.x()/rx, rNorm*p.y()/rx, zNorm );
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
//
|
||||
// G4Polyhedra.cc
|
||||
//
|
||||
// Implementation of a CSG polyhedra, as an inherited class of G4VCSGfaceted.
|
||||
//
|
||||
// To be done:
|
||||
// * Checks for bad input should be improved. It is now possible for
|
||||
// users to specify crazy polyhedra parameters without complaint that
|
||||
// could produce unpredictable results.
|
||||
// * Cracks: there are probably small cracks in the seams between the
|
||||
// phi face (G4PolyPhiFace) and sides (G4PolyhedraSide) that are not
|
||||
// entirely leakproof. Also, I am not sure all vertices are leak proof.
|
||||
// * Many optimizations are possible, but not implemented.
|
||||
// * Visualization needs to be updated outside of this routine.
|
||||
//
|
||||
#include "G4Polyhedra.hh"
|
||||
#include "G4PolyhedraSide.hh"
|
||||
#include "G4PolyPhiFace.hh"
|
||||
|
||||
#include "G4Polyhedron.hh"
|
||||
#include "G4EnclosingCylinder.hh"
|
||||
|
||||
|
||||
//
|
||||
// Constructor (GEANT3 style parameters)
|
||||
//
|
||||
// GEANT3 PGON radii are specified in the distance to the norm of each face.
|
||||
//
|
||||
G4Polyhedra::G4Polyhedra( G4String name,
|
||||
const G4double phiStart,
|
||||
const G4double thePhiTotal,
|
||||
const G4double theNumSide,
|
||||
const G4int numZPlanes,
|
||||
const G4double zPlane[],
|
||||
const G4double rInner[],
|
||||
const G4double rOuter[] ) : G4VCSGfaceted( name )
|
||||
{
|
||||
if (theNumSide <= 0) G4Exception( "G4Polyhedra:: must have at least one side" );
|
||||
|
||||
//
|
||||
// Calculate conversion factor from G3 radius to G4 radius
|
||||
//
|
||||
G4double phiTotal = thePhiTotal;
|
||||
if (phiTotal <=0 || phiTotal > 2*M_PI) phiTotal = 2*M_PI;
|
||||
G4double convertRad = cos(0.5*phiTotal/theNumSide);
|
||||
|
||||
//
|
||||
// Real ugly
|
||||
//
|
||||
original_parameters.exist = true;
|
||||
|
||||
original_parameters.numSide = theNumSide;
|
||||
original_parameters.Start_angle = phiStart;
|
||||
original_parameters.Opening_angle = phiTotal;
|
||||
original_parameters.Num_z_planes = numZPlanes;
|
||||
original_parameters.Z_values = new G4double[numZPlanes];
|
||||
original_parameters.Rmin = new G4double[numZPlanes];
|
||||
original_parameters.Rmax = new G4double[numZPlanes];
|
||||
|
||||
G4int i;
|
||||
for (i=0; i<numZPlanes; i++) {
|
||||
original_parameters.Z_values[i] = zPlane[i];
|
||||
original_parameters.Rmin[i] = rInner[i]/convertRad;
|
||||
original_parameters.Rmax[i] = rOuter[i]/convertRad;
|
||||
}
|
||||
|
||||
//
|
||||
// Translate GEANT3 into generic parameters
|
||||
// Duplicate vertices and divided surfaces are (or should be) dealt with
|
||||
// by routine "Create."
|
||||
//
|
||||
G4double *r = new G4double[numZPlanes*2];
|
||||
G4double *z = new G4double[numZPlanes*2];
|
||||
|
||||
G4double *rOut = r + numZPlanes,
|
||||
*zOut = z + numZPlanes,
|
||||
*rIn = rOut-1,
|
||||
*zIn = zOut-1;
|
||||
|
||||
for( i=0; i < numZPlanes; i++, rOut++, zOut++, rIn--, zIn-- ) {
|
||||
*rOut = rOuter[i]/convertRad;
|
||||
*rIn = rInner[i]/convertRad;
|
||||
*zOut = *zIn = zPlane[i];
|
||||
}
|
||||
|
||||
Create( phiStart, phiTotal, theNumSide, numZPlanes*2, r, z );
|
||||
|
||||
delete [] r;
|
||||
delete [] z;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Constructor (generic parameters)
|
||||
//
|
||||
G4Polyhedra::G4Polyhedra( G4String name,
|
||||
const G4double phiStart,
|
||||
const G4double phiTotal,
|
||||
const G4double theNumSide,
|
||||
const G4int numRZ,
|
||||
const G4double r[],
|
||||
const G4double z[] ) : G4VCSGfaceted( name )
|
||||
{
|
||||
original_parameters.exist = false;
|
||||
|
||||
Create( phiStart, phiTotal, theNumSide, numRZ, r, z );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Create
|
||||
//
|
||||
// Generic create routine, called by each constructor after conversion of arguments
|
||||
//
|
||||
void G4Polyhedra::Create( const G4double phiStart,
|
||||
const G4double phiTotal,
|
||||
const G4double theNumSide,
|
||||
const G4int numRZ,
|
||||
const G4double r[],
|
||||
const G4double z[] )
|
||||
{
|
||||
//
|
||||
// Phi opening? Account for some possible roundoff, and interpret
|
||||
// nonsense value as representing no phi opening
|
||||
//
|
||||
if (phiTotal <= 0 || phiTotal > 2.0*M_PI-1E-10) {
|
||||
phiIsOpen = false;
|
||||
startPhi = 0;
|
||||
endPhi = 2*M_PI;
|
||||
}
|
||||
else {
|
||||
phiIsOpen = true;
|
||||
|
||||
//
|
||||
// Convert phi into our convention
|
||||
//
|
||||
startPhi = phiStart;
|
||||
while( startPhi < 0 ) startPhi += 2*M_PI;
|
||||
|
||||
endPhi = phiStart+phiTotal;
|
||||
while( endPhi < startPhi ) endPhi += 2*M_PI;
|
||||
}
|
||||
|
||||
//
|
||||
// Save number sides
|
||||
//
|
||||
numSide = theNumSide;
|
||||
|
||||
//
|
||||
// Allocate corner array. We may not end up using all of this array,
|
||||
// since we delete duplicate corners, but that's not so bad
|
||||
//
|
||||
corners = new G4PolyhedraSideRZ[numRZ];
|
||||
|
||||
//
|
||||
// Copy corners, avoiding duplicates on the way
|
||||
//
|
||||
// We should also look for divided conical surfaces...
|
||||
// We must also look for overlapping surfaces...
|
||||
//
|
||||
G4PolyhedraSideRZ *next = corners;
|
||||
const G4double *rOne = r;
|
||||
const G4double *zOne = z;
|
||||
const G4double *rNext, *zNext;
|
||||
G4bool notFinished;
|
||||
do {
|
||||
rNext = rOne + 1;
|
||||
zNext = zOne + 1;
|
||||
if (notFinished = (rNext < r+numRZ)) {
|
||||
if (*rNext == *rOne && *zNext == *zOne) continue;
|
||||
}
|
||||
|
||||
next->r = *rOne;
|
||||
next->z = *zOne;
|
||||
next++;
|
||||
} while( rOne=rNext, zOne=zNext, notFinished );
|
||||
|
||||
numCorner = next - corners;
|
||||
|
||||
//
|
||||
// Allocate face pointer array
|
||||
//
|
||||
numFace = phiIsOpen ? numCorner+2 : numCorner;
|
||||
faces = new G4VCSGface*[numFace];
|
||||
|
||||
//
|
||||
// Construct side faces
|
||||
//
|
||||
// To do so properly, we need to keep track of four successive RZ
|
||||
// corners.
|
||||
//
|
||||
// But! Don't construct a face if both points are at zero radius!
|
||||
//
|
||||
G4PolyhedraSideRZ *corner = corners,
|
||||
*prev = corners + numCorner-1,
|
||||
*nextNext;
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
next = corner+1;
|
||||
if (next >= corners+numCorner) next = corners;
|
||||
nextNext = next+1;
|
||||
if (nextNext >= corners+numCorner) nextNext = corners;
|
||||
|
||||
if (corner->r < 1/kInfinity && next->r < 1/kInfinity) continue;
|
||||
|
||||
*face++ = new G4PolyhedraSide( prev, corner, next, nextNext,
|
||||
numSide, startPhi, endPhi-startPhi, phiIsOpen );
|
||||
} while( prev=corner, corner=next, corner > corners );
|
||||
|
||||
if (phiIsOpen) {
|
||||
//
|
||||
// Construct phi open edges
|
||||
//
|
||||
*face++ = new G4PolyPhiFace( r, z, numRZ, startPhi, phiTotal/numSide, true );
|
||||
*face++ = new G4PolyPhiFace( r, z, numRZ, endPhi, phiTotal/numSide, false );
|
||||
}
|
||||
|
||||
//
|
||||
// We might have dropped a face or two: recalculate numFace
|
||||
//
|
||||
numFace = face-faces;
|
||||
|
||||
//
|
||||
// Make enclosingCylinder
|
||||
//
|
||||
enclosingCylinder = new G4EnclosingCylinder( r, z, numRZ,
|
||||
phiIsOpen, phiStart, phiTotal );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4Polyhedra::~G4Polyhedra()
|
||||
{
|
||||
delete [] corners;
|
||||
|
||||
if (original_parameters.exist) {
|
||||
delete [] original_parameters.Z_values;
|
||||
delete [] original_parameters.Rmin;
|
||||
delete [] original_parameters.Rmax;
|
||||
}
|
||||
|
||||
delete enclosingCylinder;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inside
|
||||
//
|
||||
EInside G4Polyhedra::Inside( const G4ThreeVector &p ) const
|
||||
{
|
||||
//
|
||||
// Quick test
|
||||
//
|
||||
if (enclosingCylinder->Outside(p)) return kOutside;
|
||||
|
||||
//
|
||||
// Long answer
|
||||
//
|
||||
return G4VCSGfaceted::Inside(p);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceToIn
|
||||
//
|
||||
G4double G4Polyhedra::DistanceToIn( const G4ThreeVector &p, const G4ThreeVector &v ) const
|
||||
{
|
||||
//
|
||||
// Quick test
|
||||
//
|
||||
if (enclosingCylinder->Misses(p,v)) return kInfinity;
|
||||
|
||||
//
|
||||
// Long answer
|
||||
//
|
||||
return G4VCSGfaceted::DistanceToIn( p, v );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ComputeDimensions
|
||||
//
|
||||
void G4Polyhedra::ComputeDimensions( G4VPVParameterisation* p,
|
||||
const G4int n,
|
||||
const G4VPhysicalVolume* pRep)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CreatePolyhedron
|
||||
//
|
||||
G4Polyhedron *G4Polyhedra::CreatePolyhedron() const
|
||||
{
|
||||
//
|
||||
// It is *really* unfortunate how the design in /graphics_reps is
|
||||
// written to parallel the design in /geometry/solids. Ugly, ugly, ugly.
|
||||
//
|
||||
// This has to be fixed, but I won't do it now. Fake it for the moment.
|
||||
//
|
||||
if (original_parameters.exist) {
|
||||
|
||||
return new G4PolyhedronPgon( original_parameters.Start_angle,
|
||||
original_parameters.Opening_angle,
|
||||
original_parameters.numSide,
|
||||
original_parameters.Num_z_planes,
|
||||
original_parameters.Z_values,
|
||||
original_parameters.Rmin,
|
||||
original_parameters.Rmax);
|
||||
}
|
||||
else {
|
||||
G4Exception( "G4Polyhedra: waiting for graphics_reps to catch up" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CreateNURBS
|
||||
//
|
||||
G4NURBS *G4Polyhedra::CreateNURBS() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,884 @@
|
||||
//
|
||||
// G4PolyhedraSide.cc
|
||||
//
|
||||
// Implemenation of the face representing one segmented side of a Polyhedra
|
||||
//
|
||||
|
||||
#include "G4PolyhedraSide.hh"
|
||||
#include "G4IntersectingCone.hh"
|
||||
#include "G4ClippablePolygon.hh"
|
||||
#include "G4AffineTransform.hh"
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
// Values for r1,z1 and r2,z2 should be specified in clockwise
|
||||
// order in (r,z).
|
||||
//
|
||||
G4PolyhedraSide::G4PolyhedraSide( const G4PolyhedraSideRZ *prevRZ,
|
||||
const G4PolyhedraSideRZ *tail,
|
||||
const G4PolyhedraSideRZ *head,
|
||||
const G4PolyhedraSideRZ *nextRZ,
|
||||
const G4int theNumSide,
|
||||
const G4double thePhiStart,
|
||||
const G4double thePhiTotal,
|
||||
const G4bool thePhiIsOpen )
|
||||
{
|
||||
//
|
||||
// Record values
|
||||
//
|
||||
r[0] = tail->r; z[0] = tail->z;
|
||||
r[1] = head->r; z[1] = head->z;
|
||||
|
||||
G4double phiTotal;
|
||||
|
||||
phiIsOpen = thePhiIsOpen;
|
||||
if (phiIsOpen) {
|
||||
phiTotal = thePhiTotal;
|
||||
startPhi = thePhiStart;
|
||||
|
||||
//
|
||||
// Set phi values to our conventions
|
||||
//
|
||||
while (startPhi < 0.0) startPhi += 2.0*M_PI;
|
||||
}
|
||||
else {
|
||||
phiTotal = 2*M_PI;
|
||||
startPhi = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Make our intersecting cone
|
||||
//
|
||||
cone = new G4IntersectingCone( r, z );
|
||||
|
||||
//
|
||||
// Construct side plane vector set
|
||||
//
|
||||
numSide = theNumSide;
|
||||
deltaPhi = phiTotal/theNumSide;
|
||||
|
||||
vecs = new G4PolyhedraSideVec[numSide];
|
||||
|
||||
edges = new G4PolyhedraSideEdge[phiIsOpen ? numSide+1 : numSide];
|
||||
|
||||
//
|
||||
// ...this is where we start
|
||||
//
|
||||
G4double phi = startPhi;
|
||||
G4ThreeVector a1( r[0]*cos(phi), r[0]*sin(phi), z[0] ),
|
||||
b1( r[1]*cos(phi), r[1]*sin(phi), z[1] ),
|
||||
c1( prevRZ->r*cos(phi), prevRZ->r*sin(phi), prevRZ->z ),
|
||||
d1( nextRZ->r*cos(phi), nextRZ->r*sin(phi), nextRZ->z ),
|
||||
a2, b2, c2, d2;
|
||||
G4PolyhedraSideEdge *edge = edges;
|
||||
|
||||
G4PolyhedraSideVec *vec = vecs;
|
||||
do {
|
||||
//
|
||||
// ...this is where we are going
|
||||
//
|
||||
phi += deltaPhi;
|
||||
a2 = G4ThreeVector( r[0]*cos(phi), r[0]*sin(phi), z[0] );
|
||||
b2 = G4ThreeVector( r[1]*cos(phi), r[1]*sin(phi), z[1] );
|
||||
c2 = G4ThreeVector( prevRZ->r*cos(phi), prevRZ->r*sin(phi), prevRZ->z );
|
||||
d2 = G4ThreeVector( nextRZ->r*cos(phi), nextRZ->r*sin(phi), nextRZ->z );
|
||||
|
||||
G4ThreeVector tt;
|
||||
|
||||
//
|
||||
// ...build some relevant vectors.
|
||||
// the point is to sacrifice a little memory with precalcs
|
||||
// to gain speed
|
||||
//
|
||||
vec->center = 0.25*( a1 + a2 + b1 + b2 );
|
||||
|
||||
tt = b2 + b1 - a2 - a1;
|
||||
vec->surfRZ = tt.unit();
|
||||
if (vec==vecs) lenRZ = 0.25*tt.mag();
|
||||
|
||||
tt = b2 - b1 + a2 - a1;
|
||||
vec->surfPhi = tt.unit();
|
||||
if (vec==vecs) {
|
||||
lenPhi[0] = 0.25*tt.mag();
|
||||
tt = b2 - b1;
|
||||
lenPhi[1] = (0.5*tt.mag()-lenPhi[0])/lenRZ;
|
||||
}
|
||||
|
||||
tt = vec->surfPhi.cross(vec->surfRZ);
|
||||
vec->normal = tt.unit();
|
||||
|
||||
//
|
||||
// ...edge normals are the average of the normals of
|
||||
// the two faces they connect.
|
||||
//
|
||||
// ...edge normals are necessary if we are to accurately
|
||||
// decide if a point is "inside" a face. For non-convex
|
||||
// shapes, it is absolutely necessary to know information
|
||||
// on adjacent faces to accurate determine this.
|
||||
//
|
||||
// ...we don't need them for the phi edges, since that
|
||||
// information is taken care of internally. The r/z edges,
|
||||
// however, depend on the adjacent G4PolyhedraSide.
|
||||
//
|
||||
G4ThreeVector a12, adj;
|
||||
|
||||
a12 = a2-a1;
|
||||
|
||||
adj = 0.5*(c1+c2-a1-a2);
|
||||
adj = adj.cross(a12);
|
||||
adj = adj.unit() + vec->normal;
|
||||
vec->edgeNorm[0] = adj.unit();
|
||||
|
||||
a12 = b1-b2;
|
||||
adj = 0.5*(d1+d2-b1-b2);
|
||||
adj = adj.cross(a12);
|
||||
adj = adj.unit() + vec->normal;
|
||||
vec->edgeNorm[1] = adj.unit();
|
||||
|
||||
//
|
||||
// ...the corners are crucial. It is important that
|
||||
// they are calculated consistently for adjacent
|
||||
// G4PolyhedraSides, to avoid gaps caused by roundoff.
|
||||
//
|
||||
vec->edges[0] = edge;
|
||||
edge->corner[0] = a1;
|
||||
edge->corner[1] = b1;
|
||||
edge++;
|
||||
vec->edges[1] = edge;
|
||||
|
||||
a1 = a2;
|
||||
b1 = b2;
|
||||
c1 = c2;
|
||||
d1 = d2;
|
||||
} while( ++vec < vecs+numSide );
|
||||
|
||||
//
|
||||
// Clean up hanging edge
|
||||
//
|
||||
if (phiIsOpen) {
|
||||
edge->corner[0] = a2;
|
||||
edge->corner[1] = b2;
|
||||
}
|
||||
else {
|
||||
vecs[numSide-1].edges[1] = edges;
|
||||
}
|
||||
|
||||
//
|
||||
// Go back and fill in remaining fields in edges
|
||||
//
|
||||
vec = vecs;
|
||||
G4PolyhedraSideVec *prev = vecs+numSide-1;
|
||||
do {
|
||||
edge = vec->edges[0]; // The edge between prev and vec
|
||||
|
||||
//
|
||||
// Okay: edge normal is average of normals of adjacent faces
|
||||
//
|
||||
G4ThreeVector eNorm = vec->normal + prev->normal;
|
||||
edge->normal = eNorm.unit();
|
||||
|
||||
//
|
||||
// Vertex normal is average of norms of attached edges
|
||||
//
|
||||
eNorm = edge->normal + vec->edgeNorm[0] + prev->edgeNorm[0];
|
||||
edge->cornNorm[0] = eNorm.unit();
|
||||
|
||||
eNorm = edge->normal + vec->edgeNorm[1] + prev->edgeNorm[1];
|
||||
edge->cornNorm[1] = eNorm.unit();
|
||||
} while( prev=vec, ++vec < vecs + numSide );
|
||||
|
||||
if (phiIsOpen) {
|
||||
G4double rFact = cos(0.5*deltaPhi);
|
||||
//
|
||||
// If phi is open, we need to patch up the first and last edges
|
||||
//
|
||||
G4double phi1 = startPhi - 0.5*M_PI;
|
||||
G4ThreeVector phiNorm( cos(phi1), sin(phi1), 0 );
|
||||
|
||||
vec = vecs;
|
||||
|
||||
//
|
||||
// Edge normal is average of vec->normal and the normal
|
||||
// of the face closing the polyhedra in phi
|
||||
//
|
||||
G4ThreeVector eNorm = vec->normal + phiNorm;
|
||||
vec->edges[0]->normal = eNorm.unit();
|
||||
|
||||
//
|
||||
// We need the edge normals (like above) of the adjacent
|
||||
// G4PolyhedraSides.
|
||||
//
|
||||
G4double dr = r[0]-prevRZ->r, dz = z[0]-prevRZ->z;
|
||||
phi1 = startPhi + 0.5*deltaPhi;
|
||||
eNorm = G4ThreeVector( dz*rFact*cos(phi1), dz*rFact*sin(phi1), -dr );
|
||||
|
||||
//
|
||||
// Average three line normals for the vertex normal
|
||||
//
|
||||
eNorm = eNorm.unit() + vec->edges[0]->normal + vec->edgeNorm[0];
|
||||
vec->edges[0]->cornNorm[0] = eNorm.unit();
|
||||
|
||||
//
|
||||
// Repeat for edgeNorm[1]
|
||||
//
|
||||
dr = nextRZ->r-r[1], dz = nextRZ->z-z[1];
|
||||
eNorm = G4ThreeVector( dz*rFact*cos(phi1), dz*rFact*sin(phi1), -dr );
|
||||
eNorm = eNorm.unit() + vec->edges[0]->normal + vec->edgeNorm[1];
|
||||
vec->edges[0]->cornNorm[1] = eNorm.unit();
|
||||
|
||||
//
|
||||
// That was bad...
|
||||
//
|
||||
// But, now repeat for ending phi (edge[1])
|
||||
//
|
||||
phi1 = startPhi + phiTotal + 0.5*M_PI;
|
||||
phiNorm = G4ThreeVector( cos(phi1), sin(phi1), 0 );
|
||||
|
||||
vec = vecs + numSide - 1;
|
||||
|
||||
eNorm = vec->normal + phiNorm;
|
||||
vec->edges[1]->normal = eNorm.unit();
|
||||
|
||||
dr = r[0]-prevRZ->r, dz = z[0]-prevRZ->z;
|
||||
phi1 = startPhi + phiTotal - 0.5*deltaPhi;
|
||||
eNorm = G4ThreeVector( dz*rFact*cos(phi1), dz*rFact*sin(phi1), -dr );
|
||||
|
||||
eNorm = eNorm.unit() + vec->edges[1]->normal + vec->edgeNorm[0];
|
||||
vec->edges[1]->cornNorm[0] = eNorm.unit();
|
||||
|
||||
dr = nextRZ->r-r[1], dz = nextRZ->z-z[1];
|
||||
eNorm = G4ThreeVector( dz*rFact*cos(phi1), dz*rFact*sin(phi1), -dr );
|
||||
eNorm = eNorm.unit() + vec->edges[1]->normal + vec->edgeNorm[1];
|
||||
vec->edges[1]->cornNorm[1] = eNorm.unit();
|
||||
|
||||
//
|
||||
// Phew! I need a beer!
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
// edgeNorm is the factor one multiplies the distance along vector phi
|
||||
// on the surface of one of our sides in order to calculate the distance
|
||||
// from the edge. (see routine DistanceAway)
|
||||
//
|
||||
edgeNorm = 1.0/sqrt( 1.0 + lenPhi[1]*lenPhi[1] );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4PolyhedraSide::~G4PolyhedraSide()
|
||||
{
|
||||
delete cone;
|
||||
delete [] vecs;
|
||||
delete [] edges;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Intersect
|
||||
//
|
||||
// Decide if a line intersects the face.
|
||||
//
|
||||
// Arguments:
|
||||
// p = (in) starting point of line segment
|
||||
// v = (in) direction of line segment (assumed a unit vector)
|
||||
// A, B = (in) 2d transform variables (see note top of file)
|
||||
// normSign = (in) desired sign for dot product with normal (see below)
|
||||
// surfTolerance = (in) minimum distance from the surface (can be < 0, see below)
|
||||
// vecs = (in) Vector set array
|
||||
// distance = (out) distance to surface furfilling all requirements
|
||||
// distFromSurface = (out) distance from the surface
|
||||
// thisNormal = (out) normal vector of the intersecting surface
|
||||
//
|
||||
// Return value:
|
||||
// true if an intersection is found. Otherwise, output parameters are undefined.
|
||||
//
|
||||
// Notes:
|
||||
// * normSign: if we are "inside" the shape and only want to find out how far
|
||||
// to leave the shape, we only want to consider intersections with surfaces in
|
||||
// which the trajectory is leaving the shape. Since the normal vectors to the
|
||||
// surface always point outwards from the inside, this means we want the dot
|
||||
// product of the trajectory direction v and the normal of the side normals[i]
|
||||
// to be positive. Thus, we should specify normSign as +1.0. Otherwise, if
|
||||
// we are outside and want to go in, normSign should be set to -1.0.
|
||||
// Don't set normSign to zero, or you will get no intersections!
|
||||
//
|
||||
// * surfTolerance: see notes on argument "surfTolerance" in routine "IntersectSide".
|
||||
// ----HOWEVER---- We should *not* apply this surface tolerance if the starting
|
||||
// point is not within phi or z of the surface. Specifically, if the starting
|
||||
// point p angle in x/y places it on a separate side from the intersection or
|
||||
// if the starting point p is outside the z bounds of the segment, surfTolerance
|
||||
// must be ignored are we should *always* accept the intersection!
|
||||
// This is simply because the sides do not have infinite extent.
|
||||
//
|
||||
//
|
||||
G4bool G4PolyhedraSide::Intersect( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
const G4bool outgoing, const G4double surfTolerance,
|
||||
G4double &distance, G4double &distFromSurface,
|
||||
G4ThreeVector &normal, G4bool &allBehind )
|
||||
{
|
||||
G4int nside, i1, i2, iStart;
|
||||
G4double normSign = outgoing ? +1 : -1;
|
||||
|
||||
allBehind = true; // this is always true for this face
|
||||
|
||||
//
|
||||
// Is the starting point outside z bounds?
|
||||
//
|
||||
iStart = (p.z() < cone->ZLo() || p.z() > cone->ZHi()) ? -1 : 0;
|
||||
|
||||
if (iStart==0) {
|
||||
//
|
||||
// Which phi segment does the starting point p belong to?
|
||||
//
|
||||
iStart = PhiSegment( p.phi() );
|
||||
}
|
||||
|
||||
//
|
||||
// Check for two possible intersections
|
||||
//
|
||||
nside = LineHitsSegments( p, v, &i1, &i2 );
|
||||
|
||||
if (nside==0) return false;
|
||||
|
||||
//
|
||||
// Try the first side first. LineHitsSegments is suppose to return
|
||||
// the nearest intersection first. If this succeeds, we are done.
|
||||
//
|
||||
if (IntersectSidePlane( p, v, vecs[i1], normSign,
|
||||
(i1 == iStart) ? surfTolerance : 0,
|
||||
distance, distFromSurface )) {
|
||||
normal = vecs[i1].normal;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nside==2) {
|
||||
//
|
||||
// No luck? Well, we have the second side
|
||||
//
|
||||
if (IntersectSidePlane( p, v, vecs[i2], normSign,
|
||||
(i2 == iStart) ? surfTolerance : 0,
|
||||
distance, distFromSurface )) {
|
||||
normal = vecs[i2].normal;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Oh well. Better luck next time.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
G4double G4PolyhedraSide::Distance( const G4ThreeVector &p, const G4bool outgoing )
|
||||
{
|
||||
G4double normSign = outgoing ? -1 : +1;
|
||||
|
||||
//
|
||||
// Try the closest phi segment first
|
||||
//
|
||||
G4int iPhi = ClosestPhiSegment( p.phi() );
|
||||
|
||||
G4ThreeVector pdotc = p - vecs[iPhi].center;
|
||||
G4double normDist = pdotc.dot(vecs[iPhi].normal);
|
||||
|
||||
if (normSign*normDist > 0) {
|
||||
return DistanceAway( p, vecs[iPhi], &normDist );
|
||||
}
|
||||
|
||||
//
|
||||
// Now we have an interesting problem... do we try to find the
|
||||
// closest facing side??
|
||||
//
|
||||
// Considered carefully, the answer is no. We know that if we
|
||||
// are asking for the distance out, we are supposed to be inside,
|
||||
// and vice versa.
|
||||
//
|
||||
|
||||
return kInfinity;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inside
|
||||
//
|
||||
EInside G4PolyhedraSide::Inside( const G4ThreeVector &p, const G4double tolerance,
|
||||
G4double *bestDistance )
|
||||
{
|
||||
//
|
||||
// Which phi segment is closest to this point?
|
||||
//
|
||||
G4int iPhi = ClosestPhiSegment( p.phi() );
|
||||
|
||||
G4double norm;
|
||||
|
||||
//
|
||||
// Get distance to this segment
|
||||
//
|
||||
*bestDistance = DistanceToOneSide( p, vecs[iPhi], &norm );
|
||||
|
||||
//
|
||||
// Use distance along normal to decide return value
|
||||
//
|
||||
if ((fabs(norm) < tolerance) && (*bestDistance < 2.0*tolerance) )
|
||||
return kSurface;
|
||||
else if (norm < 0)
|
||||
return kInside;
|
||||
else
|
||||
return kOutside;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Normal
|
||||
//
|
||||
G4ThreeVector G4PolyhedraSide::Normal( const G4ThreeVector &p, G4double *bestDistance )
|
||||
{
|
||||
G4int iPhi = ClosestPhiSegment( p.phi() );
|
||||
return vecs[iPhi].normal;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Extent
|
||||
//
|
||||
G4double G4PolyhedraSide::Extent( const G4ThreeVector axis )
|
||||
{
|
||||
if (axis.perp2() < 1.0/kInfinity) {
|
||||
//
|
||||
// Special case
|
||||
//
|
||||
return axis.z() < 0 ? -cone->ZLo() : cone->ZHi();
|
||||
}
|
||||
|
||||
G4int iPhi, i1, i2;
|
||||
G4double best;
|
||||
G4ThreeVector *list[4];
|
||||
|
||||
//
|
||||
// Which phi segment, if any, does the axis belong to
|
||||
//
|
||||
iPhi = PhiSegment( axis.phi() );
|
||||
|
||||
if (iPhi < 0) {
|
||||
//
|
||||
// No phi segment? Check front edge of first side and
|
||||
// last edge of second side
|
||||
//
|
||||
i1 = 0; i2 = numSide-1;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Check all corners of matching phi side
|
||||
//
|
||||
i1 = iPhi; i2 = iPhi;
|
||||
}
|
||||
|
||||
list[0] = vecs[i1].edges[0]->corner;
|
||||
list[1] = vecs[i1].edges[0]->corner+1;
|
||||
list[2] = vecs[i2].edges[1]->corner;
|
||||
list[3] = vecs[i2].edges[1]->corner+1;
|
||||
|
||||
//
|
||||
// Who's biggest?
|
||||
//
|
||||
best = -kInfinity;
|
||||
G4ThreeVector **vec = list;
|
||||
do {
|
||||
G4double answer = (*vec)->dot(axis);
|
||||
if (answer > best) best = answer;
|
||||
} while( ++vec < list+4 );
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CalculateExtent
|
||||
//
|
||||
// See notes in G4VCSGface
|
||||
//
|
||||
void G4PolyhedraSide::CalculateExtent( const EAxis axis,
|
||||
const G4VoxelLimits &voxelLimit,
|
||||
const G4AffineTransform &transform,
|
||||
G4double &min, G4double &max )
|
||||
{
|
||||
G4ClippablePolygon polygon;
|
||||
|
||||
//
|
||||
// Loop over all sides
|
||||
//
|
||||
G4PolyhedraSideVec *vec = vecs;
|
||||
do {
|
||||
//
|
||||
// Fill our polygon with the four corners of
|
||||
// this side, after the specified transformation
|
||||
//
|
||||
polygon.ClearAllVertices();
|
||||
|
||||
polygon.AddVertexInOrder( transform.TransformPoint( vec->edges[0]->corner[0] ) );
|
||||
polygon.AddVertexInOrder( transform.TransformPoint( vec->edges[0]->corner[1] ) );
|
||||
polygon.AddVertexInOrder( transform.TransformPoint( vec->edges[1]->corner[1] ) );
|
||||
polygon.AddVertexInOrder( transform.TransformPoint( vec->edges[1]->corner[0] ) );
|
||||
|
||||
//
|
||||
// Get extent
|
||||
//
|
||||
polygon.Clip( voxelLimit );
|
||||
polygon.GetExtent( axis, min, max );
|
||||
} while( ++vec < vecs+numSide );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// -------------------------------------------------------
|
||||
|
||||
//
|
||||
// IntersectSidePlane
|
||||
//
|
||||
// Decide if a line correctly intersects one side plane of our segment.
|
||||
// It is assumed that the correct side has been chosen, and thus only
|
||||
// the z bounds (of the entire segment) are checked.
|
||||
//
|
||||
// normSign - To be multiplied against normal:
|
||||
// = +1.0 normal is unchanged
|
||||
// = -1.0 normal is reversed (now points inward)
|
||||
//
|
||||
//
|
||||
G4bool G4PolyhedraSide::IntersectSidePlane( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
const G4PolyhedraSideVec vec,
|
||||
const G4double normSign,
|
||||
const G4double surfTolerance,
|
||||
G4double &distance, G4double &distFromSurface )
|
||||
{
|
||||
//
|
||||
// Correct normal? Here we have straight sides, and can safely ignore
|
||||
// intersections where the dot product with the normal is zero.
|
||||
//
|
||||
G4double dotProd = normSign*v.dot(vec.normal);
|
||||
|
||||
if (dotProd <= 0) return false;
|
||||
|
||||
//
|
||||
// Calculate distance to surface. If the side is too far
|
||||
// behind the point, we must reject it.
|
||||
//
|
||||
G4ThreeVector delta = p - vec.center;
|
||||
distFromSurface = -normSign*delta.dot(vec.normal);
|
||||
|
||||
if (distFromSurface < surfTolerance) return false;
|
||||
|
||||
//
|
||||
// Calculate precise distance to intersection with the side
|
||||
// (along the trajectory, not normal to the surface)
|
||||
//
|
||||
distance = distFromSurface/dotProd;
|
||||
|
||||
//
|
||||
// Do we fall off the r/z extent of the segment?
|
||||
//
|
||||
// Calculate this very, very carefully! Why?
|
||||
// 1. If a RZ end is at R=0, you can't miss!
|
||||
// 2. If you just fall off in RZ, the answer must
|
||||
// be consistent with adjacent G4PolyhedraSide faces.
|
||||
// (2) implies that only variables used by other G4PolyhedraSide
|
||||
// faces may be used, which includes only: p, v, and the edge corners.
|
||||
// It also means that one side is a ">" or "<", which the other
|
||||
// must be ">=" or "<=". Fortunately, this isn't a new problem.
|
||||
// The solution below I borrowed from Joseph O'Rourke,
|
||||
// "Computational Geometry in C (Second Edition)"
|
||||
// See: http://cs.smith.edu/~orourke/
|
||||
//
|
||||
G4ThreeVector ic = p + distance*v - vec.center;
|
||||
G4double atRZ = vec.surfRZ.dot(ic);
|
||||
if (atRZ < 0) {
|
||||
if (r[0]==0) return true; // Can't miss!
|
||||
|
||||
if (atRZ < -lenRZ*1.2) return false; // Forget it! Missed by a mile.
|
||||
|
||||
G4ThreeVector q = p + v;
|
||||
G4ThreeVector qa = q - vec.edges[0]->corner[0],
|
||||
qb = q - vec.edges[1]->corner[0];
|
||||
G4ThreeVector qacb = qa.cross(qb);
|
||||
if (normSign*qacb.dot(v) < 0) return false;
|
||||
}
|
||||
else if (atRZ > 0) {
|
||||
if (r[1]==0) return true; // Can't miss!
|
||||
|
||||
if (atRZ > lenRZ*1.2) return false; // Missed by a mile
|
||||
|
||||
G4ThreeVector q = p + v;
|
||||
G4ThreeVector qa = q - vec.edges[0]->corner[1],
|
||||
qb = q - vec.edges[1]->corner[1];
|
||||
G4ThreeVector qacb = qa.cross(qb);
|
||||
if (normSign*qacb.dot(v) >= 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LineHitsSegments
|
||||
//
|
||||
// Calculate which phi segments a line intersections in three dimensions.
|
||||
// No check is made as to whether the intersections are within the z bounds of
|
||||
// the segment.
|
||||
//
|
||||
G4int G4PolyhedraSide::LineHitsSegments( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
G4int *i1, G4int *i2 )
|
||||
{
|
||||
G4double s1, s2;
|
||||
//
|
||||
// First, decide if and where the line intersects the cone
|
||||
//
|
||||
G4int n = cone->LineHitsCone( p, v, &s1, &s2 );
|
||||
|
||||
//
|
||||
// Check intersections
|
||||
//
|
||||
if (n==0) return 0;
|
||||
|
||||
*i1 = PhiSegment( atan2( p.y() + s1*v.y(), p.x() + s1*v.x() ) );
|
||||
if (n==1) {
|
||||
return (*i1 < 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
*i2 = PhiSegment( atan2( p.y() + s2*v.y(), p.x() + s2*v.x() ) );
|
||||
if (*i1 == *i2) return 0;
|
||||
|
||||
if (*i1 < 0) {
|
||||
if (*i2 < 0) return 0;
|
||||
*i1 = *i2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*i2 < 0) return 1;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ClosestPhiSegment
|
||||
//
|
||||
// Decide which phi segment is closest in phi to the point.
|
||||
// The result is the same as PhiSegment if there is no phi opening.
|
||||
//
|
||||
G4int G4PolyhedraSide::ClosestPhiSegment( const G4double phi0 )
|
||||
{
|
||||
G4int iPhi = PhiSegment( phi0 );
|
||||
if (iPhi >= 0) return iPhi;
|
||||
|
||||
//
|
||||
// Boogers! The points falls inside the phi segment.
|
||||
// Look for the closest point: the start, or end
|
||||
//
|
||||
G4double phi = phi0;
|
||||
|
||||
while( phi < startPhi ) phi += 2*M_PI;
|
||||
G4double d1 = phi-startPhi-deltaPhi;
|
||||
|
||||
while( phi > startPhi ) phi -= 2*M_PI;
|
||||
G4double d2 = startPhi-phi;
|
||||
|
||||
return (d2 < d1) ? 0 : numSide-1;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// PhiSegment
|
||||
//
|
||||
// Decide which phi segment an angle belongs to, counting from zero.
|
||||
// A value of -1 indicates that the phi value is outside the shape
|
||||
// (only possible if phiTotal < 360 degrees).
|
||||
//
|
||||
G4int G4PolyhedraSide::PhiSegment( const G4double phi0 )
|
||||
{
|
||||
//
|
||||
// How far are we from phiStart? Come up with a positive answer
|
||||
// that is less than 2*PI
|
||||
//
|
||||
G4double phi = phi0 - startPhi;
|
||||
while( phi < 0 ) phi += 2*M_PI;
|
||||
while( phi > 2*M_PI ) phi -= 2*M_PI;
|
||||
|
||||
//
|
||||
// Divide
|
||||
//
|
||||
G4int answer = phi/deltaPhi;
|
||||
|
||||
if (answer >= numSide) {
|
||||
if (phiIsOpen) {
|
||||
return -1; // Looks like we missed
|
||||
}
|
||||
else {
|
||||
answer = numSide-1; // Probably just roundoff
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// DistanceToOneSide
|
||||
//
|
||||
// Arguments:
|
||||
// p - (in) Point to check
|
||||
// vec - (in) vector set of this side
|
||||
// normDist - (out) distance normal to the side or edge, as appropriate, signed
|
||||
// Return value = total distance from the side
|
||||
//
|
||||
G4double G4PolyhedraSide::DistanceToOneSide( const G4ThreeVector &p,
|
||||
const G4PolyhedraSideVec vec,
|
||||
G4double *normDist )
|
||||
{
|
||||
G4ThreeVector pc = p - vec.center;
|
||||
|
||||
//
|
||||
// Get normal distance
|
||||
//
|
||||
*normDist = vec.normal.dot(pc);
|
||||
|
||||
//
|
||||
// Add edge penalty
|
||||
//
|
||||
return DistanceAway( p, vec, normDist );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceAway
|
||||
//
|
||||
// Add distance from side edges, if necesssary, to total distance,
|
||||
// and updates normDist appropriate depending on edge normals.
|
||||
//
|
||||
G4double G4PolyhedraSide::DistanceAway( const G4ThreeVector &p,
|
||||
const G4PolyhedraSideVec vec,
|
||||
G4double *normDist )
|
||||
{
|
||||
G4double distOut2;
|
||||
G4ThreeVector pc = p - vec.center;
|
||||
G4double distFaceNorm = *normDist;
|
||||
|
||||
//
|
||||
// Okay, are we inside bounds?
|
||||
//
|
||||
G4double pcDotRZ = pc.dot(vec.surfRZ);
|
||||
G4double pcDotPhi = pc.dot(vec.surfPhi);
|
||||
|
||||
//
|
||||
// Go through all permutations.
|
||||
// Phi
|
||||
// | | ^
|
||||
// B | H | E |
|
||||
// ------[1]------------[3]----- |
|
||||
// |XXXXXXXXXXXXXX| +----> RZ
|
||||
// C |XXXXXXXXXXXXXX| F
|
||||
// |XXXXXXXXXXXXXX|
|
||||
// ------[0]------------[2]----
|
||||
// A | G | D
|
||||
// | |
|
||||
//
|
||||
// It's real messy, but at least it's quick
|
||||
//
|
||||
|
||||
if (pcDotRZ < -lenRZ) {
|
||||
G4double lenPhiZ = lenPhi[0] - lenRZ*lenPhi[1];
|
||||
G4double distOutZ = pcDotRZ+lenRZ;
|
||||
//
|
||||
// Below in RZ
|
||||
//
|
||||
*normDist = pc.dot(vec.edgeNorm[0]);
|
||||
|
||||
if (pcDotPhi < -lenPhiZ) {
|
||||
//
|
||||
// ...and below in phi. Find distance to point (A)
|
||||
//
|
||||
G4double distOutPhi = pcDotPhi+lenPhiZ;
|
||||
distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edges[0]->cornNorm[0]);
|
||||
}
|
||||
else if (pcDotPhi > lenPhiZ) {
|
||||
//
|
||||
// ...and above in phi. Find distance to point (B)
|
||||
//
|
||||
G4double distOutPhi = pcDotPhi-lenPhiZ;
|
||||
distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edges[1]->cornNorm[0]);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// ...and inside in phi. Find distance to line (C)
|
||||
//
|
||||
distOut2 = distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edgeNorm[0]);
|
||||
}
|
||||
}
|
||||
else if (pcDotRZ > lenRZ) {
|
||||
G4double lenPhiZ = lenPhi[0] + lenRZ*lenPhi[1];
|
||||
G4double distOutZ = pcDotRZ-lenRZ;
|
||||
//
|
||||
// Above in RZ
|
||||
//
|
||||
if (pcDotPhi < -lenPhiZ) {
|
||||
//
|
||||
// ...and below in phi. Find distance to point (D)
|
||||
//
|
||||
G4double distOutPhi = pcDotPhi+lenPhiZ;
|
||||
distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edges[0]->cornNorm[1]);
|
||||
}
|
||||
else if (pcDotPhi > lenPhiZ) {
|
||||
//
|
||||
// ...and above in phi. Find distance to point (E)
|
||||
//
|
||||
G4double distOutPhi = pcDotPhi-lenPhiZ;
|
||||
distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edges[1]->cornNorm[1]);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// ...and inside in phi. Find distance to line (F)
|
||||
//
|
||||
distOut2 = distOutZ*distOutZ;
|
||||
*normDist = pc.dot(vec.edgeNorm[1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
G4double lenPhiZ = lenPhi[0] + pcDotRZ*lenPhi[1];
|
||||
//
|
||||
// We are inside RZ bounds
|
||||
//
|
||||
if (pcDotPhi < -lenPhiZ) {
|
||||
//
|
||||
// ...and below in phi. Find distance to line (G)
|
||||
//
|
||||
G4double distOut = edgeNorm*(pcDotPhi+lenPhiZ);
|
||||
distOut2 = distOut*distOut;
|
||||
*normDist = pc.dot(vec.edges[0]->normal);
|
||||
}
|
||||
else if (pcDotPhi > lenPhiZ) {
|
||||
//
|
||||
// ...and above in phi. Find distance to line (H)
|
||||
//
|
||||
G4double distOut = edgeNorm*(pcDotPhi-lenPhiZ);
|
||||
distOut2 = distOut*distOut;
|
||||
*normDist = pc.dot(vec.edges[1]->normal);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Inside bounds! No penalty.
|
||||
//
|
||||
return fabs(distFaceNorm);
|
||||
}
|
||||
}
|
||||
return sqrt( distFaceNorm*distFaceNorm + distOut2 );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,329 @@
|
||||
//
|
||||
// G4VCSGfaceted.cc
|
||||
//
|
||||
// Implementation of the virtual class of a CSG type shape that is built
|
||||
// entirely out of G4VCSGface faces.
|
||||
//
|
||||
// \begin{preach mode}
|
||||
//
|
||||
// Do not be fooled by the content, the algorithms in here are not
|
||||
// very clever. This is obvious if one (tries) to read a good textbook
|
||||
// on 3D modeling.
|
||||
//
|
||||
// GEANT4 has some rather esoteric demands on its geometric models,
|
||||
// which makes most canned 3D routines not useful. So we have to
|
||||
// try to invent a few. This is dangerous, because 3D modeling is
|
||||
// a serious programming game.
|
||||
//
|
||||
// One of the real simplifications in the methods I've used here for
|
||||
// a shape is that each face of a solid is treated separately. Or, at
|
||||
// least this is the illusion. In fact, for non-convex solids (which
|
||||
// abound in GEANT4), the face routine Inside cannot be correctly written
|
||||
// unless each face knows something about all of it's neighbor. Furthermore,
|
||||
// is is absolutely *crucial* that the algebraic instructions for
|
||||
// deciding if a track intersection falls outside a face matches
|
||||
// for the edge between adjacent faces. If not, THERE WILL BE A
|
||||
// CRACK IN YOUR SOLID, GUARANTEED. It will be small, but it will
|
||||
// be there.
|
||||
//
|
||||
// So? If we were writing a 3D display routine, cracks wouldn't
|
||||
// matter. But we are writing instead a tracking simulation. One crack,
|
||||
// and things may fall about very quickly. Probably not, if you generate a
|
||||
// 100 events, or a thousand, but millions?? *BEWARE*
|
||||
//
|
||||
// Note that none of this is obvious in the pretty code below. Such
|
||||
// invisible interdependencies are a evil sin for a software designer.
|
||||
// So, I *confess*.
|
||||
//
|
||||
// Now, I should explain what you have to do.
|
||||
//
|
||||
// \end{preach mode}
|
||||
//
|
||||
#include "G4VCSGfaceted.hh"
|
||||
#include "G4VCSGface.hh"
|
||||
|
||||
#include "G4VoxelLimits.hh"
|
||||
#include "G4AffineTransform.hh"
|
||||
|
||||
#include "G4Polyhedron.hh"
|
||||
#include "G4VGraphicsScene.hh"
|
||||
#include "G4NURBS.hh"
|
||||
#include "G4NURBSbox.hh"
|
||||
#include "G4VisExtent.hh"
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
G4VCSGfaceted::~G4VCSGfaceted()
|
||||
{
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
delete *face;
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
delete [] faces;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CalculateExtent
|
||||
//
|
||||
G4bool G4VCSGfaceted::CalculateExtent( const EAxis pAxis,
|
||||
const G4VoxelLimits& pVoxelLimit,
|
||||
const G4AffineTransform& pTransform,
|
||||
G4double &pMin, G4double &pMax ) const
|
||||
{
|
||||
//
|
||||
// Loop over all faces, testing each as we go
|
||||
//
|
||||
G4VCSGface **face = faces;
|
||||
G4double max = -kInfinity, min = +kInfinity;
|
||||
do {
|
||||
(*face)->CalculateExtent( pAxis, pVoxelLimit, pTransform, min, max );
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
//
|
||||
// Any luck?
|
||||
//
|
||||
if (max == -kInfinity) return false;
|
||||
|
||||
//
|
||||
// What are the voxel limits along this particular axis?
|
||||
//
|
||||
if (pVoxelLimit.IsLimited(pAxis)) {
|
||||
G4double vMax = pVoxelLimit.GetMaxExtent(pAxis),
|
||||
vMin = pVoxelLimit.GetMinExtent(pAxis);
|
||||
|
||||
if (max < vMin) return false;
|
||||
if (min > vMax) return false;
|
||||
|
||||
pMin = min < vMin ? vMin : min;
|
||||
pMax = max > vMax ? vMax : max;
|
||||
}
|
||||
else {
|
||||
pMin = min;
|
||||
pMax = max;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Inside
|
||||
//
|
||||
// It could be a good idea to override this virtual
|
||||
// member to add first a simple test (such as spherical
|
||||
// test or whatnot) and to call this version only if
|
||||
// the simplier test fails.
|
||||
//
|
||||
EInside G4VCSGfaceted::Inside( const G4ThreeVector &p ) const
|
||||
{
|
||||
EInside answer;
|
||||
G4VCSGface **face = faces;
|
||||
G4double best = kInfinity;
|
||||
do {
|
||||
G4double distance;
|
||||
EInside result = (*face)->Inside( p, kCarTolerance/2, &distance );
|
||||
if (result == kSurface) return kSurface;
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
answer = result;
|
||||
}
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
//
|
||||
// SurfaceNormal
|
||||
//
|
||||
G4ThreeVector G4VCSGfaceted::SurfaceNormal( const G4ThreeVector& p) const
|
||||
{
|
||||
G4ThreeVector answer;
|
||||
G4VCSGface **face = faces;
|
||||
G4double best = kInfinity;
|
||||
do {
|
||||
G4double distance;
|
||||
G4ThreeVector normal = (*face)->Normal( p, &distance );
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
answer = normal;
|
||||
}
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
//
|
||||
// DistanceToIn(p,v)
|
||||
//
|
||||
G4double G4VCSGfaceted::DistanceToIn( const G4ThreeVector &p, const G4ThreeVector &v ) const
|
||||
{
|
||||
G4double distance = kInfinity;
|
||||
G4double distFromSurface;
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
G4double faceDistance,
|
||||
faceDistFromSurface;
|
||||
G4ThreeVector faceNormal;
|
||||
G4bool faceAllBehind;
|
||||
if ((*face)->Intersect( p, v, false, kCarTolerance/2,
|
||||
faceDistance, faceDistFromSurface,
|
||||
faceNormal, faceAllBehind ) ) {
|
||||
//
|
||||
// Intersecting face
|
||||
//
|
||||
if (faceDistance < distance) {
|
||||
distance = faceDistance;
|
||||
distFromSurface = faceDistFromSurface;
|
||||
}
|
||||
}
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
if ((distance < kInfinity) && (fabs(distFromSurface)<kCarTolerance/2) ) distance = 0;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceToIn(p)
|
||||
//
|
||||
G4double G4VCSGfaceted::DistanceToIn( const G4ThreeVector &p ) const
|
||||
{
|
||||
return DistanceTo( p, false );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceToOut(p,v)
|
||||
//
|
||||
G4double G4VCSGfaceted::DistanceToOut( const G4ThreeVector &p, const G4ThreeVector &v,
|
||||
const G4bool calcNorm,
|
||||
G4bool *validNorm, G4ThreeVector *n ) const
|
||||
{
|
||||
G4bool allBehind = true;
|
||||
G4double distance = kInfinity;
|
||||
G4double distFromSurface;
|
||||
G4ThreeVector normal;
|
||||
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
G4double faceDistance,
|
||||
faceDistFromSurface;
|
||||
G4ThreeVector faceNormal;
|
||||
G4bool faceAllBehind;
|
||||
if ((*face)->Intersect( p, v, true, kCarTolerance/2,
|
||||
faceDistance, faceDistFromSurface,
|
||||
faceNormal, faceAllBehind ) ) {
|
||||
//
|
||||
// Intersecting face
|
||||
//
|
||||
if ( (distance < kInfinity) || (!faceAllBehind) ) allBehind = false;
|
||||
if (faceDistance < distance) {
|
||||
distance = faceDistance;
|
||||
distFromSurface = faceDistFromSurface;
|
||||
normal = faceNormal;
|
||||
}
|
||||
}
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
if (distance < kInfinity) {
|
||||
if (fabs(distFromSurface)<kCarTolerance/2) distance = 0;
|
||||
|
||||
if (calcNorm) {
|
||||
*validNorm = allBehind;
|
||||
*n = normal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (calcNorm) *validNorm = false;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceToOut(p)
|
||||
//
|
||||
G4double G4VCSGfaceted::DistanceToOut( const G4ThreeVector &p ) const
|
||||
{
|
||||
return DistanceTo( p, true );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DistanceTo
|
||||
//
|
||||
// Protected routine called by DistanceToIn and DistanceToOut
|
||||
//
|
||||
G4double G4VCSGfaceted::DistanceTo( const G4ThreeVector &p, const G4bool outgoing ) const
|
||||
{
|
||||
G4VCSGface **face = faces;
|
||||
G4double best = kInfinity;
|
||||
do {
|
||||
G4double distance = (*face)->Distance( p, outgoing );
|
||||
if (distance < best) best = distance;
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DescribeYourselfTo
|
||||
//
|
||||
void G4VCSGfaceted::DescribeYourselfTo( G4VGraphicsScene& scene ) const
|
||||
{
|
||||
scene.AddThis( *this );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// GetExtent
|
||||
//
|
||||
// This routine might need testing
|
||||
//
|
||||
G4VisExtent G4VCSGfaceted::GetExtent() const
|
||||
{
|
||||
G4ThreeVector plusX(1,0,0), minusX(-1,0,0),
|
||||
plusY(0,1,0), minusY(0,-1,0),
|
||||
plusZ(0,0,1), minusZ(0,0,-0);
|
||||
G4double answer, maxX = -kInfinity,
|
||||
minX = -kInfinity,
|
||||
maxY = -kInfinity,
|
||||
minY = -kInfinity,
|
||||
maxZ = -kInfinity,
|
||||
minZ = -kInfinity;
|
||||
|
||||
//
|
||||
// Ask everyone about x, y, and z
|
||||
//
|
||||
G4VCSGface **face = faces;
|
||||
do {
|
||||
answer = (*face)->Extent( plusX );
|
||||
if (answer > maxX) maxX = answer;
|
||||
|
||||
answer = (*face)->Extent( minusX );
|
||||
if (answer > minX) minX = answer;
|
||||
|
||||
answer = (*face)->Extent( plusY );
|
||||
if (answer > maxY) maxY = answer;
|
||||
|
||||
answer = (*face)->Extent( minusY );
|
||||
if (answer > minY) minY = answer;
|
||||
|
||||
answer = (*face)->Extent( plusZ );
|
||||
if (answer > maxZ) maxZ = answer;
|
||||
|
||||
answer = (*face)->Extent( minusZ );
|
||||
if (answer > minZ) minZ = answer;
|
||||
} while( ++face < faces + numFace );
|
||||
|
||||
//
|
||||
// Yes, the signs are correct!
|
||||
//
|
||||
return G4VisExtent( -minX, maxY, -minY, maxY, -minZ, maxZ );
|
||||
}
|
||||
Reference in New Issue
Block a user