G4PolyhedraSide.cc

Go to the documentation of this file.
00001 //
00002 // ********************************************************************
00003 // * License and Disclaimer                                           *
00004 // *                                                                  *
00005 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
00006 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
00007 // * conditions of the Geant4 Software License,  included in the file *
00008 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
00009 // * include a list of copyright holders.                             *
00010 // *                                                                  *
00011 // * Neither the authors of this software system, nor their employing *
00012 // * institutes,nor the agencies providing financial support for this *
00013 // * work  make  any representation or  warranty, express or implied, *
00014 // * regarding  this  software system or assume any liability for its *
00015 // * use.  Please see the license in the file  LICENSE  and URL above *
00016 // * for the full disclaimer and the limitation of liability.         *
00017 // *                                                                  *
00018 // * This  code  implementation is the result of  the  scientific and *
00019 // * technical work of the GEANT4 collaboration.                      *
00020 // * By using,  copying,  modifying or  distributing the software (or *
00021 // * any work based  on the software)  you  agree  to acknowledge its *
00022 // * use  in  resulting  scientific  publications,  and indicate your *
00023 // * acceptance of all terms of the Geant4 Software license.          *
00024 // ********************************************************************
00025 //
00026 //
00027 // $Id: G4PolyhedraSide.cc 67011 2013-01-29 16:17:41Z gcosmo $
00028 //
00029 // 
00030 // --------------------------------------------------------------------
00031 // GEANT 4 class source file
00032 //
00033 //
00034 // G4PolyhedraSide.cc
00035 //
00036 // Implementation of the face representing one segmented side of a Polyhedra
00037 //
00038 // --------------------------------------------------------------------
00039 
00040 #include "G4PolyhedraSide.hh"
00041 #include "G4PhysicalConstants.hh"
00042 #include "G4IntersectingCone.hh"
00043 #include "G4ClippablePolygon.hh"
00044 #include "G4AffineTransform.hh"
00045 #include "G4SolidExtentList.hh"
00046 #include "G4GeometryTolerance.hh"
00047 
00048 #include "Randomize.hh"
00049 
00050 //
00051 // Constructor
00052 //
00053 // Values for r1,z1 and r2,z2 should be specified in clockwise
00054 // order in (r,z).
00055 //
00056 G4PolyhedraSide::G4PolyhedraSide( const G4PolyhedraSideRZ *prevRZ,
00057                                   const G4PolyhedraSideRZ *tail,
00058                                   const G4PolyhedraSideRZ *head,
00059                                   const G4PolyhedraSideRZ *nextRZ,
00060                                         G4int theNumSide, 
00061                                         G4double thePhiStart, 
00062                                         G4double thePhiTotal, 
00063                                         G4bool thePhiIsOpen,
00064                                         G4bool isAllBehind )
00065 {
00066   kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
00067   fSurfaceArea=0.;
00068   fPhi.first = G4ThreeVector(0,0,0);
00069   fPhi.second= 0.0;
00070 
00071   //
00072   // Record values
00073   //
00074   r[0] = tail->r; z[0] = tail->z;
00075   r[1] = head->r; z[1] = head->z;
00076   
00077   G4double phiTotal;
00078   
00079   //
00080   // Set phi to our convention
00081   //
00082   startPhi = thePhiStart;
00083   while (startPhi < 0.0) startPhi += twopi;
00084   
00085   phiIsOpen = thePhiIsOpen;
00086   phiTotal = (phiIsOpen) ? thePhiTotal : twopi;
00087   
00088   allBehind = isAllBehind;
00089     
00090   //
00091   // Make our intersecting cone
00092   //
00093   cone = new G4IntersectingCone( r, z );
00094   
00095   //
00096   // Construct side plane vector set
00097   //
00098   numSide = theNumSide;
00099   deltaPhi = phiTotal/theNumSide;
00100   endPhi = startPhi+phiTotal;
00101   
00102   vecs = new G4PolyhedraSideVec[numSide];
00103   
00104   edges = new G4PolyhedraSideEdge[phiIsOpen ? numSide+1 : numSide];
00105   
00106   //
00107   // ...this is where we start
00108   //
00109   G4double phi = startPhi;
00110   G4ThreeVector a1( r[0]*std::cos(phi), r[0]*std::sin(phi), z[0] ),
00111           b1( r[1]*std::cos(phi), r[1]*std::sin(phi), z[1] ),
00112           c1( prevRZ->r*std::cos(phi), prevRZ->r*std::sin(phi), prevRZ->z ),
00113           d1( nextRZ->r*std::cos(phi), nextRZ->r*std::sin(phi), nextRZ->z ),
00114           a2, b2, c2, d2;
00115   G4PolyhedraSideEdge *edge = edges;
00116           
00117   G4PolyhedraSideVec *vec = vecs;
00118   do
00119   {
00120     //
00121     // ...this is where we are going
00122     //
00123     phi += deltaPhi;
00124     a2 = G4ThreeVector( r[0]*std::cos(phi), r[0]*std::sin(phi), z[0] );
00125     b2 = G4ThreeVector( r[1]*std::cos(phi), r[1]*std::sin(phi), z[1] );
00126     c2 = G4ThreeVector( prevRZ->r*std::cos(phi), prevRZ->r*std::sin(phi), prevRZ->z );
00127     d2 = G4ThreeVector( nextRZ->r*std::cos(phi), nextRZ->r*std::sin(phi), nextRZ->z );
00128     
00129     G4ThreeVector tt;  
00130     
00131     //
00132     // ...build some relevant vectors.
00133     //    the point is to sacrifice a little memory with precalcs 
00134     //    to gain speed
00135     //
00136     vec->center = 0.25*( a1 + a2 + b1 + b2 );
00137     
00138     tt = b2 + b1 - a2 - a1;
00139     vec->surfRZ = tt.unit();
00140     if (vec==vecs) lenRZ = 0.25*tt.mag();
00141     
00142     tt = b2 - b1 + a2 - a1;
00143     vec->surfPhi = tt.unit();
00144     if (vec==vecs)
00145     {
00146       lenPhi[0] = 0.25*tt.mag();
00147       tt = b2 - b1;
00148       lenPhi[1] = (0.5*tt.mag()-lenPhi[0])/lenRZ;
00149     }
00150     
00151     tt = vec->surfPhi.cross(vec->surfRZ);
00152     vec->normal = tt.unit();
00153     
00154     //
00155     // ...edge normals are the average of the normals of
00156     //    the two faces they connect.
00157     //
00158     // ...edge normals are necessary if we are to accurately
00159     //    decide if a point is "inside" a face. For non-convex
00160     //    shapes, it is absolutely necessary to know information
00161     //    on adjacent faces to accurate determine this.
00162     //
00163     // ...we don't need them for the phi edges, since that
00164     //    information is taken care of internally. The r/z edges,
00165     //    however, depend on the adjacent G4PolyhedraSide.
00166     //
00167     G4ThreeVector a12, adj;
00168     
00169     a12 = a2-a1;
00170 
00171     adj = 0.5*(c1+c2-a1-a2);
00172     adj = adj.cross(a12);  
00173     adj = adj.unit() + vec->normal;       
00174     vec->edgeNorm[0] = adj.unit();
00175     
00176     a12 = b1-b2;
00177     adj = 0.5*(d1+d2-b1-b2);
00178     adj = adj.cross(a12);  
00179     adj = adj.unit() + vec->normal;       
00180     vec->edgeNorm[1] = adj.unit();
00181     
00182     //
00183     // ...the corners are crucial. It is important that
00184     //    they are calculated consistently for adjacent
00185     //    G4PolyhedraSides, to avoid gaps caused by roundoff.
00186     //
00187     vec->edges[0] = edge;
00188     edge->corner[0] = a1;
00189     edge->corner[1] = b1;
00190     edge++;
00191     vec->edges[1] = edge;
00192 
00193     a1 = a2;
00194     b1 = b2;
00195     c1 = c2;
00196     d1 = d2;
00197   } while( ++vec < vecs+numSide );
00198   
00199   //
00200   // Clean up hanging edge
00201   //
00202   if (phiIsOpen)
00203   {
00204     edge->corner[0] = a2;
00205     edge->corner[1] = b2;
00206   }
00207   else
00208   {
00209     vecs[numSide-1].edges[1] = edges;
00210   }
00211   
00212   //
00213   // Go back and fill in remaining fields in edges
00214   //
00215   vec = vecs;
00216   G4PolyhedraSideVec *prev = vecs+numSide-1;
00217   do
00218   {
00219     edge = vec->edges[0];    // The edge between prev and vec
00220     
00221     //
00222     // Okay: edge normal is average of normals of adjacent faces
00223     //
00224     G4ThreeVector eNorm = vec->normal + prev->normal;
00225     edge->normal = eNorm.unit();  
00226     
00227     //
00228     // Vertex normal is average of norms of adjacent surfaces (all four)
00229     // However, vec->edgeNorm is unit vector in some direction
00230     // as the sum of normals of adjacent PolyhedraSide with vec.
00231     // The normalization used for this vector should be the same
00232     // for vec and prev.
00233     //
00234     eNorm = vec->edgeNorm[0] + prev->edgeNorm[0];
00235     edge->cornNorm[0] = eNorm.unit();
00236   
00237     eNorm = vec->edgeNorm[1] + prev->edgeNorm[1];
00238     edge->cornNorm[1] = eNorm.unit();
00239   } while( prev=vec, ++vec < vecs + numSide );
00240   
00241   if (phiIsOpen)
00242   {
00243     // G4double rFact = std::cos(0.5*deltaPhi);
00244     //
00245     // If phi is open, we need to patch up normals of the
00246     // first and last edges and their corresponding
00247     // vertices.
00248     //
00249     // We use vectors that are in the plane of the
00250     // face. This should be safe.
00251     //
00252     vec = vecs;
00253     
00254     G4ThreeVector normvec = vec->edges[0]->corner[0]
00255                           - vec->edges[0]->corner[1];
00256     normvec = normvec.cross(vec->normal);
00257     if (normvec.dot(vec->surfPhi) > 0) normvec = -normvec;
00258 
00259     vec->edges[0]->normal = normvec.unit();
00260     
00261     vec->edges[0]->cornNorm[0] = (vec->edges[0]->corner[0]
00262                                 - vec->center).unit();
00263     vec->edges[0]->cornNorm[1] = (vec->edges[0]->corner[1]
00264                                 - vec->center).unit();
00265     
00266     //
00267     // Repeat for ending phi
00268     //
00269     vec = vecs + numSide - 1;
00270     
00271     normvec = vec->edges[1]->corner[0] - vec->edges[1]->corner[1];
00272     normvec = normvec.cross(vec->normal);
00273     if (normvec.dot(vec->surfPhi) < 0) normvec = -normvec;
00274 
00275     vec->edges[1]->normal = normvec.unit();
00276     
00277     vec->edges[1]->cornNorm[0] = (vec->edges[1]->corner[0]
00278                                 - vec->center).unit();
00279     vec->edges[1]->cornNorm[1] = (vec->edges[1]->corner[1]
00280                                 - vec->center).unit();
00281   }
00282   
00283   //
00284   // edgeNorm is the factor one multiplies the distance along vector phi
00285   // on the surface of one of our sides in order to calculate the distance
00286   // from the edge. (see routine DistanceAway)
00287   //
00288   edgeNorm = 1.0/std::sqrt( 1.0 + lenPhi[1]*lenPhi[1] );
00289 }
00290 
00291 
00292 //
00293 // Fake default constructor - sets only member data and allocates memory
00294 //                            for usage restricted to object persistency.
00295 //
00296 G4PolyhedraSide::G4PolyhedraSide( __void__&)
00297   : numSide(0), startPhi(0.), deltaPhi(0.), endPhi(0.),
00298     phiIsOpen(false), allBehind(false), cone(0), vecs(0), edges(0),
00299     lenRZ(0.), edgeNorm(0.), kCarTolerance(0.), fSurfaceArea(0.)
00300 {
00301   r[0] = r[1] = 0.;
00302   z[0] = z[1] = 0.;
00303   lenPhi[0] = lenPhi[1] = 0.;
00304 }
00305 
00306 
00307 //
00308 // Destructor
00309 //  
00310 G4PolyhedraSide::~G4PolyhedraSide()
00311 {
00312   delete cone;
00313   delete [] vecs;
00314   delete [] edges;
00315 }
00316 
00317 
00318 //
00319 // Copy constructor
00320 //
00321 G4PolyhedraSide::G4PolyhedraSide( const G4PolyhedraSide &source )
00322   : G4VCSGface()
00323 {
00324   CopyStuff( source );
00325 }
00326 
00327 
00328 //
00329 // Assignment operator
00330 //
00331 G4PolyhedraSide& G4PolyhedraSide::operator=( const G4PolyhedraSide &source )
00332 {
00333   if (this == &source) return *this;
00334   
00335   delete cone;
00336   delete [] vecs;
00337   delete [] edges;
00338   
00339   CopyStuff( source );
00340 
00341   return *this;
00342 }
00343 
00344 
00345 //
00346 // CopyStuff
00347 //
00348 void G4PolyhedraSide::CopyStuff( const G4PolyhedraSide &source )
00349 {
00350   //
00351   // The simple stuff
00352   //
00353   numSide    = source.numSide;
00354   r[0]    = source.r[0];
00355   r[1]    = source.r[1];
00356   z[0]    = source.z[0];
00357   z[1]    = source.z[1];
00358   startPhi  = source.startPhi;
00359   deltaPhi  = source.deltaPhi;
00360   endPhi    = source.endPhi;
00361   phiIsOpen = source.phiIsOpen;
00362   allBehind = source.allBehind;
00363   
00364   lenRZ     = source.lenRZ;
00365   lenPhi[0] = source.lenPhi[0];
00366   lenPhi[1] = source.lenPhi[1];
00367   edgeNorm  = source.edgeNorm;
00368 
00369   kCarTolerance = source.kCarTolerance;
00370   fSurfaceArea = source.fSurfaceArea;
00371 
00372   cone = new G4IntersectingCone( *source.cone );
00373 
00374   //
00375   // Duplicate edges
00376   //
00377   G4int  numEdges = phiIsOpen ? numSide+1 : numSide;
00378   edges = new G4PolyhedraSideEdge[numEdges];
00379   
00380   G4PolyhedraSideEdge *edge = edges,
00381           *sourceEdge = source.edges;
00382   do
00383   {
00384     *edge = *sourceEdge;
00385   } while( ++sourceEdge, ++edge < edges + numEdges);
00386 
00387   //
00388   // Duplicate vecs
00389   //
00390   vecs = new G4PolyhedraSideVec[numSide];
00391   
00392   G4PolyhedraSideVec *vec = vecs,
00393          *sourceVec = source.vecs;
00394   do
00395   {
00396     *vec = *sourceVec;
00397     vec->edges[0] = edges + (sourceVec->edges[0] - source.edges);
00398     vec->edges[1] = edges + (sourceVec->edges[1] - source.edges);
00399   } while( ++sourceVec, ++vec < vecs + numSide );
00400 }
00401   
00402 
00403 //
00404 // Intersect
00405 //
00406 // Decide if a line intersects the face.
00407 //
00408 // Arguments:
00409 //  p    = (in) starting point of line segment
00410 //  v    = (in) direction of line segment (assumed a unit vector)
00411 //  A, B    = (in) 2d transform variables (see note top of file)
00412 //  normSign  = (in) desired sign for dot product with normal (see below)
00413 //  surfTolerance  = (in) minimum distance from the surface
00414 //  vecs    = (in) Vector set array
00415 //  distance  = (out) distance to surface furfilling all requirements
00416 //  distFromSurface = (out) distance from the surface
00417 //  thisNormal  = (out) normal vector of the intersecting surface
00418 //
00419 // Return value:
00420 //  true if an intersection is found. Otherwise, output parameters are
00421 //  undefined.
00422 //
00423 // Notes:
00424 // * normSign: if we are "inside" the shape and only want to find out how far
00425 //   to leave the shape, we only want to consider intersections with surfaces in
00426 //   which the trajectory is leaving the shape. Since the normal vectors to the
00427 //   surface always point outwards from the inside, this means we want the dot
00428 //   product of the trajectory direction v and the normal of the side normals[i]
00429 //   to be positive. Thus, we should specify normSign as +1.0. Otherwise, if
00430 //   we are outside and want to go in, normSign should be set to -1.0.
00431 //   Don't set normSign to zero, or you will get no intersections!
00432 //
00433 // * surfTolerance: see notes on argument "surfTolerance" in routine
00434 //   "IntersectSidePlane".
00435 //   ----HOWEVER---- We should *not* apply this surface tolerance if the
00436 //   starting point is not within phi or z of the surface. Specifically,
00437 //   if the starting point p angle in x/y places it on a separate side from the
00438 //   intersection or if the starting point p is outside the z bounds of the
00439 //   segment, surfTolerance must be ignored or we should *always* accept the
00440 //   intersection! 
00441 //   This is simply because the sides do not have infinite extent.
00442 //      
00443 //
00444 G4bool G4PolyhedraSide::Intersect( const G4ThreeVector &p,
00445                                    const G4ThreeVector &v,  
00446                                          G4bool outgoing,
00447                                          G4double surfTolerance,
00448                                          G4double &distance,
00449                                          G4double &distFromSurface,
00450                                          G4ThreeVector &normal,
00451                                          G4bool &isAllBehind )
00452 {
00453   G4double normSign = outgoing ? +1 : -1;
00454   
00455   //
00456   // ------------------TO BE IMPLEMENTED---------------------
00457   // Testing the intersection of individual phi faces is
00458   // pretty straight forward. The simple thing therefore is to
00459   // form a loop and check them all in sequence.
00460   //
00461   // But, I worry about one day someone making
00462   // a polygon with a thousands sides. A linear search
00463   // would not be ideal in such a case.
00464   //
00465   // So, it would be nice to be able to quickly decide
00466   // which face would be intersected. One can make a very
00467   // good guess by using the intersection with a cone.
00468   // However, this is only reliable in 99% of the cases.
00469   //
00470   // My solution: make a decent guess as to the one or
00471   // two potential faces might get intersected, and then
00472   // test them. If we have the wrong face, use the test
00473   // to make a better guess.
00474   //
00475   // Since we might have two guesses, form a queue of
00476   // potential intersecting faces. Keep an array of 
00477   // already tested faces to avoid doing one more than
00478   // once.
00479   //
00480   // Result: at worst, an iterative search. On average,
00481   // a little more than two tests would be required.
00482   //
00483   G4ThreeVector q = p + v;
00484   
00485   G4int face = 0;
00486   G4PolyhedraSideVec *vec = vecs;
00487   do
00488   {
00489     //
00490     // Correct normal?
00491     //
00492     G4double dotProd = normSign*v.dot(vec->normal);
00493     if (dotProd <= 0) continue;
00494   
00495     //
00496     // Is this face in front of the point along the trajectory?
00497     //
00498     G4ThreeVector delta = p - vec->center;
00499     distFromSurface = -normSign*delta.dot(vec->normal);
00500     
00501     if (distFromSurface < -surfTolerance) continue;
00502     
00503     //
00504     //                            phi
00505     //      c -------- d           ^
00506     //      |          |           |
00507     //      a -------- b           +---> r/z
00508     //
00509     //
00510     // Do we remain on this particular segment?
00511     //
00512     G4ThreeVector qc = q - vec->edges[1]->corner[0];
00513     G4ThreeVector qd = q - vec->edges[1]->corner[1];
00514     
00515     if (normSign*qc.cross(qd).dot(v) < 0) continue;
00516     
00517     G4ThreeVector qa = q - vec->edges[0]->corner[0];
00518     G4ThreeVector qb = q - vec->edges[0]->corner[1];
00519     
00520     if (normSign*qa.cross(qb).dot(v) > 0) continue;
00521     
00522     //
00523     // We found the one and only segment we might be intersecting.
00524     // Do we remain within r/z bounds?
00525     //
00526     
00527     if (r[0] > 1/kInfinity && normSign*qa.cross(qc).dot(v) < 0) return false;
00528     if (r[1] > 1/kInfinity && normSign*qb.cross(qd).dot(v) > 0) return false;
00529     
00530     //
00531     // We allow the face to be slightly behind the trajectory
00532     // (surface tolerance) only if the point p is within
00533     // the vicinity of the face
00534     //
00535     if (distFromSurface < 0)
00536     {
00537       G4ThreeVector ps = p - vec->center; 
00538       
00539       G4double rz = ps.dot(vec->surfRZ);
00540       if (std::fabs(rz) > lenRZ+surfTolerance) return false; 
00541 
00542       G4double pp = ps.dot(vec->surfPhi);
00543       if (std::fabs(pp) > lenPhi[0] + lenPhi[1]*rz + surfTolerance) return false;
00544     }
00545       
00546 
00547     //
00548     // Intersection found. Return answer.
00549     //
00550     distance = distFromSurface/dotProd;
00551     normal = vec->normal;
00552     isAllBehind = allBehind;
00553     return true;
00554   } while( ++vec, ++face < numSide );
00555 
00556   //
00557   // Oh well. Better luck next time.
00558   //
00559   return false;
00560 }
00561 
00562 
00563 G4double G4PolyhedraSide::Distance( const G4ThreeVector &p, G4bool outgoing )
00564 {
00565   G4double normSign = outgoing ? -1 : +1;
00566   
00567   //
00568   // Try the closest phi segment first
00569   //
00570   G4int iPhi = ClosestPhiSegment( GetPhi(p) );
00571   
00572   G4ThreeVector pdotc = p - vecs[iPhi].center;
00573   G4double normDist = pdotc.dot(vecs[iPhi].normal);
00574   
00575   if (normSign*normDist > -0.5*kCarTolerance)
00576   {
00577     return DistanceAway( p, vecs[iPhi], &normDist );
00578   }
00579 
00580   //
00581   // Now we have an interesting problem... do we try to find the
00582   // closest facing side??
00583   //
00584   // Considered carefully, the answer is no. We know that if we
00585   // are asking for the distance out, we are supposed to be inside,
00586   // and vice versa.
00587   //
00588   
00589   return kInfinity;
00590 }
00591 
00592 
00593 //
00594 // Inside
00595 //
00596 EInside G4PolyhedraSide::Inside( const G4ThreeVector &p,
00597                                        G4double tolerance, 
00598                                        G4double *bestDistance )
00599 {
00600   //
00601   // Which phi segment is closest to this point?
00602   //
00603   G4int iPhi = ClosestPhiSegment( GetPhi(p) );
00604   
00605   G4double norm;
00606   
00607   //
00608   // Get distance to this segment
00609   //
00610   *bestDistance = DistanceToOneSide( p, vecs[iPhi], &norm );
00611   
00612   //
00613   // Use distance along normal to decide return value
00614   //
00615   if ( (std::fabs(norm) < tolerance) && (*bestDistance < 2.0*tolerance) )
00616     return kSurface;
00617   else if (norm < 0)
00618     return kInside;
00619   else  
00620     return kOutside;
00621 }
00622 
00623 
00624 //
00625 // Normal
00626 //
00627 G4ThreeVector G4PolyhedraSide::Normal( const G4ThreeVector &p,
00628                                              G4double *bestDistance )
00629 {
00630   //
00631   // Which phi segment is closest to this point?
00632   //
00633   G4int iPhi = ClosestPhiSegment( GetPhi(p) );
00634 
00635   //
00636   // Get distance to this segment
00637   //
00638   G4double norm;
00639   *bestDistance = DistanceToOneSide( p, vecs[iPhi], &norm );
00640 
00641   return vecs[iPhi].normal;
00642 }
00643 
00644 
00645 //
00646 // Extent
00647 //
00648 G4double G4PolyhedraSide::Extent( const G4ThreeVector axis )
00649 {
00650   if (axis.perp2() < DBL_MIN)
00651   {
00652     //
00653     // Special case
00654     //
00655     return axis.z() < 0 ? -cone->ZLo() : cone->ZHi();
00656   }
00657 
00658   G4int iPhi, i1, i2;
00659   G4double best;
00660   G4ThreeVector *list[4];
00661   
00662   //
00663   // Which phi segment, if any, does the axis belong to
00664   //
00665   iPhi = PhiSegment( GetPhi(axis) );
00666   
00667   if (iPhi < 0)
00668   {
00669     //
00670     // No phi segment? Check front edge of first side and
00671     // last edge of second side
00672     //
00673     i1 = 0; i2 = numSide-1;
00674   }
00675   else
00676   {
00677     //
00678     // Check all corners of matching phi side
00679     //
00680     i1 = iPhi; i2 = iPhi;
00681   }
00682   
00683   list[0] = vecs[i1].edges[0]->corner;
00684   list[1] = vecs[i1].edges[0]->corner+1;
00685   list[2] = vecs[i2].edges[1]->corner;
00686   list[3] = vecs[i2].edges[1]->corner+1;
00687         
00688   //
00689   // Who's biggest?
00690   //
00691   best = -kInfinity;
00692   G4ThreeVector **vec = list;
00693   do
00694   {
00695     G4double answer = (*vec)->dot(axis);
00696     if (answer > best) best = answer;
00697   } while( ++vec < list+4 );
00698   
00699   return best;
00700 }
00701 
00702 
00703 //
00704 // CalculateExtent
00705 //
00706 // See notes in G4VCSGface
00707 //
00708 void G4PolyhedraSide::CalculateExtent( const EAxis axis, 
00709                                        const G4VoxelLimits &voxelLimit,
00710                                        const G4AffineTransform &transform,
00711                                              G4SolidExtentList &extentList )
00712 {
00713   //
00714   // Loop over all sides
00715   //
00716   G4PolyhedraSideVec *vec = vecs;
00717   do
00718   {
00719     //
00720     // Fill our polygon with the four corners of
00721     // this side, after the specified transformation
00722     //
00723     G4ClippablePolygon polygon;
00724     
00725     polygon.AddVertexInOrder(transform.
00726                              TransformPoint(vec->edges[0]->corner[0]));
00727     polygon.AddVertexInOrder(transform.
00728                              TransformPoint(vec->edges[0]->corner[1]));
00729     polygon.AddVertexInOrder(transform.
00730                              TransformPoint(vec->edges[1]->corner[1]));
00731     polygon.AddVertexInOrder(transform.
00732                              TransformPoint(vec->edges[1]->corner[0]));
00733     
00734     //
00735     // Get extent
00736     //  
00737     if (polygon.PartialClip( voxelLimit, axis ))
00738     {
00739       //
00740       // Get dot product of normal along target axis
00741       //
00742       polygon.SetNormal( transform.TransformAxis(vec->normal) );
00743 
00744       extentList.AddSurface( polygon );
00745     }
00746   } while( ++vec < vecs+numSide );
00747   
00748   return;
00749 }
00750 
00751 
00752 //
00753 // IntersectSidePlane
00754 //
00755 // Decide if a line correctly intersects one side plane of our segment.
00756 // It is assumed that the correct side has been chosen, and thus only 
00757 // the z bounds (of the entire segment) are checked.
00758 //
00759 // normSign - To be multiplied against normal:
00760 //            = +1.0 normal is unchanged
00761 //            = -1.0 normal is reversed (now points inward)
00762 //
00763 // Arguments:
00764 //  p    - (in) Point
00765 //  v    - (in) Direction
00766 //  vec    - (in) Description record of the side plane
00767 //  normSign  - (in) Sign (+/- 1) to apply to normal
00768 //  surfTolerance  - (in) Surface tolerance (generally > 0, see below)
00769 //  distance  - (out) Distance along v to intersection
00770 //  distFromSurface - (out) Distance from surface normal
00771 //
00772 // Notes:
00773 //   surfTolerance  - Used to decide if a point is behind the surface,
00774 //        a point is allow to be -surfTolerance behind the
00775 //        surface (as measured along the normal), but *only*
00776 //        if the point is within the r/z bounds + surfTolerance
00777 //        of the segment.
00778 //
00779 G4bool G4PolyhedraSide::IntersectSidePlane( const G4ThreeVector &p,
00780                                             const G4ThreeVector &v,
00781                                             const G4PolyhedraSideVec& vec,
00782                                                   G4double normSign, 
00783                                                   G4double surfTolerance,
00784                                                   G4double &distance,
00785                                                   G4double &distFromSurface )
00786 {
00787   //
00788   // Correct normal? Here we have straight sides, and can safely ignore
00789   // intersections where the dot product with the normal is zero.
00790   //
00791   G4double dotProd = normSign*v.dot(vec.normal);
00792   
00793   if (dotProd <= 0) return false;
00794   
00795   //
00796   // Calculate distance to surface. If the side is too far
00797   // behind the point, we must reject it.
00798   //
00799   G4ThreeVector delta = p - vec.center;
00800   distFromSurface = -normSign*delta.dot(vec.normal);
00801     
00802   if (distFromSurface < -surfTolerance) return false;
00803 
00804   //
00805   // Calculate precise distance to intersection with the side
00806   // (along the trajectory, not normal to the surface)
00807   //
00808   distance = distFromSurface/dotProd;
00809   
00810   //
00811   // Do we fall off the r/z extent of the segment?
00812   //
00813   // Calculate this very, very carefully! Why?
00814   //         1. If a RZ end is at R=0, you can't miss!
00815   //         2. If you just fall off in RZ, the answer must
00816   //            be consistent with adjacent G4PolyhedraSide faces.
00817   // (2) implies that only variables used by other G4PolyhedraSide
00818   // faces may be used, which includes only: p, v, and the edge corners.
00819   // It also means that one side is a ">" or "<", which the other
00820   // must be ">=" or "<=". Fortunately, this isn't a new problem.
00821   // The solution below I borrowed from Joseph O'Rourke,
00822   // "Computational Geometry in C (Second Edition)"
00823   // See: http://cs.smith.edu/~orourke/
00824   //
00825   G4ThreeVector ic = p + distance*v - vec.center;
00826   G4double atRZ = vec.surfRZ.dot(ic);
00827   
00828   if (atRZ < 0)
00829   {
00830     if (r[0]==0) return true;    // Can't miss!
00831     
00832     if (atRZ < -lenRZ*1.2) return false;  // Forget it! Missed by a mile.
00833     
00834     G4ThreeVector q = p + v;    
00835     G4ThreeVector qa = q - vec.edges[0]->corner[0],
00836                   qb = q - vec.edges[1]->corner[0];
00837     G4ThreeVector qacb = qa.cross(qb);
00838     if (normSign*qacb.dot(v) < 0) return false;
00839     
00840     if (distFromSurface < 0)
00841     {
00842       if (atRZ < -lenRZ-surfTolerance) return false;
00843     }
00844   }
00845   else if (atRZ > 0)
00846   {
00847     if (r[1]==0) return true;    // Can't miss!
00848     
00849     if (atRZ > lenRZ*1.2) return false;  // Missed by a mile
00850     
00851     G4ThreeVector q = p + v;    
00852     G4ThreeVector qa = q - vec.edges[0]->corner[1],
00853                   qb = q - vec.edges[1]->corner[1];
00854     G4ThreeVector qacb = qa.cross(qb);
00855     if (normSign*qacb.dot(v) >= 0) return false;
00856     
00857     if (distFromSurface < 0)
00858     {
00859       if (atRZ > lenRZ+surfTolerance) return false;
00860     }
00861   }
00862 
00863   return true;
00864 }
00865 
00866 
00867 //
00868 // LineHitsSegments
00869 //
00870 // Calculate which phi segments a line intersects in three dimensions.
00871 // No check is made as to whether the intersections are within the z bounds of
00872 // the segment.
00873 //
00874 G4int G4PolyhedraSide::LineHitsSegments( const G4ThreeVector &p,
00875                                          const G4ThreeVector &v,
00876                                                G4int *i1, G4int *i2 )
00877 {
00878   G4double s1, s2;
00879   //
00880   // First, decide if and where the line intersects the cone
00881   //
00882   G4int n = cone->LineHitsCone( p, v, &s1, &s2 );
00883   
00884   if (n==0) return 0;
00885   
00886   //
00887   // Try first intersection.
00888   //
00889   *i1 = PhiSegment( std::atan2( p.y() + s1*v.y(), p.x() + s1*v.x() ) );
00890   if (n==1)
00891   {
00892     return (*i1 < 0) ? 0 : 1;
00893   }
00894   
00895   //
00896   // Try second intersection
00897   //
00898   *i2 = PhiSegment( std::atan2( p.y() + s2*v.y(), p.x() + s2*v.x() ) );
00899   if (*i1 == *i2) return 0;
00900   
00901   if (*i1 < 0)
00902   {
00903     if (*i2 < 0) return 0;
00904     *i1 = *i2;
00905     return 1;
00906   }
00907 
00908   if (*i2 < 0) return 1;
00909   
00910   return 2;
00911 }
00912 
00913 
00914 //
00915 // ClosestPhiSegment
00916 //
00917 // Decide which phi segment is closest in phi to the point.
00918 // The result is the same as PhiSegment if there is no phi opening.
00919 //
00920 G4int G4PolyhedraSide::ClosestPhiSegment( G4double phi0 )
00921 {
00922   G4int iPhi = PhiSegment( phi0 );
00923   if (iPhi >= 0) return iPhi;
00924   
00925   //
00926   // Boogers! The points falls inside the phi segment.
00927   // Look for the closest point: the start, or  end
00928   //
00929   G4double phi = phi0;
00930   
00931   while( phi < startPhi ) phi += twopi;
00932   G4double d1 = phi-endPhi;
00933 
00934   while( phi > startPhi ) phi -= twopi;
00935   G4double d2 = startPhi-phi;
00936   
00937   return (d2 < d1) ? 0 : numSide-1;
00938 }
00939 
00940 
00941 //
00942 // PhiSegment
00943 //
00944 // Decide which phi segment an angle belongs to, counting from zero.
00945 // A value of -1 indicates that the phi value is outside the shape
00946 // (only possible if phiTotal < 360 degrees).
00947 //
00948 G4int G4PolyhedraSide::PhiSegment( G4double phi0 )
00949 {
00950   //
00951   // How far are we from phiStart? Come up with a positive answer
00952   // that is less than 2*PI
00953   //
00954   G4double phi = phi0 - startPhi;
00955   while( phi < 0      ) phi += twopi;
00956   while( phi > twopi ) phi -= twopi;
00957 
00958   //
00959   // Divide
00960   //
00961   G4int answer = (G4int)(phi/deltaPhi);
00962   
00963   if (answer >= numSide)
00964   {
00965     if (phiIsOpen)
00966     {
00967       return -1;  // Looks like we missed
00968     }
00969     else
00970     {
00971       answer = numSide-1;  // Probably just roundoff
00972     }
00973   }
00974   
00975   return answer;
00976 }
00977 
00978 
00979 //
00980 // GetPhi
00981 //
00982 // Calculate Phi for a given 3-vector (point), if not already cached for the
00983 // same point, in the attempt to avoid consecutive computation of the same
00984 // quantity
00985 //
00986 G4double G4PolyhedraSide::GetPhi( const G4ThreeVector& p )
00987 {
00988   G4double val=0.;
00989 
00990   if (fPhi.first != p)
00991   {
00992     val = p.phi();
00993     fPhi.first = p;
00994     fPhi.second = val;
00995   }
00996   else
00997   {
00998     val = fPhi.second;
00999   }
01000   return val;
01001 }
01002 
01003 
01004 //
01005 // DistanceToOneSide
01006 //
01007 // Arguments:
01008 //  p   - (in) Point to check
01009 //  vec   - (in) vector set of this side
01010 //  normDist - (out) distance normal to the side or edge, as appropriate, signed
01011 // Return value = total distance from the side
01012 //
01013 G4double G4PolyhedraSide::DistanceToOneSide( const G4ThreeVector &p,
01014                                              const G4PolyhedraSideVec &vec,
01015                                                    G4double *normDist )
01016 {
01017   G4ThreeVector pct = p - vec.center;
01018   
01019   //
01020   // Get normal distance
01021   //
01022   *normDist = vec.normal.dot(pct);
01023 
01024   //
01025   // Add edge penalty
01026   //
01027   return DistanceAway( p, vec, normDist );
01028 }
01029 
01030 
01031 //
01032 // DistanceAway
01033 //
01034 // Add distance from side edges, if necesssary, to total distance,
01035 // and updates normDist appropriate depending on edge normals.
01036 //
01037 G4double G4PolyhedraSide::DistanceAway( const G4ThreeVector &p,
01038                                         const G4PolyhedraSideVec &vec,
01039                                               G4double *normDist )
01040 {
01041   G4double distOut2;
01042   G4ThreeVector pct = p - vec.center;
01043   G4double distFaceNorm = *normDist;
01044   
01045   //
01046   // Okay, are we inside bounds?
01047   //
01048   G4double pcDotRZ  = pct.dot(vec.surfRZ);
01049   G4double pcDotPhi = pct.dot(vec.surfPhi);
01050   
01051   //
01052   // Go through all permutations.
01053   //                                                   Phi
01054   //               |              |                     ^
01055   //           B   |      H       |   E                 |
01056   //        ------[1]------------[3]-----               |
01057   //               |XXXXXXXXXXXXXX|                     +----> RZ
01058   //           C   |XXXXXXXXXXXXXX|   F
01059   //               |XXXXXXXXXXXXXX|
01060   //        ------[0]------------[2]----
01061   //           A   |      G       |   D
01062   //               |              |
01063   //
01064   // It's real messy, but at least it's quick
01065   //
01066   
01067   if (pcDotRZ < -lenRZ)
01068   {
01069     G4double lenPhiZ = lenPhi[0] - lenRZ*lenPhi[1];
01070     G4double distOutZ = pcDotRZ+lenRZ;
01071     //
01072     // Below in RZ
01073     //
01074     if (pcDotPhi < -lenPhiZ)
01075     {
01076       //
01077       // ...and below in phi. Find distance to point (A)
01078       //
01079       G4double distOutPhi = pcDotPhi+lenPhiZ;
01080       distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
01081       G4ThreeVector pa = p - vec.edges[0]->corner[0];
01082       *normDist = pa.dot(vec.edges[0]->cornNorm[0]);
01083     }
01084     else if (pcDotPhi > lenPhiZ)
01085     {
01086       //
01087       // ...and above in phi. Find distance to point (B)
01088       //
01089       G4double distOutPhi = pcDotPhi-lenPhiZ;
01090       distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
01091       G4ThreeVector pb = p - vec.edges[1]->corner[0];
01092       *normDist = pb.dot(vec.edges[1]->cornNorm[0]);
01093     }
01094     else
01095     {
01096       //
01097       // ...and inside in phi. Find distance to line (C)
01098       //
01099       G4ThreeVector pa = p - vec.edges[0]->corner[0];
01100       distOut2 = distOutZ*distOutZ;
01101       *normDist = pa.dot(vec.edgeNorm[0]);
01102     }
01103   }
01104   else if (pcDotRZ > lenRZ)
01105   {
01106     G4double lenPhiZ = lenPhi[0] + lenRZ*lenPhi[1];
01107     G4double distOutZ = pcDotRZ-lenRZ;
01108     //
01109     // Above in RZ
01110     //
01111     if (pcDotPhi < -lenPhiZ)
01112     {
01113       //
01114       // ...and below in phi. Find distance to point (D)
01115       //
01116       G4double distOutPhi = pcDotPhi+lenPhiZ;
01117       distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
01118       G4ThreeVector pd = p - vec.edges[0]->corner[1];
01119       *normDist = pd.dot(vec.edges[0]->cornNorm[1]);
01120     }
01121     else if (pcDotPhi > lenPhiZ)
01122     {
01123       //
01124       // ...and above in phi. Find distance to point (E)
01125       //
01126       G4double distOutPhi = pcDotPhi-lenPhiZ;
01127       distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
01128       G4ThreeVector pe = p - vec.edges[1]->corner[1];
01129       *normDist = pe.dot(vec.edges[1]->cornNorm[1]);
01130     }
01131     else
01132     {
01133       //
01134       // ...and inside in phi. Find distance to line (F)
01135       //
01136       distOut2 = distOutZ*distOutZ;
01137       G4ThreeVector pd = p - vec.edges[0]->corner[1];
01138       *normDist = pd.dot(vec.edgeNorm[1]);
01139     }
01140   }
01141   else
01142   {
01143     G4double lenPhiZ = lenPhi[0] + pcDotRZ*lenPhi[1];
01144     //
01145     // We are inside RZ bounds
01146     // 
01147     if (pcDotPhi < -lenPhiZ)
01148     {
01149       //
01150       // ...and below in phi. Find distance to line (G)
01151       //
01152       G4double distOut = edgeNorm*(pcDotPhi+lenPhiZ);
01153       distOut2 = distOut*distOut;
01154       G4ThreeVector pd = p - vec.edges[0]->corner[1];
01155       *normDist = pd.dot(vec.edges[0]->normal);
01156     }
01157     else if (pcDotPhi > lenPhiZ)
01158     {
01159       //
01160       // ...and above in phi. Find distance to line (H)
01161       //
01162       G4double distOut = edgeNorm*(pcDotPhi-lenPhiZ);
01163       distOut2 = distOut*distOut;
01164       G4ThreeVector pe = p - vec.edges[1]->corner[1];
01165       *normDist = pe.dot(vec.edges[1]->normal);
01166     }
01167     else
01168     {
01169       //
01170       // Inside bounds! No penalty.
01171       //
01172       return std::fabs(distFaceNorm);
01173     }
01174   }
01175   return std::sqrt( distFaceNorm*distFaceNorm + distOut2 );
01176 }
01177 
01178 
01179 //
01180 // Calculation of surface area of a triangle. 
01181 // At the same time a random point in the triangle is given
01182 //
01183 G4double G4PolyhedraSide::SurfaceTriangle( G4ThreeVector p1,
01184                                            G4ThreeVector p2,
01185                                            G4ThreeVector p3,
01186                                            G4ThreeVector *p4 )
01187 {
01188   G4ThreeVector v, w;
01189   
01190   v = p3 - p1;
01191   w = p1 - p2;
01192   G4double lambda1 = G4UniformRand();
01193   G4double lambda2 = lambda1*G4UniformRand();
01194  
01195   *p4=p2 + lambda1*w + lambda2*v;
01196   return 0.5*(v.cross(w)).mag();
01197 }
01198 
01199 
01200 //
01201 // GetPointOnPlane
01202 //
01203 // Auxiliary method for GetPointOnSurface()
01204 //
01205 G4ThreeVector
01206 G4PolyhedraSide::GetPointOnPlane( G4ThreeVector p0, G4ThreeVector p1, 
01207                                   G4ThreeVector p2, G4ThreeVector p3,
01208                                   G4double *Area )
01209 {
01210   G4double chose,aOne,aTwo;
01211   G4ThreeVector point1,point2;
01212   aOne = SurfaceTriangle(p0,p1,p2,&point1);
01213   aTwo = SurfaceTriangle(p2,p3,p0,&point2);
01214   *Area= aOne+aTwo;
01215 
01216   chose = G4UniformRand()*(aOne+aTwo);
01217   if( (chose>=0.) && (chose < aOne) )
01218   {
01219    return (point1);    
01220   }
01221   return (point2);
01222 }
01223 
01224 
01225 //
01226 // SurfaceArea()
01227 //
01228 G4double G4PolyhedraSide::SurfaceArea()
01229 {
01230   if( fSurfaceArea==0. )
01231   { 
01232     // Define the variables
01233     //
01234     G4double area,areas;
01235     G4ThreeVector point1;
01236     G4ThreeVector v1,v2,v3,v4; 
01237     G4PolyhedraSideVec *vec = vecs;
01238     areas=0.;
01239 
01240     // Do a loop on all SideEdge
01241     //
01242     do
01243     {
01244       // Define 4points for a Plane or Triangle
01245       //
01246       v1=vec->edges[0]->corner[0];
01247       v2=vec->edges[0]->corner[1];
01248       v3=vec->edges[1]->corner[1];
01249       v4=vec->edges[1]->corner[0];
01250       point1=GetPointOnPlane(v1,v2,v3,v4,&area);
01251       areas+=area;
01252     } while( ++vec < vecs + numSide);
01253 
01254     fSurfaceArea=areas;
01255   }
01256   return fSurfaceArea;
01257 }
01258 
01259 
01260 //
01261 // GetPointOnFace()
01262 //
01263 G4ThreeVector G4PolyhedraSide::GetPointOnFace()
01264 {
01265   // Define the variables
01266   //
01267   std::vector<G4double>areas;
01268   std::vector<G4ThreeVector>points;
01269   G4double area=0;
01270   G4double result1;
01271   G4ThreeVector point1;
01272   G4ThreeVector v1,v2,v3,v4; 
01273   G4PolyhedraSideVec *vec = vecs;
01274 
01275   // Do a loop on all SideEdge
01276   //
01277   do
01278   {
01279     // Define 4points for a Plane or Triangle
01280     //
01281     v1=vec->edges[0]->corner[0];
01282     v2=vec->edges[0]->corner[1];
01283     v3=vec->edges[1]->corner[1];
01284     v4=vec->edges[1]->corner[0];
01285     point1=GetPointOnPlane(v1,v2,v3,v4,&result1);
01286     points.push_back(point1);
01287     areas.push_back(result1);
01288     area+=result1;
01289   } while( ++vec < vecs+numSide );
01290 
01291   // Choose randomly one of the surfaces and point on it
01292   //
01293   G4double chose = area*G4UniformRand();
01294   G4double Achose1,Achose2;
01295   Achose1=0;Achose2=0.; 
01296   G4int i=0;
01297   do 
01298   {
01299     Achose2+=areas[i];
01300     if(chose>=Achose1 && chose<Achose2)
01301     {
01302       point1=points[i] ; break;     
01303     }
01304     i++; Achose1=Achose2;
01305   } while( i<numSide );
01306  
01307   return point1;
01308 }

Generated on Mon May 27 17:49:23 2013 for Geant4 by  doxygen 1.4.7