Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/solids/specific/src/G4Polyhedra.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 // Implementation of G4Polyhedra, a CSG polyhedra,
 27 // as an inherited class of G4VCSGfaceted.
 28 //
 29 // Utility classes:
 30 //    * G4EnclosingCylinder: decided a quick check of geometry would be a
 31 //      good idea (for CPU speed). If the quick check fails, the regular
 32 //      full-blown G4VCSGfaceted version is invoked.
 33 //    * G4ReduciblePolygon: Really meant as a check of input parameters,
 34 //      this utility class also "converts" the GEANT3-like PGON/PCON
 35 //      arguments into the newer ones.
 36 // Both these classes are implemented outside this file because they are
 37 // shared with G4Polycone.
 38 //
 39 // Author: David C. Williams (davidw@scipp.ucsc.edu)
 40 // --------------------------------------------------------------------
 41 
 42 #include "G4Polyhedra.hh"
 43 
 44 #if !defined(G4GEOM_USE_UPOLYHEDRA)
 45 
 46 #include "G4PolyhedraSide.hh"
 47 #include "G4PolyPhiFace.hh"
 48 
 49 #include "G4GeomTools.hh"
 50 #include "G4VoxelLimits.hh"
 51 #include "G4AffineTransform.hh"
 52 #include "G4BoundingEnvelope.hh"
 53 
 54 #include "G4QuickRand.hh"
 55 
 56 #include "G4EnclosingCylinder.hh"
 57 #include "G4ReduciblePolygon.hh"
 58 #include "G4VPVParameterisation.hh"
 59 
 60 namespace
 61 {
 62   G4Mutex surface_elementsMutex = G4MUTEX_INITIALIZER;
 63 }
 64 
 65 using namespace CLHEP;
 66 
 67 // Constructor (GEANT3 style parameters)
 68 //
 69 // GEANT3 PGON radii are specified in the distance to the norm of each face.
 70 //
 71 G4Polyhedra::G4Polyhedra( const G4String& name,
 72                                 G4double phiStart,
 73                                 G4double thePhiTotal,
 74                                 G4int theNumSide,
 75                                 G4int numZPlanes,
 76                           const G4double zPlane[],
 77                           const G4double rInner[],
 78                           const G4double rOuter[]  )
 79   : G4VCSGfaceted( name )
 80 {
 81   if (theNumSide <= 0)
 82   {
 83     std::ostringstream message;
 84     message << "Solid must have at least one side - " << GetName() << G4endl
 85             << "        No sides specified !";
 86     G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
 87                 FatalErrorInArgument, message);
 88   }
 89 
 90   //
 91   // Calculate conversion factor from G3 radius to G4 radius
 92   //
 93   G4double phiTotal = thePhiTotal;
 94   if ( (phiTotal <=0) || (phiTotal >= twopi*(1-DBL_EPSILON)) )
 95     { phiTotal = twopi; }
 96   G4double convertRad = std::cos(0.5*phiTotal/theNumSide);
 97 
 98   //
 99   // Some historical stuff
