Files
geant4/source/geometry/solids/BREPS/src/G4BREPSolid.cc
T
2016-06-08 15:34:16 +02:00

1508 lines
35 KiB
C++

// 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: G4BREPSolid.cc,v 1.10 1999/12/15 14:49:58 gunter Exp $
// GEANT4 tag $Name: geant4-01-01 $
#include "G4BREPSolid.hh"
#include "G4VoxelLimits.hh"
#include "G4AffineTransform.hh"
#include "G4VGraphicsScene.hh"
#include "G4Polyhedron.hh"
#include "G4NURBSbox.hh"
#include "G4BoundingBox3D.hh"
#include "G4FPlane.hh"
#include "G4BSplineSurface.hh"
#include "G4ToroidalSurface.hh"
#include "G4SphericalSurface.hh"
#include "instmgr.h"
G4Ray G4BREPSolid::Track;
G4double G4BREPSolid::ShortestDistance= kInfinity;
int G4BREPSolid::NumberOfSolids=0;
InstMgr G4BREPSolid::InstanceList;
G4BREPSolid::G4BREPSolid(const G4String name) : G4VSolid(name)
{
place=0;
Box=0;
Convex=0;
AxisBox=0;
PlaneSolid=0;
active=1;
intersectionDistance=kInfinity;
startInside=0;
solidname = name;
}
G4BREPSolid::G4BREPSolid( const G4String name ,
G4Surface** srfVec ,
G4int numberOfSrfs ) : G4VSolid(name)
{
place = 0;
Box = 0;
Convex = 0;
AxisBox = 0;
PlaneSolid = 0;
active = 1;
intersectionDistance = kInfinity;
startInside = 0;
nb_of_surfaces = numberOfSrfs;
SurfaceVec = srfVec;
Initialize();
}
G4BREPSolid::~G4BREPSolid()
{
if(place)
delete place;
delete bbox;
for(G4int a=0;a<nb_of_surfaces;a++)
delete SurfaceVec[a];
delete [] SurfaceVec;
}
void G4BREPSolid::Initialize()
{
if(active)
{
// Calc bounding box for solids and surfaces
// Convert concave planes to convex
ShortestDistance= kInfinity;
IsBox();
CheckSurfaceNormals();
if(!Box || !AxisBox)
IsConvex();
CalcBBoxes();
}
}
void G4BREPSolid::CheckSurfaceNormals()
{
if(!PlaneSolid)
return; // All faces must be planar
Convex=1;
// Checks that the normals of the surfaces point outwards.
// If not, turns the Normal to point out.
// Loop through each face and check the G4Vector3D of the Normal
G4Surface* srf;
G4Vector3D *Normal1, Normal2;
G4Point3D V;
G4int PointNum=0;
G4int SrfNum = 0;
G4double YValue=0;
G4Point3D Pt;
// const int Faces = all_surfaces.number_of_elements;
// const int Faces = surfaces.entries();
G4int a, b;
for(a=0; a<nb_of_surfaces; a++)
{
//Find vertex point containing extreme y value
// srf = all_surfaces.GetSurface(a);
// srf = surfaces(a);
srf = SurfaceVec[a];
int Points = srf->GetNumberOfPoints();
for(b =0; b<Points; b++)
{
Pt = (G4Point3D)srf->GetPoint(b);
if(YValue < Pt.y())
{
YValue = Pt.y();
PointNum = b;// Save point number
SrfNum = a; // Save srf number
}
}
}
// Move the selected face to the first in the List
// srf = all_surfaces.GetSurface(SrfNum);
// srf = surfaces(SrfNum);
srf = SurfaceVec[SrfNum];
// all_surfaces.MoveToFirst(srf);
// Start handling the surfaces in order and compare
// the neighbouring ones and turn their normals if they
// point inwards
G4Point3D Pt1;
G4Point3D Pt2;
G4Point3D Pt3;
G4Point3D Pt4;
int ConnectingPoints=0;
G4Vector3D N1;
G4Vector3D N2;
G4Vector3D N3;
G4Vector3D N4;
int* ConnectedList = new int[nb_of_surfaces];
for(a=0; a<nb_of_surfaces; a++)
ConnectedList[a]=0;
int Connections=0;
G4Surface* ConnectedSrf;
for(a=0; a<nb_of_surfaces-1; a++)
{
if(ConnectedList[a] == 0)
break;
else
ConnectedList[a]=1;
srf = SurfaceVec[a];
int SrfPoints = srf->GetNumberOfPoints();
N1 = (srf->Norm())->GetDir();
for(b=a+1; b<nb_of_surfaces; b++)
{
if(ConnectedList[b] == 1)
break;
else
ConnectedList[b]=1;
// Get next in List
// ConnectedSrf = all_surfaces.GetSurface(b);
// ConnectedSrf = surfaces(b);
ConnectedSrf = SurfaceVec[b];
// Check if it is connected to srf by looping through the
// points.
int ConnSrfPoints = ConnectedSrf->GetNumberOfPoints();
for(G4int c=0;c<SrfPoints;c++)
{
Pt1 = srf->GetPoint(c);
for(G4int d=0;d<ConnSrfPoints;d++)
{
// Find common points
Pt2 = (ConnectedSrf)->GetPoint(d);
if( Pt1 == Pt2 )
{
// Common point found.
// Compare normals
N2 = ((ConnectedSrf)->Norm())->GetDir();
// Check cross product.
G4Vector3D CP1 = N1.cross(N2);
G4double CrossProd1 = CP1.x()+CP1.y()+CP1.z();
// Create the other normals
if(c==0)
Pt3 = srf->GetPoint(c+1);
else
Pt3 = srf->GetPoint(0);
N3 = (Pt1-Pt3);
if(d==0)
Pt4 = (ConnectedSrf)->GetPoint(d+1);
else
Pt4 = (ConnectedSrf)->GetPoint(0);
N4 = (Pt1-Pt4);
G4Vector3D CP2 = N3.cross(N4);
G4double CrossProd2 = CP2.x()+CP2.y()+CP2.z();
G4cout << "\nCroosProd2: " << CrossProd2;
if( (CrossProd1 < 0 && CrossProd2 < 0) ||
(CrossProd1 > 0 && CrossProd2 > 0) )
{
// Turn Normal
(ConnectedSrf)->Norm()
->SetDir(-1 * (ConnectedSrf)->Norm()->GetDir());
// Take the CrossProd1 again as the other Normal was turned.
CP1 = N1.cross(N2);
CrossProd1 = CP1.x()+CP1.y()+CP1.z();
}
if(CrossProd1 > 0)
Convex=0;
}
}
}
}
}
delete []ConnectedList;
}
int G4BREPSolid::IsBox()
{
// This is Done by checking that the solid consist of 6 planes.
// Then the type is checked to be planar face for each face.
// For each G4Plane the Normal is computed. The dot product
// of one face Normal and each other face Normal is computed.
// One result should be 1 and the rest 0 in order to the solid
// to be a box.
Box=0;
G4Surface* srf1, *srf2;
register G4int a;
// Calc the Normal for the planes
for(a=0; a < nb_of_surfaces;a++)
{
srf1 = SurfaceVec[a];
if(srf1->MyType()==1)
(srf1)->Project(); // Calc the projection
else
{
PlaneSolid=0;
return 0;
}
}
// Check that all faces are planar
for(a=0; a < nb_of_surfaces;a++)
{
srf1 = SurfaceVec[a];
if (srf1->MyType()!=1)
return 0;
}
PlaneSolid = 1;
// Check that the amount of faces is correct
if(nb_of_surfaces!=6) return 0;
G4Point3D Pt;
G4int Points;
G4int Sides=0;
G4int Opposite=0;
srf1 = SurfaceVec[0];
Points = (srf1)->GetNumberOfPoints();
if(Points!=4)
return 0;
G4Vector3D Normal1 = (srf1->Norm())->GetDir();
G4double Result;
for(G4int b=1; b < nb_of_surfaces;b++)
{
srf2 = SurfaceVec[b];
G4Vector3D Normal2 = ((srf2)->Norm())->GetDir();
Result = fabs(Normal1 * Normal2);
if((Result != 0) && (Result != 1))
return 0;
else
{
if(!(int)Result)
Sides++;
else
if(((int)Result) == 1)
Opposite++;
}
}
if((Opposite != 1) && (Sides != nb_of_surfaces-2))
return 0;
G4Vector3D x_axis(1,0,0);
G4Vector3D y_axis(0,1,0);
if(((fabs(x_axis * Normal1) == 1) && (fabs(y_axis * Normal1) == 0)) ||
((fabs(x_axis * Normal1) == 0) && (fabs(y_axis * Normal1) == 1)) ||
((fabs(x_axis * Normal1) == 0) && (fabs(y_axis * Normal1) == 0)))
AxisBox=1;
else
Box=1;
return 1;
}
G4bool G4BREPSolid::IsConvex()
{
if(!PlaneSolid)
return 0; // All faces must be planar
// This is not robust. There can be concave solids
// where the concavity comes for example from
// three triangles.
// Additional checking 20.8. For each face the connecting faces are
// found and the cross product computed between the face and each
// connecting face. If the result changes value at any point the
// solid is concave.
G4Surface* Srf;
G4Surface* ConnectedSrf;
int Result;
Convex = 1;
G4int a, b, c, d;
for(a=0;a<nb_of_surfaces;a++)
{
Srf = SurfaceVec[a];
// Primary test. Test wether any one of the faces
// is concave -> solid is concave. This is not enough to
// distinguish all the cases of concavity.
Result = Srf->IsConvex();
if(Result != -1)
{
Convex = 0;
return 0;
}
}
Srf = SurfaceVec[0];
G4Point3D Pt1;
G4Point3D Pt2;
int ConnectingPoints=0;
G4Vector3D N1;
G4Vector3D N2;
// L. Broglia
// The number of connecting points can be
// (nb_of_surfaces-1) * nb_of_surfaces (loop a & loop b)
// HandledList is not used : why ?
// G4int* ConnectedList = new G4int[nb_of_surfaces];
G4int* ConnectedList = new G4int[(nb_of_surfaces-1) * nb_of_surfaces];
G4int* HandledList = new G4int[nb_of_surfaces];
for(a=0; a<nb_of_surfaces; a++)
{
HandledList[a]=0;
ConnectedList[a]=0;
}
HandledList[0]=1;
G4int Connections=0;
for(a=0; a<nb_of_surfaces-1; a++)
{
Srf = SurfaceVec[a];
G4int SrfPoints = Srf->GetNumberOfPoints();
Result=0;
for(b=0; b<nb_of_surfaces; b++)
{
if(b==a)
b++;
if(b==nb_of_surfaces)
break;
// Get next in List
ConnectedSrf = SurfaceVec[b];
// Check if it is connected to Srf by looping through the
// points.
G4int ConnSrfPoints = ConnectedSrf->GetNumberOfPoints();
for(c=0; c<SrfPoints; c++)
{
const G4Point3D& Pts1 =Srf->GetPoint(c);
for(d=0; d<ConnSrfPoints; d++)
{
// Find common points
const G4Point3D& Pts2 = ConnectedSrf->GetPoint(d);
if(Pts1 == Pts2)
ConnectingPoints++;
}
if(ConnectingPoints > 0)
break;
}
if( ConnectingPoints > 0 )
{
Connections++;
ConnectedList[Connections]=b;
}
ConnectingPoints=0;
}
}
// If connected, check for concavity.
// Get surfaces from ConnectedList and compare their normals
for(c=0; c<Connections; c++)
{
G4int Left=0;
G4int Right =0;
G4int tmp = ConnectedList[c];
Srf = SurfaceVec[tmp];
ConnectedSrf = SurfaceVec[tmp+1];
// Get normals.
N1 = Srf->Norm()->GetDir();
N2 = ConnectedSrf->Norm()->GetDir();
// Check cross product.
G4Vector3D CP = N1.cross(N2);
G4double CrossProd = CP.x()+CP.y()+CP.z();
if( CrossProd > 0 )
Left++;
if(CrossProd < 0)
Right++;
if(Left&&Right)
{
Convex = 0;
return 0;
}
Connections=0;
}
Convex=1;
// L. Broglia
// Problems with this delete when there are many solids to create
// delete []ConnectedList;
// delete []HandledList;
return 1;
}
G4bool G4BREPSolid::CalculateExtent(const EAxis pAxis,
const G4VoxelLimits& pVoxelLimit,
const G4AffineTransform& pTransform,
G4double& pMin, G4double& pMax) const
{
G4Point3D Min = bbox->GetBoxMin();
G4Point3D Max = bbox->GetBoxMax();
if (!pTransform.IsRotated())
{
// Special case handling for unrotated boxes
// Compute x/y/z mins and maxs respecting limits, with early returns
// if outside limits. Then switch() on pAxis
G4double xoffset,xMin,xMax;
G4double yoffset,yMin,yMax;
G4double zoffset,zMin,zMax;
xoffset=pTransform.NetTranslation().x();
xMin=xoffset+Min.x();
xMax=xoffset+Max.x();
if (pVoxelLimit.IsXLimited())
{
if (xMin>pVoxelLimit.GetMaxXExtent()
||xMax<pVoxelLimit.GetMinXExtent())
{
return false;
}
else
{
if (xMin<pVoxelLimit.GetMinXExtent())
{
xMin=pVoxelLimit.GetMinXExtent();
}
if (xMax>pVoxelLimit.GetMaxXExtent())
{
xMax=pVoxelLimit.GetMaxXExtent();
}
}
}
yoffset=pTransform.NetTranslation().y();
yMin=yoffset+Min.y();
yMax=yoffset+Max.y();
if (pVoxelLimit.IsYLimited())
{
if (yMin>pVoxelLimit.GetMaxYExtent()
||yMax<pVoxelLimit.GetMinYExtent())
{
return false;
}
else
{
if (yMin<pVoxelLimit.GetMinYExtent())
{
yMin=pVoxelLimit.GetMinYExtent();
}
if (yMax>pVoxelLimit.GetMaxYExtent())
{
yMax=pVoxelLimit.GetMaxYExtent();
}
}
}
zoffset=pTransform.NetTranslation().z();
zMin=zoffset+Min.z();
zMax=zoffset+Max.z();
if (pVoxelLimit.IsZLimited())
{
if (zMin>pVoxelLimit.GetMaxZExtent()
||zMax<pVoxelLimit.GetMinZExtent())
{
return false;
}
else
{
if (zMin<pVoxelLimit.GetMinZExtent())
{
zMin=pVoxelLimit.GetMinZExtent();
}
if (zMax>pVoxelLimit.GetMaxZExtent())
{
zMax=pVoxelLimit.GetMaxZExtent();
}
}
}
switch (pAxis)
{
case kXAxis:
pMin=xMin;
pMax=xMax;
break;
case kYAxis:
pMin=yMin;
pMax=yMax;
break;
case kZAxis:
pMin=zMin;
pMax=zMax;
break;
}
pMin-=kCarTolerance;
pMax+=kCarTolerance;
return true;
}
else
{
// General rotated case - create and clip mesh to boundaries
G4bool existsAfterClip=false;
G4ThreeVectorList *vertices;
pMin=+kInfinity;
pMax=-kInfinity;
// Calculate rotated vertex coordinates
vertices=CreateRotatedVertices(pTransform);
ClipCrossSection(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
ClipCrossSection(vertices,4,pVoxelLimit,pAxis,pMin,pMax);
ClipBetweenSections(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
if (pMin!=kInfinity||pMax!=-kInfinity)
{
existsAfterClip=true;
// Add 2*tolerance to avoid precision troubles
pMin-=kCarTolerance;
pMax+=kCarTolerance;
}
else
{
// Check for case where completely enveloping clipping volume
// If point inside then we are confident that the solid completely
// envelopes the clipping volume. Hence set min/max extents according
// to clipping volume extents along the specified axis.
G4ThreeVector clipCentre(
(pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
(pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
(pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5);
if (Inside(pTransform.Inverse().TransformPoint(clipCentre))!=kOutside)
{
existsAfterClip=true;
pMin=pVoxelLimit.GetMinExtent(pAxis);
pMax=pVoxelLimit.GetMaxExtent(pAxis);
}
}
delete vertices;
return existsAfterClip;
}
}
G4ThreeVectorList*
G4BREPSolid::CreateRotatedVertices(const G4AffineTransform& pTransform) const
{
G4Point3D Min = bbox->GetBoxMin();
G4Point3D Max = bbox->GetBoxMax();
G4ThreeVectorList *vertices;
vertices=new G4ThreeVectorList(8);
if (vertices)
{
G4ThreeVector vertex0(Min.x(),Min.y(),Min.z());
G4ThreeVector vertex1(Max.x(),Min.y(),Min.z());
G4ThreeVector vertex2(Max.x(),Max.y(),Min.z());
G4ThreeVector vertex3(Min.x(),Max.y(),Min.z());
G4ThreeVector vertex4(Min.x(),Min.y(),Max.z());
G4ThreeVector vertex5(Max.x(),Min.y(),Max.z());
G4ThreeVector vertex6(Max.x(),Max.y(),Max.z());
G4ThreeVector vertex7(Min.x(),Max.y(),Max.z());
vertices->insert(pTransform.TransformPoint(vertex0));
vertices->insert(pTransform.TransformPoint(vertex1));
vertices->insert(pTransform.TransformPoint(vertex2));
vertices->insert(pTransform.TransformPoint(vertex3));
vertices->insert(pTransform.TransformPoint(vertex4));
vertices->insert(pTransform.TransformPoint(vertex5));
vertices->insert(pTransform.TransformPoint(vertex6));
vertices->insert(pTransform.TransformPoint(vertex7));
}
else
{
G4Exception("G4BREPSolid::CreateRotatedVertices Out of memory - Cannot alloc vertices");
}
return vertices;
}
EInside G4BREPSolid::Inside(register const G4ThreeVector& Pt)const
{
// This function find if the point Pt is inside,
// outside or on the surface of the solid
G4double halfTolerance = kCarTolerance*0.5;
G4Vector3D v(1, 0, 0.01);
G4Vector3D Pttmp(Pt);
G4Vector3D Vtmp(v);
G4Ray r(Pttmp, Vtmp);
// Check if point is inside the PCone bounding box
if( !GetBBox()->Inside(Pttmp) )
return kOutside;
// Set the surfaces to active again
Reset();
// Test if the bounding box of each surface is intersected
// by the ray. If not, the surface become deactive.
TestSurfaceBBoxes(r);
G4int hits=0, samehit=0;
for(G4int a=0; a < nb_of_surfaces; a++)
{
if(SurfaceVec[a]->Active())
{
// count the number of intersections.
// if this number is odd, the start of the ray is
// inside the volume bounded by the surfaces, so
// increment the number of intersection by 1 if the
// point is not on the surface and if this intersection
// was not founded before
if( (SurfaceVec[a]->Intersect(r)) & 1 )
{
// test if the point is on the surface
if(SurfaceVec[a]->Distance() < kCarTolerance)
return kSurface;
// test if this intersection was founded before
for(G4int i=0; i<a; i++)
if(SurfaceVec[a]->Distance() == SurfaceVec[i]->Distance())
{
samehit++;
break;
}
// count the number of surfaces intersected by the ray
if(!samehit)
hits++;
}
}
}
// if the number of surfaces intersected is odd,
// the point is inside the solid
if(hits&1)
return kInside;
else
return kOutside;
}
G4ThreeVector G4BREPSolid::SurfaceNormal(const G4ThreeVector& Pt)const
{
// This function calculates the normal of the surface
// at a point on the surface
// Note : the sense of the normal depends on the sense of the surface
G4Vector3D n(0,0,0);
G4int iplane;
G4Vector3D norm;
G4Ray r( Pt, G4Vector3D(1, 0, 0) );
// Find on which surface the point is
for(iplane = 0; iplane < nb_of_surfaces; iplane++)
{
if(SurfaceVec[iplane]->HowNear(Pt) < kCarTolerance)
// the point is on this surface
break;
}
// calcul of the normal at this point
norm = SurfaceVec[iplane]->SurfaceNormal(Pt);
n = G4ThreeVector ( norm.x(), norm.y(), norm.z() );
n = n.unit();
return n;
}
G4double G4BREPSolid::DistanceToIn(const G4ThreeVector& Pt)const
{
// Calculates the shortest distance ("safety") from a point
// outside the solid to any boundary of this solid.
// Return 0 if the point is already inside.
G4double *dists = new G4double[nb_of_surfaces];
G4double halfTolerance = kCarTolerance*0.5;
G4int a;
// Set the surfaces to active again
Reset();
// calcul of the shortest distance of the point to each surfaces
// Be carreful : it's a signed value
for(a=0; a< nb_of_surfaces; a++)
dists[a] = SurfaceVec[a]->HowNear(Pt);
G4double Dist = kInfinity;
// if dists[] is positive, the point is outside
// so take the shortest of the shortest positive distances
// dists[] can be equal to 0 : point on a surface
// ( Problem with the G4FPlane : there is no inside and no outside...
// So, to test if the point is inside to return 0, utilize the Inside
// function. But I don`t know if it is really needed because dToIn is
// called only if the point is outside )
for(a = 0; a < nb_of_surfaces; a++)
if( fabs(Dist) > fabs(dists[a]) )
//if( dists[a] >= 0)
Dist = dists[a];
delete[] dists;
if(Dist == kInfinity)
// the point is inside the solid or on a surface
return 0;
else
//return Dist;
return fabs(Dist);
}
G4double G4BREPSolid::DistanceToIn(register const G4ThreeVector& Pt,
register const G4ThreeVector& V )const
{
// Calculates the distance from a point outside the solid
// to the solid`s boundary along a specified direction vector.
//
// Note : Intersections with boundaries less than the
// tolerance must be ignored if the direction
// is away from the boundary
G4int a;
// Set the surfaces to active again
Reset();
G4double halfTolerance = kCarTolerance*0.5;
G4Vector3D Pttmp(Pt);
G4Vector3D Vtmp(V);
G4Ray r(Pttmp, Vtmp);
// Test if the bounding box of each surface is intersected
// by the ray. If not, the surface become deactive.
TestSurfaceBBoxes(r);
ShortestDistance = kInfinity;
for(a=0; a< nb_of_surfaces; a++)
{
if(SurfaceVec[a]->Active())
{
// test if the ray intersect the surface
if( (SurfaceVec[a]->Intersect(r)) )
{
// if more than 1 surface is intersected,
// take the nearest one
if( SurfaceVec[a]->Distance() < ShortestDistance )
if( SurfaceVec[a]->Distance() > halfTolerance )
{
ShortestDistance = SurfaceVec[a]->Distance();
}
else
{
// the point is within the boundary
// ignored it if the direction is away from the boundary
G4Vector3D Norm = SurfaceVec[a]->SurfaceNormal(Pttmp);
if( (Norm * Vtmp) < 0 )
ShortestDistance = SurfaceVec[a]->Distance();
}
}
}
}
// Be carreful !
// SurfaceVec->Distance is in fact the squared distance
if(ShortestDistance != kInfinity)
return sqrt(ShortestDistance);
else
// no intersection, return kInfinity
return kInfinity;
}
G4double G4BREPSolid::DistanceToOut(register const G4ThreeVector& P,
register const G4ThreeVector& D,
const G4bool calcNorm,
G4bool *validNorm,
G4ThreeVector *n ) const
{
// Calculates the distance from a point inside the solid
// to the solid`s boundary along a specified direction vector.
// Return 0 if the point is already outside.
//
// Note : If the shortest distance to a boundary is less
// than the tolerance, it is ignored. This allows
// for a point within a tolerant boundary to leave
// immediately
// Set the surfaces to active again
Reset();
const G4double halfTolerance = kCarTolerance*0.5;
G4Vector3D Ptv = P;
G4int a;
// I don`t understand this line
if(validNorm)
*validNorm=false;
G4Vector3D Pttmp(Ptv);
G4Vector3D Vtmp(D);
G4Ray r(Pttmp, Vtmp);
// Test if the bounding box of each surface is intersected
// by the ray. If not, the surface become deactive.
TestSurfaceBBoxes(r);
ShortestDistance = kInfinity;
for(a=0; a< nb_of_surfaces; a++)
{
if(SurfaceVec[a]->Active())
{
// test if the ray intersect the surface
if( (SurfaceVec[a]->Intersect(r)) )
{
// if more than 1 surface is intersected,
// take the nearest one
if( SurfaceVec[a]->Distance() < ShortestDistance )
if( SurfaceVec[a]->Distance() > halfTolerance )
{
ShortestDistance = SurfaceVec[a]->Distance();
}
else
{
// the point is within the boundary: ignored it
}
}
}
}
// Be carreful !
// SurfaceVec->Distance is in fact the squared distance
if(ShortestDistance != kInfinity)
return sqrt(ShortestDistance);
else
// if no intersection is founded, the point is outside
// so return 0
return 0.0;
}
G4double G4BREPSolid::DistanceToOut(const G4ThreeVector& Pt)const
{
// Calculates the shortest distance ("safety") from a point
// inside the solid to any boundary of this solid.
// Return 0 if the point is already outside.
G4double *dists = new G4double[nb_of_surfaces];
G4double halfTolerance = kCarTolerance*0.5;
G4int a;
// Set the surfaces to active again
Reset();
// calcul of the shortest distance of the point to each surfaces
// Be carreful : it's a signed value
for(a=0; a< nb_of_surfaces; a++)
dists[a] = SurfaceVec[a]->HowNear(Pt);
G4double Dist = kInfinity;
// if dists[] is negative, the point is inside
// so take the shortest of the shortest negative distances
// dists[] can be equal to 0 : point on a surface
// ( Problem with the G4FPlane : there is no inside and no outside...
// So, to test if the point is outside to return 0, utilize the Inside
// function. But I don`t know if it is really needed because dToOut is
// called only if the point is inside )
for(a = 0; a < nb_of_surfaces; a++)
if( fabs(Dist) > fabs(dists[a]) )
//if( dists[a] <= 0)
Dist = dists[a];
delete[] dists;
if(Dist == kInfinity)
// the point is ouside the solid or on a surface
return 0;
else
// return Dist;
return fabs(Dist);
}
void G4BREPSolid::DescribeYourselfTo (G4VGraphicsScene& scene) const
{
scene.AddThis (*this);
}
G4VisExtent G4BREPSolid::GetExtent() const
{
G4Point3D Min = bbox->GetBoxMin();
G4Point3D Max = bbox->GetBoxMax();
return G4VisExtent (Min.x(), Max.x(), Min.y(), Max.y(), Min.z(), Max.z());
}
G4Polyhedron* G4BREPSolid::CreatePolyhedron () const
{
// temporary
G4Point3D Min = bbox->GetBoxMin();
G4Point3D Max = bbox->GetBoxMax();
return new G4PolyhedronBox (Max.x(), Max.y(), Max.z());
}
G4NURBS* G4BREPSolid::CreateNURBS () const
{
// temporary
G4Point3D Min = bbox->GetBoxMin();
G4Point3D Max = bbox->GetBoxMax();
return new G4NURBSbox (Max.x(), Max.y(), Max.z());
}
int G4BREPSolid::CreateSTEPData()
{
// create the solid entity
//STEPentity* ent = new STEPentity();
//stateEnum *sEnu = new stateEnum("newSE");
//MgrNode *mnode = new MgrNode(ent, sEnu);
// create the attributelist & attributes for this solid
// STEPattributeList *aList = new STEPattributeList();
//AttrDescriptor *aDesc = new AttrDescriptor();
// STEPattribute *name_attr = new STEPattribute(aDesc, name);
// create the mgrnode neede by the instance List
// append node to instance List
// call entoity creation routines for child entities.
return 0; // to shut up compilers
}
void G4BREPSolid::CalcBBoxes()
{
// First initialization
// Calculates the bounding boxes for the surfaces and
// for the solid.
G4Surface* srf;
register G4Point3D min, max;
if(active)
{
min = PINFINITY;
max = -PINFINITY;
for(G4int a = 0;a < nb_of_surfaces;a++)
{
// Get first in List
srf = SurfaceVec[a];
G4int convex=1;
G4int concavepoint=-1;
if (srf->MyType() == 1)
{
concavepoint = srf->IsConvex();
convex = srf->GetConvex();
}
// Make bbox for face
// if(convex && Concavepoint==-1)
{
srf->CalcBBox();
G4Point3D box_min = srf->bbox->GetBoxMin();
G4Point3D box_max = srf->bbox->GetBoxMax();
// Find max and min of face bboxes to make
// solids bbox.
// replace by Extend
// max < box_max;
if(max.x() < box_max.x()) max.setX(box_max.x());
if(max.y() < box_max.y()) max.setY(box_max.y());
if(max.z() < box_max.z()) max.setZ(box_max.z());
// min > box_min;
if(min.x() > box_min.x()) min.setX(box_min.x());
if(min.y() > box_min.y()) min.setY(box_min.y());
if(min.z() > box_min.z()) min.setZ(box_min.z());
}
}
bbox = new G4BoundingBox3D(min, max);
// G4cout << "\nBox " << min.X() << " " << min.Y() << " " << min.Z();
//G4cout << "\n--- " << max.X() << " " << max.Y() << " " << max.Z();
return;
}
G4cout << "\n No bbox calculated for solid. Error.";
}
void G4BREPSolid::RemoveHiddenFaces(register const G4Ray& rayref, int In) const
{
// Deactivates the planar faces that are
// on the "back" side of a solid.
// B-splines are not handled by this routine. Also cases
// where the ray starting point is Inside the bbox of the solid
// are ignored as we don't know if the starting point is Inside the actual
// solid except for axisoriented boxlike solids
register G4Surface* srf;
register const G4Vector3D& RayDir = rayref.GetDir();
register G4double Result;
G4int a;
// if(!AxisBox)
// In all other cases the ray starting point is outside the solid
if(!In)// In all other cases the ray starting point is outside the solid
for(a=0; a<nb_of_surfaces; a++)
{
// Deactivates the solids faces that are hidden
srf = SurfaceVec[a];
if(srf->MyType()==1)
{
const G4Vector3D& Normal = (srf->Norm())->GetDir();
Result = (RayDir * Normal);
if( Result >= 0 )
srf->Deactivate();
}
}
else
for(a=0; a<nb_of_surfaces; a++)
{
// Deactivates the AxisBox type solids faces whos normals
// point in the G4Vector3D opposite to the rays G4Vector3D
// i.e. are behind the ray starting point as in this case the
// ray starts from Inside the solid.
srf = SurfaceVec[a];
if(srf->MyType()==1)
{
const G4Vector3D& Normal = (srf->Norm())->GetDir();
Result = (RayDir * Normal);
if( Result < 0 )
srf->Deactivate();
}
}
}
void G4BREPSolid::TestSurfaceBBoxes(register const G4Ray& rayref) const
{
register G4Surface* srf;
G4int active_srfs = nb_of_surfaces;
// Do the bbox tests to all surfaces in List
// for planar faces the intersection is instead evaluated.
G4int intersection=0;
for(G4int a=0;a<nb_of_surfaces;a++)
{
// Get first in List
srf = SurfaceVec[a];
if(srf->Active())
{
// Get type
if(srf->MyType() != 1) // 1 == planar face
{
if(srf->bbox->Test(rayref))
srf->Distance(bbox->GetDistance());
else
{
// Test failed. Flag as inactive.
srf->Deactivate();
active_srfs--;
}
}
else
{
// Type was convex planar face
intersection = srf->Intersect(rayref);
if(!intersection)
active_srfs--;
}
}
else
active_srfs--;
}
if(!active_srfs) Active(0);
}
int G4BREPSolid::Intersect(register const G4Ray& rayref) const
{
// Gets the roughly calculated closest
// intersection point for a b_spline & accurate point for others
register G4Surface* srf;
G4double HitDistance = -1;
const G4Point3D& RayStart = rayref.GetStart();
const G4Point3D& RayDir = rayref.GetDir();
G4int result=1;
// Sort List of active surfaces according to
// bbox distances to ray starting point.
QuickSort(SurfaceVec, 0, nb_of_surfaces-1);
G4int Number=0;
// Start handling active surfaces in order
for(register G4int a=0;a<nb_of_surfaces;a++)
{
srf = SurfaceVec[a];
int included = 0;
if(srf->Active())
{
result = srf->Intersect(rayref);
if(result)
{
register G4Surface* tmp;
// Get the evaluated point on the surface
G4Point3D& closest_point = srf->closest_hit;
// Test for DistanceToIn(pt, vec)
// if d = 0 and vec.norm > 0, do not see the surface
if( !( (srf->Distance() < kCarTolerance/2) ||
(RayDir.dot(srf->SurfaceNormal(closest_point)) > 0) ) )
{
if(srf->MyType()==1)
HitDistance = srf->Distance();
else
{
// Check if the evaluated point is in front of the
// bbox of the next surface.
// Took sqrt away to gain speed. Squaredistances may
// as well be used.
HitDistance = RayStart.distance2(closest_point);
}
/*
included = 0;
if(tmp->MyType()==1)
{
if(HitDistance >= tmp->Distance())
included = 1;
}
else
{
G4double Dist = tmp->bbox->GetDistance();
Dist = Dist*Dist;
if(HitDistance >= Dist)
included = 1;
}
if(included) // Check also which other surfaces it is included in
{
if(Number+2<nb_of_surfaces)
for(G4int a=Number+2; a < nb_of_surfaces; a++)
{
tmp = SurfaceVec[a];
if(HitDistance < tmp->Distance())
tmp->Deactivate();
}
}
else
{
// Mark rest surfaces as inactive
for(register int c=a+1;c<nb_of_surfaces;c++)
{
tmp = SurfaceVec[c];
if(tmp->MyType()!=1)
tmp->Deactivate();
}
}
}*/
}
}
else
{
// No Hit.
included = 1;
srf->Deactivate();
}//if(result...
}//if(srf->act...
Number++;
} // while...
if(HitDistance < 0)
return 0;
QuickSort(SurfaceVec, 0, nb_of_surfaces-1);
if(!(SurfaceVec[0]->Active()))
return 0;
((G4BREPSolid*)this)->intersection_point = SurfaceVec[0]->closest_hit;
bbox->SetDistance(HitDistance);
return 1;
}
int G4BREPSolid::FinalEvaluation(register const G4Ray& rayref,
const int ToIn ) const
{
register G4Surface* srf;
G4double halfTolerance = 0.5*kCarTolerance;
G4double Dist=0;
G4int count=0;
((G4BREPSolid*)this)->intersectionDistance = kInfinity;
for(register G4int a=0;a<nb_of_surfaces;a++)
{
srf = SurfaceVec[a];
if(srf->Active())
{
const G4Point3D& srf_intersection = srf->Evaluation(rayref);
// Calc Hit point distance from ray starting point.
if(srf->MyType() != 1)
{
// took sqrt away to gain speed...
// Square distances are used instead
G4Point3D start = rayref.GetStart();
Dist = srf_intersection.distance2(start);
}
else
Dist = srf->Distance();
// Skip point wichare on the surface i.e. within
// tolerance of the surface
// Special handling for DistanceToIn & reflections
if(sqrt(Dist) < halfTolerance)
{
if(ToIn)
{
const G4Vector3D& Dir = rayref.GetDir();
const G4Point3D& Hit = srf->closest_hit;
const G4Vector3D& Norm = srf->SurfaceNormal(Hit);
if(( Dir * Norm ) >= 0)
{
Dist = kInfinity;
srf->Deactivate();
}
// else continue with the distance,
// even though < tolerance
}
else
{
Dist = kInfinity;
srf->Deactivate();
}
}
// If more than one surfaces are evaluated til the
// final stage, only the closest point is taken
if(Dist < intersectionDistance)
{
// Check that Hit is in the direction of the ray
// from the starting point
const G4Point3D& Pt = rayref.GetStart();
const G4Vector3D& Dir = rayref.GetDir();
G4Point3D TestPoint = (0.00001*Dir) + Pt;
G4double TestDistance = srf_intersection.distance2(TestPoint);
if(TestDistance > Dist)
{
// Hit behind ray starting point, no intersection.
Dist = kInfinity;
srf->Deactivate();
}
else
{
((G4BREPSolid*)this)->intersectionDistance = Dist;
((G4BREPSolid*)this)->intersection_point = srf_intersection;
}
// Check that the intersection is closer than the
// next surfaces approx point.
if(srf->Active())
{
if(a+1<nb_of_surfaces)
{
const G4Vector3D& Dir = rayref.GetDir();
const G4Point3D& Hit = srf->closest_hit;
const G4Vector3D& Norm = srf->SurfaceNormal(Hit);
// L. Broglia
//if(( Dir * Norm ) >= 0)
if(( Dir * Norm ) < 0)
{
Dist = kInfinity;
srf->Deactivate();
}
// else continue with the distance,
// even though < tolerance
// L. Broglia
// I think that this line has been forgeted
ShortestDistance = Dist;
}
else
{
ShortestDistance = Dist;
return 1;
}
}
}//if(Dist...
}//if srf->Active()
else
{
/* if(intersectionDistance < kInfinity)
return 1;
return 0;*/
}
}//for...
if(intersectionDistance < kInfinity)
return 1;
return 0;
}
G4Point3D G4BREPSolid::Scope()
{
G4Point3D scope;
G4Point3D Max = bbox->GetBoxMax();
G4Point3D Min = bbox->GetBoxMin();
scope.setX(fabs(Max.x()) - fabs(Min.x()));
scope.setY(fabs(Max.y()) - fabs(Min.y()));
scope.setZ(fabs(Max.z()) - fabs(Min.z()));
return scope;
}