1281 lines
33 KiB
C++
1281 lines
33 KiB
C++
//
|
|
// ********************************************************************
|
|
// * DISCLAIMER *
|
|
// * *
|
|
// * The following disclaimer summarizes all the specific disclaimers *
|
|
// * of contributors to this software. The specific disclaimers,which *
|
|
// * govern, are listed with their locations in: *
|
|
// * http://cern.ch/geant4/license *
|
|
// * *
|
|
// * Neither the authors of this software system, nor their employing *
|
|
// * institutes,nor the agencies providing financial support for this *
|
|
// * work make any representation or warranty, express or implied, *
|
|
// * regarding this software system or assume any liability for its *
|
|
// * use. *
|
|
// * *
|
|
// * This code implementation is the intellectual property of the *
|
|
// * 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: G4Trd.cc,v 1.19 2004/01/26 09:03:20 gcosmo Exp $
|
|
// GEANT4 tag $Name: geant4-06-00-patch-01 $
|
|
//
|
|
//
|
|
// Implementation for G4Trd class
|
|
//
|
|
// History:
|
|
// ~1996, V.Grichine, 1st implementation based on old code of P.Kent
|
|
// 07.05.00, V.Grichine, in d = DistanceToIn(p,v), if d<0.5*kCarTolerance, d=0
|
|
// --------------------------------------------------------------------
|
|
|
|
#include "G4Trd.hh"
|
|
|
|
#include "G4VPVParameterisation.hh"
|
|
#include "G4VoxelLimits.hh"
|
|
#include "G4AffineTransform.hh"
|
|
|
|
#include "G4VGraphicsScene.hh"
|
|
#include "G4Polyhedron.hh"
|
|
#include "G4NURBS.hh"
|
|
#include "G4NURBSbox.hh"
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constructor - check & set half widths
|
|
|
|
G4Trd::G4Trd( const G4String& pName,
|
|
G4double pdx1, G4double pdx2,
|
|
G4double pdy1, G4double pdy2,
|
|
G4double pdz )
|
|
: G4CSGSolid(pName)
|
|
{
|
|
CheckAndSetAllParameters (pdx1, pdx2, pdy1, pdy2, pdz);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set and check (coplanarity) of trd parameters
|
|
|
|
void G4Trd::CheckAndSetAllParameters ( G4double pdx1, G4double pdx2,
|
|
G4double pdy1, G4double pdy2,
|
|
G4double pdz )
|
|
{
|
|
if ( pdx1>0&&pdx2>0&&pdy1>0&&pdy2>0&&pdz>0 )
|
|
{
|
|
fDx1=pdx1; fDx2=pdx2;
|
|
fDy1=pdy1; fDy2=pdy2;
|
|
fDz=pdz;
|
|
}
|
|
else
|
|
{
|
|
if ( pdx1>=0 && pdx2>=0 && pdy1>=0 && pdy2>=0 && pdz>=0 )
|
|
{
|
|
// G4double Minimum_length= (1+per_thousand) * kCarTolerance/2.;
|
|
// FIX-ME : temporary solution for ZERO or very-small parameters
|
|
//
|
|
G4double Minimum_length= kCarTolerance/2.;
|
|
fDx1=std::max(pdx1,Minimum_length);
|
|
fDx2=std::max(pdx2,Minimum_length);
|
|
fDy1=std::max(pdy1,Minimum_length);
|
|
fDy2=std::max(pdy2,Minimum_length);
|
|
fDz=std::max(pdz,Minimum_length);
|
|
}
|
|
else
|
|
{
|
|
G4cerr << "ERROR - G4Trd()::CheckAndSetAllParameters(): " << GetName()
|
|
<< G4endl
|
|
<< " Invalid dimensions, some are < 0 !" << G4endl
|
|
<< " X - " << pdx1 << ", " << pdx2 << G4endl
|
|
<< " Y - " << pdy1 << ", " << pdy2 << G4endl
|
|
<< " Z - " << pdz << G4endl;
|
|
G4Exception("G4Trd::CheckAndSetAllParameters()",
|
|
"InvalidSetup", FatalException,
|
|
"Invalid parameters.");
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Destructor
|
|
|
|
G4Trd::~G4Trd()
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
void G4Trd::SetAllParameters ( G4double pdx1, G4double pdx2, G4double pdy1,
|
|
G4double pdy2, G4double pdz )
|
|
{
|
|
CheckAndSetAllParameters (pdx1, pdx2, pdy1, pdy2, pdz);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Dispatch to parameterisation for replication mechanism dimension
|
|
// computation & modification.
|
|
|
|
void G4Trd::ComputeDimensions( G4VPVParameterisation* p,
|
|
const G4int n,
|
|
const G4VPhysicalVolume* pRep )
|
|
{
|
|
p->ComputeDimensions(*this,n,pRep);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calculate extent under transform and specified limit
|
|
|
|
G4bool G4Trd::CalculateExtent( const EAxis pAxis,
|
|
const G4VoxelLimits& pVoxelLimit,
|
|
const G4AffineTransform& pTransform,
|
|
G4double& pMin, G4double& pMax ) const
|
|
{
|
|
if (!pTransform.IsRotated())
|
|
{
|
|
// Special case handling for unrotated solids
|
|
// 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;
|
|
|
|
zoffset=pTransform.NetTranslation().z();
|
|
zMin=zoffset-fDz;
|
|
zMax=zoffset+fDz;
|
|
if (pVoxelLimit.IsZLimited())
|
|
{
|
|
if ( (zMin>pVoxelLimit.GetMaxZExtent()+kCarTolerance)
|
|
|| (zMax<pVoxelLimit.GetMinZExtent()-kCarTolerance) )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (zMin<pVoxelLimit.GetMinZExtent())
|
|
{
|
|
zMin=pVoxelLimit.GetMinZExtent();
|
|
}
|
|
if (zMax>pVoxelLimit.GetMaxZExtent())
|
|
{
|
|
zMax=pVoxelLimit.GetMaxZExtent();
|
|
}
|
|
}
|
|
}
|
|
xoffset=pTransform.NetTranslation().x();
|
|
if (fDx2 >= fDx1)
|
|
{
|
|
xMax = xoffset+(fDx1+fDx2)/2+(zMax-zoffset)*(fDx2-fDx1)/(2*fDz) ;
|
|
xMin = 2*xoffset - xMax ;
|
|
}
|
|
else
|
|
{
|
|
xMax = xoffset+(fDx1+fDx2)/2+(zMin-zoffset)*(fDx2-fDx1)/(2*fDz) ;
|
|
xMin = 2*xoffset - xMax ;
|
|
}
|
|
if (pVoxelLimit.IsXLimited())
|
|
{
|
|
if ( (xMin>pVoxelLimit.GetMaxXExtent()+kCarTolerance)
|
|
|| (xMax<pVoxelLimit.GetMinXExtent()-kCarTolerance) )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (xMin<pVoxelLimit.GetMinXExtent())
|
|
{
|
|
xMin=pVoxelLimit.GetMinXExtent();
|
|
}
|
|
if (xMax>pVoxelLimit.GetMaxXExtent())
|
|
{
|
|
xMax=pVoxelLimit.GetMaxXExtent();
|
|
}
|
|
}
|
|
}
|
|
yoffset= pTransform.NetTranslation().y() ;
|
|
if(fDy2 >= fDy1)
|
|
{
|
|
yMax = yoffset+(fDy2+fDy1)/2+(zMax-zoffset)*(fDy2-fDy1)/(2*fDz) ;
|
|
yMin = 2*yoffset - yMax ;
|
|
}
|
|
else
|
|
{
|
|
yMax = yoffset+(fDy2+fDy1)/2+(zMin-zoffset)*(fDy2-fDy1)/(2*fDz) ;
|
|
yMin = 2*yoffset - yMax ;
|
|
}
|
|
if (pVoxelLimit.IsYLimited())
|
|
{
|
|
if ( (yMin>pVoxelLimit.GetMaxYExtent()+kCarTolerance)
|
|
|| (yMax<pVoxelLimit.GetMinYExtent()-kCarTolerance) )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (yMin<pVoxelLimit.GetMinYExtent())
|
|
{
|
|
yMin=pVoxelLimit.GetMinYExtent();
|
|
}
|
|
if (yMax>pVoxelLimit.GetMaxYExtent())
|
|
{
|
|
yMax=pVoxelLimit.GetMaxYExtent();
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (pAxis)
|
|
{
|
|
case kXAxis:
|
|
pMin=xMin;
|
|
pMax=xMax;
|
|
break;
|
|
case kYAxis:
|
|
pMin=yMin;
|
|
pMax=yMax;
|
|
break;
|
|
case kZAxis:
|
|
pMin=zMin;
|
|
pMax=zMax;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Add 2*Tolerance to avoid precision troubles ?
|
|
//
|
|
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 G4Trd::Inside( const G4ThreeVector& p ) const
|
|
{
|
|
EInside in=kOutside;
|
|
G4double x,y,zbase1,zbase2;
|
|
|
|
if (fabs(p.z())<=fDz-kCarTolerance/2)
|
|
{
|
|
zbase1=p.z()+fDz; // Dist from -ve z plane
|
|
zbase2=fDz-p.z(); // Dist from +ve z plane
|
|
|
|
// Check whether inside x tolerance
|
|
//
|
|
x=0.5*(fDx2*zbase1+fDx1*zbase2)/fDz - kCarTolerance/2;
|
|
if (fabs(p.x())<=x)
|
|
{
|
|
y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz - kCarTolerance/2;
|
|
if (fabs(p.y())<=y)
|
|
{
|
|
in=kInside;
|
|
}
|
|
else if (fabs(p.y())<=y+kCarTolerance)
|
|
{
|
|
in=kSurface;
|
|
}
|
|
}
|
|
else if (fabs(p.x())<=x+kCarTolerance)
|
|
{
|
|
// y = y half width of shape at z of point + tolerant boundary
|
|
//
|
|
y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz + kCarTolerance/2;
|
|
if (fabs(p.y())<=y)
|
|
{
|
|
in=kSurface;
|
|
}
|
|
}
|
|
}
|
|
else if (fabs(p.z())<=fDz+kCarTolerance/2)
|
|
{
|
|
// Only need to check outer tolerant boundaries
|
|
//
|
|
zbase1=p.z()+fDz; // Dist from -ve z plane
|
|
zbase2=fDz-p.z(); // Dist from +ve z plane
|
|
|
|
// x = x half width of shape at z of point plus tolerance
|
|
//
|
|
x=0.5*(fDx2*zbase1+fDx1*zbase2)/fDz + kCarTolerance/2;
|
|
if (fabs(p.x())<=x)
|
|
{
|
|
// y = y half width of shape at z of point
|
|
//
|
|
y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz + kCarTolerance/2;
|
|
if (fabs(p.y())<=y) 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 G4Trd::SurfaceNormal( const G4ThreeVector& p ) const
|
|
{
|
|
G4ThreeVector norm;
|
|
G4double z,tanx,secx,newpx,widx;
|
|
G4double tany,secy,newpy,widy;
|
|
G4double distx,disty,distz,fcos;
|
|
|
|
z=2.0*fDz;
|
|
|
|
tanx=(fDx2-fDx1)/z;
|
|
secx=sqrt(1.0+tanx*tanx);
|
|
newpx=fabs(p.x())-p.z()*tanx;
|
|
widx=fDx2-fDz*tanx;
|
|
|
|
tany=(fDy2-fDy1)/z;
|
|
secy=sqrt(1.0+tany*tany);
|
|
newpy=fabs(p.y())-p.z()*tany;
|
|
widy=fDy2-fDz*tany;
|
|
|
|
distx=fabs(newpx-widx)/secx; // perpendicular distance to x side
|
|
disty=fabs(newpy-widy)/secy; // to y side
|
|
distz=fabs(fabs(p.z())-fDz); // to z side
|
|
|
|
// find closest side
|
|
//
|
|
if (distx<=disty)
|
|
{
|
|
if (distx<=distz)
|
|
{
|
|
// Closest to X
|
|
//
|
|
fcos=1.0/secx;
|
|
// normal=(+/-cos(ang),0,-sin(ang))
|
|
if (p.x()>=0)
|
|
norm=G4ThreeVector(fcos,0,-tanx*fcos);
|
|
else
|
|
norm=G4ThreeVector(-fcos,0,-tanx*fcos);
|
|
}
|
|
else
|
|
{
|
|
// Closest to Z
|
|
//
|
|
if (p.z()>=0)
|
|
norm=G4ThreeVector(0,0,1);
|
|
else
|
|
norm=G4ThreeVector(0,0,-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (disty<=distz)
|
|
{
|
|
// Closest to Y
|
|
//
|
|
fcos=1.0/secy;
|
|
if (p.y()>=0)
|
|
norm=G4ThreeVector(0,fcos,-tany*fcos);
|
|
else
|
|
norm=G4ThreeVector(0,-fcos,-tany*fcos);
|
|
}
|
|
else
|
|
{
|
|
// Closest to Z
|
|
//
|
|
if (p.z()>=0)
|
|
norm=G4ThreeVector(0,0,1);
|
|
else
|
|
norm=G4ThreeVector(0,0,-1);
|
|
}
|
|
}
|
|
return norm;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calculate distance to shape from outside
|
|
// - return kInfinity if no intersection
|
|
//
|
|
// ALGORITHM:
|
|
// For each component, calculate pair of minimum and maximum intersection
|
|
// values for which the particle is in the extent of the shape
|
|
// - The smallest (MAX minimum) allowed distance of the pairs is intersect
|
|
// - Z plane intersectin uses tolerance
|
|
// - XZ YZ planes use logic & *SLIGHTLY INCORRECT* tolerance
|
|
// (this saves at least 1 sqrt, 1 multiply and 1 divide... in applicable
|
|
// cases)
|
|
// - Note: XZ and YZ planes each divide space into four regions,
|
|
// characterised by ss1 ss2
|
|
// NOTE:
|
|
//
|
|
// `Inside' safe - meaningful answers given if point is inside the exact
|
|
// shape.
|
|
|
|
G4double G4Trd::DistanceToIn( const G4ThreeVector& p,
|
|
const G4ThreeVector& v ) const
|
|
{
|
|
G4double snxt = kInfinity ; // snxt = default return value
|
|
G4double smin,smax;
|
|
G4double s1,s2,tanxz,tanyz,ds1,ds2;
|
|
G4double ss1,ss2,sn1=0.,sn2=0.,Dist;
|
|
|
|
if ( v.z() ) // Calculate valid z intersect range
|
|
{
|
|
if ( v.z() > 0 ) // Calculate smax: must be +ve or no intersection.
|
|
{
|
|
Dist = fDz - p.z() ; // to plane at +dz
|
|
|
|
if (Dist >= 0.5*kCarTolerance)
|
|
{
|
|
smax = Dist/v.z() ;
|
|
smin = -(fDz + p.z())/v.z() ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
else // v.z <0
|
|
{
|
|
Dist=fDz+p.z(); // plane at -dz
|
|
|
|
if ( Dist >= 0.5*kCarTolerance )
|
|
{
|
|
smax = -Dist/v.z() ;
|
|
smin = (fDz - p.z())/v.z() ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
if (smin < 0 ) smin = 0 ;
|
|
}
|
|
else // v.z=0
|
|
{
|
|
if (fabs(p.z()) >= fDz ) return snxt ; // Outside & no intersect
|
|
else
|
|
{
|
|
smin = 0 ; // Always inside z range
|
|
smax = kInfinity;
|
|
}
|
|
}
|
|
|
|
// Calculate x intersection range
|
|
//
|
|
// Calc half width at p.z, and components towards planes
|
|
|
|
tanxz = (fDx2 - fDx1)*0.5/fDz ;
|
|
s1 = 0.5*(fDx1+fDx2) + tanxz*p.z() ; // x half width at p.z
|
|
ds1 = v.x() - tanxz*v.z() ; // Components of v towards faces at +-x
|
|
ds2 = v.x() + tanxz*v.z() ;
|
|
ss1 = s1 - p.x() ; // -delta x to +ve plane
|
|
// -ve when outside
|
|
ss2 = -s1 - p.x() ; // -delta x to -ve plane
|
|
// +ve when outside
|
|
|
|
if (ss1 < 0 && ss2 <= 0 )
|
|
{
|
|
if (ds1 < 0) // In +ve coord Area
|
|
{
|
|
sn1 = ss1/ds1 ;
|
|
|
|
if ( ds2 < 0 ) sn2 = ss2/ds2 ;
|
|
else sn2 = kInfinity ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
else if ( ss1 >= 0 && ss2 > 0 )
|
|
{
|
|
if ( ds2 > 0 ) // In -ve coord Area
|
|
{
|
|
sn1 = ss2/ds2 ;
|
|
|
|
if (ds1 > 0) sn2 = ss1/ds1 ;
|
|
else sn2 = kInfinity;
|
|
|
|
}
|
|
else return snxt ;
|
|
}
|
|
else if (ss1 >= 0 && ss2 <= 0 )
|
|
{
|
|
// Inside Area - calculate leaving distance
|
|
// *Don't* use exact distance to side for tolerance
|
|
// = ss1*cos(ang xz)
|
|
// = ss1/sqrt(1.0+tanxz*tanxz)
|
|
sn1 = 0 ;
|
|
|
|
if ( ds1 > 0 )
|
|
{
|
|
if (ss1 > 0.5*kCarTolerance) sn2 = ss1/ds1 ; // Leave +ve side extent
|
|
else return snxt ; // Leave immediately by +ve
|
|
}
|
|
else sn2 = kInfinity ;
|
|
|
|
if ( ds2 < 0 )
|
|
{
|
|
if ( ss2 < -0.5*kCarTolerance )
|
|
{
|
|
Dist = ss2/ds2 ; // Leave -ve side extent
|
|
if ( Dist < sn2 ) sn2 = Dist ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
}
|
|
else if (ss1 < 0 && ss2 > 0 )
|
|
{
|
|
// Within +/- plane cross-over areas (not on boundaries ss1||ss2==0)
|
|
|
|
if ( ds1 >= 0 || ds2 <= 0 )
|
|
{
|
|
return snxt ;
|
|
}
|
|
else // Will intersect & stay inside
|
|
{
|
|
sn1 = ss1/ds1 ;
|
|
Dist = ss2/ds2 ;
|
|
if (Dist > sn1 ) sn1 = Dist ;
|
|
sn2 = kInfinity ;
|
|
}
|
|
}
|
|
|
|
// Reduce allowed range of distances as appropriate
|
|
|
|
if ( sn1 > smin ) smin = sn1 ;
|
|
if ( sn2 < smax ) smax = sn2 ;
|
|
|
|
// Check for incompatible ranges (eg z intersects between 50 ->100 and x
|
|
// only 10-40 -> no intersection)
|
|
|
|
if ( smax < smin ) return snxt ;
|
|
|
|
// Calculate valid y intersection range
|
|
// (repeat of x intersection code)
|
|
|
|
tanyz = (fDy2-fDy1)*0.5/fDz ;
|
|
s2 = 0.5*(fDy1+fDy2) + tanyz*p.z() ; // y half width at p.z
|
|
ds1 = v.y() - tanyz*v.z() ; // Components of v towards faces at +-y
|
|
ds2 = v.y() + tanyz*v.z() ;
|
|
ss1 = s2 - p.y() ; // -delta y to +ve plane
|
|
ss2 = -s2 - p.y() ; // -delta y to -ve plane
|
|
|
|
if ( ss1 < 0 && ss2 <= 0 )
|
|
{
|
|
if (ds1 < 0 ) // In +ve coord Area
|
|
{
|
|
sn1 = ss1/ds1 ;
|
|
if ( ds2 < 0 ) sn2 = ss2/ds2 ;
|
|
else sn2 = kInfinity ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
else if ( ss1 >= 0 && ss2 > 0 )
|
|
{
|
|
if ( ds2 > 0 ) // In -ve coord Area
|
|
{
|
|
sn1 = ss2/ds2 ;
|
|
if ( ds1 > 0 ) sn2 = ss1/ds1 ;
|
|
else sn2 = kInfinity ;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
else if (ss1 >= 0 && ss2 <= 0 )
|
|
{
|
|
// Inside Area - calculate leaving distance
|
|
// *Don't* use exact distance to side for tolerance
|
|
// = ss1*cos(ang yz)
|
|
// = ss1/sqrt(1.0+tanyz*tanyz)
|
|
sn1 = 0 ;
|
|
|
|
if ( ds1 > 0 )
|
|
{
|
|
if (ss1 > 0.5*kCarTolerance) sn2 = ss1/ds1 ; // Leave +ve side extent
|
|
else return snxt ; // Leave immediately by +ve
|
|
}
|
|
else sn2 = kInfinity ;
|
|
|
|
if ( ds2 < 0 )
|
|
{
|
|
if ( ss2 < -0.5*kCarTolerance )
|
|
{
|
|
Dist = ss2/ds2 ; // Leave -ve side extent
|
|
if (Dist < sn2) sn2=Dist;
|
|
}
|
|
else return snxt ;
|
|
}
|
|
}
|
|
else if (ss1 < 0 && ss2 > 0 )
|
|
{
|
|
// Within +/- plane cross-over areas (not on boundaries ss1||ss2==0)
|
|
|
|
if (ds1 >= 0 || ds2 <= 0 )
|
|
{
|
|
return snxt ;
|
|
}
|
|
else // Will intersect & stay inside
|
|
{
|
|
sn1 = ss1/ds1 ;
|
|
Dist = ss2/ds2 ;
|
|
if (Dist > sn1 ) sn1 = Dist ;
|
|
sn2 = kInfinity ;
|
|
}
|
|
}
|
|
|
|
// Reduce allowed range of distances as appropriate
|
|
|
|
if ( sn1 > smin) smin = sn1 ;
|
|
if ( sn2 < smax) smax = sn2 ;
|
|
|
|
// Check for incompatible ranges (eg x intersects between 50 ->100 and y
|
|
// only 10-40 -> no intersection). Set snxt if ok
|
|
|
|
if ( smax > smin ) snxt = smin ;
|
|
if (snxt < 0.5*kCarTolerance ) snxt = 0.0 ;
|
|
|
|
return snxt ;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Approximate distance to shape
|
|
// Calculate perpendicular distances to z/x/y surfaces, return largest
|
|
// which is the most fast estimation of shortest distance to Trd
|
|
// - Safe underestimate
|
|
// - If point within exact shape, return 0
|
|
|
|
G4double G4Trd::DistanceToIn( const G4ThreeVector& p ) const
|
|
{
|
|
G4double safe=0.0;
|
|
G4double tanxz,distx,safx;
|
|
G4double tanyz,disty,safy;
|
|
G4double zbase;
|
|
|
|
safe=fabs(p.z())-fDz;
|
|
if (safe<0) safe=0; // Also used to ensure x/y distances
|
|
// POSITIVE
|
|
|
|
zbase=fDz+p.z();
|
|
|
|
// Find distance along x direction to closest x plane
|
|
//
|
|
tanxz=(fDx2-fDx1)*0.5/fDz;
|
|
// widx=fDx1+tanxz*(fDz+p.z()); // x width at p.z
|
|
// distx=fabs(p.x())-widx; // distance to plane
|
|
distx=fabs(p.x())-(fDx1+tanxz*zbase);
|
|
if (distx>safe)
|
|
{
|
|
safx=distx/sqrt(1.0+tanxz*tanxz); // vector Dist=Dist*cos(ang)
|
|
if (safx>safe) safe=safx;
|
|
}
|
|
|
|
// Find distance along y direction to slanted wall
|
|
tanyz=(fDy2-fDy1)*0.5/fDz;
|
|
// widy=fDy1+tanyz*(fDz+p.z()); // y width at p.z
|
|
// disty=fabs(p.y())-widy; // distance to plane
|
|
disty=fabs(p.y())-(fDy1+tanyz*zbase);
|
|
if (disty>safe)
|
|
{
|
|
safy=disty/sqrt(1.0+tanyz*tanyz); // distance along vector
|
|
if (safy>safe) safe=safy;
|
|
}
|
|
return safe;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calcluate distance to surface of shape from inside
|
|
// Calculate distance to x/y/z planes - smallest is exiting distance
|
|
// - z planes have std. check for tolerance
|
|
// - xz yz planes have check based on distance || to x or y axis
|
|
// (not corrected for slope of planes)
|
|
// ?BUG? If v.z==0 are there cases when snside not set????
|
|
|
|
G4double G4Trd::DistanceToOut( const G4ThreeVector& p,
|
|
const G4ThreeVector& v,
|
|
const G4bool calcNorm,
|
|
G4bool *validNorm,
|
|
G4ThreeVector *n ) const
|
|
{
|
|
ESide side = kUndefined, snside = kUndefined;
|
|
G4double snxt,pdist;
|
|
G4double central,ss1,ss2,ds1,ds2,sn=0.,sn2=0.;
|
|
G4double tanxz=0.,cosxz=0.,tanyz=0.,cosyz=0.;
|
|
|
|
if (calcNorm) *validNorm=true; // All normals are valid
|
|
|
|
// Calculate z plane intersection
|
|
if (v.z()>0)
|
|
{
|
|
pdist=fDz-p.z();
|
|
if (pdist>kCarTolerance/2)
|
|
{
|
|
snxt=pdist/v.z();
|
|
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/2)
|
|
{
|
|
snxt=-pdist/v.z();
|
|
side=kMZ;
|
|
}
|
|
else
|
|
{
|
|
if (calcNorm)
|
|
{
|
|
*n=G4ThreeVector(0,0,-1);
|
|
}
|
|
return snxt=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snxt=kInfinity;
|
|
}
|
|
|
|
//
|
|
// Calculate x intersection
|
|
//
|
|
tanxz=(fDx2-fDx1)*0.5/fDz;
|
|
central=0.5*(fDx1+fDx2);
|
|
|
|
// +ve plane (1)
|
|
//
|
|
ss1=central+tanxz*p.z()-p.x(); // distance || x axis to plane
|
|
// (+ve if point inside)
|
|
ds1=v.x()-tanxz*v.z(); // component towards plane at +x
|
|
// (-ve if +ve -> -ve direction)
|
|
// -ve plane (2)
|
|
//
|
|
ss2=-tanxz*p.z()-p.x()-central; //distance || x axis to plane
|
|
// (-ve if point inside)
|
|
ds2=tanxz*v.z()+v.x(); // component towards plane at -x
|
|
|
|
if (ss1>0&&ss2<0)
|
|
{
|
|
// Normal case - entirely inside region
|
|
if (ds1<=0&&ds2<0)
|
|
{
|
|
if (ss2<-kCarTolerance/2)
|
|
{
|
|
sn=ss2/ds2; // Leave by -ve side
|
|
snside=kMX;
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by -ve side
|
|
snside=kMX;
|
|
}
|
|
}
|
|
else if (ds1>0&&ds2>=0)
|
|
{
|
|
if (ss1>kCarTolerance/2)
|
|
{
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
snside=kPX;
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by +ve side
|
|
snside=kPX;
|
|
}
|
|
}
|
|
else if (ds1>0&&ds2<0)
|
|
{
|
|
if (ss1>kCarTolerance/2)
|
|
{
|
|
// sn=ss1/ds1; // Leave by +ve side
|
|
if (ss2<-kCarTolerance/2)
|
|
{
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
sn2=ss2/ds2;
|
|
if (sn2<sn)
|
|
{
|
|
sn=sn2;
|
|
snside=kMX;
|
|
}
|
|
else
|
|
{
|
|
snside=kPX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by -ve
|
|
snside=kMX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by +ve side
|
|
snside=kPX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
else if (ss1<=0&&ss2<0)
|
|
{
|
|
// Outside, in +ve Area
|
|
|
|
if (ds1>0)
|
|
{
|
|
sn=0; // Away from shape
|
|
// Left by +ve side
|
|
snside=kPX;
|
|
}
|
|
else
|
|
{
|
|
if (ds2<0)
|
|
{
|
|
// Ignore +ve plane and use -ve plane intersect
|
|
//
|
|
sn=ss2/ds2; // Leave by -ve side
|
|
snside=kMX;
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both -> exit determined by other axes
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
}
|
|
else if (ss1>0&&ss2>=0)
|
|
{
|
|
// Outside, in -ve Area
|
|
|
|
if (ds2<0)
|
|
{
|
|
sn=0; // away from shape
|
|
// Left by -ve side
|
|
snside=kMX;
|
|
}
|
|
else
|
|
{
|
|
if (ds1>0)
|
|
{
|
|
// Ignore +ve plane and use -ve plane intersect
|
|
//
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
snside=kPX;
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both -> exit determined by other axes
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update minimum exit distance
|
|
|
|
if (sn<snxt)
|
|
{
|
|
snxt=sn;
|
|
side=snside;
|
|
}
|
|
if (snxt>0)
|
|
{
|
|
// Calculate y intersection
|
|
|
|
tanyz=(fDy2-fDy1)*0.5/fDz;
|
|
central=0.5*(fDy1+fDy2);
|
|
|
|
// +ve plane (1)
|
|
//
|
|
ss1=central+tanyz*p.z()-p.y(); // distance || y axis to plane
|
|
// (+ve if point inside)
|
|
ds1=v.y()-tanyz*v.z(); // component towards +ve plane
|
|
// (-ve if +ve -> -ve direction)
|
|
// -ve plane (2)
|
|
//
|
|
ss2=-tanyz*p.z()-p.y()-central; // distance || y axis to plane
|
|
// (-ve if point inside)
|
|
ds2=tanyz*v.z()+v.y(); // component towards -ve plane
|
|
|
|
if (ss1>0&&ss2<0)
|
|
{
|
|
// Normal case - entirely inside region
|
|
|
|
if (ds1<=0&&ds2<0)
|
|
{
|
|
if (ss2<-kCarTolerance/2)
|
|
{
|
|
sn=ss2/ds2; // Leave by -ve side
|
|
snside=kMY;
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by -ve side
|
|
snside=kMY;
|
|
}
|
|
}
|
|
else if (ds1>0&&ds2>=0)
|
|
{
|
|
if (ss1>kCarTolerance/2)
|
|
{
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
snside=kPY;
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by +ve side
|
|
snside=kPY;
|
|
}
|
|
}
|
|
else if (ds1>0&&ds2<0)
|
|
{
|
|
if (ss1>kCarTolerance/2)
|
|
{
|
|
// sn=ss1/ds1; // Leave by +ve side
|
|
if (ss2<-kCarTolerance/2)
|
|
{
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
sn2=ss2/ds2;
|
|
if (sn2<sn)
|
|
{
|
|
sn=sn2;
|
|
snside=kMY;
|
|
}
|
|
else
|
|
{
|
|
snside=kPY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by -ve
|
|
snside=kMY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sn=0; // Leave immediately by +ve side
|
|
snside=kPY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
else if (ss1<=0&&ss2<0)
|
|
{
|
|
// Outside, in +ve Area
|
|
|
|
if (ds1>0)
|
|
{
|
|
sn=0; // Away from shape
|
|
// Left by +ve side
|
|
snside=kPY;
|
|
}
|
|
else
|
|
{
|
|
if (ds2<0)
|
|
{
|
|
// Ignore +ve plane and use -ve plane intersect
|
|
//
|
|
sn=ss2/ds2; // Leave by -ve side
|
|
snside=kMY;
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both -> exit determined by other axes
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
}
|
|
else if (ss1>0&&ss2>=0)
|
|
{
|
|
// Outside, in -ve Area
|
|
if (ds2<0)
|
|
{
|
|
sn=0; // away from shape
|
|
// Left by -ve side
|
|
snside=kMY;
|
|
}
|
|
else
|
|
{
|
|
if (ds1>0)
|
|
{
|
|
// Ignore +ve plane and use -ve plane intersect
|
|
//
|
|
sn=ss1/ds1; // Leave by +ve side
|
|
snside=kPY;
|
|
}
|
|
else
|
|
{
|
|
// Must be || to both -> exit determined by other axes
|
|
//
|
|
sn=kInfinity; // Don't leave by either side
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update minimum exit distance
|
|
|
|
if (sn<snxt)
|
|
{
|
|
snxt=sn;
|
|
side=snside;
|
|
}
|
|
}
|
|
|
|
if (calcNorm)
|
|
{
|
|
switch (side)
|
|
{
|
|
case kPX:
|
|
cosxz=1.0/sqrt(1.0+tanxz*tanxz);
|
|
*n=G4ThreeVector(cosxz,0,-tanxz*cosxz);
|
|
break;
|
|
case kMX:
|
|
cosxz=-1.0/sqrt(1.0+tanxz*tanxz);
|
|
*n=G4ThreeVector(cosxz,0,tanxz*cosxz);
|
|
break;
|
|
case kPY:
|
|
cosyz=1.0/sqrt(1.0+tanyz*tanyz);
|
|
*n=G4ThreeVector(0,cosyz,-tanyz*cosyz);
|
|
break;
|
|
case kMY:
|
|
cosyz=-1.0/sqrt(1.0+tanyz*tanyz);
|
|
*n=G4ThreeVector(0,cosyz,tanyz*cosyz);
|
|
break;
|
|
case kPZ:
|
|
*n=G4ThreeVector(0,0,1);
|
|
break;
|
|
case kMZ:
|
|
*n=G4ThreeVector(0,0,-1);
|
|
break;
|
|
default:
|
|
DumpInfo();
|
|
G4Exception("G4Trd::DistanceToOut(p,v,..)","Notification",JustWarning,
|
|
"Undefined side for valid surface normal to solid.");
|
|
break;
|
|
}
|
|
}
|
|
return snxt;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Calculate exact shortest distance to any boundary from inside
|
|
// - Returns 0 is point outside
|
|
|
|
G4double G4Trd::DistanceToOut( const G4ThreeVector& p ) const
|
|
{
|
|
G4double safe=0.0;
|
|
G4double tanxz,xdist,saf1;
|
|
G4double tanyz,ydist,saf2;
|
|
G4double zbase;
|
|
|
|
#ifdef G4CSGDEBUG
|
|
if( Inside(p) == kOutside )
|
|
{
|
|
G4cout.precision(16) ;
|
|
G4cout << G4endl ;
|
|
DumpInfo();
|
|
G4cout << "Position:" << G4endl << G4endl ;
|
|
G4cout << "p.x() = " << p.x()/mm << " mm" << G4endl ;
|
|
G4cout << "p.y() = " << p.y()/mm << " mm" << G4endl ;
|
|
G4cout << "p.z() = " << p.z()/mm << " mm" << G4endl << G4endl ;
|
|
G4Exception("G4Trd::DistanceToOut(p)", "Notification", JustWarning,
|
|
"Point p is outside !?" );
|
|
}
|
|
#endif
|
|
|
|
safe=fDz-fabs(p.z()); // z perpendicular Dist
|
|
|
|
zbase=fDz+p.z();
|
|
|
|
// xdist = distance perpendicular to z axis to closest x plane from p
|
|
// = (x half width of shape at p.z) - fabs(p.x)
|
|
//
|
|
tanxz=(fDx2-fDx1)*0.5/fDz;
|
|
xdist=fDx1+tanxz*zbase-fabs(p.x());
|
|
saf1=xdist/sqrt(1.0+tanxz*tanxz); // x*cos(ang_xz) =
|
|
// shortest (perpendicular)
|
|
// distance to plane
|
|
tanyz=(fDy2-fDy1)*0.5/fDz;
|
|
ydist=fDy1+tanyz*zbase-fabs(p.y());
|
|
saf2=ydist/sqrt(1.0+tanyz*tanyz);
|
|
|
|
// Return minimum x/y/z distance
|
|
//
|
|
if (safe>saf1) safe=saf1;
|
|
if (safe>saf2) safe=saf2;
|
|
|
|
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*
|
|
G4Trd::CreateRotatedVertices( const G4AffineTransform& pTransform ) const
|
|
{
|
|
G4ThreeVectorList *vertices;
|
|
vertices=new G4ThreeVectorList();
|
|
vertices->reserve(8);
|
|
if (vertices)
|
|
{
|
|
G4ThreeVector vertex0(-fDx1,-fDy1,-fDz);
|
|
G4ThreeVector vertex1(fDx1,-fDy1,-fDz);
|
|
G4ThreeVector vertex2(fDx1,fDy1,-fDz);
|
|
G4ThreeVector vertex3(-fDx1,fDy1,-fDz);
|
|
G4ThreeVector vertex4(-fDx2,-fDy2,fDz);
|
|
G4ThreeVector vertex5(fDx2,-fDy2,fDz);
|
|
G4ThreeVector vertex6(fDx2,fDy2,fDz);
|
|
G4ThreeVector vertex7(-fDx2,fDy2,fDz);
|
|
|
|
vertices->push_back(pTransform.TransformPoint(vertex0));
|
|
vertices->push_back(pTransform.TransformPoint(vertex1));
|
|
vertices->push_back(pTransform.TransformPoint(vertex2));
|
|
vertices->push_back(pTransform.TransformPoint(vertex3));
|
|
vertices->push_back(pTransform.TransformPoint(vertex4));
|
|
vertices->push_back(pTransform.TransformPoint(vertex5));
|
|
vertices->push_back(pTransform.TransformPoint(vertex6));
|
|
vertices->push_back(pTransform.TransformPoint(vertex7));
|
|
}
|
|
else
|
|
{
|
|
DumpInfo();
|
|
G4Exception("G4Trd::CreateRotatedVertices()",
|
|
"FatalError", FatalException,
|
|
"Error in allocation of vertices. Out of memory !");
|
|
}
|
|
return vertices;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetEntityType
|
|
|
|
G4GeometryType G4Trd::GetEntityType() const
|
|
{
|
|
return G4String("G4Trd");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Stream object contents to an output stream
|
|
|
|
std::ostream& G4Trd::StreamInfo( std::ostream& os ) const
|
|
{
|
|
os << "-----------------------------------------------------------\n"
|
|
<< " *** Dump for solid - " << GetName() << " ***\n"
|
|
<< " ===================================================\n"
|
|
<< " Solid type: G4Trd\n"
|
|
<< " Parameters: \n"
|
|
<< " half length X, surface -dZ: " << fDx1/mm << " mm \n"
|
|
<< " half length X, surface +dZ: " << fDx2/mm << " mm \n"
|
|
<< " half length Y, surface -dZ: " << fDy1/mm << " mm \n"
|
|
<< " half length Y, surface +dZ: " << fDy2/mm << " mm \n"
|
|
<< " half length Z : " << fDz/mm << " mm \n"
|
|
<< "-----------------------------------------------------------\n";
|
|
|
|
return os;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Methods for visualisation
|
|
|
|
void G4Trd::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
|
|
{
|
|
scene.AddThis (*this);
|
|
}
|
|
|
|
G4Polyhedron* G4Trd::CreatePolyhedron () const
|
|
{
|
|
return new G4PolyhedronTrd2 (fDx1, fDx2, fDy1, fDy2, fDz);
|
|
}
|
|
|
|
G4NURBS* G4Trd::CreateNURBS () const
|
|
{
|
|
// return new G4NURBSbox (fDx, fDy, fDz);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|