// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // $Id: G4BREPSolidPolyhedra.cc,v 1.35 2008/01/22 16:04:58 tnikitin Exp $ // GEANT4 tag $Name: geant4-09-02-ref-02 $ // // ---------------------------------------------------------------------- // GEANT 4 class source file // // G4BREPSolidPolyhedra.cc // // ---------------------------------------------------------------------- // The polygonal solid G4BREPSolidPolyhedra is a shape defined by an inner // and outer polygonal surface and two planes perpendicular to the Z axis. // Each polygonal surface is created by linking a series of polygons created // at different planes perpendicular to the Z-axis. All these polygons all // have the same number of sides (sides) and are defined at the same Z planes // for both inner and outer polygonal surfaces. // ---------------------------------------------------------------------- // // History // ------- // // Bug-fix #266 by R.Chytracek: // - The situation when phi1 = 0 dphi1 = 2*pi and all RMINs = 0.0 is handled // now. In this case the inner planes are not created. The fix goes even // further this means it considers more than 2 z-planes and inner planes // are not created whenever two consecutive RMINs are = 0.0 . // // Corrections by S.Giani: // - Xaxis now corresponds to phi=0 // - partial angle = phiTotal / Nsides // - end planes exact boundary calculation for phiTotal < 2pi // (also including case with RMIN=RMAX) // - Xaxis now properly rotated to compute correct scope of vertixes // - corrected surface orientation for outer faces parallel to Z // - completed explicit setting of the orientation for all faces // - some comparison between doubles avoided by using tolerances // - visualisation parameters made consistent with the use made by // constructor of the input arguments (i.e. circumscribed radius). // ---------------------------------------------------------------------- #include "G4BREPSolidPolyhedra.hh" #include "G4FPlane.hh" #include G4BREPSolidPolyhedra::G4BREPSolidPolyhedra(const G4String& name, G4double start_angle, G4double opening_angle, G4int sides, G4int num_z_planes, G4double z_start, G4double z_values[], G4double RMIN[], G4double RMAX[] ) : G4BREPSolid(name) { G4int sections = num_z_planes - 1; if( opening_angle >= 2*pi-perMillion ) { nb_of_surfaces = 2*(sections * sides) + 2; } else { nb_of_surfaces = 2*(sections * sides) + 4; } //SurfaceVec = new G4Surface*[nb_of_surfaces]; G4int MaxNbOfSurfaces = nb_of_surfaces; G4Surface** MaxSurfaceVec = new G4Surface*[MaxNbOfSurfaces]; G4Vector3D Axis(0,0,1); G4Vector3D XAxis(1,0,0); G4Vector3D TmpAxis; G4Point3D Origin(0,0,z_start); G4Point3D LocalOrigin(0,0,z_start); G4double Length; G4int Count = 0 ; G4double PartAngle = (opening_angle)/sides; /////////////////////////////////////////////////// // Preconditions check // Detecting minimal required number of sides // if( sides < 3 ) { G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, "The solid must have at least 3 sides!" ); } // Detecting minimal required number of z-sections // if( num_z_planes < 2 ) { G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, "The solid must have at least 2 z-sections!" ); } // Detect invalid configurations at the ends of polyhedra which // would not lead to a valid solid creation and likely to a crash // if( z_values[0] == z_values[1] || z_values[sections-1] == z_values[sections] ) { G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, "The solid must have the first 2 and the last 2 z-values different!" ); } // Find out how the z-values sequence is ordered // G4bool increasing; if( z_values[0] < z_values[1] ) { increasing = true; } else { increasing = false; } // Detecting polyhedra teeth. // It's forbidden to specify unordered, e.g. non-increasing or // non-decreasing sequence of z-values. It may be provided by a // specific solid in a future. // for( G4int idx = 0; idx < sections; idx++ ) { if( ( z_values[idx] > z_values[idx+1] && increasing ) || ( z_values[idx] < z_values[idx+1] && !increasing ) ) { // ERROR! Invalid sequence of z-values // std::ostringstream msgstr; msgstr << G4endl << "ERROR: unordered, non-increasing or non-decreasing sequence" << G4endl << " of z_values detected!" << G4endl << " Check z_values with indexes: " << idx << " " << (idx+1) << "." << G4endl << std::ends; G4String message = msgstr.str(); G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, message ); } } /////////////////////////////////////////////////// #ifdef G4_EXPERIMENTAL_CODE // There is one problem when sequence of z values is not increasing in a // regular way, in other words, it's not purely increasing or decreasing // Irregular sequence can be provided in order to define a polyhedra having // teeth as shown on the picture bellow // In this sequence can happen the following: // z[a-1] > z[a] < z[a+1] && z[a+1] >= z[a-1] // One has to check the RMAX and RMIN values due to the possible // intersections. // // 1 2 3 // ___ ___ ____ // 00/ 00/ _ 000/ // 0/ 0/ |0 00| // V___ V__+0 00+-- // 0000 00000 00000 // ---- ----- ----- // ------------------------------------ z-axis // // // NOTE: This picture doesn't show all the possible configurations of // a polyhedra having teeth when looking at its profile. // The picture shows only one half of the polyhedra's profile ///////////////////////////////////////////////////////////////////////// // Experimental code! Not recommended for production, it's incomplete! // The task is to identify invalid combination of z, RMIN and RMAX values // in the case of toothydra :-) // G4int toothIdx; for( G4int idx = 1; idx < sections+1; idx++ ) { if( z_values[idx-1] > z_values[idx] ) { G4double toothdist = std::fabs( z_values[idx-1] - z_values[idx] ); G4double aftertoothdist = std::fabs( z_values[idx+1] - z_values[idx] ); if( toothdist > aftertoothdist ) { // Check for possible intersection // if( RMAX[idx-1] < RMAX[idx+1] || RMIN[idx-1] > RMIN[idx+1] ) { // ERROR! The surface conflict! // std::ostringstream msgstr; msgstr << G4endl << "ERROR: unordered sequence of z_values detected with" << G4endl << " conflicting RMAX or RMIN values!" << G4endl << " Check z_values with indexes: " << (idx-1) << " " << idx << " " << (idx+1) << "." << G4endl << std::ends; G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, msgstr.str() ); } } } } #endif // G4_EXPERIMENTAL_CODE /////////////////////////////////////////////////// for(G4int a=0;a changed // to explicit sense set-up by R. Chytracek, 12/02/2002 // We must check if a pair of two consecutive RMINs is not = 0.0, // this means no inner plane exists! // if( RMIN[a] != 0.0 ) { if( RMIN[a+1] != 0.0 ) { // Standard case // MaxSurfaceVec[Count] = CreateTrapezoidalSurface( RMIN[a], RMIN[a+1], LocalOrigin, Length, TmpAxis, PartAngle, EInverse ); } else { // The special case of r1 > r2 where we end at the // point (0,0,z[a+1]) // MaxSurfaceVec[Count] = CreateTriangularSurface( RMIN[a], RMIN[a+1], LocalOrigin, Length, TmpAxis, PartAngle, EInverse ); } } else if( RMIN[a+1] != 0.0 ) { // The special case of r1 < r2 where we start at the // point ( 0,0,z[a]) // MaxSurfaceVec[Count] = CreateTriangularSurface( RMIN[a], RMIN[a+1], LocalOrigin, Length, TmpAxis, PartAngle, EInverse ); } else { // Insert nothing into the vector of sufaces, we'll replicate // the vector anyway later // MaxSurfaceVec[Count] = 0; // We need to reduce the number of planes by 1, // one we have just skipped // nb_of_surfaces--; } if( MaxSurfaceVec[Count] != 0 ) { // Rotate axis back for the other surface point calculation // only in the case any of the Create* methods above have been // called because they modify the passed in TmpAxis // TmpAxis.rotateZ(-PartAngle); } Count++; // Create outer side if( RMAX[a] != 0.0 ) { if( RMAX[a+1] != 0.0 ) { // Standard case // MaxSurfaceVec[Count] = CreateTrapezoidalSurface( RMAX[a], RMAX[a+1], LocalOrigin, Length, TmpAxis, PartAngle, ENormal ); } else { // The special case of r1 > r2 where we end // at the point (0,0,z[a+1]) // MaxSurfaceVec[Count] = CreateTriangularSurface( RMAX[a], RMAX[a+1], LocalOrigin, Length, TmpAxis, PartAngle, ENormal ); } } else if( RMAX[a+1] != 0.0 ) { // The special case of r1 < r2 where we start // at the point ( 0,0,z[a]) // MaxSurfaceVec[Count] = CreateTriangularSurface( RMAX[a], RMAX[a+1], LocalOrigin, Length, TmpAxis, PartAngle, ENormal ); } else { // Two consecutive RMAX values can't be zero as // it's against the definition of BREP polyhedra // G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, "Two consecutive RMAX values cannot be zero!" ); } Count++; } // End of for loop over sides } else { // Create planar surfaces perpendicular to z-axis ESurfaceSense OuterSurfSense, InnerSurfSense; if( RMAX[a] != RMAX[a+1] && RMIN[a] != RMIN[a+1] ) { // We're about to create a planar surface perpendicular to z-axis // We can have the 8 following configurations here: // // 1. 2. 3. 4. // --+ +-- --+ +-- // xx|-> <-|xx xx| |xx // xx+-- --+xx --+ +-- // xxxxx xxxxx | | // xxxxx xxxxx +-- --+ // xx+-- --+xx |xx xx| // xx|-> <-|xx +-- --+ // --+ +-- // -------------------------- Z axis // ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // // 5. 6. 7. 8. // --+ +-- --+ +-- // xx|-> <-|xx xx|-> <-|xx // --+-- --+-- xx+-- --+xx // <-|xx xx|-> xxxxx xxxxx // +-- --+ --+xx xx+-- // <-|xx xx|-> // +-- --+ // -------------------------- Z axis // // NOTE: The pictures shows only one half of polyhedra! // The arrows show the expected surface normal direction. // The configuration No. 3 and 4 are not valid solids! // Eliminate the invalid cases 3 and 4. // At this point is guaranteed that each RMIN[i] < RMAX[i] // where i in in interval 0 < i < num_z_planes-1. So: // if( RMIN[a] > RMAX[a+1] || RMAX[a] < RMIN[a+1] ) { std::stringstream s; s << G4endl << "The values of RMIN[" << a << "] & RMAX[" << a+1 << "] or RMAX[" << a << "] & RMIN[" << a+1 << "] " << "generate an invalid configuration of solid: " << name.c_str() << "!" << G4endl << std::ends; G4String message = s.str(); G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "InvalidSetup", FatalException, message ); } // We need to clasify all the cases in order to figure out // the planar surface sense // if( RMAX[a] > RMAX[a+1] ) { // Cases 1, 5, 7 // if( RMIN[a] < RMIN[a+1] ) { // Case 1 OuterSurfSense = EInverse; InnerSurfSense = EInverse; } else if( RMAX[a+1] != RMIN[a]) { // Case 7 OuterSurfSense = EInverse; InnerSurfSense = ENormal; } else { // Case 5 OuterSurfSense = EInverse; InnerSurfSense = ENormal; } } else { // Cases 2, 6, 8 if( RMIN[a] > RMIN[a+1] ) { // Case 2 OuterSurfSense = ENormal; InnerSurfSense = ENormal; } else if( RMIN[a+1] != RMAX[a] ) { // Case 8 OuterSurfSense = ENormal; InnerSurfSense = EInverse; } else { // Case 6 OuterSurfSense = ENormal; InnerSurfSense = EInverse; } } TmpAxis= XAxis; TmpAxis.rotateZ(start_angle); // Compute the outer planar surface // MaxSurfaceVec[Count] = ComputePlanarSurface( RMAX[a], RMAX[a+1], LocalOrigin, TmpAxis, sides, PartAngle, OuterSurfSense ); if( MaxSurfaceVec[Count] == 0 ) { // No surface was created // nb_of_surfaces--; } Count++; TmpAxis= XAxis; TmpAxis.rotateZ(start_angle); // Compute the inner planar surface // MaxSurfaceVec[Count] = ComputePlanarSurface( RMIN[a], RMIN[a+1], LocalOrigin, TmpAxis, sides, PartAngle, InnerSurfSense ); if( MaxSurfaceVec[Count] == 0 ) { // No surface was created // nb_of_surfaces--; } Count++; // Since we can create here at maximum 2 surfaces // we need to reflect this in the total // nb_of_surfaces -= (2*(sides-1)); } else { // The case where only one of the radius values has changed // // RMAX RMIN // change change // // 1 2 3 4 // --+ +-- ----- ----- // 00|-> <-|00 00000 00000 // 00+-- --+00 --+00 00+-- // 00000 00000 <-|00 00|-> // +-- --+ // --------------------------- Z axis // // NOTE: The picture shows only one half of polyhedra! G4double R1, R2; ESurfaceSense SurfSense; // The case by case classification // if( RMAX[a] != RMAX[a+1] ) { // Cases 1, 2 // R1 = RMAX[a]; R2 = RMAX[a+1]; if( R1 > R2 ) { // Case 1 // SurfSense = EInverse; } else { // Case 2 // SurfSense = ENormal; } } else if(RMIN[a] != RMIN[a+1]) { // Cases 3, 4 // R1 = RMIN[a]; R2 = RMIN[a+1]; if( R1 > R2 ) { // Case 3 // SurfSense = ENormal; } else { // Case 4 // SurfSense = EInverse; } } else { G4cerr << "ERROR - G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()" << G4endl << " Error in construction." << G4endl << " Exactly the same z, rmin and rmax given for" << G4endl << " consecutive indices, " << a << " and " << a+1 << G4endl; G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "Notification", JustWarning, "Construction Error!" ); continue; } TmpAxis= XAxis; TmpAxis.rotateZ(start_angle); MaxSurfaceVec[Count] = ComputePlanarSurface( R1, R2, LocalOrigin, TmpAxis, sides, PartAngle, SurfSense ); if( MaxSurfaceVec[Count] == 0 ) { // No surface was created // nb_of_surfaces--; } Count++; // Since we can create here at maximum 1 surface // we need to reflect this in the total // nb_of_surfaces -= ((2*sides) - 1); } } // End of if( Length != 0.0 ) LocalOrigin = LocalOrigin + (Length*Axis); } // End of for loop over z sections if(opening_angle >= 2*pi-perMillion) { // Create the end planes for the configuration where delta phi >= 2*PI TmpAxis = XAxis; TmpAxis.rotateZ(start_angle); MaxSurfaceVec[Count] = ComputePlanarSurface( RMIN[0], RMAX[0], Origin, TmpAxis, sides, PartAngle, ENormal ); if( MaxSurfaceVec[Count] == 0 ) { // No surface was created // nb_of_surfaces--; } Count++; // Reset plane axis // TmpAxis = XAxis; TmpAxis.rotateZ(start_angle); MaxSurfaceVec[Count] = ComputePlanarSurface( RMIN[sections], RMAX[sections], LocalOrigin, TmpAxis, sides, PartAngle, EInverse ); if( MaxSurfaceVec[Count] == 0 ) { // No surface was created // nb_of_surfaces--; } Count++; } else { // If delta phi < 2*PI then create a single boundary // (case with RMIN=0 included) // Create the lateral planars // TmpAxis = XAxis; G4Vector3D TmpAxis2 = XAxis; TmpAxis.rotateZ(start_angle); TmpAxis2.rotateZ(start_angle); TmpAxis2.rotateZ(start_angle); LocalOrigin = Origin; G4int points = sections*2+2; G4int PointCount = 0; G4Point3DVector GapPointList(points); G4Point3DVector GapPointList2(points); for(G4int d=0;d= perMillion) { MaxSurfaceVec[Count] = new G4FPlane( &EndPointList, 0, EInverse ); } else { MaxSurfaceVec[Count] = 0; nb_of_surfaces--; } Count++; if(RMAX[sections]-RMIN[sections] >= perMillion) { MaxSurfaceVec[Count] = new G4FPlane( &EndPointList2, 0, ENormal ); } else { MaxSurfaceVec[Count] = 0; nb_of_surfaces--; } } // Now let's replicate the relevant surfaces into // G4BREPSolid's vector of surfaces // SurfaceVec = new G4Surface*[nb_of_surfaces]; G4int sf = 0; G4int zeroCount = 0; for( G4int srf = 0; srf < MaxNbOfSurfaces; srf++ ) { if( MaxSurfaceVec[srf] != 0 ) { if( sf < nb_of_surfaces ) { SurfaceVec[sf] = MaxSurfaceVec[srf]; } sf++; } else { zeroCount++; } } if( sf != nb_of_surfaces ) { G4cerr << "ERROR - G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()" << G4endl << " Bad number of surfaces!" << G4endl << " sf : " << sf << " nb_of_surfaces: " << nb_of_surfaces << " Count : " << Count << G4endl; G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "FatalError", FatalException, "INTERNAL ERROR: Going bananas!" ); } // Clean up the temporary vector of surfaces // delete [] MaxSurfaceVec; // Store the original parameters, to be used in visualisation // Note radii are not scaled because this BREP uses the radius of the // circumscribed circle and also graphics_reps/G4Polyhedron uses the // radius of the circumscribed circle. // Save contructor parameters // constructorParams.start_angle = start_angle; constructorParams.opening_angle = opening_angle; constructorParams.sides = sides; constructorParams.num_z_planes = num_z_planes; constructorParams.z_start = z_start; constructorParams.z_values = 0; constructorParams.RMIN = 0; constructorParams.RMAX = 0; if( num_z_planes > 0 ) { constructorParams.z_values = new G4double[num_z_planes]; constructorParams.RMIN = new G4double[num_z_planes]; constructorParams.RMAX = new G4double[num_z_planes]; for( G4int idx = 0; idx < num_z_planes; idx++ ) { constructorParams.z_values[idx] = z_values[idx]; constructorParams.RMIN[idx] = RMIN[idx]; constructorParams.RMAX[idx] = RMAX[idx]; } } // z_values[0] should be equal to z_start, for consistency // with what the constructor does. // Otherwise the z_values that are shifted by (z_values[0] - z_start) , // because z_values are only used in the form // length = z_values[d+1] - z_values[d]; // JA Apr 2, 97 if( z_values[0] != z_start ) { G4cerr << "ERROR - G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()" << G4endl << " Wrong solid parameters: " << " z_values[0]= " << z_values[0] << " is not equal to " << " z_start= " << z_start << "." << G4endl; G4Exception( "G4BREPSolidPolyhedra::G4BREPSolidPolyhedra()", "Notification", JustWarning, "Construction Error. z_values[0] must be equal to z_start!" ); constructorParams.z_values[0]= z_start; } active=1; Initialize(); } G4BREPSolidPolyhedra::G4BREPSolidPolyhedra( __void__& a ) : G4BREPSolid(a) { } G4BREPSolidPolyhedra::~G4BREPSolidPolyhedra() { if( constructorParams.num_z_planes > 0 ) { delete [] constructorParams.z_values; delete [] constructorParams.RMIN; delete [] constructorParams.RMAX; } } void G4BREPSolidPolyhedra::Initialize() { // Calc bounding box for solids and surfaces // Convert concave planes to convex // ShortestDistance=1000000; CheckSurfaceNormals(); if(!Box || !AxisBox) IsConvex(); CalcBBoxes(); } void G4BREPSolidPolyhedra::Reset() const { Active(1); ((G4BREPSolidPolyhedra*)this)->intersectionDistance=kInfinity; StartInside(0); for(register G4int a=0;aReset(); ShortestDistance = kInfinity; } EInside G4BREPSolidPolyhedra::Inside(register const G4ThreeVector& Pt) const { // This function find if the point Pt is inside, // outside or on the surface of the solid const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25; G4Vector3D v(1, 0, 0.01); G4Vector3D Pttmp(Pt); G4Vector3D Vtmp(v); G4Ray r(Pttmp, Vtmp); // Check if point is inside the Polyhedra 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 is deactivated. // TestSurfaceBBoxes(r); G4int hits=0, samehit=0; for(G4int a=0; a < nb_of_surfaces; a++) { G4Surface* surface = SurfaceVec[a]; if(surface->IsActive()) { // 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 found before // if( (surface->Intersect(r)) & 1 ) { // test if the point is on the surface // if(surface->GetDistance() < sqrHalfTolerance) { return kSurface; } // test if this intersection was found before // for(G4int i=0; iGetDistance() == SurfaceVec[i]->GetDistance()) { 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 // return ( (hits&1) ? kInside : kOutside ); } G4ThreeVector G4BREPSolidPolyhedra::SurfaceNormal(const G4ThreeVector& Pt) const { // This function calculates the normal of the closest surface // to the given point // Note : the sense of the normal depends on the sense of the surface G4int iplane; G4bool normflag = false; const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25; // Determine if the point is on the surface // G4double minDist = kInfinity; G4int normPlane = 0; for(iplane = 0; iplane < nb_of_surfaces; iplane++) { G4double dist = std::fabs(SurfaceVec[iplane]->HowNear(Pt)); if( minDist > dist ) { minDist = dist; normPlane = iplane; } if( dist < sqrHalfTolerance) { // the point is on this surface, so take this as the // the surface to consider for computing the normal // normflag = true; break; } } // Calculate the normal at this point, if the point is on the // surface, otherwise compute the normal to the closest surface // if ( normflag ) // point on surface { G4ThreeVector norm = SurfaceVec[iplane]->SurfaceNormal(Pt); return norm.unit(); } else // point not on surface { G4FPlane* nPlane = (G4FPlane*)(SurfaceVec[normPlane]); G4ThreeVector hitPt = nPlane->GetSrfPoint(); G4ThreeVector hitNorm = nPlane->SurfaceNormal(hitPt); return hitNorm.unit(); } } G4double G4BREPSolidPolyhedra::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]; G4int a; // Set the surfaces to active again // Reset(); // compute the shortest distance of the point to each surfaces // Be careful : 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( std::fabs(Dist) > std::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 std::fabs(Dist); } } G4double G4BREPSolidPolyhedra::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(); const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25; 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]->IsActive() ) { // test if the ray intersect the surface // if( SurfaceVec[a]->Intersect(r) ) { G4double surfDistance = SurfaceVec[a]->GetDistance(); // if more than 1 surface is intersected, // take the nearest one // if( surfDistance < ShortestDistance ) { if( surfDistance > sqrHalfTolerance ) { ShortestDistance = surfDistance; } else { // the point is within the boundary // ignore it if the direction is away from the boundary // G4Vector3D Norm = SurfaceVec[a]->SurfaceNormal(Pttmp); if( (Norm * Vtmp) < 0 ) { ShortestDistance = surfDistance; // ShortestDistance = surfDistance==0 // ? sqrHalfTolerance // : surfDistance; } } } } } } // Be careful ! // SurfaceVec->Distance is in fact the squared distance // if(ShortestDistance != kInfinity) { return std::sqrt(ShortestDistance); } else // no intersection { return kInfinity; } } G4double G4BREPSolidPolyhedra::DistanceToOut(register const G4ThreeVector& Pt, register const G4ThreeVector& V, const G4bool, G4bool *validNorm, G4ThreeVector * ) 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 (even number of // intersections greater than the tolerance). // // 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 G4int parity = 0; // Set the surfaces to active again // Reset(); const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25; G4Vector3D Ptv = Pt; G4int a; // I don`t understand this line // if(validNorm) *validNorm=false; 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; // this is actually the square of the distance for(a=0; a< nb_of_surfaces; a++) { G4double surfDistance = SurfaceVec[a]->GetDistance(); if(SurfaceVec[a]->IsActive()) { G4int intersects = SurfaceVec[a]->Intersect(r); // test if the ray intersects the surface // if( intersects != 0 ) { parity += 1; // if more than 1 surface is intersected, take the nearest one // if( surfDistance < ShortestDistance ) { if( surfDistance > sqrHalfTolerance ) { ShortestDistance = surfDistance; } else { // the point is within the boundary: ignore it // parity -= 1; } } } } } G4double distance = 0.; // Be careful ! // SurfaceVec->Distance is in fact the squared distance // // This condition was changed in order to give not zero answer // when particle is passing the border of two Touching Surfaces // and the distance to this surfaces is not zero. // parity is for the points on the boundary, // parity is counting only surfDistanceHowNear(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( std::fabs(Dist) > std::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 std::fabs(Dist); } } std::ostream& G4BREPSolidPolyhedra::StreamInfo(std::ostream& os) const { // Streams solid contents to output stream. G4BREPSolid::StreamInfo( os ) << "\n start_angle: " << constructorParams.start_angle << "\n opening_angle: " << constructorParams.opening_angle << "\n sides: " << constructorParams.sides << "\n num_z_planes: " << constructorParams.num_z_planes << "\n z_start: " << constructorParams.z_start << "\n z_values: "; G4int idx; for( idx = 0; idx < constructorParams.num_z_planes; idx++ ) { os << constructorParams.z_values[idx] << " "; } os << "\n RMIN: "; for( idx = 0; idx < constructorParams.num_z_planes; idx++ ) { os << constructorParams.RMIN[idx] << " "; } os << "\n RMAX: "; for( idx = 0; idx < constructorParams.num_z_planes; idx++ ) { os << constructorParams.RMAX[idx] << " "; } os << "\n-----------------------------------------------------------\n"; return os; } G4Surface* G4BREPSolidPolyhedra::CreateTrapezoidalSurface( G4double r1, G4double r2, const G4Point3D& origin, G4double distance, G4Vector3D& xAxis, G4double partAngle, ESurfaceSense sense ) { // The surface to be returned // G4Surface* trapsrf = 0; G4Point3DVector PointList(4); G4Vector3D zAxis(0,0,1); PointList[0] = origin + ( r1 * xAxis); PointList[3] = origin + ( distance * zAxis) + (r2 * xAxis); xAxis.rotateZ( partAngle ); PointList[2] = origin + ( distance * zAxis) + (r2 * xAxis); PointList[1] = origin + ( r1 * xAxis); // Return the planar trapezoidal surface // trapsrf = new G4FPlane( &PointList, 0, sense ); return trapsrf; } G4Surface* G4BREPSolidPolyhedra::CreateTriangularSurface( G4double r1, G4double r2, const G4Point3D& origin, G4double distance, G4Vector3D& xAxis, G4double partAngle, ESurfaceSense sense ) { // The surface to be returned // G4Surface* trapsrf = 0; G4Point3DVector PointList(3); G4Vector3D zAxis(0,0,1); PointList[0] = origin + ( r1 * xAxis); PointList[2] = origin + ( distance * zAxis) + (r2 * xAxis); xAxis.rotateZ( partAngle ); if( r1 < r2 ) { PointList[1] = origin + ( distance * zAxis) + (r2 * xAxis); } else { PointList[1] = origin + ( r1 * xAxis); } // Return the planar trapezoidal surface // trapsrf = new G4FPlane( &PointList, 0, sense ); return trapsrf; } G4Surface* G4BREPSolidPolyhedra::ComputePlanarSurface( G4double r1, G4double r2, const G4Point3D& origin, G4Vector3D& xAxis, G4int sides, G4double partAngle, ESurfaceSense sense ) { // This method can be called only when r1 != r2, // otherwise it returns 0 which means that no surface can be // created out of the given radius pair. // This method requires the xAxis to be pre-rotated properly. G4Point3DVector OuterPointList( sides ); G4Point3DVector InnerPointList( sides ); G4double rIn, rOut; G4Surface* planarSrf = 0; if( r1 < r2 ) { rIn = r1; rOut = r2; } else if( r1 > r2 ) { rIn = r2; rOut = r1; } else { // Invalid precondition, the radius values are r1 == r2, // which means we can create only polyline but no surface // return 0; } for( G4int pidx = 0; pidx < sides; pidx++ ) { // Outer polyline // OuterPointList[pidx] = origin + ( rOut * xAxis); // Inner polyline // InnerPointList[pidx] = origin + ( rIn * xAxis); xAxis.rotateZ( partAngle ); } if( rIn != 0.0 && rOut != 0.0 ) { // Standard case // planarSrf = new G4FPlane( &OuterPointList, &InnerPointList, sense ); } else if( rOut != 0.0 ) { // Special case where inner radius is zero so no polyline // is actually created // planarSrf = new G4FPlane( &OuterPointList, 0, sense ); } else { // No surface being created // This should not happen as filtered out by precondition check above } return planarSrf; } // In graphics_reps: #include "G4Polyhedron.hh" G4Polyhedron* G4BREPSolidPolyhedra::CreatePolyhedron() const { return new G4PolyhedronPgon( constructorParams.start_angle, constructorParams.opening_angle, constructorParams.sides, constructorParams.num_z_planes, constructorParams.z_values, constructorParams.RMIN, constructorParams.RMAX); }