100   //
101   original_parameters = new G4PolyhedraHistorical;
102 
103   original_parameters->numSide = theNumSide;
104   original_parameters->Start_angle = phiStart;
105   original_parameters->Opening_angle = phiTotal;
106   original_parameters->Num_z_planes = numZPlanes;
107   original_parameters->Z_values = new G4double[numZPlanes];
108   original_parameters->Rmin = new G4double[numZPlanes];
109   original_parameters->Rmax = new G4double[numZPlanes];
110 
111   for (G4int i=0; i<numZPlanes; ++i)
112   {
113     if (( i < numZPlanes-1) && ( zPlane[i] == zPlane[i+1] ))
114     {
115       if( (rInner[i]   > rOuter[i+1])
116         ||(rInner[i+1] > rOuter[i])   )
117       {
118         DumpInfo();
119         std::ostringstream message;
120         message << "Cannot create a Polyhedra with no contiguous segments."
121                 << G4endl
122                 << "        Segments are not contiguous !" << G4endl
123                 << "        rMin[" << i << "] = " << rInner[i]
124                 << " -- rMax[" << i+1 << "] = " << rOuter[i+1] << G4endl
125                 << "        rMin[" << i+1 << "] = " << rInner[i+1]
126                 << " -- rMax[" << i << "] = " << rOuter[i];
127         G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
128                     FatalErrorInArgument, message);
129       }
130     }
131     original_parameters->Z_values[i] = zPlane[i];
132     original_parameters->Rmin[i] = rInner[i]/convertRad;
133     original_parameters->Rmax[i] = rOuter[i]/convertRad;
134   }
135 
136 
137   //
138   // Build RZ polygon using special PCON/PGON GEANT3 constructor
139   //
140   auto rz = new G4ReduciblePolygon( rInner, rOuter, zPlane, numZPlanes );
141   rz->ScaleA( 1/convertRad );
142 
143   //
144   // Do the real work
145   //
146   Create( phiStart, phiTotal, theNumSide, rz );
147 
148   delete rz;
149 }
150 
151 // Constructor (generic parameters)
152 //
153 G4Polyhedra::G4Polyhedra( const G4String& name,
154                                 G4double phiStart,
155                                 G4double phiTotal,
156                                 G4int    theNumSide,
157                                 G4int    numRZ,
158                           const G4double r[],
159                           const G4double z[]   )
160   : G4VCSGfaceted( name ), genericPgon(true)
161 {
162   if (theNumSide <= 0)
163   {
164     std::ostringstream message;
165     message << "Solid must have at least one side - " << GetName() << G4endl
166             << "        No sides specified !";
167     G4Exception("G4Polyhedra::G4Polyhedra()", "GeomSolids0002",
168                 FatalErrorInArgument, message);
169   }
170 
171   auto rz = new G4ReduciblePolygon( r, z, numRZ );
172 
173   Create( phiStart, phiTotal, theNumSide, rz );
174 
175   // Set original_parameters struct for consistency
176   //
177   SetOriginalParameters(rz);
178 
179   delete rz;
180 }
181 
182 // Create
183 //
184 // Generic create routine, called by each constructor
185 // after conversion of arguments
186 //
187 void G4Polyhedra::Create( G4double phiStart,
188                           G4double phiTotal,
189                           G4int    theNumSide,
190                           G4ReduciblePolygon* rz  )
191 {
192   //
193   // Perform checks of rz values
194   //
195   if (rz->Amin() < 0.0)
196   {
197     std::ostringstream message;
198     message << "Illegal input parameters - " << GetName() << G4endl
199             << "        All R values must be >= 0 !";
200     G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
201                 FatalErrorInArgument, message);
202   }
203 
204   G4double rzArea = rz->Area();
205   if (rzArea < -kCarTolerance)
206   {
207     rz->ReverseOrder();
208   }
209   else if (rzArea < kCarTolerance)
210   {
211     std::ostringstream message;
212     message << "Illegal input parameters - " << GetName() << G4endl
213             << "        R/Z cross section is zero or near zero: " << rzArea;
214     G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
215                 FatalErrorInArgument, message);
216   }
217 
218   if ( (!rz->RemoveDuplicateVertices( kCarTolerance ))
219     || (!rz->RemoveRedundantVertices( kCarTolerance )) )
220   {
221     std::ostringstream message;
222     message << "Illegal input parameters - " << GetName() << G4endl
223             << "        Too few unique R/Z values !";
224     G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
225                 FatalErrorInArgument, message);
226   }
227 
228   if (rz->CrossesItself( 1/kInfinity ))
229   {
230     std::ostringstream message;
231     message << "Illegal input parameters - " << GetName() << G4endl
232             << "        R/Z segments cross !";
233     G4Exception("G4Polyhedra::Create()", "GeomSolids0002",
234                 FatalErrorInArgument, message);
235   }
236 
237   numCorner = rz->NumVertices();
238 
239 
240   startPhi = phiStart;
241   while( startPhi < 0 )    // Loop checking, 13.08.2015, G.Cosmo
242     startPhi += twopi;
243   //
244   // Phi opening? Account for some possible roundoff, and interpret
245   // nonsense value as representing no phi opening
246   //
247   if ( (phiTotal <= 0) || (phiTotal > twopi*(1-DBL_EPSILON)) )
248   {
249     phiIsOpen = false;
250     endPhi = startPhi + twopi;
251   }
252   else
253   {
254     phiIsOpen = true;
255     endPhi = startPhi + phiTotal;
256   }
257 
258   //
259   // Save number sides
260   //
261   numSide = theNumSide;
262 
263   //
264   // Allocate corner array.
265   //
266   corners = new G4PolyhedraSideRZ[numCorner];
267 
268   //
269   // Copy corners
270   //
271   G4ReduciblePolygonIterator iterRZ(rz);
272 
273   G4PolyhedraSideRZ *next = corners;
274   iterRZ.Begin();
275   do    // Loop checking, 13.08.2015, G.Cosmo
276   {
277     next->r = iterRZ.GetA();
278     next->z = iterRZ.GetB();
279   } while( ++next, iterRZ.Next() );
280 
281   //
282   // Allocate face pointer array
283   //
284   numFace = phiIsOpen ? numCorner+2 : numCorner;
285   faces = new G4VCSGface*[numFace];
286 
287   //
288   // Construct side faces
289   //
290   // To do so properly, we need to keep track of four successive RZ
291   // corners.
292   //
293   // But! Don't construct a face if both points are at zero radius!
294   //
295   G4PolyhedraSideRZ* corner = corners,
296                    * prev = corners + numCorner-1,
297                    * nextNext;
298   G4VCSGface** face = faces;
299   do    // Loop checking, 13.08.2015, G.Cosmo
300   {
301     next = corner+1;
302     if (next >= corners+numCorner) next = corners;
303     nextNext = next+1;
304     if (nextNext >= corners+numCorner) nextNext = corners;
305 
306     if (corner->r < 1/kInfinity && next->r < 1/kInfinity) continue;
307 /*
308     // We must decide here if we can dare declare one of our faces
309     // as having a "valid" normal (i.e. allBehind = true). This
310     // is never possible if the face faces "inward" in r *unless*
311     // we have only one side
312     //
313     G4bool allBehind;
314     if ((corner->z > next->z) && (numSide > 1))
315     {
316       allBehind = false;
317     }
318     else
319     {
320       //
321       // Otherwise, it is only true if the line passing
322       // through the two points of the segment do not
323       // split the r/z cross section
324       //
325       allBehind = !rz->BisectedBy( corner->r, corner->z,
326                                    next->r, next->z, kCarTolerance );
327     }
328 */
329     *face++ = new G4PolyhedraSide( prev, corner, next, nextNext,
330                  numSide, startPhi, endPhi-startPhi, phiIsOpen );
331   } while( prev=corner, corner=next, corner > corners );
332 
333   if (phiIsOpen)
334   {
335     //
336     // Construct phi open edges
337     //
338     *face++ = new G4PolyPhiFace( rz, startPhi, phiTotal/numSide, endPhi );
339     *face++ = new G4PolyPhiFace( rz, endPhi,   phiTotal/numSide, startPhi );
340   }
341 
342   //
343   // We might have dropped a face or two: recalculate numFace
344   //
345   numFace = (G4int)(face-faces);
346 
347   //
348   // Make enclosingCylinder
349   //
350   enclosingCylinder =
351     new G4EnclosingCylinder( rz, phiIsOpen, phiStart, phiTotal );
352 }
353 
354 // Fake default constructor - sets only member data and allocates memory
355 //                            for usage restricted to object persistency.
356 //
357 G4Polyhedra::G4Polyhedra( __void__& a )
358   : G4VCSGfaceted(a), startPhi(0.), endPhi(0.)
359 {
360 }
361 
362 // Destructor
363 //
364 G4Polyhedra::~G4Polyhedra()
365 {
366   delete [] corners;
367   delete original_parameters;
368   delete enclosingCylinder;
369   delete fElements;
370   delete fpPolyhedron;
371   corners = nullptr;
372   original_parameters = nullptr;
373   enclosingCylinder = nullptr;
374   fElements = nullptr;
375   fpPolyhedron = nullptr;
376 }
377 
378 // Copy constructor
379 //
380 G4Polyhedra::G4Polyhedra( const G4Polyhedra& source )
381   : G4VCSGfaceted( source )
382 {
383   CopyStuff( source );
384 }
385 
386 // Assignment operator
387 //
388 G4Polyhedra &G4Polyhedra::operator=( const G4Polyhedra& source )
389 {
390   if (this == &source) return *this;
391 
392   G4VCSGfaceted::operator=( source );
393 
394   delete [] corners;
395   delete original_parameters;
396   delete enclosingCylinder;
397 
398   CopyStuff( source );
399 
400   return *this;
401 }
402 
403 // CopyStuff
404 //
405 void G4Polyhedra::CopyStuff( const G4Polyhedra& source )
406 {
407   //
408   // Simple stuff
409   //
410   numSide    = source.numSide;
411   startPhi   = source.startPhi;
412   endPhi     = source.endPhi;
413   phiIsOpen  = source.phiIsOpen;
414   numCorner  = source.numCorner;
415   genericPgon= source.genericPgon;
416 
417   //
418   // The corner array
419   //
420   corners = new G4PolyhedraSideRZ[numCorner];
421 
422   G4PolyhedraSideRZ* corn = corners,
423                    * sourceCorn = source.corners;
424   do    // Loop checking, 13.08.2015, G.Cosmo
425   {
426     *corn = *sourceCorn;
427   } while( ++sourceCorn, ++corn < corners+numCorner );
428 
429   //
430   // Original parameters
431   //
432   if (source.original_parameters != nullptr)
433   {
434     original_parameters =
435       new G4PolyhedraHistorical( *source.original_parameters );
436   }
437 
438   //
439   // Enclosing cylinder
440   //
441   enclosingCylinder = new G4EnclosingCylinder( *source.enclosingCylinder );
442 
443   //
444   // Surface elements
445   //
446   delete fElements;
447   fElements = nullptr;
448 
449   //
450   // Polyhedron
451   //
452   fRebuildPolyhedron = false;
453   delete fpPolyhedron;
454   fpPolyhedron = nullptr;
455 }
456 
457 // Reset
458 //
459 // Recalculates and reshapes the solid, given pre-assigned scaled
460 // original_parameters.
461 //
462 G4bool G4Polyhedra::Reset()
463 {
464   if (genericPgon)
465   {
466     std::ostringstream message;
467     message << "Solid " << GetName() << " built using generic construct."
468             << G4endl << "Not applicable to the generic construct !";
469     G4Exception("G4Polyhedra::Reset()", "GeomSolids1001",
470                 JustWarning, message, "Parameters NOT resetted.");
471     return true;
472   }
473 
474   //
475   // Clear old setup
476   //
477   G4VCSGfaceted::DeleteStuff();
478   delete [] corners;
479   delete enclosingCylinder;
480   delete fElements;
481   corners = nullptr;
482   fElements = nullptr;
483   enclosingCylinder = nullptr;
484 
485   //
486   // Rebuild polyhedra
487   //
488   auto rz = new G4ReduciblePolygon( original_parameters->Rmin,
489                                     original_parameters->Rmax,
490                                     original_parameters->Z_values,
491                                     original_parameters->Num_z_planes );
492   Create( original_parameters->Start_angle,
493           original_parameters->Opening_angle,
494           original_parameters->numSide, rz );
495   delete rz;
496 
497   return false;
498 }
499 
500 // Inside
501 //
502 // This is an override of G4VCSGfaceted::Inside, created in order
503 // to speed things up by first checking with G4EnclosingCylinder.
504 //
505 EInside G4Polyhedra::Inside( const G4ThreeVector& p ) const
506 {
507   //
508   // Quick test
509   //
510   if (enclosingCylinder->MustBeOutside(p)) return kOutside;
511 
512   //
513   // Long answer
514   //
515   return G4VCSGfaceted::Inside(p);
516 }
517 
518 // DistanceToIn
519 //
520 // This is an override of G4VCSGfaceted::Inside, created in order
521 // to speed things up by first checking with G4EnclosingCylinder.
522 //
523 G4double G4Polyhedra::DistanceToIn( const G4ThreeVector& p,
524                                     const G4ThreeVector& v ) const
525 {
526   //
527   // Quick test
528   //
529   if (enclosingCylinder->ShouldMiss(p,v))
530     return kInfinity;
531 
532   //
533   // Long answer
534   //
535   return G4VCSGfaceted::DistanceToIn( p, v );
536 }
537 
538 // DistanceToIn
539 //
540 G4double G4Polyhedra::DistanceToIn( const G4ThreeVector& p ) const
541 {
542   return G4VCSGfaceted::DistanceToIn(p);
543 }
544 
545 // Get bounding box
546 //
547 void G4Polyhedra::BoundingLimits(G4ThreeVector& pMin,
548                                  G4ThreeVector& pMax) const
549 {
550   G4double rmin = kInfinity, rmax = -kInfinity;
551   G4double zmin = kInfinity, zmax = -kInfinity;
552   for (G4int i=0; i<GetNumRZCorner(); ++i)
553   {
554     G4PolyhedraSideRZ corner = GetCorner(i);
555     if (corner.r < rmin) rmin = corner.r;
556     if (corner.r > rmax) rmax = corner.r;
557     if (corner.z < zmin) zmin = corner.z;
558     if (corner.z > zmax) zmax = corner.z;
559   }
560 
561   G4double sphi    = GetStartPhi();
562   G4double ephi    = GetEndPhi();
563   G4double dphi    = IsOpen() ? ephi-sphi : twopi;
564   G4int    ksteps  = GetNumSide();
565   G4double astep   = dphi/ksteps;
566   G4double sinStep = std::sin(astep);
567   G4double cosStep = std::cos(astep);
568 
569   G4double sinCur = GetSinStartPhi();
570   G4double cosCur = GetCosStartPhi();
571   if (!IsOpen()) rmin = 0.;
572   G4double xmin = rmin*cosCur, xmax = xmin;
573   G4double ymin = rmin*sinCur, ymax = ymin;
574   for (G4int k=0; k<ksteps+1; ++k)
575   {
576     G4double x = rmax*cosCur;
577     if (x < xmin) xmin = x;
578     if (x > xmax) xmax = x;
579     G4double y = rmax*sinCur;
580     if (y < ymin) ymin = y;
581     if (y > ymax) ymax = y;
582     if (rmin > 0)
583     {
584       G4double xx = rmin*cosCur;
585       if (xx < xmin) xmin = xx;
586       if (xx > xmax) xmax = xx;
587       G4double yy = rmin*sinCur;
588       if (yy < ymin) ymin = yy;
589       if (yy > ymax) ymax = yy;
590     }
591     G4double sinTmp = sinCur;
592     sinCur = sinCur*cosStep + cosCur*sinStep;
593     cosCur = cosCur*cosStep - sinTmp*sinStep;
594   }
595   pMin.set(xmin,ymin,zmin);
596   pMax.set(xmax,ymax,zmax);
597 
598   // Check correctness of the bounding box
599   //
600   if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
601   {
602     std::ostringstream message;
603     message << "Bad bounding box (min >= max) for solid: "
604             << GetName() << " !"
605             << "\npMin = " << pMin
606             << "\npMax = " << pMax;
607     G4Exception("G4Polyhedra::BoundingLimits()", "GeomMgt0001",
608                 JustWarning, message);
609     DumpInfo();
610   }
611 }
612 
613 // Calculate extent under transform and specified limit
614 //
615 G4bool G4Polyhedra::CalculateExtent(const EAxis pAxis,
616                                     const G4VoxelLimits& pVoxelLimit,
617                                     const G4AffineTransform& pTransform,
618                                     G4double& pMin, G4double& pMax) const
619 {
620   G4ThreeVector bmin, bmax;
621   G4bool exist;
622 
623   // Check bounding box (bbox)
624   //
625   BoundingLimits(bmin,bmax);
626   G4BoundingEnvelope bbox(bmin,bmax);
627 #ifdef G4BBOX_EXTENT
628   return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
629 #endif
630   if (bbox.BoundingBoxVsVoxelLimits(pAxis,pVoxelLimit,pTransform,pMin,pMax))
631   {
632     return exist = pMin < pMax;
633   }
634 
635   // To find the extent, RZ contour of the polycone is subdivided
636   // in triangles. The extent is calculated as cumulative extent of
637   // all sub-polycones formed by rotation of triangles around Z
638   //
639   G4TwoVectorList contourRZ;
640   G4TwoVectorList triangles;
641   std::vector<G4int> iout;
642   G4double eminlim = pVoxelLimit.GetMinExtent(pAxis);
643   G4double emaxlim = pVoxelLimit.GetMaxExtent(pAxis);
644 
645   // get RZ contour, ensure anticlockwise order of corners
646   for (G4int i=0; i<GetNumRZCorner(); ++i)
647   {
648     G4PolyhedraSideRZ corner = GetCorner(i);
649     contourRZ.emplace_back(corner.r,corner.z);
650   }
651   G4GeomTools::RemoveRedundantVertices(contourRZ,iout,2*kCarTolerance);
652   G4double area = G4GeomTools::PolygonArea(contourRZ);
653   if (area < 0.) std::reverse(contourRZ.begin(),contourRZ.end());
654 
655   // triangulate RZ countour
656   if (!G4GeomTools::TriangulatePolygon(contourRZ,triangles))
657   {
658     std::ostringstream message;
659     message << "Triangulation of RZ contour has failed for solid: "
660             << GetName() << " !"
661             << "\nExtent has been calculated using boundary box";
662     G4Exception("G4Polyhedra::CalculateExtent()",
663                 "GeomMgt1002",JustWarning,message);
664     return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
665   }
666 
667   // set trigonometric values
668   G4double sphi     = GetStartPhi();
669   G4double ephi     = GetEndPhi();
670   G4double dphi     = IsOpen() ? ephi-sphi : twopi;
671   G4int    ksteps   = GetNumSide();
672   G4double astep    = dphi/ksteps;
673   G4double sinStep  = std::sin(astep);
674   G4double cosStep  = std::cos(astep);
675   G4double sinStart = GetSinStartPhi();
676   G4double cosStart = GetCosStartPhi();
677 
678   // allocate vector lists
679   std::vector<const G4ThreeVectorList *> polygons;
680   polygons.resize(ksteps+1);
681   for (G4int k=0; k<ksteps+1; ++k)
682   {
683     polygons[k] = new G4ThreeVectorList(3);
684   }
685 
686   // main loop along triangles
687   pMin =  kInfinity;
688   pMax = -kInfinity;
689   G4int ntria = (G4int)triangles.size()/3;
690   for (G4int i=0; i<ntria; ++i)
691   {
692     G4double sinCur = sinStart;
693     G4double cosCur = cosStart;
694     G4int i3 = i*3;
695     for (G4int k=0; k<ksteps+1; ++k) // rotate triangle
696     {
697       auto ptr = const_cast<G4ThreeVectorList*>(polygons[k]);
698       auto iter = ptr->begin();
699       iter->set(triangles[i3+0].x()*cosCur,
700                 triangles[i3+0].x()*sinCur,
701                 triangles[i3+0].y());
702       iter++;
703       iter->set(triangles[i3+1].x()*cosCur,
704                 triangles[i3+1].x()*sinCur,
705                 triangles[i3+1].y());
706       iter++;
707       iter->set(triangles[i3+2].x()*cosCur,
708                 triangles[i3+2].x()*sinCur,
709                 triangles[i3+2].y());
710 
711       G4double sinTmp = sinCur;
712       sinCur = sinCur*cosStep + cosCur*sinStep;
713       cosCur = cosCur*cosStep - sinTmp*sinStep;
714     }
715 
716     // set sub-envelope and adjust extent
717     G4double emin,emax;
718     G4BoundingEnvelope benv(polygons);
719     if (!benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,emin,emax)) continue;
720     if (emin < pMin) pMin = emin;
721     if (emax > pMax) pMax = emax;
722     if (eminlim > pMin && emaxlim < pMax) break; // max possible extent
723   }
724   // free memory
725   for (G4int k=0; k<ksteps+1; ++k) { delete polygons[k]; polygons[k]=nullptr;}
726   return (pMin < pMax);
727 }
728 
729 // ComputeDimensions
730 //
731 void G4Polyhedra::ComputeDimensions(       G4VPVParameterisation* p,
732                                      const G4int n,
733                                      const G4VPhysicalVolume* pRep )
734 {
735   p->ComputeDimensions(*this,n,pRep);
736 }
737 
738 // GetEntityType
739 //
740 G4GeometryType G4Polyhedra::GetEntityType() const
741 {
742   return {"G4Polyhedra"};
743 }
744 
745 // IsFaceted
746 //
747 G4bool G4Polyhedra::IsFaceted() const
748 {
749   return true;
750 }
751 
752 // Make a clone of the object
753 //
754 G4VSolid* G4Polyhedra::Clone() const
755 {
756   return new G4Polyhedra(*this);
757 }
758 
759 // Stream object contents to an output stream
760 //
761 std::ostream& G4Polyhedra::StreamInfo( std::ostream& os ) const
762 {
763   G4long oldprc = os.precision(16);
764   os << "-----------------------------------------------------------\n"
765      << "    *** Dump for solid - " << GetName() << " ***\n"
766      << "    ===================================================\n"
767      << " Solid type: G4Polyhedra\n"
768      << " Parameters: \n"
769      << "    starting phi angle : " << startPhi/degree << " degrees \n"
770      << "    ending phi angle   : " << endPhi/degree << " degrees \n"
771      << "    number of sides    : " << numSide << " \n";
772   G4int i=0;
773   if (!genericPgon)
774   {
775     G4int numPlanes = original_parameters->Num_z_planes;
776     os << "    number of Z planes: " << numPlanes << "\n"
777        << "              Z values: \n";
778     for (i=0; i<numPlanes; ++i)
779     {
780       os << "              Z plane " << i << ": "
781          << original_parameters->Z_values[i] << "\n";
782     }
783     os << "              Tangent distances to inner surface (Rmin): \n";
784     for (i=0; i<numPlanes; ++i)
785     {
786       os << "              Z plane " << i << ": "
787          << original_parameters->Rmin[i] << "\n";
788     }
789     os << "              Tangent distances to outer surface (Rmax): \n";
790     for (i=0; i<numPlanes; ++i)
791     {
792       os << "              Z plane " << i << ": "
793          << original_parameters->Rmax[i] << "\n";
794     }
795   }
796   os << "    number of RZ points: " << numCorner << "\n"
797      << "              RZ values (corners): \n";
798      for (i=0; i<numCorner; ++i)
799      {
800        os << "                         "
801           << corners[i].r << ", " << corners[i].z << "\n";
802      }
803   os << "-----------------------------------------------------------\n";
804   os.precision(oldprc);
805 
806   return os;
807 }
808 
809 //////////////////////////////////////////////////////////////////////////
810 //
811 // Return volume
812 
813 G4double G4Polyhedra::GetCubicVolume()
814 {
815   if (fCubicVolume == 0.)
816   {
817     G4double total = 0.;
818     G4int nrz = GetNumRZCorner();
819     G4PolyhedraSideRZ a = GetCorner(nrz - 1);
820     for (G4int i=0; i<nrz; ++i)
821     {
822       G4PolyhedraSideRZ b = GetCorner(i);
823       total += (b.r*b.r + b.r*a.r + a.r*a.r)*(b.z - a.z);
824       a = b;
825     }
826     fCubicVolume = std::abs(total)*
827       std::sin((GetEndPhi() - GetStartPhi())/GetNumSide())*GetNumSide()/6.;
828   }
829   return fCubicVolume;
830 }
831 
832 //////////////////////////////////////////////////////////////////////////
833 //
834 // Return surface area
835 
836 G4double G4Polyhedra::GetSurfaceArea()
837 {
838   if (fSurfaceArea == 0.)
839   {
840     G4double total = 0.;
841     G4int nrz = GetNumRZCorner();
842     if (IsOpen())
843     {
844       G4PolyhedraSideRZ a = GetCorner(nrz - 1);
845       for (G4int i=0; i<nrz; ++i)
846       {
847         G4PolyhedraSideRZ b = GetCorner(i);
848         total += a.r*b.z - a.z*b.r;
849         a = b;
850       }
851       total = std::abs(total);
852     }
853     G4double alp = (GetEndPhi() - GetStartPhi())/GetNumSide();
854     G4double cosa = std::cos(alp);
855     G4double sina = std::sin(alp);
856     G4PolyhedraSideRZ a = GetCorner(nrz - 1);
857     for (G4int i=0; i<nrz; ++i)
858     {
859       G4PolyhedraSideRZ b = GetCorner(i);
860       G4ThreeVector p1(a.r, 0, a.z);
861       G4ThreeVector p2(a.r*cosa, a.r*sina, a.z);
862       G4ThreeVector p3(b.r*cosa, b.r*sina, b.z);
863       G4ThreeVector p4(b.r, 0, b.z);
864       total += GetNumSide()*(G4GeomTools::QuadAreaNormal(p1, p2, p3, p4)).mag();
865       a = b;
866     }
867     fSurfaceArea = total;
868   }
869   return fSurfaceArea;
870 }
871 
872 //////////////////////////////////////////////////////////////////////////
873 //
874 // Set vector of surface elements, auxiliary method for sampling
875 // random points on surface
876 
877 void G4Polyhedra::SetSurfaceElements() const
878 {
879   fElements = new std::vector<G4Polyhedra::surface_element>;
880   G4double total = 0.;
881   G4int nrz = GetNumRZCorner();
882 
883   // set lateral surface elements
884   G4double dphi = (GetEndPhi() - GetStartPhi())/GetNumSide();
885   G4double cosa = std::cos(dphi);
886   G4double sina = std::sin(dphi);
887   G4int ia = nrz - 1;
888   for (G4int ib=0; ib<nrz; ++ib)
889   {
890     G4PolyhedraSideRZ a = GetCorner(ia);
891     G4PolyhedraSideRZ b = GetCorner(ib);
892     G4Polyhedra::surface_element selem;
893     selem.i0 = ia;
894     selem.i1 = ib;
895     ia = ib;
896     if (a.r == 0. && b.r == 0.) continue;
897     G4ThreeVector p1(a.r, 0, a.z);
898     G4ThreeVector p2(a.r*cosa, a.r*sina, a.z);
899     G4ThreeVector p3(b.r*cosa, b.r*sina, b.z);
900     G4ThreeVector p4(b.r, 0, b.z);
901     if (a.r > 0.)
902     {
903       selem.i2 = -1;
904       total += GetNumSide()*(G4GeomTools::TriangleAreaNormal(p1, p2, p3)).mag();
905       selem.area = total;
906       fElements->push_back(selem);
907     }
908     if (b.r > 0.)
909     {
910       selem.i2 = -2;
911       total += GetNumSide()*(G4GeomTools::TriangleAreaNormal(p1, p3, p4)).mag();
912       selem.area = total;
913       fElements->push_back(selem);
914     }
915   }
916 
917   // set elements for phi cuts
918   if (IsOpen())
919   {
920     G4TwoVectorList contourRZ;
921     std::vector<G4int> triangles;
922     for (G4int i=0; i<nrz; ++i)
923     {
924       G4PolyhedraSideRZ corner = GetCorner(i);
925       contourRZ.emplace_back(corner.r, corner.z);
926     }
927     G4GeomTools::TriangulatePolygon(contourRZ, triangles);
928     auto ntria = (G4int)triangles.size();
929     for (G4int i=0; i<ntria; i+=3)
930     {
931       G4Polyhedra::surface_element selem;
932       selem.i0 = triangles[i];
933       selem.i1 = triangles[i+1];
934       selem.i2 = triangles[i+2];
935       G4PolyhedraSideRZ a = GetCorner(selem.i0);
936       G4PolyhedraSideRZ b = GetCorner(selem.i1);
937       G4PolyhedraSideRZ c = GetCorner(selem.i2);
938       G4double stria =
939         std::abs(G4GeomTools::TriangleArea(a.r, a.z, b.r, b.z, c.r, c.z));
940       total += stria;
941       selem.area = total;
942       fElements->push_back(selem); // start phi
943       total += stria;
944       selem.area = total;
945       selem.i0 += nrz;
946       fElements->push_back(selem); // end phi
947     }
948   }
949 }
950 
951 //////////////////////////////////////////////////////////////////////////
952 //
953 // Generate random point on surface
954 
955 G4ThreeVector G4Polyhedra::GetPointOnSurface() const
956 {
957   // Set surface elements
958   if (fElements == nullptr)
959   {
960     G4AutoLock l(&surface_elementsMutex);
961     SetSurfaceElements();
962     l.unlock();
963   }
964 
965   // Select surface element
966   G4Polyhedra::surface_element selem;
967   selem = fElements->back();
968   G4double select = selem.area*G4QuickRand();
969   auto it = std::lower_bound(fElements->begin(), fElements->end(), select,
970                              [](const G4Polyhedra::surface_element& x, G4double val)
971                              -> G4bool { return x.area < val; });
972 
973   // Generate random point
974   G4double x = 0, y = 0, z = 0;
975   G4double u = G4QuickRand();
976   G4double v = G4QuickRand();
977   if (u + v > 1.) { u = 1. - u; v = 1. - v; }
978   G4int i0 = (*it).i0;
979   G4int i1 = (*it).i1;
980   G4int i2 = (*it).i2;
981   if (i2 < 0) // lateral surface
982   {
983     // sample point
984     G4int nside = GetNumSide();
985     G4double dphi = (GetEndPhi() - GetStartPhi())/nside;
986     G4double cosa = std::cos(dphi);
987     G4double sina = std::sin(dphi);
988     G4PolyhedraSideRZ a = GetCorner(i0);
989     G4PolyhedraSideRZ b = GetCorner(i1);
990     G4ThreeVector p0(a.r, 0, a.z);
991     G4ThreeVector p1(b.r, 0, b.z);
992     G4ThreeVector p2(b.r*cosa, b.r*sina, b.z);
993     if (i2 == -1) p1.set(a.r*cosa, a.r*sina, a.z);
994     p0 += (p1 - p0)*u + (p2 - p0)*v;
995     // find selected side and rotate point
996     G4double scurr = (*it).area;
997     G4double sprev = (it == fElements->begin()) ? 0. : (*(--it)).area;
998     G4int iside = nside*(select - sprev)/(scurr - sprev);
999     if (iside == 0 && GetStartPhi() == 0.)
1000     {
1001       x = p0.x();
1002       y = p0.y();
1003       z = p0.z();
1004     }
1005     else
1006     {
1007       if (iside == nside) --iside; // iside must be less then nside
1008       G4double phi = iside*dphi + GetStartPhi();
1009       G4double cosphi = std::cos(phi);
1010       G4double sinphi = std::sin(phi);
1011       x = p0.x()*cosphi - p0.y()*sinphi;
1012       y = p0.x()*sinphi + p0.y()*cosphi;
1013       z = p0.z();
1014     }
1015   }
1016   else // phi cut
1017   {
1018     G4int nrz = GetNumRZCorner();
1019     G4double phi = (i0 < nrz) ? GetStartPhi() : GetEndPhi();
1020     if (i0 >= nrz) { i0 -= nrz; }
1021     G4PolyhedraSideRZ p0 = GetCorner(i0);
1022     G4PolyhedraSideRZ p1 = GetCorner(i1);
1023     G4PolyhedraSideRZ p2 = GetCorner(i2);
1024     G4double r = (p1.r - p0.r)*u + (p2.r - p0.r)*v + p0.r;
1025     x = r*std::cos(phi);
1026     y = r*std::sin(phi);
1027     z = (p1.z - p0.z)*u + (p2.z - p0.z)*v + p0.z;
1028   }
1029   return {x, y, z};
1030 }
1031 
1032 //////////////////////////////////////////////////////////////////////////
1033 //
1034 // CreatePolyhedron
1035 
1036 G4Polyhedron* G4Polyhedra::CreatePolyhedron() const
1037 {
1038   std::vector<G4TwoVector> rz(numCorner);
1039   for (G4int i = 0; i < numCorner; ++i)
1040     rz[i].set(corners[i].r, corners[i].z);
1041   return new G4PolyhedronPgon(startPhi, endPhi - startPhi, numSide, rz);
1042 }
1043 
1044 // SetOriginalParameters
1045 //
1046 void G4Polyhedra::SetOriginalParameters(G4ReduciblePolygon* rz)
1047 {
1048   G4int numPlanes = numCorner;
1049   G4bool isConvertible = true;
1050   G4double Zmax=rz->Bmax();
1051   rz->StartWithZMin();
1052 
1053   // Prepare vectors for storage
1054   //
1055   std::vector<G4double> Z;
1056   std::vector<G4double> Rmin;
1057   std::vector<G4double> Rmax;
1058 
1059   G4int countPlanes=1;
1060   G4int icurr=0;
1061   G4int icurl=0;
1062 
1063   // first plane Z=Z[0]
1064   //
1065   Z.push_back(corners[0].z);
1066   G4double Zprev=Z[0];
1067   if (Zprev == corners[1].z)
1068   {
1069     Rmin.push_back(corners[0].r);
1070     Rmax.push_back (corners[1].r);icurr=1;
1071   }
1072   else if (Zprev == corners[numPlanes-1].z)
1073   {
1074     Rmin.push_back(corners[numPlanes-1].r);
1075     Rmax.push_back (corners[0].r);
1076     icurl=numPlanes-1;
1077   }
1078   else
1079   {
1080     Rmin.push_back(corners[0].r);
1081     Rmax.push_back (corners[0].r);
1082   }
1083 
1084   // next planes until last
1085   //
1086   G4int inextr=0, inextl=0;
1087   for (G4int i=0; i < numPlanes-2; ++i)
1088   {
1089     inextr=1+icurr;
1090     inextl=(icurl <= 0)? numPlanes-1 : icurl-1;
1091 
1092     if((corners[inextr].z >= Zmax) & (corners[inextl].z >= Zmax))  { break; }
1093 
1094     G4double Zleft = corners[inextl].z;
1095     G4double Zright = corners[inextr].z;
1096     if(Zright>Zleft)
1097     {
1098       Z.push_back(Zleft);
1099       countPlanes++;
1100       G4double difZr=corners[inextr].z - corners[icurr].z;
1101       G4double difZl=corners[inextl].z - corners[icurl].z;
1102 
1103       if(std::fabs(difZl) < kCarTolerance)
1104       {
1105         if(std::fabs(difZr) < kCarTolerance)
1106         {
1107           Rmin.push_back(corners[inextl].r);
1108           Rmax.push_back(corners[icurr].r);
1109         }
1110         else
1111         {
1112           Rmin.push_back(corners[inextl].r);
1113           Rmax.push_back(corners[icurr].r + (Zleft-corners[icurr].z)/difZr
1114                                 *(corners[inextr].r - corners[icurr].r));
1115         }
1116       }
1117       else if (difZl >= kCarTolerance)
1118       {
1119         if(std::fabs(difZr) < kCarTolerance)
1120         {
1121           Rmin.push_back(corners[icurl].r);
1122           Rmax.push_back(corners[icurr].r);
1123         }
1124         else
1125         {
1126           Rmin.push_back(corners[icurl].r);
1127           Rmax.push_back(corners[icurr].r + (Zleft-corners[icurr].z)/difZr
1128                                 *(corners[inextr].r - corners[icurr].r));
1129         }
1130       }
1131       else
1132       {
1133         isConvertible=false; break;
1134       }
1135       icurl=(icurl == 0)? numPlanes-1 : icurl-1;
1136     }
1137     else if(std::fabs(Zright-Zleft)<kCarTolerance)  // Zright=Zleft
1138     {
1139       Z.push_back(Zleft);
1140       ++countPlanes;
1141       ++icurr;
1142 
1143       icurl=(icurl == 0)? numPlanes-1 : icurl-1;
1144 
1145       Rmin.push_back(corners[inextl].r);
1146       Rmax.push_back (corners[inextr].r);
1147     }
1148     else  // Zright<Zleft
1149     {
1150       Z.push_back(Zright);
1151       ++countPlanes;
1152 
1153       G4double difZr=corners[inextr].z - corners[icurr].z;
1154       G4double difZl=corners[inextl].z - corners[icurl].z;
1155       if(std::fabs(difZr) < kCarTolerance)
1156       {
1157         if(std::fabs(difZl) < kCarTolerance)
1158         {
1159           Rmax.push_back(corners[inextr].r);
1160           Rmin.push_back(corners[icurr].r);
1161         }
1162         else
1163         {
1164           Rmin.push_back(corners[icurl].r + (Zright-corners[icurl].z)/difZl
1165                                 * (corners[inextl].r - corners[icurl].r));
1166           Rmax.push_back(corners[inextr].r);
1167         }
1168         ++icurr;
1169       }           // plate
1170       else if (difZr >= kCarTolerance)
1171       {
1172         if(std::fabs(difZl) < kCarTolerance)
1173         {
1174           Rmax.push_back(corners[inextr].r);
1175           Rmin.push_back (corners[icurr].r);
1176         }
1177         else
1178         {
1179           Rmax.push_back(corners[inextr].r);
1180           Rmin.push_back (corners[icurl].r+(Zright-corners[icurl].z)/difZl
1181                                   * (corners[inextl].r - corners[icurl].r));
1182         }
1183         ++icurr;
1184       }
1185       else
1186       {
1187         isConvertible=false; break;
1188       }
1189     }
1190   }   // end for loop
1191 
1192   // last plane Z=Zmax
1193   //
1194   Z.push_back(Zmax);
1195   ++countPlanes;
1196   inextr=1+icurr;
1197   inextl=(icurl <= 0)? numPlanes-1 : icurl-1;
1198 
1199   Rmax.push_back(corners[inextr].r);
1200   Rmin.push_back(corners[inextl].r);
1201 
1202   // Set original parameters Rmin,Rmax,Z
1203   //
1204   if(isConvertible)
1205   {
1206    original_parameters = new G4PolyhedraHistorical;
1207    original_parameters->numSide = numSide;
1208    original_parameters->Z_values = new G4double[countPlanes];
1209    original_parameters->Rmin = new G4double[countPlanes];
1210    original_parameters->Rmax = new G4double[countPlanes];
1211 
1212    for(G4int j=0; j < countPlanes; ++j)
1213    {
1214      original_parameters->Z_values[j] = Z[j];
1215      original_parameters->Rmax[j] = Rmax[j];
1216      original_parameters->Rmin[j] = Rmin[j];
1217    }
1218    original_parameters->Start_angle = startPhi;
1219    original_parameters->Opening_angle = endPhi-startPhi;
1220    original_parameters->Num_z_planes = countPlanes;
1221 
1222   }
1223   else  // Set parameters(r,z) with Rmin==0 as convention
1224   {
1225 #ifdef G4SPECSDEBUG
1226     std::ostringstream message;
1227     message << "Polyhedra " << GetName() << G4endl
1228       << "cannot be converted to Polyhedra with (Rmin,Rmaz,Z) parameters!";
1229     G4Exception("G4Polyhedra::SetOriginalParameters()",
1230                 "GeomSolids0002", JustWarning, message);
1231 #endif
1232     original_parameters = new G4PolyhedraHistorical;
1233     original_parameters->numSide = numSide;
1234     original_parameters->Z_values = new G4double[numPlanes];
1235     original_parameters->Rmin = new G4double[numPlanes];
1236     original_parameters->Rmax = new G4double[numPlanes];
1237 
1238     for(G4int j=0; j < numPlanes; ++j)
1239     {
1240       original_parameters->Z_values[j] = corners[j].z;
1241       original_parameters->Rmax[j] = corners[j].r;
1242       original_parameters->Rmin[j] = 0.0;
1243     }
1244     original_parameters->Start_angle = startPhi;
1245     original_parameters->Opening_angle = endPhi-startPhi;
1246     original_parameters->Num_z_planes = numPlanes;
1247   }
1248 }
1249 
1250 #endif
1251