Import Geant4 2.0.0 source tree
This commit is contained in:
@@ -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, ¬Used, 0 );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// InsideEdges (care about distance)
|
||||
//
|
||||
// Decide if the point in r,z is inside the edges of our face
|
||||
//
|
||||
G4bool G4PolyPhiFace::InsideEdges( const G4double r, const G4double z,
|
||||
G4double *bestDist2,
|
||||
G4PolyPhiFaceVertex **base3Dnorm,
|
||||
G4ThreeVector **head3Dnorm )
|
||||
{
|
||||
G4double bestDistance2 = kInfinity;
|
||||
G4bool answer;
|
||||
|
||||
G4PolyPhiFaceEdge *edge = edges;
|
||||
do {
|
||||
G4PolyPhiFaceVertex *testMe;
|
||||
//
|
||||
// Get distance perpendicular to the edge
|
||||
//
|
||||
G4double dr = (r-edge->v0->r), dz = (z-edge->v0->z);
|
||||
|
||||
G4double distOut = dr*edge->tz - dz*edge->tr;
|
||||
G4double distance2 = distOut*distOut;
|
||||
if (distance2 > bestDistance2) continue; // No hope!
|
||||
|
||||
//
|
||||
// Check to see if normal intersects edge within the edge's boundary
|
||||
//
|
||||
G4double s = dr*edge->tr + dz*edge->tz;
|
||||
|
||||
//
|
||||
// If it doesn't, penalize distance2 appropriately
|
||||
//
|
||||
if (s < 0) {
|
||||
distance2 += s*s;
|
||||
testMe = edge->v0;
|
||||
}
|
||||
else if (s > edge->length) {
|
||||
G4double s2 = s-edge->length;
|
||||
distance2 += s2*s2;
|
||||
testMe = edge->v1;
|
||||
}
|
||||
else {
|
||||
testMe = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Closest edge so far?
|
||||
//
|
||||
if (distance2 < bestDistance2) {
|
||||
bestDistance2 = distance2;
|
||||
if (testMe) {
|
||||
G4double distNorm = dr*testMe->rNorm + dz*testMe->zNorm;
|
||||
answer = (distNorm <= 0);
|
||||
if (base3Dnorm) {
|
||||
*base3Dnorm = testMe;
|
||||
*head3Dnorm = &testMe->norm3D;
|
||||
}
|
||||
}
|
||||
else {
|
||||
answer = (distOut <= 0);
|
||||
if (base3Dnorm) {
|
||||
*base3Dnorm = edge->v0;
|
||||
*head3Dnorm = &edge->norm3D;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while( ++edge < edges + numEdges );
|
||||
|
||||
*bestDist2 = bestDistance2;
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,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] );
|
||||
}
|
||||
Reference in New Issue
Block a user