Import Geant4 2.0.0 source tree

This commit is contained in:
Gabriele Cosmo
2016-06-08 15:42:07 +02:00
parent 103bda00c8
commit e7d7193284
3106 changed files with 171117 additions and 90550 deletions
@@ -0,0 +1,441 @@
// 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: G4ClippablePolygon.cc,v 1.2 2000/04/18 19:07:11 davidw Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4ClippablePolygon.cc
//
// Includes 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
//
G4bool G4ClippablePolygon::Clip( const G4VoxelLimits &voxelLimit )
{
if (voxelLimit.IsLimited()) {
ClipAlongOneAxis( voxelLimit, kXAxis );
ClipAlongOneAxis( voxelLimit, kYAxis );
ClipAlongOneAxis( voxelLimit, kZAxis );
}
return (vertices.entries() > 0);
}
//
// PartialClip
//
// Clip, while ignoring the indicated axis
//
G4bool G4ClippablePolygon::PartialClip( const G4VoxelLimits &voxelLimit, const EAxis IgnoreMe )
{
if (voxelLimit.IsLimited()) {
if (IgnoreMe != kXAxis) ClipAlongOneAxis( voxelLimit, kXAxis );
if (IgnoreMe != kYAxis) ClipAlongOneAxis( voxelLimit, kYAxis );
if (IgnoreMe != kZAxis) ClipAlongOneAxis( voxelLimit, kZAxis );
}
return (vertices.entries() > 0);
}
//
// GetExtent
//
G4bool G4ClippablePolygon::GetExtent( const EAxis axis,
G4double &min, G4double &max ) const
{
//
// Okay, how many entries do we have?
//
G4int noLeft = vertices.entries();
//
// Return false if nothing is left
//
if (noLeft == 0) return false;
//
// Initialize min and max to our first vertex
//
min = max = vertices(0).operator()( axis );
//
// Compare to the rest
//
G4int i;
for( i=1; i<noLeft; i++ ) {
G4double component = vertices(i).operator()( axis );
if (component < min )
min = component;
else if (component > max )
max = component;
}
return true;
}
//
// GetMinPoint
//
// Returns pointer to minimum point along the specified axis.
// Take care! Do not use pointer after destroying parent polygon.
//
const G4ThreeVector *G4ClippablePolygon::GetMinPoint( const EAxis axis ) const
{
G4int noLeft = vertices.entries();
if (noLeft==0) G4Exception( "G4ClippablePolygon::GetMinPoint -- empty polygon" );
const G4ThreeVector *answer = &(vertices[0]);
G4double min = answer->operator()(axis);
G4int i;
for( i=1; i<noLeft; i++ ) {
G4double component = vertices(i).operator()( axis );
if (component < min) {
answer = &(vertices[i]);
min = component;
}
}
return answer;
}
//
// GetMaxPoint
//
// Returns pointer to maximum point along the specified axis.
// Take care! Do not use pointer after destroying parent polygon.
//
const G4ThreeVector *G4ClippablePolygon::GetMaxPoint( const EAxis axis ) const
{
G4int noLeft = vertices.entries();
if (noLeft==0) G4Exception( "G4ClippablePolygon::GetMaxPoint -- empty polygon" );
const G4ThreeVector *answer = &(vertices[0]);
G4double max = answer->operator()(axis);
G4int i;
for( i=1; i<noLeft; i++ ) {
G4double component = vertices(i).operator()( axis );
if (component > max) {
answer = &(vertices[i]);
max = component;
}
}
return answer;
}
//
// InFrontOf
//
// Decide if this polygon is in "front" of another when
// viewed along the specified axis. For our purposes here,
// it is sufficient to use the minimum extent of the
// polygon along the axis to determine this.
//
// In case the minima of the two polygons are equal,
// we use a more sophisticated test.
//
// Note that it is possible for the two following
// statements to both return true or both return false:
// polygon1.InFrontOf(polygon2)
// polygon2.BehindOf(polygon1)
//
G4bool G4ClippablePolygon::InFrontOf( const G4ClippablePolygon &other, EAxis axis ) const
{
//
// If things are empty, do something semi-sensible
//
G4int noLeft = vertices.entries();
if (noLeft==0) return false;
if (other.Empty()) return true;
//
// Get minimum of other polygon
//
const G4ThreeVector *minPointOther = other.GetMinPoint( axis );
const G4double minOther = minPointOther->operator()(axis);
//
// Get minimum of this polygon
//
const G4ThreeVector *minPoint = GetMinPoint( axis );
const G4double min = minPoint->operator()(axis);
//
// Easy decision
//
if (min < minOther-kCarTolerance) return true; // Clear winner
if (minOther < min-kCarTolerance) return false; // Clear loser
//
// We have a tie (this will not be all that rare since our
// polygons are connected)
//
// Check to see if there is a vertex in the other polygon
// that is behind this one (or vice versa)
//
G4bool answer;
G4ThreeVector normalOther = other.GetNormal();
if (fabs(normalOther(axis)) > fabs(normal(axis))) {
G4double minP, maxP;
GetPlanerExtent( *minPointOther, normalOther, minP, maxP );
answer = (normalOther(axis) > 0) ? (minP < -kCarTolerance) : (maxP > +kCarTolerance);
}
else {
G4double minP, maxP;
other.GetPlanerExtent( *minPoint, normal, minP, maxP );
answer = (normal(axis) > 0) ? (maxP > +kCarTolerance) : (minP < -kCarTolerance);
}
return answer;
}
//
// BehindOf
//
// Decide if this polygon is behind another.
// See notes in method "InFrontOf"
//
G4bool G4ClippablePolygon::BehindOf( const G4ClippablePolygon &other, EAxis axis ) const
{
//
// If things are empty, do something semi-sensible
//
G4int noLeft = vertices.entries();
if (noLeft==0) return false;
if (other.Empty()) return true;
//
// Get minimum of other polygon
//
const G4ThreeVector *maxPointOther = other.GetMaxPoint( axis );
const G4double maxOther = maxPointOther->operator()(axis);
//
// Get minimum of this polygon
//
const G4ThreeVector *maxPoint = GetMaxPoint( axis );
const G4double max = maxPoint->operator()(axis);
//
// Easy decision
//
if (max > maxOther+kCarTolerance) return true; // Clear winner
if (maxOther > max+kCarTolerance) return false; // Clear loser
//
// We have a tie (this will not be all that rare since our
// polygons are connected)
//
// Check to see if there is a vertex in the other polygon
// that is in front of this one (or vice versa)
//
G4bool answer;
G4ThreeVector normalOther = other.GetNormal();
if (fabs(normalOther(axis)) > fabs(normal(axis))) {
G4double minP, maxP;
GetPlanerExtent( *maxPointOther, normalOther, minP, maxP );
answer = (normalOther(axis) > 0) ? (maxP > +kCarTolerance) : (minP < -kCarTolerance);
}
else {
G4double minP, maxP;
other.GetPlanerExtent( *maxPoint, normal, minP, maxP );
answer = (normal(axis) > 0) ? (minP < -kCarTolerance) : (maxP > +kCarTolerance);
}
return answer;
}
//
// GetPlanerExtent
//
// Get min/max distance in or out of a plane
//
G4bool G4ClippablePolygon::GetPlanerExtent( const G4ThreeVector &pointOnPlane,
const G4ThreeVector &planeNormal,
G4double &min, G4double &max ) const
{
//
// Okay, how many entries do we have?
//
G4int noLeft = vertices.entries();
//
// Return false if nothing is left
//
if (noLeft == 0) return false;
//
// Initialize min and max to our first vertex
//
min = max = planeNormal.dot(vertices(0)-pointOnPlane);
//
// Compare to the rest
//
G4int i;
for( i=1; i<noLeft; i++ ) {
G4double component = planeNormal.dot(vertices(i) - pointOnPlane);
if (component < min )
min = component;
else if (component > max )
max = component;
}
return true;
}
//
// Clip along just one axis, as specified in voxelLimit
//
void G4ClippablePolygon::ClipAlongOneAxis( const G4VoxelLimits &voxelLimit, const EAxis axis )
{
if (!voxelLimit.IsLimited(axis)) return;
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;
}
// 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
{
}
}
}
}
@@ -0,0 +1,709 @@
// 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: G4EllipticalTube.cc,v 1.7 2000/04/19 19:09:07 davidw Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4EllipticalTube.cc
//
// Implementation of a CSG volume representing a tube with elliptical cross
// section (geant3 solid 'ELTU')
//
// --------------------------------------------------------------------
#include "G4EllipticalTube.hh"
#include "G4ClippablePolygon.hh"
#include "G4AffineTransform.hh"
#include "G4SolidExtentList.hh"
#include "G4VoxelLimits.hh"
#include "meshdefs.hh"
#include "G4VGraphicsScene.hh"
#include "G4Polyhedron.hh"
#include "G4VisExtent.hh"
//
// Constructor
//
G4EllipticalTube::G4EllipticalTube( const G4String &name,
const G4double theDx, const G4double theDy, const G4double theDz )
: G4VSolid( name )
{
dx = theDx;
dy = theDy;
dz = theDz;
}
//
// Destructor
//
G4EllipticalTube::~G4EllipticalTube() {;}
//
// CalculateExtent
//
G4bool G4EllipticalTube::CalculateExtent( const EAxis axis,
const G4VoxelLimits &voxelLimit,
const G4AffineTransform &transform,
G4double &min, G4double &max ) const
{
G4SolidExtentList extentList( axis, voxelLimit );
//
// We are going to divide up our elliptical face into small
// pieces
//
//
// Choose phi size of our segment(s) based on constants as
// defined in meshdefs.hh
//
G4int numPhi = kMaxMeshSections;
G4double sigPhi = 2*M_PI/numPhi;
//
// We have to be careful to keep our segments completely outside
// of the elliptical surface. To do so we imagine we have
// a simple (unit radius) circular cross section (as in G4Tubs)
// and then "stretch" the dimensions as necessary to fit the ellipse.
//
G4double rFudge = 1.0/cos(0.5*sigPhi);
G4double dxFudge = dx*rFudge,
dyFudge = dy*rFudge;
//
// As we work around the elliptical surface, we build
// a "phi" segment on the way, and keep track of two
// additional polygons for the two ends.
//
G4ClippablePolygon endPoly1, endPoly2, phiPoly;
G4double phi = 0,
cosPhi = cos(phi),
sinPhi = sin(phi);
G4ThreeVector v0( dxFudge*cosPhi, dyFudge*sinPhi, +dz ),
v1( dxFudge*cosPhi, dyFudge*sinPhi, -dz ),
w0, w1;
transform.ApplyPointTransform( v0 );
transform.ApplyPointTransform( v1 );
do {
phi += sigPhi;
if (numPhi == 1) phi = 0; // Try to avoid roundoff
cosPhi = cos(phi),
sinPhi = sin(phi);
w0 = G4ThreeVector( dxFudge*cosPhi, dyFudge*sinPhi, +dz );
w1 = G4ThreeVector( dxFudge*cosPhi, dyFudge*sinPhi, -dz );
transform.ApplyPointTransform( w0 );
transform.ApplyPointTransform( w1 );
//
// Add a point to our z ends
//
endPoly1.AddVertexInOrder( v0 );
endPoly2.AddVertexInOrder( v1 );
//
// Build phi polygon
//
phiPoly.ClearAllVertices();
phiPoly.AddVertexInOrder( v0 );
phiPoly.AddVertexInOrder( v1 );
phiPoly.AddVertexInOrder( w1 );
phiPoly.AddVertexInOrder( w0 );
if (phiPoly.PartialClip( voxelLimit, axis )) {
//
// Get unit normal
//
phiPoly.SetNormal( (v1-v0).cross(w0-v0).unit() );
extentList.AddSurface( phiPoly );
}
//
// Next vertex
//
v0 = w0;
v1 = w1;
} while( --numPhi > 0 );
//
// Process the end pieces
//
if (endPoly1.PartialClip( voxelLimit, axis )) {
static const G4ThreeVector normal(0,0,+1);
endPoly1.SetNormal( transform.TransformAxis(normal) );
extentList.AddSurface( endPoly1 );
}
if (endPoly2.PartialClip( voxelLimit, axis )) {
static const G4ThreeVector normal(0,0,-1);
endPoly2.SetNormal( transform.TransformAxis(normal) );
extentList.AddSurface( endPoly2 );
}
//
// Return min/max value
//
return extentList.GetExtent( min, max );
}
//
// Inside
//
// Note that for this solid, we've decided to define the tolerant
// surface as that which is bounded by ellipses with axes
// at +/- 0.5*kCarTolerance.
//
EInside G4EllipticalTube::Inside( const G4ThreeVector& p) const
{
static const G4double halfTol = 0.5*kCarTolerance;
//
// Check z extents: are we outside?
//
G4double absZ = fabs(p.z());
if (absZ > dz+halfTol) return kOutside;
//
// Check x,y: are we outside?
//
G4double x = p.x(), y = p.y();
if (CheckXY(p.x(), p.y(), +halfTol) > 1.0) return kOutside;
//
// We are either inside or on the surface: recheck z extents
//
if (absZ > dz-halfTol) return kSurface;
//
// Recheck x,y
//
if (CheckXY(p.x(), p.y(), -halfTol) > 1.0) return kSurface;
return kInside;
}
//
// SurfaceNormal
//
G4ThreeVector G4EllipticalTube::SurfaceNormal( const G4ThreeVector& p) const
{
//
// Which of the three surfaces are we closest to (approximately)?
//
G4double distZ = fabs(p.z()) - dz;
G4double rxy = CheckXY( p.x(), p.y() );
G4double distR2 = (rxy < DBL_MIN) ? DBL_MAX : 1.0/rxy;
//
// Closer to z?
//
if (distZ*distZ < distR2)
return G4ThreeVector( 0.0, 0.0, p.z() < 0 ? -1.0 : 1.0 );
//
// Closer to x/y
//
return G4ThreeVector( p.x()*dy*dy, p.y()*dx*dx, 0.0 ).unit();
}
//
// DistanceToIn(p,v)
//
// Unlike DistanceToOut(p,v), it is possible for the trajectory
// to miss. The geometric calculations here are quite simple.
// More difficult is the logic required to prevent particles
// from sneaking (or leaking) between the elliptical and end
// surfaces.
//
// Keep in mind that the true distance is allowed to be
// negative if the point is currently on the surface. For oblique
// angles, it can be very negative.
//
G4double G4EllipticalTube::DistanceToIn( const G4ThreeVector& p,const G4ThreeVector& v ) const
{
static const G4double halfTol = 0.5*kCarTolerance;
//
// Check z = -dz planer surface
//
G4double sigz = p.z()+dz;
if (sigz < halfTol) {
//
// We are "behind" the shape in z, and so can
// potentially hit the rear face. Correct direction?
//
if (v.z() <= 0) {
//
// As long as we are far enough away, we know we
// can't intersect
//
if (sigz < 0) return kInfinity;
//
// Otherwise, we don't intersect unless we are
// on the surface of the ellipse
//
if (CheckXY(p.x(),p.y(),-halfTol) <= 1.0) return kInfinity;
}
else {
//
// How far?
//
G4double s = -sigz/v.z();
//
// Where does that place us?
//
G4double xi = p.x() + s*v.x(),
yi = p.y() + s*v.y();
//
// Is this on the surface (within ellipse)?
//
if (CheckXY(xi,yi) <= 1.0) {
//
// Yup. Return s, unless we are on the surface
//
return (sigz < -halfTol) ? s : 0;
}
else if (xi*dy*dy*v.x() + yi*dx*dx*v.y() >= 0) {
//
// Else, if we are traveling outwards, we know
// we must miss
//
return kInfinity;
}
}
}
//
// Check z = +dz planer surface
//
sigz = p.z() - dz;
if (sigz > -halfTol) {
if (v.z() >= 0) {
if (sigz > 0) return kInfinity;
if (CheckXY(p.x(),p.y(),-halfTol) <= 1.0) return kInfinity;
}
else {
G4double s = -sigz/v.z();
G4double xi = p.x() + s*v.x(),
yi = p.y() + s*v.y();
if (CheckXY(xi,yi) <= 1.0) {
return (sigz > -halfTol) ? s : 0;
}
else if (xi*dy*dy*v.x() + yi*dx*dx*v.y() >= 0) {
return kInfinity;
}
}
}
//
// Check intersection with the elliptical tube
//
G4double s[2];
G4int n = IntersectXY( p, v, s );
if (n==0) return kInfinity;
//
// Is the original point on the surface?
//
if (fabs(p.z()) < dz+halfTol) {
if (CheckXY( p.x(), p.y(), halfTol ) < 1.0) {
//
// Well, yes, but are we traveling inwards at this point?
//
if (p.x()*dy*dy*v.x() + p.y()*dx*dx*v.y() < 0) return 0;
}
}
//
// We are now certain that point p is not on the surface of
// the solid (and thus fabs(s[0]) > halfTol).
// Return kInfinity if the intersection is "behind" the point.
//
if (s[0] < 0) return kInfinity;
//
// Check to see if we intersect the tube within
// dz, but only when we know it might miss
//
G4double zi = p.z() + s[0]*v.z();
if (v.z() < 0) {
if (zi < -dz) return kInfinity;
}
else if (v.z() > 0) {
if (zi > +dz) return kInfinity;
}
return s[0];
}
//
// DistanceToIn(p)
//
// The distance from a point to an ellipse (in 2 dimensions) is a
// surprisingly complicated quadric expression (this is easy to
// appreciate once one understands that there may be up to
// four lines normal to the ellipse intersecting any point). To
// solve it exactly would be rather time consuming. This method,
// however, is supposed to be a quick check, and is allowed to be an
// underestimate.
//
// So, I will use the following underestimate of the distance
// from an outside point to an ellipse. First: find the intersection "A"
// of the line from the origin to the point with the ellipse.
// Find the line passing through "A" and tangent to the ellipse
// at A. The distance of the point p from the ellipse will be approximated
// as the distance to this line.
//
G4double G4EllipticalTube::DistanceToIn( const G4ThreeVector& p ) const
{
static const G4double halfTol = 0.5*kCarTolerance;
if (CheckXY( p.x(), p.y(), +halfTol ) < 1.0) {
//
// We are inside or on the surface of the
// elliptical cross section in x/y. Check z
//
if (p.z() < -dz-halfTol)
return -p.z()-dz;
else if (p.z() > dz+halfTol)
return p.z()-dz;
else
return 0; // On any surface here (or inside)
}
//
// Find point on ellipse
//
G4double qnorm = CheckXY( p.x(), p.y() );
if (qnorm < DBL_MIN) return 0; // This should never happen
G4double q = 1.0/sqrt(qnorm);
G4double xe = q*p.x(), ye = q*p.y();
//
// Get tangent to ellipse
//
G4double tx = -ye*dx*dx, ty = +xe*dy*dy;
G4double tnorm = sqrt( tx*tx + ty*ty );
//
// Calculate distance
//
G4double distR = ( (p.x()-xe)*ty - (p.y()-ye)*tx )/tnorm;
//
// Add the result in quadrature if we are, in addition,
// outside the z bounds of the shape
//
// We could save some time by returning the maximum rather
// than the quadrature sum
//
if (p.z() < -dz)
return sqrt( (p.z()+dz)*(p.z()+dz) + distR*distR );
else if (p.z() > dz)
return sqrt( (p.z()-dz)*(p.z()-dz) + distR*distR );
return distR;
}
//
// DistanceToOut(p,v)
//
// This method can be somewhat complicated for a general shape.
// For a convex one, like this, there are several simplifications,
// the most important of which is that one can treat the surfaces
// as infinite in extent when deciding if the p is on the surface.
//
G4double G4EllipticalTube::DistanceToOut( const G4ThreeVector& p,const G4ThreeVector& v,
const G4bool calcNorm,
G4bool *validNorm,G4ThreeVector *norm ) const
{
static const G4double halfTol = 0.5*kCarTolerance;
//
// Our normal is always valid
//
if (calcNorm) *validNorm = true;
G4double sBest = kInfinity;
const G4ThreeVector *nBest;
//
// Might we intersect the -dz surface?
//
if (v.z() < 0) {
static const G4ThreeVector normHere(0.0,0.0,-1.0);
//
// Yup. What distance?
//
sBest = -(p.z()+dz)/v.z();
//
// Are we on the surface? If so, return zero
//
if (p.z() < -dz+halfTol) {
if (calcNorm) *norm = normHere;
return 0;
}
else
nBest = &normHere;
}
//
// How about the +dz surface?
//
if (v.z() > 0) {
static const G4ThreeVector normHere(0.0,0.0,+1.0);
//
// Yup. What distance?
//
G4double s = (dz-p.z())/v.z();
//
// Are we on the surface? If so, return zero
//
if (p.z() > +dz-halfTol) {
if (calcNorm) *norm = normHere;
return 0;
}
//
// Best so far?
//
if (s < sBest) { sBest = s; nBest = &normHere; }
}
//
// Check furthest intersection with ellipse
//
G4double s[2];
G4int n = IntersectXY( p, v, s );
if (n == 0) {
if (sBest == kInfinity)
G4Exception( "G4EllipticalTube::DistanceToOut - Point is outside" );
if (calcNorm) *norm = *nBest;
return sBest;
}
else if (s[n-1] > sBest) {
if (calcNorm) *norm = *nBest;
return sBest;
}
sBest = s[n-1];
//
// Intersection with ellipse. Get normal at intersection point.
//
if (calcNorm) {
G4ThreeVector ip = p + sBest*v;
*norm = G4ThreeVector( ip.x()*dy*dy, ip.y()*dx*dx, 0.0 ).unit();
}
//
// Do we start on the surface?
//
if (CheckXY( p.x(), p.y(), -halfTol ) > 1.0) {
//
// Well, yes, but are we traveling outwards at this point?
//
if (p.x()*dy*dy*v.x() + p.y()*dx*dx*v.y() > 0) return 0;
}
return sBest;
}
//
// DistanceToOut(p)
//
// See DistanceToIn(p) for notes on the distance from a point
// to an ellipse in two dimensions.
//
// The approximation used here for a point inside the ellipse
// is to find the intersection with the ellipse of the lines
// through the point and parallel to the x and y axes. The
// distance of the point from the line connecting the two
// intersecting points is then used.
//
G4double G4EllipticalTube::DistanceToOut( const G4ThreeVector& p ) const
{
static const G4double halfTol = 0.5*kCarTolerance;
//
// We need to calculate the distances to all surfaces,
// and then return the smallest
//
// Check -dz and +dz surface
//
G4double sBest = dz - fabs(p.z());
if (sBest < halfTol) return 0;
//
// Check elliptical surface: find intersection of
// line through p and parallel to x axis
//
G4double radical = 1.0 - p.y()*p.y()/dy/dy;
if (radical < +DBL_MIN) return 0;
G4double xi = dx*sqrt( radical );
if (p.x() < 0) xi = -xi;
//
// Do the same with y axis
//
radical = 1.0 - p.x()*p.x()/dx/dx;
if (radical < +DBL_MIN) return 0;
G4double yi = dy*sqrt( radical );
if (p.y() < 0) yi = -yi;
//
// Get distance from p to the line connecting
// these two points
//
G4double xdi = p.x() - xi,
ydi = yi - p.y();
G4double normi = sqrt( xdi*xdi + ydi*ydi );
if (normi < halfTol) return 0;
xdi /= normi;
ydi /= normi;
G4double s = 0.5*(xdi*(p.y()-yi) - ydi*(p.x()-xi));
if (xi*yi < 0) s = -s;
if (s < sBest) sBest = s;
//
// Return best answer
//
return sBest < halfTol ? 0 : sBest;
}
//
// CreatePolyhedron
//
G4Polyhedron* G4EllipticalTube::CreatePolyhedron() const
{
if (dx==dy) {
//
// Special case (useful for debugging)
//
return new G4PolyhedronTubs( 0.0, dx, dz, 0, 2*M_PI );
}
G4cerr << "G4EllipticalTube: visualization of this type of solid is not supported at this time" << G4endl;
return 0;
}
//
// DescribeYourselfTo
//
void G4EllipticalTube::DescribeYourselfTo( G4VGraphicsScene& scene ) const
{
scene.AddThis (*this);
}
//
// GetExtent
//
G4VisExtent G4EllipticalTube::GetExtent() const
{
return G4VisExtent( -dx, dx, -dy, dy, -dz, dz );
}
//
// IntersectXY
//
// Decide if and where the x/y trajectory hits the elliptical cross
// section.
//
// Arguments:
// p - (in) Point on trajectory
// v - (in) Vector along trajectory
// s - (out) Up to two points of intersection, where the
// intersection point is p + s*v, and if there are
// two intersections, s[0] < s[1]. May be negative.
// Returns:
// The number of intersections. If 0, the trajectory misses. If 1, the
// trajectory just grazes the surface.
//
// Solution:
// One needs to solve: ( (p.x + s*v.x)/dx )**2 + ( (p.y + s*v.y)/dy )**2 = 1
//
// The solution is quadratic: a*s**2 + b*s + c = 0
//
// a = (v.x/dx)**2 + (v.y/dy)**2
// b = 2*p.x*v.x/dx**2 + 2*p.y*v.y/dy**2
// c = (p.x/dx)**2 + (p.y/dy)**2 - 1
//
G4int G4EllipticalTube::IntersectXY( const G4ThreeVector &p,
const G4ThreeVector &v, G4double s[2] ) const
{
G4double px = p.x(), py = p.y();
G4double vx = v.x(), vy = v.y();
G4double a = (vx/dx)*(vx/dx) + (vy/dy)*(vy/dy);
G4double b = 2.0*( px*vx/dx/dx + py*vy/dy/dy );
G4double c = (px/dx)*(px/dx) + (py/dy)*(py/dy) - 1.0;
if (a < DBL_MIN) return 0; // Trajectory parallel to z axis
G4double radical = b*b - 4*a*c;
if (radical < -DBL_MIN) return 0; // No solution
if (radical < DBL_MIN) {
//
// Grazes surface
//
s[0] = -b/a/2.0;
return 1;
}
radical = sqrt(radical);
G4double q = -0.5*( b + (b < 0 ? -radical : +radical) );
G4double sa = q/a;
G4double sb = c/q;
if (sa < sb) { s[0] = sa; s[1] = sb; } else { s[0] = sb; s[1] = sa; }
return 2;
}
@@ -0,0 +1,121 @@
// 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: G4EnclosingCylinder.cc,v 1.1 2000/04/07 11:00:35 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4EnclosingCylinder.cc
//
// Implementation of a utility class for a quick check of geometry.
//
// --------------------------------------------------------------------
#include "G4EnclosingCylinder.hh"
#include "G4ReduciblePolygon.hh"
//
// Constructor
//
G4EnclosingCylinder::G4EnclosingCylinder( const G4ReduciblePolygon *rz,
const G4bool thePhiIsOpen,
const G4double theStartPhi, const G4double theTotalPhi )
{
//
// Obtain largest r and smallest and largest z
//
radius = rz->Amax();
zHi = rz->Bmax();
zLo = rz->Bmin();
//
// 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;
concave = totalPhi > M_PI;
}
//
// 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::MustBeOutside( 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 (concave) {
if ( ((p.x()-dx1)*ry1 - (p.y()-dy1)*rx1) < 0) return false;
if ( ((p.x()-dx2)*ry2 - (p.y()-dy2)*rx2) > 0) return false;
}
else {
if ( ((p.x()-dx1)*ry1 - (p.y()-dy1)*rx1) > 0) return true;
if ( ((p.x()-dx2)*ry2 - (p.y()-dy2)*rx2) < 0) 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::ShouldMiss( const G4ThreeVector &p, const G4ThreeVector &v ) const
{
if (!MustBeOutside(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,314 @@
// 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: G4IntersectingCone.cc,v 1.1 2000/04/07 11:01:12 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// 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*ty - y0*tx = 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(a) > 1/kInfinity) {
if ( fabs(x0*ty - y0*tx) < fabs(1E-6/B)) {
*s1 = -0.5*b/a;
return 1;
}
return 0;
}
}
else {
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 (A + B*(z0+(*s1)*tz) < 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 = (B*tz > 0)^(sa > sb) ? sb : sa;
return 1;
}
else if (fabs(b) < 1/kInfinity) {
return 0;
}
else {
*s1 = -c/b;
if (A + B*(z0+(*s1)*tz) < 0) return 0;
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(a) > 1/kInfinity) {
if ( fabs(x0*ty - y0*tx) < fabs(1E-6/B)) {
*s1 = -0.5*b/a;
return 1;
}
return 0;
}
}
else {
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;
if ((z0 + (*s1)*tz - A)/B < 0) return 0;
return 1;
}
}
@@ -0,0 +1,813 @@
// 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: G4PolyPhiFace.cc,v 1.1 2000/04/07 11:01:31 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4PolyPhiFace.cc
//
// Implementation of the face that bounds a polycone or polyhedra at
// its phi opening.
//
// --------------------------------------------------------------------
#include "G4PolyPhiFace.hh"
#include "G4ClippablePolygon.hh"
#include "G4ReduciblePolygon.hh"
#include "G4AffineTransform.hh"
#include "G4SolidExtentList.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 G4ReduciblePolygon *rz, const G4double phi,
const G4double deltaPhi, const G4double phiOther )
{
numEdges = rz->NumVertices();
rMin = rz->Amin();
rMax = rz->Amax();
zMin = rz->Bmin();
zMax = rz->Bmax();
//
// Is this the "starting" phi edge of the two?
//
G4bool start = (phiOther > phi);
//
// 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 );
//
// Is allBehind?
//
allBehind = (zSign*(cos(phiOther)*radial.y() - sin(phiOther)*radial.x()) < 0);
//
// Adjacent edges
//
G4double midPhi = phi + (start ? +0.5 : -0.5)*deltaPhi;
G4double cosMid = cos(midPhi),
sinMid = sin(midPhi);
//
// Allocate corners
//
corners = new G4PolyPhiFaceVertex[numEdges];
//
// Fill them
//
G4ReduciblePolygonIterator iterRZ(rz);
G4PolyPhiFaceVertex *corn = corners;
iterRZ.Begin();
do {
corn->r = iterRZ.GetA();
corn->z = iterRZ.GetB();
corn->x = corn->r*radial.x();
corn->y = corn->r*radial.y();
} while( ++corn, iterRZ.Next() );
//
// Allocate edges
//
edges = new G4PolyPhiFaceEdge[numEdges];
//
// Fill them
//
G4double rFact = cos(0.5*deltaPhi);
G4double rFactNormalize = 1.0/sqrt(1.0+rFact*rFact);
G4PolyPhiFaceVertex *prev = corners+numEdges-1,
*here = corners;
G4PolyPhiFaceEdge *edge = edges;
do {
G4ThreeVector sideNorm;
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;
if ((here->r < DBL_MIN) && (prev->r < DBL_MIN)) {
//
// Sigh! Always exceptions!
// This edge runs at r==0, so its adjoing surface is not a
// PolyconeSide or PolyhedraSide, but the opposite PolyPhiFace.
//
G4double zSignOther = start ? -1 : 1;
sideNorm = G4ThreeVector( zSignOther*sin(phiOther),
-zSignOther*cos(phiOther), 0 );
}
else {
sideNorm = G4ThreeVector( edge->tz*cosMid, edge->tz*sinMid, -edge->tr*rFact );
sideNorm *= rFactNormalize;
}
sideNorm += normal;
edge->norm3D = sideNorm.unit();
} while( edge++, prev=here, ++here < corners+numEdges );
//
// Go back and fill in corner "normals"
//
G4PolyPhiFaceEdge *prevEdge = edges+numEdges-1;
edge = edges;
do {
//
// Calculate vertex 2D normals (on the phi surface)
//
G4double rPart = prevEdge->tr + edge->tr;
G4double zPart = prevEdge->tz + edge->tz;
G4double norm = sqrt( rPart*rPart + zPart*zPart );
G4double rNorm = +zPart/norm;
G4double zNorm = -rPart/norm;
edge->v0->rNorm = rNorm;
edge->v0->zNorm = zNorm;
//
// Calculate the 3D normals.
//
// Find the vector perpendicular to the z axis
// that defines the plane that contains the vertex normal
//
G4ThreeVector xyVector;
if (edge->v0->r < DBL_MIN) {
//
// This is a vertex at r==0, which is a special
// case. The normal we will construct lays in the
// plane at the center of the phi opening.
//
// We also know that rNorm < 0
//
G4double zSignOther = start ? -1 : 1;
G4ThreeVector normalOther( zSignOther*sin(phiOther),
-zSignOther*cos(phiOther), 0 );
xyVector = - normal - normalOther;
}
else {
//
// This is a vertex at r > 0. The plane
// is the average of the normal and the
// normal of the adjacent phi face
//
xyVector = G4ThreeVector( cosMid, sinMid, 0 );
if (rNorm < 0)
xyVector -= normal;
else
xyVector += normal;
}
//
// Combine it with the r/z direction from the face
//
edge->v0->norm3D = rNorm*xyVector.unit() + G4ThreeVector( 0, 0, zNorm );
} while( prevEdge=edge, ++edge < edges+numEdges );
//
// Build point on surface
//
G4double rAve = 0.5*(rMax-rMin),
zAve = 0.5*(zMax-zMin);
surface = G4ThreeVector( rAve*radial.x(), rAve*radial.y(), zAve );
}
//
// Diagnose
//
// Throw an exception if something is found inconsistent with
// the solid.
//
// For debugging purposes only
//
void G4PolyPhiFace::Diagnose( G4VSolid *owner )
{
G4PolyPhiFaceVertex *corner = corners;
do {
G4ThreeVector test(corner->x, corner->y, corner->z);
test -= 1E-6*corner->norm3D;
if (owner->Inside(test) != kInside)
G4Exception( "G4PolyPhiFace::Diagnose -- Bad vertex normal found" );
} while( ++corner < corners+numEdges );
}
//
// Destructor
//
G4PolyPhiFace::~G4PolyPhiFace()
{
delete [] edges;
delete [] corners;
}
//
// Copy constructor
//
G4PolyPhiFace::G4PolyPhiFace( const G4PolyPhiFace &source )
{
CopyStuff( source );
}
//
// Assignment operator
//
G4PolyPhiFace *G4PolyPhiFace::operator=( const G4PolyPhiFace &source )
{
if (this == &source) return this;
delete [] edges;
delete [] corners;
CopyStuff( source );
return this;
}
//
// CopyStuff (protected)
//
void G4PolyPhiFace::CopyStuff( const G4PolyPhiFace &source )
{
//
// The simple stuff
//
numEdges = source.numEdges;
normal = source.normal;
radial = source.radial;
surface = source.surface;
rMin = source.rMin;
rMax = source.rMax;
zMin = source.zMin;
zMax = source.zMax;
allBehind = source.allBehind;
//
// Corner dynamic array
//
corners = new G4PolyPhiFaceVertex[numEdges];
G4PolyPhiFaceVertex *corn = corners,
*sourceCorn = source.corners;
do {
*corn = *sourceCorn;
} while( ++sourceCorn, ++corn < corners+numEdges );
//
// Edge dynamic array
//
edges = new G4PolyPhiFaceEdge[numEdges];
G4PolyPhiFaceVertex *prev = corners+numEdges-1,
*here = corners;
G4PolyPhiFaceEdge *edge = edges,
*sourceEdge = source.edges;
do {
*edge = *sourceEdge;
edge->v0 = prev;
edge->v1 = here;
} while( ++sourceEdge, ++edge, prev=here, ++here < corners+numEdges );
}
//
// Intersect
//
G4bool G4PolyPhiFace::Intersect( const G4ThreeVector &p, const G4ThreeVector &v,
const G4bool outgoing, const G4double surfTolerance,
G4double &distance, G4double &distFromSurface,
G4ThreeVector &aNormal, G4bool &isAllBehind )
{
G4double normSign = outgoing ? +1 : -1;
//
// These don't change
//
isAllBehind = allBehind;
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 InsideEdgesExact( r, ip.z(), normSign, p, v );
}
//
// 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.5*kCarTolerance)
return kInfinity;
else if (distPhi < 0)
distPhi = 0.0;
//
// 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;
if (InsideEdges( r, p.z(), &distRZ2, &base3Dnorm, &head3Dnorm )) {
//
// Looks like we're inside. Distance is distance in phi.
//
*bestDistance = fabs(distPhi);
//
// Use distPhi to decide fate
//
if (distPhi < -tolerance) return kInside;
if (distPhi < tolerance) return kSurface;
return kOutside;
}
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 );
//
// Use edge normal to decide fate
//
G4ThreeVector cc( base3Dnorm->r*radial.x(),
base3Dnorm->r*radial.y(),
base3Dnorm->z );
cc = p - cc;
G4double normDist = head3Dnorm->dot(cc);
if ( distRZ2 > tolerance*tolerance ) {
//
// We're far enough away that kSurface is not possible
//
return normDist < 0 ? kInside : kOutside;
}
if (normDist < -tolerance) return kInside;
if (normDist < tolerance) return kSurface;
return 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,
G4SolidExtentList &extentList )
{
//
// 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
//
if (polygon.PartialClip( voxelLimit, axis )) {
//
// Add it to the list
//
polygon.SetNormal( transform.TransformAxis(normal) );
extentList.AddSurface( polygon );
}
}
//
//-------------------------------------------------------
//
// InsideEdgesExact
//
// Decide if the point in r,z is inside the edges of our face,
// **but** do so consistently with other faces.
//
// This routine has functionality similar to InsideEdges, but uses
// an algorithm to decide if a trajectory falls inside or outside the
// face that uses only the trajectory p,v values and the three dimensional
// points representing the edges of the polygon. The objective is to plug up
// any leaks between touching G4PolyPhiFaces (at r==0) and any other face
// that uses the same convention.
//
// See: "Computational Geometry in C (Second Edition)"
// http://cs.smith.edu/~orourke/
//
G4bool G4PolyPhiFace::InsideEdgesExact( const G4double r, const G4double z,
const G4double normSign, const G4ThreeVector &p, const G4ThreeVector &v )
{
//
// Quick check of extent
//
if ( r < rMin-kCarTolerance ||
r > rMax+kCarTolerance ) return false;
if ( z < zMin-kCarTolerance ||
z > zMax+kCarTolerance ) return false;
//
// Exact check: loop over all vertices
//
G4double qx = p.x() + v.x(),
qy = p.y() + v.y(),
qz = p.z() + v.z();
int answer = 0;
G4PolyPhiFaceVertex *corn = corners,
*prev = corners+numEdges-1;
G4double cornZ, prevZ;
prevZ = ExactZOrder( z, qx, qy, qz, v, normSign, prev );
do {
//
// Get z order of this vertex, and compare to previous vertex
//
cornZ = ExactZOrder( z, qx, qy, qz, v, normSign, corn );
if (cornZ < 0) {
if (prevZ < 0) continue;
}
else if (cornZ > 0) {
if (prevZ > 0) continue;
}
else {
//
// By chance, we overlap exactly (within precision) with
// the current vertex. Continue if the same happened previously
// (e.g. the previous vertex had the same z value)
//
if (prevZ == 0) continue;
//
// Otherwise, to decide what to do, we need to know what is
// coming up next. Specifically, we need to find the next vertex
// with a non-zero z order.
//
// One might worry about infinite loops, but the above conditional
// should prevent it
//
G4PolyPhiFaceVertex *next = corn;
G4double nextZ;
do {
next++;
if (next == corners+numEdges) next = corners;
nextZ = ExactZOrder( z, qx, qy, qz, v, normSign, next );
} while( nextZ == 0 );
//
// If we won't be changing direction, go to the next vertex
//
if (nextZ*prevZ < 0) continue;
}
//
// We overlap in z with the side of the face that stretches from
// vertex "prev" to "corn". On which side (left or right) do
// we lay with respect to this segment?
//
G4ThreeVector qa( qx - prev->x, qy - prev->y, qz - prev->z ),
qb( qx - corn->x, qy - corn->y, qz - corn->z );
G4double aboveOrBelow = normSign*qa.cross(qb).dot(v);
if (aboveOrBelow > 0)
answer++;
else if (aboveOrBelow < 0)
answer--;
else {
//
// A precisely zero answer here means we exactly
// intersect (within roundoff) the edge of the face.
// Return true in this case.
//
return true;
}
} while( prevZ = cornZ, prev=corn, ++corn < corners+numEdges );
// G4int fanswer = abs(answer);
// if (fanswer==1 || fanswer>2) {
// G4cerr << "G4PolyPhiFace::InsideEdgesExact: answer is " << answer << G4endl;
// }
return answer!=0;
}
//
// 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, &notUsed, 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,420 @@
// 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: G4Polycone.cc,v 1.3 2000/06/27 16:20:11 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4Polycone.cc
//
// Implementation of a CSG polycone
//
// --------------------------------------------------------------------
#include "G4Polycone.hh"
#include "G4PolyconeSide.hh"
#include "G4PolyPhiFace.hh"
#include "G4Polyhedron.hh"
#include "G4EnclosingCylinder.hh"
#include "G4ReduciblePolygon.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 )
{
//
// Some historical ugliness
//
original_parameters = new G4PolyconeHistorical();
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];
}
//
// Build RZ polygon using special PCON/PGON GEANT3 constructor
//
G4ReduciblePolygon *rz = new G4ReduciblePolygon( rInner, rOuter, zPlane, numZPlanes );
//
// Do the real work
//
Create( phiStart, phiTotal, rz );
delete rz;
}
//
// 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 = 0;
G4ReduciblePolygon *rz = new G4ReduciblePolygon( r, z, numRZ );
Create( phiStart, phiTotal, rz );
delete rz;
}
//
// Create
//
// Generic create routine, called by each constructor after conversion of arguments
//
void G4Polycone::Create( const G4double phiStart,
const G4double phiTotal,
G4ReduciblePolygon *rz )
{
//
// Perform checks of rz values
//
if (rz->Amin() < 0.0)
G4Exception( "G4Polycone: Illegal input parameters: All R values must be >= 0" );
G4double rzArea = rz->Area();
if (rzArea < -kCarTolerance) rz->ReverseOrder();
else if (rzArea < -kCarTolerance)
G4Exception( "G4Polycone: Illegal input parameters: R/Z cross section is zero or near zero" );
if ((!rz->RemoveDuplicateVertices( kCarTolerance )) ||
(!rz->RemoveRedundantVertices( kCarTolerance )) )
G4Exception( "G4Polycone: Illegal input parameters: Too few unique R/Z values" );
if (rz->CrossesItself(1/kInfinity))
G4Exception( "G4Polycone: Illegal input parameters: R/Z segments cross" );
numCorner = rz->NumVertices();
//
// 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.
//
corners = new G4PolyconeSideRZ[numCorner];
//
// Copy corners
//
G4ReduciblePolygonIterator iterRZ(rz);
G4PolyconeSideRZ *next = corners;
iterRZ.Begin();
do {
next->r = iterRZ.GetA();
next->z = iterRZ.GetB();
} while( ++next, iterRZ.Next() );
//
// 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;
//
// We must decide here if we can dare declare one of our faces
// as having a "valid" normal (i.e. allBehind = true). This
// is never possible if the face faces "inward" in r.
//
G4bool allBehind;
if (corner->z > next->z) {
allBehind = false;
}
else {
//
// Otherwise, it is only true if the line passing
// through the two points of the segment do not
// split the r/z cross section
//
allBehind = !rz->BisectedBy( corner->r, corner->z,
next->r, next->z, kCarTolerance );
}
*face++ = new G4PolyconeSide( prev, corner, next, nextNext,
startPhi, endPhi-startPhi, phiIsOpen, allBehind );
} while( prev=corner, corner=next, corner > corners );
if (phiIsOpen) {
//
// Construct phi open edges
//
*face++ = new G4PolyPhiFace( rz, startPhi, 0, endPhi );
*face++ = new G4PolyPhiFace( rz, endPhi, 0, startPhi );
}
//
// We might have dropped a face or two: recalculate numFace
//
numFace = face-faces;
//
// Make enclosingCylinder
//
enclosingCylinder = new G4EnclosingCylinder( rz, phiIsOpen, phiStart, phiTotal );
}
//
// Destructor
//
G4Polycone::~G4Polycone()
{
delete [] corners;
if (original_parameters) delete original_parameters;
if (enclosingCylinder) delete enclosingCylinder;
}
//
// Copy constructor
//
G4Polycone::G4Polycone( const G4Polycone &source ) : G4VCSGfaceted( source )
{
CopyStuff( source );
}
//
// Assignment operator
//
const G4Polycone &G4Polycone::operator=( const G4Polycone &source )
{
if (this == &source) return *this;
G4VCSGfaceted::operator=( source );
delete [] corners;
if (original_parameters) delete original_parameters;
delete enclosingCylinder;
CopyStuff( source );
return *this;
}
//
// CopyStuff
//
void G4Polycone::CopyStuff( const G4Polycone &source )
{
//
// Simple stuff
//
startPhi = source.startPhi;
endPhi = source.endPhi;
phiIsOpen = source.phiIsOpen;
numCorner = source.numCorner;
//
// The corner array
//
corners = new G4PolyconeSideRZ[numCorner];
G4PolyconeSideRZ *corn = corners,
*sourceCorn = source.corners;
do {
*corn = *sourceCorn;
} while( ++sourceCorn, ++corn < corners+numCorner );
//
// Original parameters
//
if (source.original_parameters) {
original_parameters = new G4PolyconeHistorical( *source.original_parameters );
}
//
// Enclosing cylinder
//
enclosingCylinder = new G4EnclosingCylinder( *source.enclosingCylinder );
}
//
// Inside
//
// This is an override of G4VCSGfaceted::Inside, created in order to speed things
// up by first checking with G4EnclosingCylinder.
//
EInside G4Polycone::Inside( const G4ThreeVector &p ) const
{
//
// Quick test
//
if (enclosingCylinder->MustBeOutside(p)) return kOutside;
//
// Long answer
//
return G4VCSGfaceted::Inside(p);
}
//
// DistanceToIn
//
// This is an override of G4VCSGfaceted::Inside, created in order to speed things
// up by first checking with G4EnclosingCylinder.
//
G4double G4Polycone::DistanceToIn( const G4ThreeVector &p, const G4ThreeVector &v ) const
{
//
// Quick test
//
if (enclosingCylinder->ShouldMiss(p,v)) return kInfinity;
//
// Long answer
//
return G4VCSGfaceted::DistanceToIn( p, v );
}
//
// ComputeDimensions
//
void G4Polycone::ComputeDimensions( G4VPVParameterisation* p,
const G4int n,
const G4VPhysicalVolume* pRep)
{
}
//
// CreatePolyhedron
//
G4Polyhedron *G4Polycone::CreatePolyhedron() const
{
//
// This has to be fixed in visualization. Fake it for the moment.
//
if (original_parameters) {
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 {
G4cerr << "G4Polycone: visualization of this type of G4Polycone is not supported at this time" << G4endl;
return 0;
}
}
//
// CreateNURBS
//
G4NURBS *G4Polycone::CreateNURBS() const
{
return 0;
}
//
// G4Polycone:G4PolyconeHistorical stuff
//
G4Polycone::G4PolyconeHistorical::~G4PolyconeHistorical()
{
delete [] Z_values;
delete [] Rmin;
delete [] Rmax;
}
G4Polycone::G4PolyconeHistorical::G4PolyconeHistorical( const G4PolyconeHistorical &source )
{
Start_angle = source.Start_angle;
Opening_angle = source.Opening_angle;
Num_z_planes = source.Num_z_planes;
Z_values = new G4double[Num_z_planes];
Rmin = new G4double[Num_z_planes];
Rmax = new G4double[Num_z_planes];
G4int i;
for( i = 0; i < Num_z_planes; i++) {
Z_values[i] = source.Z_values[i];
Rmin[i] = source.Rmin[i];
Rmax[i] = source.Rmax[i];
}
}
@@ -0,0 +1,946 @@
// 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: G4PolyconeSide.cc,v 1.1 2000/04/07 11:02:07 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4PolyconeSide.cc
//
// Implementation 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"
#include "G4SolidExtentList.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,
const G4bool isAllBehind )
{
//
// 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;
//
// Calculate corner coordinates
//
corners = new G4ThreeVector[4];
corners[0] = G4ThreeVector( tail->r*cos(startPhi), tail->r*sin(startPhi), tail->z );
corners[1] = G4ThreeVector( head->r*cos(startPhi), head->r*sin(startPhi), head->z );
corners[2] = G4ThreeVector( tail->r*cos(startPhi+deltaPhi), tail->r*sin(startPhi+deltaPhi), tail->z );
corners[3] = G4ThreeVector( head->r*cos(startPhi+deltaPhi), head->r*sin(startPhi+deltaPhi), head->z );
}
else {
deltaPhi = 2*M_PI;
startPhi = 0.0;
}
allBehind = isAllBehind;
//
// 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 lAdj;
prevRS = r[0]-prevRZ->r;
prevZS = z[0]-prevRZ->z;
lAdj = sqrt( prevRS*prevRS + prevZS*prevZS );
prevRS /= lAdj;
prevZS /= lAdj;
rNormEdge[0] = rNorm + prevZS;
zNormEdge[0] = zNorm - prevRS;
lAdj = sqrt( rNormEdge[0]*rNormEdge[0] + zNormEdge[0]*zNormEdge[0] );
rNormEdge[0] /= lAdj;
zNormEdge[0] /= lAdj;
nextRS = nextRZ->r-r[1];
nextZS = nextRZ->z-z[1];
lAdj = sqrt( nextRS*nextRS + nextZS*nextZS );
nextRS /= lAdj;
nextZS /= lAdj;
rNormEdge[1] = rNorm + nextZS;
zNormEdge[1] = zNorm - nextRS;
lAdj = sqrt( rNormEdge[1]*rNormEdge[1] + zNormEdge[1]*zNormEdge[1] );
rNormEdge[1] /= lAdj;
zNormEdge[1] /= lAdj;
}
//
// Destructor
//
G4PolyconeSide::~G4PolyconeSide()
{
delete cone;
if (phiIsOpen) delete [] corners;
}
//
// Copy constructor
//
G4PolyconeSide::G4PolyconeSide( const G4PolyconeSide &source )
{
CopyStuff( source );
}
//
// Assignment operator
//
G4PolyconeSide *G4PolyconeSide::operator=( const G4PolyconeSide &source )
{
if (this == &source) return this;
delete cone;
if (phiIsOpen) delete [] corners;
CopyStuff( source );
return this;
}
//
// CopyStuff
//
void G4PolyconeSide::CopyStuff( const G4PolyconeSide &source )
{
r[0] = source.r[0];
r[1] = source.r[1];
z[0] = source.z[0];
z[1] = source.z[1];
startPhi = source.startPhi;
deltaPhi = source.deltaPhi;
phiIsOpen = source.phiIsOpen;
allBehind = source.allBehind;
cone = new G4IntersectingCone( *source.cone );
rNorm = source.rNorm;
zNorm = source.zNorm;
rS = source.rS;
zS = source.zS;
length = source.length;
prevRS = source.prevRS;
prevZS = source.prevZS;
nextRS = source.nextRS;
nextZS = source.nextZS;
rNormEdge[0] = source.rNormEdge[0];
rNormEdge[1] = source.rNormEdge[1];
zNormEdge[0] = source.zNormEdge[0];
zNormEdge[1] = source.zNormEdge[1];
if (phiIsOpen) {
corners = new G4ThreeVector[4];
corners[0] = source.corners[0];
corners[1] = source.corners[1];
corners[2] = source.corners[2];
corners[3] = source.corners[3];
}
}
//
// Intersect
//
G4bool G4PolyconeSide::Intersect( const G4ThreeVector &p, const G4ThreeVector &v,
const G4bool outgoing, const G4double surfTolerance,
G4double &distance, G4double &distFromSurface,
G4ThreeVector &normal, G4bool &isAllBehind )
{
G4double s1, s2;
G4double normSign = outgoing ? +1 : -1;
isAllBehind = allBehind;
//
// 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, normSign, p, v, normal )) {
//
// Good intersection! What about the normal?
//
if (normSign*v.dot(normal) > 0) {
//
// We have a valid intersection, but it could very easily
// be behind the point. To decide if we tolerate this,
// we have to see if the point p is on the surface near
// the intersecting point.
//
// What does it mean exactly for the point p to be "near"
// the intersection? It means that if we draw a line from
// p to the hit, the line remains entirely within the
// tolerance bounds of the cone. To test this, we can
// ask if the normal is correct near p.
//
G4double pr = p.perp();
if (pr < DBL_MIN) pr = DBL_MIN;
G4ThreeVector pNormal( rNorm*p.x()/pr, rNorm*p.y()/pr, zNorm );
if (normSign*v.dot(pNormal) > 0) {
//
// p and intersection in same hemisphere
//
G4double distOutside2;
distFromSurface = -normSign*DistanceAway( p, false, distOutside2 );
if (distOutside2 < surfTolerance*surfTolerance) {
if (distFromSurface > -surfTolerance) {
//
// We are just inside or away from the
// surface. Accept *any* value of distance.
//
distance = s1;
return true;
}
}
}
else
distFromSurface = s1;
//
// Accept positive distances
//
if (s1 > 0) {
distance = s1;
return true;
}
}
}
if (nside==1) return false;
//
// Well, try the second hit
//
hit = p + s2*v;
if (PointOnCone( hit, normSign, p, v, normal )) {
//
// Good intersection! What about the normal?
//
if (normSign*v.dot(normal) > 0) {
G4double pr = p.perp();
if (pr < DBL_MIN) pr = DBL_MIN;
G4ThreeVector pNormal( rNorm*p.x()/pr, rNorm*p.y()/pr, zNorm );
if (normSign*v.dot(pNormal) > 0) {
G4double distOutside2;
distFromSurface = -normSign*DistanceAway( p, false, distOutside2 );
if (distOutside2 < surfTolerance*surfTolerance) {
if (distFromSurface > -surfTolerance) {
distance = s2;
return true;
}
}
}
else
distFromSurface = s2;
if (s2 > 0) {
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 = normSign*DistanceAway( p, false, distOut2 );
if (distFrom > -0.5*kCarTolerance ) {
//
// Good answer
//
if (distOut2 > 0)
return sqrt( distFrom*distFrom + distOut2 );
else
return fabs(distFrom);
}
//
// Try second side.
//
distFrom = normSign*DistanceAway( p, true, distOut2 );
if (distFrom > -0.5*kCarTolerance) {
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 );
*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() < DBL_MIN) {
//
// 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,
G4SolidExtentList &extentList )
{
G4ClippablePolygon polygon;
//
// Here we will approximate (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 = (G4int)(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(0.5*sigPhi);
//
// Decide which radius to use on each end of the side,
// and whether a transition mesh is required
//
// {r0,z0} - Beginning of this side
// {r1,z1} - Ending of this side
// {r2,z0} - Beginning of transition piece connecting previous
// side (and ends at beginning of this side)
//
// So, order is 2 --> 0 --> 1.
// -------
//
// r2 < 0 indicates that no transition piece is required
//
G4double r0, r1, r2, z0, z1;
r2 = -1; // By default: no transition piece
if (rNorm < -DBL_MIN) {
//
// This side faces *inward*, and so our mesh has
// the same radius
//
r1 = r[1];
z1 = z[1];
z0 = z[0];
r0 = r[0];
r2 = -1;
if (prevZS > DBL_MIN) {
//
// The previous side is facing outwards
//
if ( prevRS*zS - prevZS*rS > 0 ) {
//
// Transition was convex: build transition piece
//
if (r[0] > DBL_MIN) r2 = r[0]*rFudge;
}
else {
//
// Transition was concave: short this side
//
FindLineIntersect( z0, r0, zS, rS,
z0, r0*rFudge, prevZS, prevRS*rFudge, z0, r0 );
}
}
if ( nextZS > DBL_MIN && (rS*nextZS - zS*nextRS < 0) ) {
//
// The next side is facing outwards, forming a
// concave transition: short this side
//
FindLineIntersect( z1, r1, zS, rS,
z1, r1*rFudge, nextZS, nextRS*rFudge, z1, r1 );
}
}
else if (rNorm > DBL_MIN) {
//
// This side faces *outward* and is given a boost to
// it radius
//
r0 = r[0]*rFudge;
z0 = z[0];
r1 = r[1]*rFudge;
z1 = z[1];
if (prevZS < -DBL_MIN) {
//
// The previous side is facing inwards
//
if ( prevRS*zS - prevZS*rS > 0 ) {
//
// Transition was convex: build transition piece
//
if (r[0] > DBL_MIN) r2 = r[0];
}
else {
//
// Transition was concave: short this side
//
FindLineIntersect( z0, r0, zS, rS*rFudge,
z0, r[0], prevZS, prevRS, z0, r0 );
}
}
if ( nextZS < -DBL_MIN && (rS*nextZS - zS*nextRS < 0) ) {
//
// The next side is facing inwards, forming a
// concave transition: short this side
//
FindLineIntersect( z1, r1, zS, rS*rFudge,
z1, r[1], nextZS, nextRS, z1, r1 );
}
}
else {
//
// This side is perpendicular to the z axis (is a disk)
//
// Whether or not r0 needs a rFudge factor depends
// on the normal of the previous edge. Similar with r1
// and the next edge. No transition piece is required.
//
r0 = r[0];
r1 = r[1];
z0 = z[0];
z1 = z[1];
if (prevZS > DBL_MIN) r0 *= rFudge;
if (nextZS > DBL_MIN) r1 *= rFudge;
}
//
// Loop
//
G4double phi = startPhi,
cosPhi = cos(phi),
sinPhi = sin(phi);
G4ThreeVector v0( r0*cosPhi, r0*sinPhi, z0 ),
v1( r1*cosPhi, r1*sinPhi, z1 ),
v2, w0, w1, w2;
transform.ApplyPointTransform( v0 );
transform.ApplyPointTransform( v1 );
if (r2 >= 0) {
v2 = G4ThreeVector( r2*cosPhi, r2*sinPhi, z0 );
transform.ApplyPointTransform( v2 );
}
do {
G4double min, max;
phi += sigPhi;
if (numPhi == 1) phi = startPhi+deltaPhi; // Try to avoid roundoff
cosPhi = cos(phi),
sinPhi = sin(phi);
w0 = G4ThreeVector( r0*cosPhi, r0*sinPhi, z0 );
w1 = G4ThreeVector( r1*cosPhi, r1*sinPhi, z1 );
transform.ApplyPointTransform( w0 );
transform.ApplyPointTransform( w1 );
G4ThreeVector deltaV = r0 > r1 ? w0-v0 : w1-v1;
//
// 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
//
if (polygon.PartialClip( voxelLimit, axis )) {
//
// Get dot product of normal with target axis
//
polygon.SetNormal( deltaV.cross(v1-v0).unit() );
extentList.AddSurface( polygon );
}
if (r2 >= 0) {
//
// Repeat, for transition piece
//
w2 = G4ThreeVector( r2*cosPhi, r2*sinPhi, z0 );
transform.ApplyPointTransform( w2 );
polygon.ClearAllVertices();
polygon.AddVertexInOrder( v2 );
polygon.AddVertexInOrder( v0 );
polygon.AddVertexInOrder( w0 );
polygon.AddVertexInOrder( w2 );
if (polygon.PartialClip( voxelLimit, axis )) {
polygon.SetNormal( deltaV.cross(v0-v2).unit() );
extentList.AddSurface( polygon );
}
v2 = w2;
}
//
// Next vertex
//
v0 = w0;
v1 = w1;
} while( --numPhi > 0 );
//
// We are almost done. But, it is important that we leave no
// gaps in the surface of our solid. By using rFudge, however,
// we've done exactly that, if we have a phi segment.
// Add two additional faces if necessary
//
if (phiIsOpen && rNorm > DBL_MIN) {
G4double min, max;
G4double cosPhi = cos(startPhi),
sinPhi = sin(startPhi);
G4ThreeVector a0( r[0]*cosPhi, r[0]*sinPhi, z[0] ),
a1( r[1]*cosPhi, r[1]*sinPhi, z[1] ),
b0( r0*cosPhi, r0*sinPhi, z[0] ),
b1( r1*cosPhi, r1*sinPhi, z[1] );
transform.ApplyPointTransform( a0 );
transform.ApplyPointTransform( a1 );
transform.ApplyPointTransform( b0 );
transform.ApplyPointTransform( b1 );
polygon.ClearAllVertices();
polygon.AddVertexInOrder( a0 );
polygon.AddVertexInOrder( a1 );
polygon.AddVertexInOrder( b0 );
polygon.AddVertexInOrder( b1 );
if (polygon.PartialClip( voxelLimit , axis)) {
G4ThreeVector normal( sinPhi, -cosPhi, 0 );
polygon.SetNormal( transform.TransformAxis( normal ) );
extentList.AddSurface( polygon );
}
cosPhi = cos(startPhi+deltaPhi);
sinPhi = sin(startPhi+deltaPhi);
a0 = G4ThreeVector( r[0]*cosPhi, r[0]*sinPhi, z[0] ),
a1 = G4ThreeVector( r[1]*cosPhi, r[1]*sinPhi, z[1] ),
b0 = G4ThreeVector( r0*cosPhi, r0*sinPhi, z[0] ),
b1 = G4ThreeVector( r1*cosPhi, r1*sinPhi, z[1] );
transform.ApplyPointTransform( a0 );
transform.ApplyPointTransform( a1 );
transform.ApplyPointTransform( b0 );
transform.ApplyPointTransform( b1 );
polygon.ClearAllVertices();
polygon.AddVertexInOrder( a0 );
polygon.AddVertexInOrder( a1 );
polygon.AddVertexInOrder( b0 );
polygon.AddVertexInOrder( b1 );
if (polygon.PartialClip( voxelLimit, axis )) {
G4ThreeVector normal( -sinPhi, cosPhi, 0 );
polygon.SetNormal( transform.TransformAxis( normal ) );
extentList.AddSurface( polygon );
}
}
return;
}
//
// -------------------------------------------------------
//
// 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
// edgeRZnorm - (out) if negative, point is inside
// 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) {
G4double deltaR = rx - r[1], deltaZ = zx - z[1];
*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
//
G4double dist = d1*rx;
distOutside2 += dist*dist;
if (edgeRZnorm) *edgeRZnorm = fabs(dist);
}
}
return answer;
}
//
// PointOnCone
//
// Decide if a point is on a cone and return normal if it is
//
G4bool G4PolyconeSide::PointOnCone( const G4ThreeVector &hit, const G4double normSign,
const G4ThreeVector &p, const G4ThreeVector &v,
G4ThreeVector &normal )
{
G4double rx = hit.perp();
//
// Check radial/z extent, as appropriate
//
if (!cone->HitOn( rx, hit.z() )) return false;
if (phiIsOpen) {
G4double phiTolerant = 2.0*kCarTolerance/(rx+kCarTolerance);
//
// Check phi segment. Here we have to be careful
// to use the standard method consistent with
// PolyPhiFace. See PolyPhiFace::InsideEdgesExact
//
G4double phi = hit.phi();
while( phi < startPhi-phiTolerant ) phi += 2*M_PI;
if (phi > startPhi+deltaPhi+phiTolerant) return false;
if (phi > startPhi+deltaPhi-phiTolerant) {
//
// Exact treatment
//
G4ThreeVector qx = p + v;
G4ThreeVector qa = qx - corners[2],
qb = qx - corners[3];
G4ThreeVector qacb = qa.cross(qb);
if (normSign*qacb.dot(v) < 0) return false;
}
else if (phi < phiTolerant) {
G4ThreeVector qx = p + v;
G4ThreeVector qa = qx - corners[1],
qb = qx - corners[0];
G4ThreeVector qacb = qa.cross(qb);
if (normSign*qacb.dot(v) < 0) return false;
}
}
//
// We have a good hit! Calculate normal
//
if (rx < DBL_MIN)
normal = G4ThreeVector( 0, 0, zNorm < 0 ? -1 : 1 );
else
normal = G4ThreeVector( rNorm*hit.x()/rx, rNorm*hit.y()/rx, zNorm );
return true;
}
//
// FindLineIntersect
//
// Decide the point at which two 2-dimensional lines intersect
//
// Equation of line: x = x1 + s*tx1
// y = y1 + s*ty1
//
// It is assumed that the lines are *not* parallel
//
void G4PolyconeSide::FindLineIntersect( const G4double x1, const G4double y1,
const G4double tx1, const G4double ty1,
const G4double x2, const G4double y2,
const G4double tx2, const G4double ty2,
G4double &x, G4double &y )
{
//
// The solution is a simple linear equation
//
G4double deter = tx1*ty2 - tx2*ty1;
G4double s1 = ((x2-x1)*ty2 - tx2*(y2-y1))/deter;
G4double s2 = ((x2-x1)*ty1 - tx1*(y2-y1))/deter;
//
// We want the answer to not depend on which order the
// lines were specified. Take average.
//
x = 0.5*( x1+s1*tx1 + x2+s2*tx2 );
y = 0.5*( y1+s1*ty1 + y2+s2*ty2 );
}
@@ -0,0 +1,466 @@
// 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: G4Polyhedra.cc,v 1.1 2000/04/07 11:02:25 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4Polyhedra.cc
//
// Implementation of a CSG polyhedra, as an inherited class of G4VCSGfaceted.
//
// To be done:
// * 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.
//
// Utility classes:
// * G4EnclosingCylinder: I decided a quick check of geometry would be a
// good idea (for CPU speed). If the quick check fails, the regular
// full-blown G4VCSGfaceted version is invoked.
// * G4ReduciblePolygon: Really meant as a check of input parameters,
// this utility class also "converts" the GEANT3-like PGON/PCON
// arguments into the newer ones.
// Both these classes are implemented outside this file because they are
// shared with G4Polycone.
//
// --------------------------------------------------------------------
#include "G4Polyhedra.hh"
#include "G4PolyhedraSide.hh"
#include "G4PolyPhiFace.hh"
#include "G4Polyhedron.hh"
#include "G4EnclosingCylinder.hh"
#include "G4ReduciblePolygon.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 G4int 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*(1-DBL_EPSILON)) phiTotal = 2*M_PI;
G4double convertRad = cos(0.5*phiTotal/theNumSide);
//
// Some historical stuff
//
original_parameters = new G4PolyhedraHistorical;
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;
}
//
// Build RZ polygon using special PCON/PGON GEANT3 constructor
//
G4ReduciblePolygon *rz = new G4ReduciblePolygon( rInner, rOuter, zPlane, numZPlanes );
rz->ScaleA( 1/convertRad );
//
// Do the real work
//
Create( phiStart, phiTotal, theNumSide, rz );
delete rz;
}
//
// Constructor (generic parameters)
//
G4Polyhedra::G4Polyhedra( G4String name,
const G4double phiStart,
const G4double phiTotal,
const G4int theNumSide,
const G4int numRZ,
const G4double r[],
const G4double z[] ) : G4VCSGfaceted( name )
{
original_parameters = 0;
G4ReduciblePolygon *rz = new G4ReduciblePolygon( r, z, numRZ );
Create( phiStart, phiTotal, theNumSide, rz );
delete rz;
}
//
// Create
//
// Generic create routine, called by each constructor after conversion of arguments
//
void G4Polyhedra::Create( const G4double phiStart,
const G4double phiTotal,
const G4int theNumSide,
G4ReduciblePolygon *rz )
{
//
// Perform checks of rz values
//
if (rz->Amin() < 0.0)
G4Exception( "G4Polyhedra: Illegal input parameters: All R values must be >= 0" );
G4double rzArea = rz->Area();
if (rzArea < -kCarTolerance) rz->ReverseOrder();
else if (rzArea < -kCarTolerance)
G4Exception( "G4Polyhedra: Illegal input parameters: R/Z cross section is zero or near zero" );
if ((!rz->RemoveDuplicateVertices( kCarTolerance )) ||
(!rz->RemoveRedundantVertices( kCarTolerance )) )
G4Exception( "G4Polyhedra: Illegal input parameters: Too few unique R/Z values" );
if (rz->CrossesItself( 1/kInfinity ))
G4Exception( "G4Polyhedra: Illegal input parameters: R/Z segments cross" );
numCorner = rz->NumVertices();
startPhi = phiStart;
while( startPhi < 0 ) startPhi += 2*M_PI;
//
// Phi opening? Account for some possible roundoff, and interpret
// nonsense value as representing no phi opening
//
if (phiTotal <= 0 || phiTotal > 2.0*M_PI*(1-DBL_EPSILON)) {
phiIsOpen = false;
endPhi = phiStart+2*M_PI;
}
else {
phiIsOpen = true;
//
// Convert phi into our convention
//
endPhi = phiStart+phiTotal;
while( endPhi < startPhi ) endPhi += 2*M_PI;
}
//
// Save number sides
//
numSide = theNumSide;
//
// Allocate corner array.
//
corners = new G4PolyhedraSideRZ[numCorner];
//
// Copy corners
//
G4ReduciblePolygonIterator iterRZ(rz);
G4PolyhedraSideRZ *next = corners;
iterRZ.Begin();
do {
next->r = iterRZ.GetA();
next->z = iterRZ.GetB();
} while( ++next, iterRZ.Next() );
//
// 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;
//
// We must decide here if we can dare declare one of our faces
// as having a "valid" normal (i.e. allBehind = true). This
// is never possible if the face faces "inward" in r *unless*
// we have only one side
//
G4bool allBehind;
if ((corner->z > next->z) && (numSide > 1)) {
allBehind = false;
}
else {
//
// Otherwise, it is only true if the line passing
// through the two points of the segment do not
// split the r/z cross section
//
allBehind = !rz->BisectedBy( corner->r, corner->z,
next->r, next->z, kCarTolerance );
}
*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( rz, startPhi, phiTotal/numSide, endPhi );
*face++ = new G4PolyPhiFace( rz, endPhi, phiTotal/numSide, startPhi );
}
//
// We might have dropped a face or two: recalculate numFace
//
numFace = face-faces;
//
// Make enclosingCylinder
//
enclosingCylinder = new G4EnclosingCylinder( rz, phiIsOpen, phiStart, phiTotal );
}
//
// Destructor
//
G4Polyhedra::~G4Polyhedra()
{
delete [] corners;
if (original_parameters) delete original_parameters;
delete enclosingCylinder;
}
//
// Copy constructor
//
G4Polyhedra::G4Polyhedra( const G4Polyhedra &source ) : G4VCSGfaceted( source )
{
CopyStuff( source );
}
//
// Assignment operator
//
const G4Polyhedra &G4Polyhedra::operator=( const G4Polyhedra &source )
{
if (this == &source) return *this;
G4VCSGfaceted::operator=( source );
delete [] corners;
if (original_parameters) delete original_parameters;
delete enclosingCylinder;
CopyStuff( source );
return *this;
}
//
// CopyStuff
//
void G4Polyhedra::CopyStuff( const G4Polyhedra &source )
{
//
// Simple stuff
//
numSide = source.numSide;
startPhi = source.startPhi;
endPhi = source.endPhi;
phiIsOpen = source.phiIsOpen;
numCorner = source.numCorner;
//
// The corner array
//
corners = new G4PolyhedraSideRZ[numCorner];
G4PolyhedraSideRZ *corn = corners,
*sourceCorn = source.corners;
do {
*corn = *sourceCorn;
} while( ++sourceCorn, ++corn < corners+numCorner );
//
// Original parameters
//
if (source.original_parameters) {
original_parameters = new G4PolyhedraHistorical( *source.original_parameters );
}
//
// Enclosing cylinder
//
enclosingCylinder = new G4EnclosingCylinder( *source.enclosingCylinder );
}
//
// Inside
//
// This is an override of G4VCSGfaceted::Inside, created in order to speed things
// up by first checking with G4EnclosingCylinder.
//
EInside G4Polyhedra::Inside( const G4ThreeVector &p ) const
{
//
// Quick test
//
if (enclosingCylinder->MustBeOutside(p)) return kOutside;
//
// Long answer
//
return G4VCSGfaceted::Inside(p);
}
//
// DistanceToIn
//
// This is an override of G4VCSGfaceted::Inside, created in order to speed things
// up by first checking with G4EnclosingCylinder.
//
G4double G4Polyhedra::DistanceToIn( const G4ThreeVector &p, const G4ThreeVector &v ) const
{
//
// Quick test
//
if (enclosingCylinder->ShouldMiss(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
{
//
// This has to be fixed in visualization. Fake it for the moment.
//
if (original_parameters) {
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 {
G4cerr << "G4Polyhedra: visualization of this type of G4Polyhedra is not supported at this time" << G4endl;
return 0;
}
}
//
// CreateNURBS
//
G4NURBS *G4Polyhedra::CreateNURBS() const
{
return 0;
}
//
// G4Polyhedra::G4PolyhedraHistorical stuff
//
G4Polyhedra::G4PolyhedraHistorical::~G4PolyhedraHistorical()
{
delete [] Z_values;
delete [] Rmin;
delete [] Rmax;
}
G4Polyhedra::G4PolyhedraHistorical::G4PolyhedraHistorical( const G4PolyhedraHistorical &source )
{
Start_angle = source.Start_angle;
Opening_angle = source.Opening_angle;
numSide = source.numSide;
Num_z_planes = source.Num_z_planes;
Z_values = new G4double[Num_z_planes];
Rmin = new G4double[Num_z_planes];
Rmax = new G4double[Num_z_planes];
G4int i;
for( i = 0; i < Num_z_planes; i++) {
Z_values[i] = source.Z_values[i];
Rmin[i] = source.Rmin[i];
Rmax[i] = source.Rmax[i];
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,521 @@
// 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: G4ReduciblePolygon.cc,v 1.1 2000/04/07 11:03:04 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4ReduciblePolygon.cc
//
// Implementation of a utility class used to specify, test, reduce,
// and/or otherwise manipulate a 2D polygon.
//
// See G4ReduciblePolygon.hh for more info.
//
// --------------------------------------------------------------------
#include "globals.hh"
#include "G4ReduciblePolygon.hh"
//
// Constructor: with simple arrays
//
G4ReduciblePolygon::G4ReduciblePolygon( const G4double a[], const G4double b[], const G4int n )
{
//
// Do all of the real work in Create
//
Create( a, b, n );
}
//
// Constructor: special PGON/PCON case
//
G4ReduciblePolygon::G4ReduciblePolygon( const G4double rmin[], const G4double rmax[],
const G4double z[], const G4int n )
{
//
// Translate
//
G4double *a = new G4double[n*2];
G4double *b = new G4double[n*2];
G4double *rOut = a + n,
*zOut = b + n,
*rIn = rOut-1,
*zIn = zOut-1;
G4int i;
for( i=0; i < n; i++, rOut++, zOut++, rIn--, zIn-- ) {
*rOut = rmax[i];
*rIn = rmin[i];
*zOut = *zIn = z[i];
}
Create( a, b, n*2 );
delete [] a;
delete [] b;
}
//
// Create
//
// To be called by constructors, fill in the list and statistics for a new
// polygon
//
void G4ReduciblePolygon::Create( const G4double a[], const G4double b[], const G4int n )
{
if (n<3) G4Exception( "G4ReduciblePolygon: less than 3 vertices specified" );
const G4double *anext = a, *bnext = b;
ABVertex *prev = 0;
do {
ABVertex *newVertex = new ABVertex;
newVertex->a = *anext;
newVertex->b = *bnext;
newVertex->next = 0;
if (prev==0) {
vertexHead = newVertex;
}
else {
prev->next = newVertex;
}
prev = newVertex;
} while( ++anext, ++bnext < b+n );
numVertices = n;
CalculateMaxMin();
}
//
// Destructor
//
G4ReduciblePolygon::~G4ReduciblePolygon()
{
ABVertex *curr = vertexHead;
while( curr ) {
ABVertex *toDelete = curr;
curr = curr->next;
delete toDelete;
}
}
//
// CopyVertices
//
// Copy contents into simple linear arrays.
// ***** CAUTION ***** Be care to declare the arrays to a large
// enough size!
//
void G4ReduciblePolygon::CopyVertices( G4double a[], G4double b[] ) const
{
G4double *anext = a, *bnext = b;
ABVertex *curr = vertexHead;
while( curr ) {
*anext++ = curr->a;
*bnext++ = curr->b;
curr = curr->next;
}
}
//
// ScaleA
//
// Multiply all a values by a common scale
//
void G4ReduciblePolygon::ScaleA( const G4double scale )
{
ABVertex *curr = vertexHead;
while( curr ) {
curr->a *= scale;
curr = curr->next;
}
}
//
// ScaleB
//
// Multiply all b values by a common scale
//
void G4ReduciblePolygon::ScaleB( const G4double scale )
{
ABVertex *curr = vertexHead;
while( curr ) {
curr->b *= scale;
curr = curr->next;
}
}
//
// RemoveDuplicateVertices
//
// Remove adjacent vertices that are equal. Returns "false" if there
// is a problem (too few vertices remaining).
//
G4bool G4ReduciblePolygon::RemoveDuplicateVertices( const G4double tolerance )
{
ABVertex *curr = vertexHead,
*prev = 0,
*next = curr->next; // A little dangerous
while( curr ) {
next = curr->next;
if (next == 0) next = vertexHead;
if (fabs(curr->a-next->a) < tolerance &&
fabs(curr->b-next->b) < tolerance ) {
//
// Duplicate found: do we have > 3 vertices?
//
if (numVertices <= 3) {
CalculateMaxMin();
return false;
}
//
// Delete
//
ABVertex *toDelete = curr;
curr = curr->next;
delete toDelete;
numVertices--;
if (prev) prev->next = curr; else vertexHead = curr;
}
else {
prev = curr;
curr = curr->next;
}
}
//
// In principle, this is not needed, but why not just play it safe?
//
CalculateMaxMin();
return true;
}
//
// RemoveRedundantVertices
//
// Remove any unneeded vertices, i.e. those vertices which
// are on the line connecting the previous and next vertices.
//
G4bool G4ReduciblePolygon::RemoveRedundantVertices( const G4double tolerance )
{
//
// Under these circumstances, we can quit now!
//
if (numVertices <= 2) return false;
G4double tolerance2 = tolerance*tolerance;
//
// Loop over all vertices
//
ABVertex *curr = vertexHead,
*prev = 0,
*next = curr->next; // A little dangerous
while( curr ) {
next = curr->next;
if (next == 0) next = vertexHead;
G4double da = next->a - curr->a,
db = next->b - curr->b;
//
// Loop over all subsequent vertices, up to curr
//
for(;;) {
//
// Get vertex after next
//
ABVertex *test = next->next;
if (test == 0) test = vertexHead;
//
// If we are back to the original vertex, stop
//
if (test==curr) break;
//
// Test for parallel line segments
//
G4double dat = test->a - curr->a,
dbt = test->b - curr->b;
if (fabs(dat*db-dbt*da)>tolerance2) break;
//
// Redundant vertex found: do we have > 3 vertices?
//
if (numVertices <= 3) {
CalculateMaxMin();
return false;
}
//
// Delete vertex pointed to by next. Carefully!
//
if (curr->next) { // next is not head
if (next->next)
curr->next = test; // next is not tail
else
curr->next = 0; // New tail
}
else
vertexHead = test; // New head
delete next;
numVertices--;
//
// Replace next by the vertex we just tested,
// and keep on going...
//
next = test;
da = dat; db = dbt;
}
curr = curr->next;
}
//
// In principle, this is not needed, but why not just play it safe?
//
CalculateMaxMin();
return true;
}
//
// ReverseOrder
//
// Reverse the order of the vertices
//
void G4ReduciblePolygon::ReverseOrder()
{
//
// Loop over all vertices
//
ABVertex *prev = vertexHead;
if (prev==0) return; // No vertices
ABVertex *curr = prev->next;
if (curr==0) return; // Just one vertex
//
// Our new tail
//
vertexHead->next = 0;
for(;;) {
//
// Save pointer to next vertex (in original order)
//
ABVertex *save = curr->next;
//
// Replace it with a pointer to the previous one
// (in original order)
//
curr->next = prev;
//
// Last vertex?
//
if (save == 0) break;
//
// Next vertex
//
prev = curr;
curr = save;
}
//
// Our new head
//
vertexHead = curr;
}
//
// CrossesItself
//
// Return "true" if the polygon crosses itself
//
// Warning: this routine is not very fast (runs as N**2)
//
G4bool G4ReduciblePolygon::CrossesItself( const G4double tolerance )
{
G4double tolerance2 = tolerance*tolerance;
G4double one = 1.0-tolerance,
zero = tolerance;
//
// Top loop over line segments. By the time we finish
// with the second to last segment, we're done.
//
ABVertex *curr1 = vertexHead, *next1;
while (next1 = curr1->next) {
G4double da1 = next1->a-curr1->a,
db1 = next1->b-curr1->b;
//
// Inner loop over subsequent line segments
//
ABVertex *curr2 = next1->next;
while( curr2 ) {
ABVertex *next2 = curr2->next;
if (next2==0) next2 = vertexHead;
G4double da2 = next2->a-curr2->a,
db2 = next2->b-curr2->b;
G4double a12 = curr2->a-curr1->a,
b12 = curr2->b-curr1->b;
//
// Calculate intersection of the two lines
//
G4double deter = da1*db2 - db1*da2;
if (fabs(deter) > tolerance2) {
G4double s1, s2;
s1 = (a12*db2-b12*da2)/deter;
if (s1 >= zero && s1 < one) {
s2 = -(da1*b12-db1*a12)/deter;
if (s2 >= zero && s2 < one) return true;
}
}
curr2 = curr2->next;
}
curr1 = next1;
}
return false;
}
//
// BisectedBy
//
// Decide if a line through two points crosses the polygon, within tolerance
//
G4bool G4ReduciblePolygon::BisectedBy( const G4double a1, const G4double b1,
const G4double a2, const G4double b2, const G4double tolerance )
{
G4int nNeg = 0, nPos = 0;
G4double a12 = a2-a1, b12 = b2-b1;
G4double len12 = sqrt( a12*a12 + b12*b12 );
a12 /= len12; b12 /= len12;
ABVertex *curr = vertexHead;
do {
G4double av = curr->a - a1,
bv = curr->b - b1;
G4double cross = av*b12 - bv*a12;
if (cross < -tolerance) {
if (nPos) return true;
nNeg++;
}
else if (cross > tolerance) {
if (nNeg) return true;
nPos++;
}
} while( curr = curr->next );
return false;
}
//
// Area
//
// Calculated signed polygon area, where polygons specified in a clockwise manner
// (where x==a, y==b) have negative area
//
// References: [O' Rourke (C)] pp. 18-27; [Gems II] pp. 5-6:
// "The Area of a Simple Polygon", Jon Rokne.
//
G4double G4ReduciblePolygon::Area()
{
G4double answer = 0;
ABVertex *curr = vertexHead, *next;
do {
next = curr->next;
if (next==0) next = vertexHead;
answer += curr->a*next->b - curr->b*next->a;
} while( curr = curr->next );
return 0.5*answer;
}
//
// Print
//
void G4ReduciblePolygon::Print()
{
ABVertex *curr = vertexHead;
do {
G4cerr << curr->a << " " << curr->b << G4endl;
} while( curr = curr->next );
}
//
// CalculateMaxMin
//
// To be called when the vertices are changed, this
// routine re-calculates global values
//
void G4ReduciblePolygon::CalculateMaxMin()
{
ABVertex *curr = vertexHead;
aMin = aMax = curr->a;
bMin = bMax = curr->b;
curr = curr->next;
while( curr ) {
if (curr->a < aMin)
aMin = curr->a;
else if (curr->a > aMax)
aMax = curr->a;
if (curr->b < bMin)
bMin = curr->b;
else if (curr->b > bMax)
bMax = curr->b;
curr = curr->next;
}
}
@@ -0,0 +1,163 @@
// 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: G4SolidExtentList.cc,v 1.1 2000/04/07 11:03:23 gcosmo Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4SolidExtentList.cc
//
// Implementation of a list of (voxel) extents along one axis
//
// --------------------------------------------------------------------
#include "G4SolidExtentList.hh"
#include "G4VoxelLimits.hh"
//
// Constructor (default)
//
G4SolidExtentList::G4SolidExtentList()
{
axis = kZAxis;
limited = false;
minLimit = -DBL_MAX;
maxLimit = +DBL_MAX;
}
//
// Constructor (limited case)
//
G4SolidExtentList::G4SolidExtentList( const EAxis targetAxis, const G4VoxelLimits &voxelLimits )
{
axis = targetAxis;
limited = voxelLimits.IsLimited( axis );
if (limited) {
minLimit = voxelLimits.GetMinExtent( axis );
maxLimit = voxelLimits.GetMaxExtent( axis );
}
else {
minLimit = -DBL_MAX;
maxLimit = +DBL_MAX;
}
}
//
// Destructor
//
G4SolidExtentList::~G4SolidExtentList() {;}
//
// AddSurface
//
//
void G4SolidExtentList::AddSurface( const G4ClippablePolygon &surface )
{
//
// Keep track of four surfaces
//
G4double min, max;
surface.GetExtent( axis, min, max );
if (min > maxLimit) {
//
// Nearest surface beyond maximum limit
//
if (surface.InFrontOf(minAbove,axis)) minAbove = surface;
}
else if (max < minLimit) {
//
// Nearest surface below minimum limit
//
if (surface.BehindOf(maxBelow,axis)) maxBelow = surface;
}
else {
//
// Max and min surfaces inside
//
if (surface.BehindOf(maxSurface,axis)) maxSurface = surface;
if (surface.InFrontOf(minSurface,axis)) minSurface = surface;
}
}
//
// GetExtent
//
// Return extent after processing all surfaces
//
G4bool G4SolidExtentList::GetExtent( G4double &min, G4double &max ) const
{
//
// Did we have any surfaces within the limits?
//
if (minSurface.Empty()) {
//
// Nothing! Do we have anything above?
//
if (minAbove.Empty()) return false;
//
// Yup. Is it facing inwards?
//
if (minAbove.GetNormal().operator()(axis) < 0) return false;
//
// No. We must be entirely within the solid
//
max = maxLimit + kCarTolerance;
min = minLimit - kCarTolerance;
return true;
}
//
// Check max surface
//
if (maxSurface.GetNormal().operator()(axis) < 0) {
//
// Inward facing: max limit must be embedded within solid
//
max = maxLimit + kCarTolerance;
}
else {
G4double sMin, sMax;
maxSurface.GetExtent( axis, sMin, sMax );
max = ( (sMax > maxLimit) ? maxLimit : sMax ) + kCarTolerance;
}
//
// Check min surface
//
if (minSurface.GetNormal().operator()(axis) > 0) {
//
// Inward facing: max limit must be embedded within solid
//
min = minLimit - kCarTolerance;
}
else {
G4double sMin, sMax;
minSurface.GetExtent( axis, sMin, sMax );
min = ( (sMin < minLimit) ? minLimit : sMin ) - kCarTolerance;
}
return true;
}
@@ -0,0 +1,347 @@
// 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: G4VCSGfaceted.cc,v 1.5 2000/06/08 17:54:01 gracia Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
//
// --------------------------------------------------------------------
// GEANT 4 class source file
//
//
// G4VCSGfaceted.cc
//
// Implementation of the virtual class of a CSG type shape that is built
// entirely out of G4VCSGface faces.
//
// --------------------------------------------------------------------
#include "G4VCSGfaceted.hh"
#include "G4VCSGface.hh"
#include "G4SolidExtentList.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()
{
DeleteStuff();
}
//
// Copy constructor
//
G4VCSGfaceted::G4VCSGfaceted( const G4VCSGfaceted &source ) : G4VSolid( source )
{
CopyStuff( source );
}
//
// Assignment operator
//
const G4VCSGfaceted &G4VCSGfaceted::operator=( const G4VCSGfaceted &source )
{
if (&source == this) return *this;
DeleteStuff();
CopyStuff( source );
return *this;
}
//
// CopyStuff (protected)
//
// Copy the contents of source
//
void G4VCSGfaceted::CopyStuff( const G4VCSGfaceted &source )
{
numFace = source.numFace;
if (numFace == 0) return; // odd, but permissable?
faces = new G4VCSGface*[numFace];
G4VCSGface **face = faces,
**sourceFace = source.faces;
do {
*face = (*sourceFace)->Clone();
} while( ++sourceFace, ++face < faces+numFace );
}
//
// DeleteStuff (protected)
//
// Delete all allocated objects
//
void G4VCSGfaceted::DeleteStuff()
{
if (numFace) {
G4VCSGface **face = faces;
do {
delete *face;
} while( ++face < faces + numFace );
delete [] faces;
}
}
//
// CalculateExtent
//
G4bool G4VCSGfaceted::CalculateExtent( const EAxis axis,
const G4VoxelLimits &voxelLimit,
const G4AffineTransform &transform,
G4double &min, G4double &max ) const
{
G4SolidExtentList extentList( axis, voxelLimit );
//
// Loop over all faces, checking min/max extent as we go.
//
G4VCSGface **face = faces;
do {
(*face)->CalculateExtent( axis, voxelLimit, transform, extentList );
} while( ++face < faces + numFace );
//
// Return min/max value
//
return extentList.GetExtent( min, max );
}
//
// 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 *bestFace;
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;
bestFace = *face;
if (distFromSurface <= 0) return 0;
}
}
} while( ++face < faces + numFace );
if (distance < kInfinity && distFromSurface<kCarTolerance/2) {
if (bestFace->Distance(p,false) < 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 *bestFace;
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;
bestFace = *face;
if (distFromSurface <= 0) break;
}
}
} while( ++face < faces + numFace );
if (distance < kInfinity) {
if (distFromSurface <= 0)
distance = 0;
else if (distFromSurface<kCarTolerance/2) {
if (bestFace->Distance(p,true) < 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 < 0.5*kCarTolerance) ? 0 : best;
}
//
// DescribeYourselfTo
//
void G4VCSGfaceted::DescribeYourselfTo( G4VGraphicsScene& scene ) const
{
scene.AddThis( *this );
}
//
// GetExtent
//
// Define the sides of the box into which our solid instance would fit.
//
G4VisExtent G4VCSGfaceted::GetExtent() const
{
static const G4ThreeVector xMax(1,0,0), xMin(-1,0,0),
yMax(0,1,0), yMin(0,-1,0),
zMax(0,0,1), zMin(0,0,-1);
static const G4ThreeVector *axes[6] = { &xMin, &xMax, &yMin, &yMax, &zMin, &zMax };
G4double answers[6] = {-kInfinity, -kInfinity, -kInfinity, -kInfinity, -kInfinity, -kInfinity};
G4VCSGface **face = faces;
do {
G4double vmax;
const G4ThreeVector **axis = axes+5 ;
G4double *answer = answers+5;
do {
G4double testFace = (*face)->Extent( **axis );
if (testFace > *answer) *answer = testFace;
}
while( --axis, --answer >= answers );
} while( ++face < faces + numFace );
return G4VisExtent( -answers[0], answers[1],
-answers[2], answers[3],
-answers[4], answers[5] );
}