Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/solids/CSG/src/G4Cons.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 for G4Cons class
 27 //
 28 // ~1994    P.Kent: Created, as main part of the geometry prototype
 29 // 13.09.96 V.Grichine: Review and final modifications
 30 // 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
 31 // 12.10.09 T.Nikitina: Added to DistanceToIn(p,v) check on the direction in
 32 //                      case of point on surface
 33 // 03.10.16 E.Tcherniaev: use G4BoundingEnvelope for CalculateExtent(),
 34 //                      removed CreateRotatedVertices()
 35 // --------------------------------------------------------------------
 36 
 37 #include "G4Cons.hh"
 38 
 39 #if !defined(G4GEOM_USE_UCONS)
 40 
 41 #include "G4GeomTools.hh"
 42 #include "G4VoxelLimits.hh"
 43 #include "G4AffineTransform.hh"
 44 #include "G4BoundingEnvelope.hh"
 45 #include "G4GeometryTolerance.hh"
 46 
 47 #include "G4VPVParameterisation.hh"
 48 
 49 #include "meshdefs.hh"
 50 
 51 #include "Randomize.hh"
 52 
 53 #include "G4VGraphicsScene.hh"
 54 
 55 using namespace CLHEP;
 56  
 57 ////////////////////////////////////////////////////////////////////////
 58 //
 59 // Private enums: Not for external use
 60 
 61 namespace
 62 {
 63   // used by DistanceToOut()
 64   //
 65   enum ESide {kNull,kRMin,kRMax,kSPhi,kEPhi,kPZ,kMZ};
 66 
 67   // used by ApproxSurfaceNormal()
 68   //
 69   enum ENorm {kNRMin,kNRMax,kNSPhi,kNEPhi,kNZ};
 70 }
 71 
 72 //////////////////////////////////////////////////////////////////////////
 73 //
 74 // constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
 75 //             - note if pDPhi>2PI then reset to 2PI
 76 
 77 G4Cons::G4Cons( const G4String& pName,
 78                       G4double  pRmin1, G4double pRmax1,
 79                       G4double  pRmin2, G4double pRmax2,
 80                       G4double pDz,
 81                       G4double pSPhi, G4double pDPhi)
 82   : G4CSGSolid(pName), fRmin1(pRmin1), fRmin2(pRmin2),
 83     fRmax1(pRmax1), fRmax2(pRmax2), fDz(pDz), fSPhi(0.), fDPhi(0.)
 84 {
 85   kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
 86   kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
 87 
 88   halfCarTolerance=kCarTolerance*0.5;
 89   halfRadTolerance=kRadTolerance*0.5;
 90   halfAngTolerance=kAngTolerance*0.5;
 91 
 92   // Check z-len
 93   //
 94   if ( pDz < 0 )
 95   {
 96     std::ostringstream message;
 97     message << "Invalid Z half-length for Solid: " << GetName() << G4endl
 98             << "        hZ = " << pDz;
 99     G4Exception("G4Cons::G4Cons()", "GeomSolids0002",
100                 FatalException, message);
101   }
102 
103   // Check radii
104   //
105   if (((pRmin1>=pRmax1) || (pRmin2>=pRmax2) || (pRmin1<0)) && (pRmin2<0))
106   {
107     std::ostringstream message;
108     message << "Invalid values of radii for Solid: " << GetName() << G4endl
109             << "        pRmin1 = " << pRmin1 << ", pRmin2 = " << pRmin2
110             << ", pRmax1 = " << pRmax1 << ", pRmax2 = " << pRmax2;
111     G4Exception("G4Cons::G4Cons()", "GeomSolids0002",
112                 FatalException, message) ;
113   }
114   if( (pRmin1 == 0.0) && (pRmin2 > 0.0) ) { fRmin1 = 1e3*kRadTolerance ; }
115   if( (pRmin2 == 0.0) && (pRmin1 > 0.0) ) { fRmin2 = 1e3*kRadTolerance ; }
116 
117   // Check angles
118   //
119   CheckPhiAngles(pSPhi, pDPhi);
120 }
121 
122 ///////////////////////////////////////////////////////////////////////
123 //
124 // Fake default constructor - sets only member data and allocates memory
125 //                            for usage restricted to object persistency.
126 //
127 G4Cons::G4Cons( __void__& a )
128   : G4CSGSolid(a)
129 {
130 }
131 
132 ///////////////////////////////////////////////////////////////////////
133 //
134 // Destructor
135 
136 G4Cons::~G4Cons() = default;
137 
138 //////////////////////////////////////////////////////////////////////////
139 //
140 // Copy constructor
141 
142 G4Cons::G4Cons(const G4Cons&) = default;
143 
144 //////////////////////////////////////////////////////////////////////////
145 //
146 // Assignment operator
147 
148 G4Cons& G4Cons::operator = (const G4Cons& rhs) 
149 {
150    // Check assignment to self
151    //
152    if (this == &rhs)  { return *this; }
153 
154    // Copy base class data
155    //
156    G4CSGSolid::operator=(rhs);
157 
158    // Copy data
159    //
160    kRadTolerance = rhs.kRadTolerance;
161    kAngTolerance = rhs.kAngTolerance;
162    fRmin1 = rhs.fRmin1; fRmin2 = rhs.fRmin2;
163    fRmax1 = rhs.fRmax1; fRmax2 = rhs.fRmax2;
164    fDz = rhs.fDz; fSPhi = rhs.fSPhi; fDPhi = rhs.fDPhi;
165    sinCPhi = rhs.sinCPhi; cosCPhi = rhs.cosCPhi; cosHDPhi = rhs.cosHDPhi;
166    cosHDPhiOT = rhs.cosHDPhiOT; cosHDPhiIT = rhs.cosHDPhiIT;
167    sinSPhi = rhs.sinSPhi; cosSPhi = rhs.cosSPhi;
168    sinEPhi = rhs.sinEPhi; cosEPhi = rhs.cosEPhi;
169    fPhiFullCone = rhs.fPhiFullCone;
170    halfCarTolerance = rhs.halfCarTolerance;
171    halfRadTolerance = rhs.halfRadTolerance;
172    halfAngTolerance = rhs.halfAngTolerance;
173 
174    return *this;
175 }
176 
177 /////////////////////////////////////////////////////////////////////
178 //
179 // Return whether point inside/outside/on surface
180 
181 EInside G4Cons::Inside(const G4ThreeVector& p) const
182 {
183   G4double r2, rl, rh, pPhi, tolRMin, tolRMax; // rh2, rl2 ;
184   EInside in;
185 
186   if (std::fabs(p.z()) > fDz + halfCarTolerance )  { return in = kOutside; }
187   else if(std::fabs(p.z()) >= fDz - halfCarTolerance )    { in = kSurface; }
188   else                                                    { in = kInside;  }
189 
190   r2 = p.x()*p.x() + p.y()*p.y() ;
191   rl = 0.5*(fRmin2*(p.z() + fDz) + fRmin1*(fDz - p.z()))/fDz ;
192   rh = 0.5*(fRmax2*(p.z()+fDz)+fRmax1*(fDz-p.z()))/fDz;
193 
194   // rh2 = rh*rh;
195 
196   tolRMin = rl - halfRadTolerance;
197   if ( tolRMin < 0 )  { tolRMin = 0; }
198   tolRMax = rh + halfRadTolerance;
199 
200   if ( (r2<tolRMin*tolRMin) || (r2>tolRMax*tolRMax) ) { return in = kOutside; }
201 
202   if (rl != 0.0) { tolRMin = rl + halfRadTolerance; }
203   else    { tolRMin = 0.0; }
204   tolRMax = rh - halfRadTolerance;
205       
206   if (in == kInside) // else it's kSurface already
207   {
208      if ( (r2 < tolRMin*tolRMin) || (r2 >= tolRMax*tolRMax) ) { in = kSurface; }
209   }
210   if ( !fPhiFullCone && ((p.x() != 0.0) || (p.y() != 0.0)) )
211   {
212     pPhi = std::atan2(p.y(),p.x()) ;
213 
214     if ( pPhi < fSPhi - halfAngTolerance  )             { pPhi += twopi; }
215     else if ( pPhi > fSPhi + fDPhi + halfAngTolerance ) { pPhi -= twopi; }
216     
217     if ( (pPhi < fSPhi - halfAngTolerance) ||          
218          (pPhi > fSPhi + fDPhi + halfAngTolerance) )  { return in = kOutside; }
219       
220     else if (in == kInside)  // else it's kSurface anyway already
221     {
222        if ( (pPhi < fSPhi + halfAngTolerance) || 
223             (pPhi > fSPhi + fDPhi - halfAngTolerance) )  { in = kSurface; }
224     }
225   }
226   else if ( !fPhiFullCone )  { in = kSurface; }
227 
228   return in ;
229 }
230 
231 /////////////////////////////////////////////////////////////////////////
232 //
233 // Dispatch to parameterisation for replication mechanism dimension
234 // computation & modification.
235 
236 void G4Cons::ComputeDimensions(      G4VPVParameterisation* p,
237                                const G4int                  n,
238                                const G4VPhysicalVolume*     pRep    )
239 {
240   p->ComputeDimensions(*this,n,pRep) ;
241 }
242 
243 ///////////////////////////////////////////////////////////////////////
244 //
245 // Get bounding box
246 
247 void G4Cons::BoundingLimits(G4ThreeVector& pMin, G4ThreeVector& pMax) const
248 {
249   G4double rmin = std::min(GetInnerRadiusMinusZ(),GetInnerRadiusPlusZ());
250   G4double rmax = std::max(GetOuterRadiusMinusZ(),GetOuterRadiusPlusZ());
251   G4double dz   = GetZHalfLength();
252 
253   // Find bounding box
254   //
255   if (GetDeltaPhiAngle() < twopi)
256   {
257     G4TwoVector vmin,vmax;
258     G4GeomTools::DiskExtent(rmin,rmax,
259                             GetSinStartPhi(),GetCosStartPhi(),
260                             GetSinEndPhi(),GetCosEndPhi(),
261                             vmin,vmax);
262     pMin.set(vmin.x(),vmin.y(),-dz);
263     pMax.set(vmax.x(),vmax.y(), dz);
264   }
265   else
266   {
267     pMin.set(-rmax,-rmax,-dz);
268     pMax.set( rmax, rmax, dz);
269   }
270 
271   // Check correctness of the bounding box
272   //
273   if (pMin.x() >= pMax.x() || pMin.y() >= pMax.y() || pMin.z() >= pMax.z())
274   {
275     std::ostringstream message;
276     message << "Bad bounding box (min >= max) for solid: "
277             << GetName() << " !"
278             << "\npMin = " << pMin
279             << "\npMax = " << pMax;
280     G4Exception("G4Cons::BoundingLimits()", "GeomMgt0001",
281                 JustWarning, message);
282     DumpInfo();
283   }
284 }
285 
286 ///////////////////////////////////////////////////////////////////////
287 //
288 // Calculate extent under transform and specified limit
289 
290 G4bool G4Cons::CalculateExtent( const EAxis              pAxis,
291                                 const G4VoxelLimits&     pVoxelLimit,
292                                 const G4AffineTransform& pTransform,
293                                       G4double&          pMin,
294                                       G4double&          pMax ) const
295 {
296   G4ThreeVector bmin, bmax;
297   G4bool exist;
298 
299   // Get bounding box
300   BoundingLimits(bmin,bmax);
301 
302   // Check bounding box
303   G4BoundingEnvelope bbox(bmin,bmax);
304 #ifdef G4BBOX_EXTENT
305   if (true) return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
306 #endif
307   if (bbox.BoundingBoxVsVoxelLimits(pAxis,pVoxelLimit,pTransform,pMin,pMax))
308   {
309     return exist = pMin < pMax;
310   }
311 
312   // Get parameters of the solid
313   G4double rmin1 = GetInnerRadiusMinusZ();
314   G4double rmax1 = GetOuterRadiusMinusZ();
315   G4double rmin2 = GetInnerRadiusPlusZ();
316   G4double rmax2 = GetOuterRadiusPlusZ();
317   G4double dz    = GetZHalfLength();
318   G4double dphi  = GetDeltaPhiAngle();
319 
320   // Find bounding envelope and calculate extent
321   //
322   const G4int NSTEPS = 24;            // number of steps for whole circle
323   G4double astep  = twopi/NSTEPS;     // max angle for one step
324   G4int    ksteps = (dphi <= astep) ? 1 : (G4int)((dphi-deg)/astep) + 1;
325   G4double ang    = dphi/ksteps;
326 
327   G4double sinHalf = std::sin(0.5*ang);
328   G4double cosHalf = std::cos(0.5*ang);
329   G4double sinStep = 2.*sinHalf*cosHalf;
330   G4double cosStep = 1. - 2.*sinHalf*sinHalf;
331   G4double rext1   = rmax1/cosHalf;
332   G4double rext2   = rmax2/cosHalf;
333 
334   // bounding envelope for full cone without hole consists of two polygons,
335   // in other cases it is a sequence of quadrilaterals
336   if (rmin1 == 0 && rmin2 == 0 && dphi == twopi)
337   {
338     G4double sinCur = sinHalf;
339     G4double cosCur = cosHalf;
340 
341     G4ThreeVectorList baseA(NSTEPS),baseB(NSTEPS);
342     for (G4int k=0; k<NSTEPS; ++k)
343     {
344       baseA[k].set(rext1*cosCur,rext1*sinCur,-dz);
345       baseB[k].set(rext2*cosCur,rext2*sinCur, dz);
346 
347       G4double sinTmp = sinCur;
348       sinCur = sinCur*cosStep + cosCur*sinStep;
349       cosCur = cosCur*cosStep - sinTmp*sinStep;
350     }
351     std::vector<const G4ThreeVectorList *> polygons(2);
352     polygons[0] = &baseA;
353     polygons[1] = &baseB;
354     G4BoundingEnvelope benv(bmin,bmax,polygons);
355     exist = benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
356   }
357   else
358   {
359     G4double sinStart = GetSinStartPhi();
360     G4double cosStart = GetCosStartPhi();
361     G4double sinEnd   = GetSinEndPhi();
362     G4double cosEnd   = GetCosEndPhi();
363     G4double sinCur   = sinStart*cosHalf + cosStart*sinHalf;
364     G4double cosCur   = cosStart*cosHalf - sinStart*sinHalf;
365 
366     // set quadrilaterals
367     G4ThreeVectorList pols[NSTEPS+2];
368     for (G4int k=0; k<ksteps+2; ++k) pols[k].resize(4);
369     pols[0][0].set(rmin2*cosStart,rmin2*sinStart, dz);
370     pols[0][1].set(rmin1*cosStart,rmin1*sinStart,-dz);
371     pols[0][2].set(rmax1*cosStart,rmax1*sinStart,-dz);
372     pols[0][3].set(rmax2*cosStart,rmax2*sinStart, dz);
373     for (G4int k=1; k<ksteps+1; ++k)
374     {
375       pols[k][0].set(rmin2*cosCur,rmin2*sinCur, dz);
376       pols[k][1].set(rmin1*cosCur,rmin1*sinCur,-dz);
377       pols[k][2].set(rext1*cosCur,rext1*sinCur,-dz);
378       pols[k][3].set(rext2*cosCur,rext2*sinCur, dz);
379 
380       G4double sinTmp = sinCur;
381       sinCur = sinCur*cosStep + cosCur*sinStep;
382       cosCur = cosCur*cosStep - sinTmp*sinStep;
383     }
384     pols[ksteps+1][0].set(rmin2*cosEnd,rmin2*sinEnd, dz);
385     pols[ksteps+1][1].set(rmin1*cosEnd,rmin1*sinEnd,-dz);
386     pols[ksteps+1][2].set(rmax1*cosEnd,rmax1*sinEnd,-dz);
387     pols[ksteps+1][3].set(rmax2*cosEnd,rmax2*sinEnd, dz);
388 
389     // set envelope and calculate extent
390     std::vector<const G4ThreeVectorList *> polygons;
391     polygons.resize(ksteps+2);
392     for (G4int k=0; k<ksteps+2; ++k) polygons[k] = &pols[k];
393     G4BoundingEnvelope benv(bmin,bmax,polygons);
394     exist = benv.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
395   }
396   return exist;
397 }
398 
399 ////////////////////////////////////////////////////////////////////////
400 //
401 // Return unit normal of surface closest to p
402 // - note if point on z axis, ignore phi divided sides
403 // - unsafe if point close to z axis a rmin=0 - no explicit checks
404 
405 G4ThreeVector G4Cons::SurfaceNormal( const G4ThreeVector& p) const
406 {
407   G4int noSurfaces = 0;
408   G4double rho, pPhi;
409   G4double distZ, distRMin, distRMax;
410   G4double distSPhi = kInfinity, distEPhi = kInfinity;
411   G4double tanRMin, secRMin, pRMin, widRMin;
412   G4double tanRMax, secRMax, pRMax, widRMax;
413 
414   G4ThreeVector norm, sumnorm(0.,0.,0.), nZ = G4ThreeVector(0.,0.,1.);
415   G4ThreeVector nR, nr(0.,0.,0.), nPs, nPe;
416 
417   distZ = std::fabs(std::fabs(p.z()) - fDz);
418   rho   = std::sqrt(p.x()*p.x() + p.y()*p.y());
419 
420   tanRMin  = (fRmin2 - fRmin1)*0.5/fDz;
421   secRMin  = std::sqrt(1 + tanRMin*tanRMin);
422   pRMin    = rho - p.z()*tanRMin;
423   widRMin  = fRmin2 - fDz*tanRMin;
424   distRMin = std::fabs(pRMin - widRMin)/secRMin;
425 
426   tanRMax  = (fRmax2 - fRmax1)*0.5/fDz;
427   secRMax  = std::sqrt(1+tanRMax*tanRMax);
428   pRMax    = rho - p.z()*tanRMax;
429   widRMax  = fRmax2 - fDz*tanRMax;
430   distRMax = std::fabs(pRMax - widRMax)/secRMax;
431 
432   if (!fPhiFullCone)   // Protected against (0,0,z) 
433   {
434     if ( rho != 0.0 )
435     {
436       pPhi = std::atan2(p.y(),p.x());
437 
438       if (pPhi  < fSPhi-halfCarTolerance)            { pPhi += twopi; }
439       else if (pPhi > fSPhi+fDPhi+halfCarTolerance)  { pPhi -= twopi; }
440 
441       distSPhi = std::fabs( pPhi - fSPhi ); 
442       distEPhi = std::fabs( pPhi - fSPhi - fDPhi ); 
443     }
444     else if( ((fRmin1) == 0.0) || ((fRmin2) == 0.0) )
445     {
446       distSPhi = 0.; 
447       distEPhi = 0.; 
448     }
449     nPs = G4ThreeVector( sinSPhi, -cosSPhi, 0 );
450     nPe = G4ThreeVector( -sinEPhi, cosEPhi, 0 );
451   }
452   if ( rho > halfCarTolerance )   
453   {
454     nR = G4ThreeVector(p.x()/rho/secRMax, p.y()/rho/secRMax, -tanRMax/secRMax);
455     if ((fRmin1 != 0.0) || (fRmin2 != 0.0))
456     {
457       nr = G4ThreeVector(-p.x()/rho/secRMin,-p.y()/rho/secRMin,tanRMin/secRMin);
458     }
459   }
460 
461   if( distRMax <= halfCarTolerance )
462   {
463     ++noSurfaces;
464     sumnorm += nR;
465   }
466   if( ((fRmin1 != 0.0) || (fRmin2 != 0.0)) && (distRMin <= halfCarTolerance) )
467   {
468     ++noSurfaces;
469     sumnorm += nr;
470   }
471   if( !fPhiFullCone )   
472   {
473     if (distSPhi <= halfAngTolerance)
474     {
475       ++noSurfaces;
476       sumnorm += nPs;
477     }
478     if (distEPhi <= halfAngTolerance) 
479     {
480       ++noSurfaces;
481       sumnorm += nPe;
482     }
483   }
484   if (distZ <= halfCarTolerance)  
485   {
486     ++noSurfaces;
487     if ( p.z() >= 0.)  { sumnorm += nZ; }
488     else               { sumnorm -= nZ; }
489   }
490   if ( noSurfaces == 0 )
491   {
492 #ifdef G4CSGDEBUG
493     G4Exception("G4Cons::SurfaceNormal(p)", "GeomSolids1002",
494                 JustWarning, "Point p is not on surface !?" );
495 #endif 
496      norm = ApproxSurfaceNormal(p);
497   }
498   else if ( noSurfaces == 1 )  { norm = sumnorm; }
499   else                         { norm = sumnorm.unit(); }
500 
501   return norm ;
502 }
503 
504 ////////////////////////////////////////////////////////////////////////////
505 //
506 // Algorithm for SurfaceNormal() following the original specification
507 // for points not on the surface
508 
509 G4ThreeVector G4Cons::ApproxSurfaceNormal( const G4ThreeVector& p ) const
510 {
511   ENorm side ;
512   G4ThreeVector norm ;
513   G4double rho, phi ;
514   G4double distZ, distRMin, distRMax, distSPhi, distEPhi, distMin ;
515   G4double tanRMin, secRMin, pRMin, widRMin ;
516   G4double tanRMax, secRMax, pRMax, widRMax ;
517 
518   distZ = std::fabs(std::fabs(p.z()) - fDz) ;
519   rho   = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
520 
521   tanRMin  = (fRmin2 - fRmin1)*0.5/fDz ;
522   secRMin  = std::sqrt(1 + tanRMin*tanRMin) ;
523   pRMin    = rho - p.z()*tanRMin ;
524   widRMin  = fRmin2 - fDz*tanRMin ;
525   distRMin = std::fabs(pRMin - widRMin)/secRMin ;
526 
527   tanRMax  = (fRmax2 - fRmax1)*0.5/fDz ;
528   secRMax  = std::sqrt(1+tanRMax*tanRMax) ;
529   pRMax    = rho - p.z()*tanRMax ;
530   widRMax  = fRmax2 - fDz*tanRMax ;
531   distRMax = std::fabs(pRMax - widRMax)/secRMax ;
532   
533   if (distRMin < distRMax)  // First minimum
534   {
535     if (distZ < distRMin)
536     {
537       distMin = distZ ;
538       side    = kNZ ;
539     }
540     else
541     {
542       distMin = distRMin ;
543       side    = kNRMin ;
544     }
545   }
546   else
547   {
548     if (distZ < distRMax)
549     {
550       distMin = distZ ;
551       side    = kNZ ;
552     }
553     else
554     {
555       distMin = distRMax ;
556       side    = kNRMax ;
557     }
558   }
559   if ( !fPhiFullCone && (rho != 0.0) )  // Protected against (0,0,z) 
560   {
561     phi = std::atan2(p.y(),p.x()) ;
562 
563     if (phi < 0)  { phi += twopi; }
564 
565     if (fSPhi < 0)  { distSPhi = std::fabs(phi - (fSPhi + twopi))*rho; }
566     else            { distSPhi = std::fabs(phi - fSPhi)*rho; }
567 
568     distEPhi = std::fabs(phi - fSPhi - fDPhi)*rho ;
569 
570     // Find new minimum
571 
572     if (distSPhi < distEPhi)
573     {
574       if (distSPhi < distMin)  { side = kNSPhi; }
575     }
576     else 
577     {
578       if (distEPhi < distMin)  { side = kNEPhi; }
579     }
580   }    
581   switch (side)
582   {
583     case kNRMin:      // Inner radius
584     {
585       rho *= secRMin ;
586       norm = G4ThreeVector(-p.x()/rho, -p.y()/rho, tanRMin/secRMin) ;
587       break ;
588     }
589     case kNRMax:      // Outer radius
590     {
591       rho *= secRMax ;
592       norm = G4ThreeVector(p.x()/rho, p.y()/rho, -tanRMax/secRMax) ;
593       break ;
594     }
595     case kNZ:         // +/- dz
596     {
597       if (p.z() > 0)  { norm = G4ThreeVector(0,0,1);  }
598       else            { norm = G4ThreeVector(0,0,-1); }
599       break ;
600     }
601     case kNSPhi:
602     {
603       norm = G4ThreeVector(sinSPhi, -cosSPhi, 0) ;
604       break ;
605     }
606     case kNEPhi:
607     {
608       norm = G4ThreeVector(-sinEPhi, cosEPhi, 0) ;
609       break ;
610     }
611     default:          // Should never reach this case...
612     {
613       DumpInfo();
614       G4Exception("G4Cons::ApproxSurfaceNormal()",
615                   "GeomSolids1002", JustWarning,
616                   "Undefined side for valid surface normal to solid.");
617       break ;    
618     }
619   }
620   return norm ;
621 }
622 
623 ////////////////////////////////////////////////////////////////////////
624 //
625 // Calculate distance to shape from outside, along normalised vector
626 // - return kInfinity if no intersection, or intersection distance <= tolerance
627 //
628 // - Compute the intersection with the z planes 
629 //        - if at valid r, phi, return
630 //
631 // -> If point is outside cone, compute intersection with rmax1*0.5
632 //        - if at valid phi,z return
633 //        - if inside outer cone, handle case when on tolerant outer cone
634 //          boundary and heading inwards(->0 to in)
635 //
636 // -> Compute intersection with inner cone, taking largest +ve root
637 //        - if valid (in z,phi), save intersction
638 //
639 //    -> If phi segmented, compute intersections with phi half planes
640 //        - return smallest of valid phi intersections and
641 //          inner radius intersection
642 //
643 // NOTE:
644 // - `if valid' implies tolerant checking of intersection points
645 // - z, phi intersection from Tubs
646 
647 G4double G4Cons::DistanceToIn( const G4ThreeVector& p,
648                                const G4ThreeVector& v   ) const
649 {
650   G4double snxt = kInfinity ;      // snxt = default return value
651   const G4double dRmax = 50*(fRmax1+fRmax2);// 100*(Rmax1+Rmax2)/2.
652 
653   G4double tanRMax,secRMax,rMaxAv,rMaxOAv ;  // Data for cones
654   G4double tanRMin,secRMin,rMinAv,rMinOAv ;
655   G4double rout,rin ;
656 
657   G4double tolORMin,tolORMin2,tolIRMin,tolIRMin2 ; // `generous' radii squared
658   G4double tolORMax2,tolIRMax,tolIRMax2 ;
659   G4double tolODz,tolIDz ;
660 
661   G4double Dist,sd,xi,yi,zi,ri=0.,risec,rhoi2,cosPsi ; // Intersection point vars
662 
663   G4double t1,t2,t3,b,c,d ;    // Quadratic solver variables 
664   G4double nt1,nt2,nt3 ;
665   G4double Comp ;
666 
667   G4ThreeVector Normal;
668 
669   // Cone Precalcs
670 
671   tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
672   secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
673   rMinAv  = (fRmin1 + fRmin2)*0.5 ;
674 
675   if (rMinAv > halfRadTolerance)
676   {
677     rMinOAv = rMinAv - halfRadTolerance ;
678   }
679   else
680   {
681     rMinOAv = 0.0 ;
682   }  
683   tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
684   secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
685   rMaxAv  = (fRmax1 + fRmax2)*0.5 ;
686   rMaxOAv = rMaxAv + halfRadTolerance ;
687    
688   // Intersection with z-surfaces
689 
690   tolIDz = fDz - halfCarTolerance ;
691   tolODz = fDz + halfCarTolerance ;
692 
693   if (std::fabs(p.z()) >= tolIDz)
694   {
695     if ( p.z()*v.z() < 0 )    // at +Z going in -Z or visa versa
696     {
697       sd = (std::fabs(p.z()) - fDz)/std::fabs(v.z()) ; // Z intersect distance
698 
699       if( sd < 0.0 )  { sd = 0.0; }                    // negative dist -> zero
700 
701       xi   = p.x() + sd*v.x() ;  // Intersection coords
702       yi   = p.y() + sd*v.y() ;
703       rhoi2 = xi*xi + yi*yi  ;
704 
705       // Check validity of intersection
706       // Calculate (outer) tolerant radi^2 at intersecion
707 
708       if (v.z() > 0)
709       {
710         tolORMin  = fRmin1 - halfRadTolerance*secRMin ;
711         tolIRMin  = fRmin1 + halfRadTolerance*secRMin ;
712         tolIRMax  = fRmax1 - halfRadTolerance*secRMin ;
713         // tolORMax2 = (fRmax1 + halfRadTolerance*secRMax)*
714         //             (fRmax1 + halfRadTolerance*secRMax) ;
715       }
716       else
717       {
718         tolORMin  = fRmin2 - halfRadTolerance*secRMin ;
719         tolIRMin  = fRmin2 + halfRadTolerance*secRMin ;
720         tolIRMax  = fRmax2 - halfRadTolerance*secRMin ;
721         // tolORMax2 = (fRmax2 + halfRadTolerance*secRMax)*
722         //             (fRmax2 + halfRadTolerance*secRMax) ;
723       }
724       if ( tolORMin > 0 ) 
725       {
726         // tolORMin2 = tolORMin*tolORMin ;
727         tolIRMin2 = tolIRMin*tolIRMin ;
728       }
729       else                
730       {
731         // tolORMin2 = 0.0 ;
732         tolIRMin2 = 0.0 ;
733       }
734       if ( tolIRMax > 0 )  { tolIRMax2 = tolIRMax*tolIRMax; }     
735       else                 { tolIRMax2 = 0.0; }
736       
737       if ( (tolIRMin2 <= rhoi2) && (rhoi2 <= tolIRMax2) )
738       {
739         if ( !fPhiFullCone && (rhoi2 != 0.0) )
740         {
741           // Psi = angle made with central (average) phi of shape
742 
743           cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
744 
745           if (cosPsi >= cosHDPhiIT)  { return sd; }
746         }
747         else
748         {
749           return sd;
750         }
751       }
752     }
753     else  // On/outside extent, and heading away  -> cannot intersect
754     {
755       return snxt ;  
756     }
757   }
758     
759 // ----> Can not intersect z surfaces
760 
761 
762 // Intersection with outer cone (possible return) and
763 //                   inner cone (must also check phi)
764 //
765 // Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
766 //
767 // Intersects with x^2+y^2=(a*z+b)^2
768 //
769 // where a=tanRMax or tanRMin
770 //       b=rMaxAv  or rMinAv
771 //
772 // (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0 ;
773 //     t1                        t2                      t3  
774 //
775 //  \--------u-------/       \-----------v----------/ \---------w--------/
776 //
777 
778   t1   = 1.0 - v.z()*v.z() ;
779   t2   = p.x()*v.x() + p.y()*v.y() ;
780   t3   = p.x()*p.x() + p.y()*p.y() ;
781   rin  = tanRMin*p.z() + rMinAv ;
782   rout = tanRMax*p.z() + rMaxAv ;
783 
784   // Outer Cone Intersection
785   // Must be outside/on outer cone for valid intersection
786 
787   nt1 = t1 - (tanRMax*v.z())*(tanRMax*v.z()) ;
788   nt2 = t2 - tanRMax*v.z()*rout ;
789   nt3 = t3 - rout*rout ;
790 
791   if (std::fabs(nt1) > kRadTolerance)  // Equation quadratic => 2 roots
792   {
793     b = nt2/nt1;
794     c = nt3/nt1;
795     d = b*b-c  ;
796     if ( (nt3 > rout*rout*kRadTolerance*kRadTolerance*secRMax*secRMax)
797       || (rout < 0) )
798     {
799       // If outside real cone (should be rho-rout>kRadTolerance*0.5
800       // NOT rho^2 etc) saves a std::sqrt() at expense of accuracy
801 
802       if (d >= 0)
803       {
804           
805         if ((rout < 0) && (nt3 <= 0))
806         {
807           // Inside `shadow cone' with -ve radius
808           // -> 2nd root could be on real cone
809 
810           if (b>0) { sd = c/(-b-std::sqrt(d)); }
811           else     { sd = -b + std::sqrt(d);   }
812         }
813         else
814         {
815           if ((b <= 0) && (c >= 0)) // both >=0, try smaller root
816           {
817             sd=c/(-b+std::sqrt(d));
818           }
819           else
820           {
821             if ( c <= 0 ) // second >=0
822             {
823               sd = -b + std::sqrt(d) ;
824               if((sd<0.0) && (sd>-halfRadTolerance)) { sd = 0.0; }
825             }
826             else  // both negative, travel away
827             {
828               return kInfinity ;
829             }
830           }
831         }
832         if ( sd >= 0 )  // If 'forwards'. Check z intersection
833         {
834           if ( sd>dRmax ) // Avoid rounding errors due to precision issues on
835           {               // 64 bits systems. Split long distances and recompute
836             G4double fTerm = sd-std::fmod(sd,dRmax);
837             sd = fTerm + DistanceToIn(p+fTerm*v,v);
838           } 
839           zi = p.z() + sd*v.z() ;
840 
841           if (std::fabs(zi) <= tolODz)
842           {
843             // Z ok. Check phi intersection if reqd
844 
845             if ( fPhiFullCone )  { return sd; }
846             else
847             {
848               xi     = p.x() + sd*v.x() ;
849               yi     = p.y() + sd*v.y() ;
850               ri     = rMaxAv + zi*tanRMax ;
851               cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
852 
853               if ( cosPsi >= cosHDPhiIT )  { return sd; }
854             }
855           }
856         }                // end if (sd>0)
857       }
858     }
859     else
860     {
861       // Inside outer cone
862       // check not inside, and heading through G4Cons (-> 0 to in)
863 
864       if ( ( t3  > (rin + halfRadTolerance*secRMin)*
865                    (rin + halfRadTolerance*secRMin) )
866         && (nt2 < 0) && (d >= 0) && (std::fabs(p.z()) <= tolIDz) )
867       {
868         // Inside cones, delta r -ve, inside z extent
869         // Point is on the Surface => check Direction using  Normal.dot(v)
870 
871         xi     = p.x() ;
872         yi     = p.y()  ;
873         risec  = std::sqrt(xi*xi + yi*yi)*secRMax ;
874         Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
875         if ( !fPhiFullCone )
876         {
877           cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(t3) ;
878           if ( cosPsi >= cosHDPhiIT )
879           {
880             if ( Normal.dot(v) <= 0 )  { return 0.0; }
881           }
882         }
883         else
884         {             
885           if ( Normal.dot(v) <= 0 )  { return 0.0; }
886         }
887       }
888     }
889   }
890   else  //  Single root case 
891   {
892     if ( std::fabs(nt2) > kRadTolerance )
893     {
894       sd = -0.5*nt3/nt2 ;
895 
896       if ( sd < 0 )  { return kInfinity; }   // travel away
897       else  // sd >= 0,  If 'forwards'. Check z intersection
898       {
899         zi = p.z() + sd*v.z() ;
900 
901         if ((std::fabs(zi) <= tolODz) && (nt2 < 0))
902         {
903           // Z ok. Check phi intersection if reqd
904 
905           if ( fPhiFullCone )  { return sd; }
906           else
907           {
908             xi     = p.x() + sd*v.x() ;
909             yi     = p.y() + sd*v.y() ;
910             ri     = rMaxAv + zi*tanRMax ;
911             cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
912 
913             if (cosPsi >= cosHDPhiIT)  { return sd; }
914           }
915         }
916       }
917     }
918     else  //    travel || cone surface from its origin
919     {
920       sd = kInfinity ;
921     }
922   }
923 
924   // Inner Cone Intersection
925   // o Space is divided into 3 areas:
926   //   1) Radius greater than real inner cone & imaginary cone & outside
927   //      tolerance
928   //   2) Radius less than inner or imaginary cone & outside tolarance
929   //   3) Within tolerance of real or imaginary cones
930   //      - Extra checks needed for 3's intersections
931   //        => lots of duplicated code
932 
933   if (rMinAv != 0.0)
934   { 
935     nt1 = t1 - (tanRMin*v.z())*(tanRMin*v.z()) ;
936     nt2 = t2 - tanRMin*v.z()*rin ;
937     nt3 = t3 - rin*rin ;
938  
939     if ( nt1 != 0.0 )
940     {
941       if ( nt3 > rin*kRadTolerance*secRMin )
942       {
943         // At radius greater than real & imaginary cones
944         // -> 2nd root, with zi check
945 
946         b = nt2/nt1 ;
947         c = nt3/nt1 ;
948         d = b*b-c ;
949         if (d >= 0)   // > 0
950         {
951            if(b>0){sd = c/( -b-std::sqrt(d));}
952            else   {sd = -b + std::sqrt(d) ;}
953 
954           if ( sd >= 0 )   // > 0
955           {
956             if ( sd>dRmax ) // Avoid rounding errors due to precision issues on
957             {               // 64 bits systems. Split long distance and recompute
958               G4double fTerm = sd-std::fmod(sd,dRmax);
959               sd = fTerm + DistanceToIn(p+fTerm*v,v);
960             } 
961             zi = p.z() + sd*v.z() ;
962 
963             if ( std::fabs(zi) <= tolODz )
964             {
965               if ( !fPhiFullCone )
966               {
967                 xi     = p.x() + sd*v.x() ;
968                 yi     = p.y() + sd*v.y() ;
969                 ri     = rMinAv + zi*tanRMin ;
970                 cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
971 
972                 if (cosPsi >= cosHDPhiIT)
973                 { 
974                   if ( sd > halfRadTolerance )  { snxt=sd; }
975                   else
976                   {
977                     // Calculate a normal vector in order to check Direction
978 
979                     risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
980                     Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
981                     if ( Normal.dot(v) <= 0 )  { snxt = sd; }
982                   } 
983                 }
984               }
985               else
986               {
987                 if ( sd > halfRadTolerance )  { return sd; }
988                 else
989                 {
990                   // Calculate a normal vector in order to check Direction
991 
992                   xi     = p.x() + sd*v.x() ;
993                   yi     = p.y() + sd*v.y() ;
994                   risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
995                   Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
996                   if ( Normal.dot(v) <= 0 )  { return sd; }
997                 }
998               }
999             }
1000           }
1001         }
1002       }
1003       else  if ( nt3 < -rin*kRadTolerance*secRMin )
1004       {
1005         // Within radius of inner cone (real or imaginary)
1006         // -> Try 2nd root, with checking intersection is with real cone
1007         // -> If check fails, try 1st root, also checking intersection is
1008         //    on real cone
1009 
1010         b = nt2/nt1 ;
1011         c = nt3/nt1 ;
1012         d = b*b - c ;
1013 
1014         if ( d >= 0 )  // > 0
1015         {
1016           if (b>0) { sd = c/(-b-std::sqrt(d)); }
1017           else     { sd = -b + std::sqrt(d);   }
1018           zi = p.z() + sd*v.z() ;
1019           ri = rMinAv + zi*tanRMin ;
1020 
1021           if ( ri > 0 )
1022           {
1023             if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd > 0
1024             {
1025               if ( sd>dRmax ) // Avoid rounding errors due to precision issues
1026               {               // seen on 64 bits systems. Split and recompute
1027                 G4double fTerm = sd-std::fmod(sd,dRmax);
1028                 sd = fTerm + DistanceToIn(p+fTerm*v,v);
1029               } 
1030               if ( !fPhiFullCone )
1031               {
1032                 xi     = p.x() + sd*v.x() ;
1033                 yi     = p.y() + sd*v.y() ;
1034                 cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
1035 
1036                 if (cosPsi >= cosHDPhiOT)
1037                 {
1038                   if ( sd > halfRadTolerance )  { snxt=sd; }
1039                   else
1040                   {
1041                     // Calculate a normal vector in order to check Direction
1042 
1043                     risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
1044                     Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
1045                     if ( Normal.dot(v) <= 0 )  { snxt = sd; } 
1046                   }
1047                 }
1048               }
1049               else
1050               {
1051                 if( sd > halfRadTolerance )  { return sd; }
1052                 else
1053                 {
1054                   // Calculate a normal vector in order to check Direction
1055 
1056                   xi     = p.x() + sd*v.x() ;
1057                   yi     = p.y() + sd*v.y() ;
1058                   risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
1059                   Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
1060                   if ( Normal.dot(v) <= 0 )  { return sd; }
1061                 } 
1062               }
1063             }
1064           }
1065           else
1066           {
1067             if (b>0) { sd = -b - std::sqrt(d);   }
1068             else     { sd = c/(-b+std::sqrt(d)); }
1069             zi = p.z() + sd*v.z() ;
1070             ri = rMinAv + zi*tanRMin ;
1071 
1072             if ( (sd >= 0) && (ri > 0) && (std::fabs(zi) <= tolODz) ) // sd>0
1073             {
1074               if ( sd>dRmax ) // Avoid rounding errors due to precision issues
1075               {               // seen on 64 bits systems. Split and recompute
1076                 G4double fTerm = sd-std::fmod(sd,dRmax);
1077                 sd = fTerm + DistanceToIn(p+fTerm*v,v);
1078               } 
1079               if ( !fPhiFullCone )
1080               {
1081                 xi     = p.x() + sd*v.x() ;
1082                 yi     = p.y() + sd*v.y() ;
1083                 cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
1084 
1085                 if (cosPsi >= cosHDPhiIT)
1086                 {
1087                   if ( sd > halfRadTolerance )  { snxt=sd; }
1088                   else
1089                   {
1090                     // Calculate a normal vector in order to check Direction
1091 
1092                     risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
1093                     Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin);
1094                     if ( Normal.dot(v) <= 0 )  { snxt = sd; } 
1095                   }
1096                 }
1097               }
1098               else
1099               {
1100                 if ( sd > halfRadTolerance )  { return sd; }
1101                 else
1102                 {
1103                   // Calculate a normal vector in order to check Direction
1104 
1105                   xi     = p.x() + sd*v.x() ;
1106                   yi     = p.y() + sd*v.y() ;
1107                   risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
1108                   Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
1109                   if ( Normal.dot(v) <= 0 )  { return sd; }
1110                 } 
1111               }
1112             }
1113           }
1114         }
1115       }
1116       else
1117       {
1118         // Within kRadTol*0.5 of inner cone (real OR imaginary)
1119         // ----> Check not travelling through (=>0 to in)
1120         // ----> if not:
1121         //    -2nd root with validity check
1122 
1123         if ( std::fabs(p.z()) <= tolODz )
1124         {
1125           if ( nt2 > 0 )
1126           {
1127             // Inside inner real cone, heading outwards, inside z range
1128 
1129             if ( !fPhiFullCone )
1130             {
1131               cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(t3) ;
1132 
1133               if (cosPsi >= cosHDPhiIT)  { return 0.0; }
1134             }
1135             else  { return 0.0; }
1136           }
1137           else
1138           {
1139             // Within z extent, but not travelling through
1140             // -> 2nd root or kInfinity if 1st root on imaginary cone
1141 
1142             b = nt2/nt1 ;
1143             c = nt3/nt1 ;
1144             d = b*b - c ;
1145 
1146             if ( d >= 0 )   // > 0
1147             {
1148               if (b>0) { sd = -b - std::sqrt(d);   }
1149               else     { sd = c/(-b+std::sqrt(d)); }
1150               zi = p.z() + sd*v.z() ;
1151               ri = rMinAv + zi*tanRMin ;
1152               
1153               if ( ri > 0 )   // 2nd root
1154               {
1155                 if (b>0) { sd = c/(-b-std::sqrt(d)); }
1156                 else     { sd = -b + std::sqrt(d);   }
1157                 
1158                 zi = p.z() + sd*v.z() ;
1159 
1160                 if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd>0
1161                 {
1162                   if ( sd>dRmax ) // Avoid rounding errors due to precision issue
1163                   {               // seen on 64 bits systems. Split and recompute
1164                     G4double fTerm = sd-std::fmod(sd,dRmax);
1165                     sd = fTerm + DistanceToIn(p+fTerm*v,v);
1166                   } 
1167                   if ( !fPhiFullCone )
1168                   {
1169                     xi     = p.x() + sd*v.x() ;
1170                     yi     = p.y() + sd*v.y() ;
1171                     ri     = rMinAv + zi*tanRMin ;
1172                     cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri ;
1173 
1174                     if ( cosPsi >= cosHDPhiIT )  { snxt = sd; }
1175                   }
1176                   else  { return sd; }
1177                 }
1178               }
1179               else  { return kInfinity; }
1180             }
1181           }
1182         }
1183         else   // 2nd root
1184         {
1185           b = nt2/nt1 ;
1186           c = nt3/nt1 ;
1187           d = b*b - c ;
1188 
1189           if ( d > 0 )
1190           {  
1191             if (b>0) { sd = c/(-b-std::sqrt(d)); }
1192             else     { sd = -b + std::sqrt(d) ;  }
1193             zi = p.z() + sd*v.z() ;
1194 
1195             if ( (sd >= 0) && (std::fabs(zi) <= tolODz) )  // sd>0
1196             {
1197               if ( sd>dRmax ) // Avoid rounding errors due to precision issues
1198               {               // seen on 64 bits systems. Split and recompute
1199                 G4double fTerm = sd-std::fmod(sd,dRmax);
1200                 sd = fTerm + DistanceToIn(p+fTerm*v,v);
1201               } 
1202               if ( !fPhiFullCone )
1203               {
1204                 xi     = p.x() + sd*v.x();
1205                 yi     = p.y() + sd*v.y();
1206                 ri     = rMinAv + zi*tanRMin ;
1207                 cosPsi = (xi*cosCPhi + yi*sinCPhi)/ri;
1208 
1209                 if (cosPsi >= cosHDPhiIT)  { snxt = sd; }
1210               }
1211               else  { return sd; }
1212             }
1213           }
1214         }
1215       }
1216     }
1217   }
1218 
1219   // Phi segment intersection
1220   //
1221   // o Tolerant of points inside phi planes by up to kCarTolerance*0.5
1222   //
1223   // o NOTE: Large duplication of code between sphi & ephi checks
1224   //         -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
1225   //            intersection check <=0 -> >=0
1226   //         -> Should use some form of loop Construct
1227 
1228   if ( !fPhiFullCone )
1229   {
1230     // First phi surface (starting phi)
1231 
1232     Comp    = v.x()*sinSPhi - v.y()*cosSPhi ;
1233                     
1234     if ( Comp < 0 )    // Component in outwards normal dirn
1235     {
1236       Dist = (p.y()*cosSPhi - p.x()*sinSPhi) ;
1237 
1238       if (Dist < halfCarTolerance)
1239       {
1240         sd = Dist/Comp ;
1241 
1242         if ( sd < snxt )
1243         {
1244           if ( sd < 0 )  { sd = 0.0; }
1245 
1246           zi = p.z() + sd*v.z() ;
1247 
1248           if ( std::fabs(zi) <= tolODz )
1249           {
1250             xi        = p.x() + sd*v.x() ;
1251             yi        = p.y() + sd*v.y() ;
1252             rhoi2     = xi*xi + yi*yi ;
1253             tolORMin2 = (rMinOAv + zi*tanRMin)*(rMinOAv + zi*tanRMin) ;
1254             tolORMax2 = (rMaxOAv + zi*tanRMax)*(rMaxOAv + zi*tanRMax) ;
1255 
1256             if ( (rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2) )
1257             {
1258               // z and r intersections good - check intersecting with
1259               // correct half-plane
1260 
1261               if ((yi*cosCPhi - xi*sinCPhi) <= 0 )  { snxt = sd; }
1262             }
1263           }
1264         }
1265       }
1266     }
1267 
1268     // Second phi surface (Ending phi)
1269 
1270     Comp    = -(v.x()*sinEPhi - v.y()*cosEPhi) ;
1271         
1272     if ( Comp < 0 )   // Component in outwards normal dirn
1273     {
1274       Dist = -(p.y()*cosEPhi - p.x()*sinEPhi) ;
1275       if (Dist < halfCarTolerance)
1276       {
1277         sd = Dist/Comp ;
1278 
1279         if ( sd < snxt )
1280         {
1281           if ( sd < 0 )  { sd = 0.0; }
1282 
1283           zi = p.z() + sd*v.z() ;
1284 
1285           if (std::fabs(zi) <= tolODz)
1286           {
1287             xi        = p.x() + sd*v.x() ;
1288             yi        = p.y() + sd*v.y() ;
1289             rhoi2     = xi*xi + yi*yi ;
1290             tolORMin2 = (rMinOAv + zi*tanRMin)*(rMinOAv + zi*tanRMin) ;
1291             tolORMax2 = (rMaxOAv + zi*tanRMax)*(rMaxOAv + zi*tanRMax) ;
1292 
1293             if ( (rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2) )
1294             {
1295               // z and r intersections good - check intersecting with
1296               // correct half-plane
1297 
1298               if ( (yi*cosCPhi - xi*sinCPhi) >= 0.0 )  { snxt = sd; }
1299             }
1300           }
1301         }
1302       }
1303     }
1304   }
1305   if (snxt < halfCarTolerance)  { snxt = 0.; }
1306 
1307   return snxt ;
1308 }
1309 
1310 //////////////////////////////////////////////////////////////////////////////
1311 // 
1312 // Calculate distance (<= actual) to closest surface of shape from outside
1313 // - Calculate distance to z, radial planes
1314 // - Only to phi planes if outside phi extent
1315 // - Return 0 if point inside
1316 
1317 G4double G4Cons::DistanceToIn(const G4ThreeVector& p) const
1318 {
1319   G4double safe=0.0, rho, safeR1, safeR2, safeZ, safePhi, cosPsi ;
1320   G4double tanRMin, secRMin, pRMin ;
1321   G4double tanRMax, secRMax, pRMax ;
1322 
1323   rho   = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
1324   safeZ = std::fabs(p.z()) - fDz ;
1325 
1326   if ( (fRmin1 != 0.0) || (fRmin2 != 0.0) )
1327   {
1328     tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
1329     secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
1330     pRMin   = tanRMin*p.z() + (fRmin1 + fRmin2)*0.5 ;
1331     safeR1  = (pRMin - rho)/secRMin ;
1332 
1333     tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
1334     secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
1335     pRMax   = tanRMax*p.z() + (fRmax1 + fRmax2)*0.5 ;
1336     safeR2  = (rho - pRMax)/secRMax ;
1337 
1338     if ( safeR1 > safeR2) { safe = safeR1; }
1339     else                  { safe = safeR2; }
1340   }
1341   else
1342   {
1343     tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
1344     secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
1345     pRMax   = tanRMax*p.z() + (fRmax1 + fRmax2)*0.5 ;
1346     safe    = (rho - pRMax)/secRMax ;
1347   }
1348   if ( safeZ > safe )  { safe = safeZ; }
1349 
1350   if ( !fPhiFullCone && (rho != 0.0) )
1351   {
1352     // Psi=angle from central phi to point
1353 
1354     cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/rho ;
1355 
1356     if ( cosPsi < cosHDPhi ) // Point lies outside phi range
1357     {
1358       if ( (p.y()*cosCPhi - p.x()*sinCPhi) <= 0.0 )
1359       {
1360         safePhi = std::fabs(p.x()*sinSPhi-p.y()*cosSPhi);
1361       }
1362       else
1363       {
1364         safePhi = std::fabs(p.x()*sinEPhi-p.y()*cosEPhi);
1365       }
1366       if ( safePhi > safe )  { safe = safePhi; }
1367     }
1368   }
1369   if ( safe < 0.0 )  { safe = 0.0; }
1370 
1371   return safe ;
1372 }
1373 
1374 ///////////////////////////////////////////////////////////////
1375 //
1376 // Calculate distance to surface of shape from 'inside', allowing for tolerance
1377 // - Only Calc rmax intersection if no valid rmin intersection
1378 
1379 G4double G4Cons::DistanceToOut( const G4ThreeVector& p,
1380                                 const G4ThreeVector& v,
1381                                 const G4bool calcNorm,
1382                                       G4bool* validNorm,
1383                                       G4ThreeVector* n) const
1384 {
1385   ESide side = kNull, sider = kNull, sidephi = kNull;
1386 
1387   G4double snxt,srd,sphi,pdist ;
1388 
1389   G4double tanRMax, secRMax, rMaxAv ;  // Data for outer cone
1390   G4double tanRMin, secRMin, rMinAv ;  // Data for inner cone
1391 
1392   G4double t1, t2, t3, rout, rin, nt1, nt2, nt3 ;
1393   G4double b, c, d, sr2, sr3 ;
1394 
1395   // Vars for intersection within tolerance
1396 
1397   ESide sidetol = kNull ;
1398   G4double slentol = kInfinity ;
1399 
1400   // Vars for phi intersection:
1401 
1402   G4double pDistS, compS, pDistE, compE, sphi2, xi, yi, risec, vphi ;
1403   G4double zi, ri, deltaRoi2 ;
1404 
1405   // Z plane intersection
1406 
1407   if ( v.z() > 0.0 )
1408   {
1409     pdist = fDz - p.z() ;
1410 
1411     if (pdist > halfCarTolerance)
1412     {
1413       snxt = pdist/v.z() ;
1414       side = kPZ ;
1415     }
1416     else
1417     {
1418       if (calcNorm)
1419       {
1420         *n         = G4ThreeVector(0,0,1) ;
1421         *validNorm = true ;
1422       }
1423       return  snxt = 0.0;
1424     }
1425   }
1426   else if ( v.z() < 0.0 )
1427   {
1428     pdist = fDz + p.z() ;
1429 
1430     if ( pdist > halfCarTolerance)
1431     {
1432       snxt = -pdist/v.z() ;
1433       side = kMZ ;
1434     }
1435     else
1436     {
1437       if ( calcNorm )
1438       {
1439         *n         = G4ThreeVector(0,0,-1) ;
1440         *validNorm = true ;
1441       }
1442       return snxt = 0.0 ;
1443     }
1444   }
1445   else     // Travel perpendicular to z axis
1446   {
1447     snxt = kInfinity ;    
1448     side = kNull ;
1449   }
1450 
1451   // Radial Intersections
1452   //
1453   // Intersection with outer cone (possible return) and
1454   //                   inner cone (must also check phi)
1455   //
1456   // Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
1457   //
1458   // Intersects with x^2+y^2=(a*z+b)^2
1459   //
1460   // where a=tanRMax or tanRMin
1461   //       b=rMaxAv  or rMinAv
1462   //
1463   // (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0 ;
1464   //     t1                        t2                      t3  
1465   //
1466   //  \--------u-------/       \-----------v----------/ \---------w--------/
1467 
1468   tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
1469   secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
1470   rMaxAv  = (fRmax1 + fRmax2)*0.5 ;
1471 
1472 
1473   t1   = 1.0 - v.z()*v.z() ;      // since v normalised
1474   t2   = p.x()*v.x() + p.y()*v.y() ;
1475   t3   = p.x()*p.x() + p.y()*p.y() ;
1476   rout = tanRMax*p.z() + rMaxAv ;
1477 
1478   nt1 = t1 - (tanRMax*v.z())*(tanRMax*v.z()) ;
1479   nt2 = t2 - tanRMax*v.z()*rout ;
1480   nt3 = t3 - rout*rout ;
1481 
1482   if (v.z() > 0.0)
1483   {
1484     deltaRoi2 = snxt*snxt*t1 + 2*snxt*t2 + t3
1485                 - fRmax2*(fRmax2 + kRadTolerance*secRMax);
1486   }
1487   else if (v.z() < 0.0)
1488   {
1489     deltaRoi2 = snxt*snxt*t1 + 2*snxt*t2 + t3
1490                 - fRmax1*(fRmax1 + kRadTolerance*secRMax);
1491   }
1492   else
1493   {
1494     deltaRoi2 = 1.0;
1495   }
1496 
1497   if ( (nt1 != 0.0) && (deltaRoi2 > 0.0) )  
1498   {
1499     // Equation quadratic => 2 roots : second root must be leaving
1500 
1501     b = nt2/nt1 ;
1502     c = nt3/nt1 ;
1503     d = b*b - c ;
1504 
1505     if ( d >= 0 )
1506     {
1507       // Check if on outer cone & heading outwards
1508       // NOTE: Should use rho-rout>-kRadTolerance*0.5
1509         
1510       if (nt3 > -halfRadTolerance && nt2 >= 0 )
1511       {
1512         if (calcNorm)
1513         {
1514           risec      = std::sqrt(t3)*secRMax ;
1515           *validNorm = true ;
1516           *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
1517         }
1518         return snxt=0 ;
1519       }
1520       else
1521       {
1522         sider = kRMax  ;
1523         if (b>0) { srd = -b - std::sqrt(d);    }
1524         else     { srd = c/(-b+std::sqrt(d)) ; }
1525 
1526         zi    = p.z() + srd*v.z() ;
1527         ri    = tanRMax*zi + rMaxAv ;
1528           
1529         if ((ri >= 0) && (-halfRadTolerance <= srd) && (srd <= halfRadTolerance))
1530         {
1531           // An intersection within the tolerance
1532           //   we will Store it in case it is good -
1533           // 
1534           slentol = srd ;
1535           sidetol = kRMax ;
1536         }            
1537         if ( (ri < 0) || (srd < halfRadTolerance) )
1538         {
1539           // Safety: if both roots -ve ensure that srd cannot `win'
1540           //         distance to out
1541 
1542           if (b>0) { sr2 = c/(-b-std::sqrt(d)); }
1543           else     { sr2 = -b + std::sqrt(d);   }
1544           zi  = p.z() + sr2*v.z() ;
1545           ri  = tanRMax*zi + rMaxAv ;
1546 
1547           if ((ri >= 0) && (sr2 > halfRadTolerance))
1548           {
1549             srd = sr2;
1550           }
1551           else
1552           {
1553             srd = kInfinity ;
1554 
1555             if( (-halfRadTolerance <= sr2) && ( sr2 <= halfRadTolerance) )
1556             {
1557               // An intersection within the tolerance.
1558               // Storing it in case it is good.
1559 
1560               slentol = sr2 ;
1561               sidetol = kRMax ;
1562             }
1563           }
1564         }
1565       }
1566     }
1567     else
1568     {
1569       // No intersection with outer cone & not parallel
1570       // -> already outside, no intersection
1571 
1572       if ( calcNorm )
1573       {
1574         risec      = std::sqrt(t3)*secRMax;
1575         *validNorm = true;
1576         *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
1577       }
1578       return snxt = 0.0 ;
1579     }
1580   }
1581   else if ( (nt2 != 0.0) && (deltaRoi2 > 0.0) )
1582   {
1583     // Linear case (only one intersection) => point outside outer cone
1584 
1585     if ( calcNorm )
1586     {
1587       risec      = std::sqrt(t3)*secRMax;
1588       *validNorm = true;
1589       *n         = G4ThreeVector(p.x()/risec,p.y()/risec,-tanRMax/secRMax);
1590     }
1591     return snxt = 0.0 ;
1592   }
1593   else
1594   {
1595     // No intersection -> parallel to outer cone
1596     // => Z or inner cone intersection
1597 
1598     srd = kInfinity ;
1599   }
1600 
1601   // Check possible intersection within tolerance
1602 
1603   if ( slentol <= halfCarTolerance )
1604   {
1605     // An intersection within the tolerance was found.  
1606     // We must accept it only if the momentum points outwards.  
1607     //
1608     // G4ThreeVector ptTol ;  // The point of the intersection  
1609     // ptTol= p + slentol*v ;
1610     // ri=tanRMax*zi+rMaxAv ;
1611     //
1612     // Calculate a normal vector,  as below
1613 
1614     xi    = p.x() + slentol*v.x();
1615     yi    = p.y() + slentol*v.y();
1616     risec = std::sqrt(xi*xi + yi*yi)*secRMax;
1617     G4ThreeVector Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax);
1618 
1619     if ( Normal.dot(v) > 0 )    // We will leave the Cone immediatelly
1620     {
1621       if ( calcNorm ) 
1622       {
1623         *n         = Normal.unit() ;
1624         *validNorm = true ;
1625       }
1626       return snxt = 0.0 ;
1627     }
1628     else // On the surface, but not heading out so we ignore this intersection
1629     {    //                                        (as it is within tolerance).
1630       slentol = kInfinity ;
1631     }
1632   }
1633 
1634   // Inner Cone intersection
1635 
1636   if ( (fRmin1 != 0.0) || (fRmin2 != 0.0) )
1637   {
1638     tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
1639     nt1     = t1 - (tanRMin*v.z())*(tanRMin*v.z()) ;
1640 
1641     if ( nt1 != 0.0 )
1642     {
1643       secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
1644       rMinAv  = (fRmin1 + fRmin2)*0.5 ;    
1645       rin     = tanRMin*p.z() + rMinAv ;
1646       nt2     = t2 - tanRMin*v.z()*rin ;
1647       nt3     = t3 - rin*rin ;
1648       
1649       // Equation quadratic => 2 roots : first root must be leaving
1650 
1651       b = nt2/nt1 ;
1652       c = nt3/nt1 ;
1653       d = b*b - c ;
1654 
1655       if ( d >= 0.0 )
1656       {
1657         // NOTE: should be rho-rin<kRadTolerance*0.5,
1658         //       but using squared versions for efficiency
1659 
1660         if (nt3 < kRadTolerance*(rin + kRadTolerance*0.25)) 
1661         {
1662           if ( nt2 < 0.0 )
1663           {
1664             if (calcNorm)  { *validNorm = false; }
1665             return          snxt      = 0.0;
1666           }
1667         }
1668         else
1669         {
1670           if (b>0) { sr2 = -b - std::sqrt(d);   }
1671           else     { sr2 = c/(-b+std::sqrt(d)); }
1672           zi  = p.z() + sr2*v.z() ;
1673           ri  = tanRMin*zi + rMinAv ;
1674 
1675           if( (ri>=0.0)&&(-halfRadTolerance<=sr2)&&(sr2<=halfRadTolerance) )
1676           {
1677             // An intersection within the tolerance
1678             // storing it in case it is good.
1679 
1680             slentol = sr2 ;
1681             sidetol = kRMax ;
1682           }
1683           if( (ri<0) || (sr2 < halfRadTolerance) )
1684           {
1685             if (b>0) { sr3 = c/(-b-std::sqrt(d)); }
1686             else     { sr3 = -b + std::sqrt(d) ;  }
1687 
1688             // Safety: if both roots -ve ensure that srd cannot `win'
1689             //         distancetoout
1690 
1691             if  ( sr3 > halfRadTolerance )
1692             {
1693               if( sr3 < srd )
1694               {
1695                 zi = p.z() + sr3*v.z() ;
1696                 ri = tanRMin*zi + rMinAv ;
1697 
1698                 if ( ri >= 0.0 )
1699                 {
1700                   srd=sr3 ;
1701                   sider=kRMin ;
1702                 }
1703               } 
1704             }
1705             else if ( sr3 > -halfRadTolerance )
1706             {
1707               // Intersection in tolerance. Store to check if it's good
1708 
1709               slentol = sr3 ;
1710               sidetol = kRMin ;
1711             }
1712           }
1713           else if ( (sr2 < srd) && (sr2 > halfCarTolerance) )
1714           {
1715             srd   = sr2 ;
1716             sider = kRMin ;
1717           }
1718           else if (sr2 > -halfCarTolerance)
1719           {
1720             // Intersection in tolerance. Store to check if it's good
1721 
1722             slentol = sr2 ;
1723             sidetol = kRMin ;
1724           }    
1725           if( slentol <= halfCarTolerance  )
1726           {
1727             // An intersection within the tolerance was found. 
1728             // We must accept it only if  the momentum points outwards. 
1729 
1730             G4ThreeVector Normal ; 
1731             
1732             // Calculate a normal vector,  as below
1733 
1734             xi     = p.x() + slentol*v.x() ;
1735             yi     = p.y() + slentol*v.y() ;
1736             if( sidetol==kRMax )
1737             {
1738               risec  = std::sqrt(xi*xi + yi*yi)*secRMax ;
1739               Normal = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
1740             }
1741             else
1742             {
1743               risec  = std::sqrt(xi*xi + yi*yi)*secRMin ;
1744               Normal = G4ThreeVector(-xi/risec,-yi/risec,tanRMin/secRMin) ;
1745             }
1746             if( Normal.dot(v) > 0 )
1747             {
1748               // We will leave the cone immediately
1749 
1750               if( calcNorm ) 
1751               {
1752                 *n         = Normal.unit() ;
1753                 *validNorm = true ;
1754               }
1755               return snxt = 0.0 ;
1756             }
1757             else 
1758             { 
1759               // On the surface, but not heading out so we ignore this
1760               // intersection (as it is within tolerance). 
1761 
1762               slentol = kInfinity ;
1763             }        
1764           }
1765         }
1766       }
1767     }
1768   }
1769 
1770   // Linear case => point outside inner cone ---> outer cone intersect
1771   //
1772   // Phi Intersection
1773   
1774   if ( !fPhiFullCone )
1775   {
1776     // add angle calculation with correction 
1777     // of the difference in domain of atan2 and Sphi
1778 
1779     vphi = std::atan2(v.y(),v.x()) ;
1780 
1781     if ( vphi < fSPhi - halfAngTolerance  )              { vphi += twopi; }
1782     else if ( vphi > fSPhi + fDPhi + halfAngTolerance )  { vphi -= twopi; }
1783 
1784     if ( (p.x() != 0.0) || (p.y() != 0.0) )   // Check if on z axis (rho not needed later)
1785     {
1786       // pDist -ve when inside
1787 
1788       pDistS = p.x()*sinSPhi - p.y()*cosSPhi ;
1789       pDistE = -p.x()*sinEPhi + p.y()*cosEPhi ;
1790 
1791       // Comp -ve when in direction of outwards normal
1792 
1793       compS = -sinSPhi*v.x() + cosSPhi*v.y() ;
1794       compE = sinEPhi*v.x() - cosEPhi*v.y() ;
1795 
1796       sidephi = kNull ;
1797 
1798       if( ( (fDPhi <= pi) && ( (pDistS <= halfCarTolerance)
1799                             && (pDistE <= halfCarTolerance) ) )
1800          || ( (fDPhi >  pi) && ((pDistS <=  halfCarTolerance)
1801                               || (pDistE <=  halfCarTolerance) ) )  )
1802       {
1803         // Inside both phi *full* planes
1804         if ( compS < 0 )
1805         {
1806           sphi = pDistS/compS ;
1807           if (sphi >= -halfCarTolerance)
1808           {
1809             xi = p.x() + sphi*v.x() ;
1810             yi = p.y() + sphi*v.y() ;
1811 
1812             // Check intersecting with correct half-plane
1813             // (if not -> no intersect)
1814             //
1815             if ( (std::fabs(xi)<=kCarTolerance)
1816               && (std::fabs(yi)<=kCarTolerance) )
1817             {
1818               sidephi= kSPhi;
1819               if ( ( fSPhi-halfAngTolerance <= vphi )
1820                 && ( fSPhi+fDPhi+halfAngTolerance >=vphi ) )
1821               {
1822                 sphi = kInfinity;
1823               }
1824             }
1825             else
1826             if ( (yi*cosCPhi-xi*sinCPhi)>=0 )
1827             {
1828               sphi = kInfinity ;
1829             }
1830             else
1831             {
1832               sidephi = kSPhi ;
1833               if ( pDistS > -halfCarTolerance )
1834               {
1835                 sphi = 0.0 ; // Leave by sphi immediately
1836               }    
1837             }       
1838           }
1839           else
1840           {
1841             sphi = kInfinity ;
1842           }
1843         }
1844         else
1845         {
1846           sphi = kInfinity ;
1847         }
1848 
1849         if ( compE < 0 )
1850         {
1851           sphi2 = pDistE/compE ;
1852 
1853           // Only check further if < starting phi intersection
1854           //
1855           if ( (sphi2 > -halfCarTolerance) && (sphi2 < sphi) )
1856           {
1857             xi = p.x() + sphi2*v.x() ;
1858             yi = p.y() + sphi2*v.y() ;
1859 
1860             // Check intersecting with correct half-plane
1861 
1862             if ( (std::fabs(xi)<=kCarTolerance)
1863               && (std::fabs(yi)<=kCarTolerance) )
1864             {
1865               // Leaving via ending phi
1866 
1867               if( (fSPhi-halfAngTolerance > vphi)
1868                  || (fSPhi+fDPhi+halfAngTolerance < vphi) )
1869               {
1870                 sidephi = kEPhi ;
1871                 if ( pDistE <= -halfCarTolerance )  { sphi = sphi2; }
1872                 else                                { sphi = 0.0; }
1873               }
1874             }
1875             else // Check intersecting with correct half-plane
1876             if ( yi*cosCPhi-xi*sinCPhi >= 0 )
1877             {
1878               // Leaving via ending phi
1879 
1880               sidephi = kEPhi ;
1881               if ( pDistE <= -halfCarTolerance )  { sphi = sphi2; }
1882               else                                { sphi = 0.0; }
1883             }
1884           }
1885         }
1886       }
1887       else
1888       {
1889         sphi = kInfinity ;
1890       }
1891     }
1892     else
1893     {
1894       // On z axis + travel not || to z axis -> if phi of vector direction
1895       // within phi of shape, Step limited by rmax, else Step =0
1896 
1897       if ( (fSPhi-halfAngTolerance <= vphi)
1898         && (vphi <= fSPhi+fDPhi+halfAngTolerance) )
1899       {
1900         sphi = kInfinity ;
1901       }
1902       else
1903       {
1904         sidephi = kSPhi  ;   // arbitrary 
1905         sphi    = 0.0 ;
1906       }
1907     }      
1908     if ( sphi < snxt )  // Order intersecttions
1909     {
1910       snxt = sphi ;
1911       side = sidephi ;
1912     }
1913   }
1914   if ( srd < snxt )  // Order intersections
1915   {
1916     snxt = srd   ;
1917     side = sider ;
1918   }
1919   if (calcNorm)
1920   {
1921     switch(side)
1922     {                     // Note: returned vector not normalised
1923       case kRMax:         // (divide by frmax for unit vector)
1924         xi         = p.x() + snxt*v.x() ;
1925         yi         = p.y() + snxt*v.y() ;
1926         risec      = std::sqrt(xi*xi + yi*yi)*secRMax ;
1927         *n         = G4ThreeVector(xi/risec,yi/risec,-tanRMax/secRMax) ;
1928         *validNorm = true ;
1929         break ;
1930       case kRMin:
1931         *validNorm = false ;  // Rmin is inconvex
1932         break ;
1933       case kSPhi:
1934         if ( fDPhi <= pi )
1935         {
1936           *n         = G4ThreeVector(sinSPhi, -cosSPhi, 0);
1937           *validNorm = true ;
1938         }
1939         else
1940         {
1941           *validNorm = false ;
1942         }
1943         break ;
1944       case kEPhi:
1945         if ( fDPhi <= pi )
1946         {
1947           *n = G4ThreeVector(-sinEPhi, cosEPhi, 0);
1948           *validNorm = true ;
1949         }
1950         else
1951         {
1952           *validNorm = false ;
1953         }
1954         break ;
1955       case kPZ:
1956         *n         = G4ThreeVector(0,0,1) ;
1957         *validNorm = true ;
1958         break ;
1959       case kMZ:
1960         *n         = G4ThreeVector(0,0,-1) ;
1961         *validNorm = true ;
1962         break ;
1963       default:
1964         G4cout << G4endl ;
1965         DumpInfo();
1966         std::ostringstream message;
1967         G4long oldprc = message.precision(16) ;
1968         message << "Undefined side for valid surface normal to solid."
1969                 << G4endl
1970                 << "Position:"  << G4endl << G4endl
1971                 << "p.x() = "   << p.x()/mm << " mm" << G4endl
1972                 << "p.y() = "   << p.y()/mm << " mm" << G4endl
1973                 << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl
1974                 << "pho at z = "   << std::sqrt( p.x()*p.x()+p.y()*p.y() )/mm
1975                 << " mm" << G4endl << G4endl ;
1976         if( p.x() != 0. || p.y() != 0.)
1977         {
1978            message << "point phi = "   << std::atan2(p.y(),p.x())/degree
1979                    << " degrees" << G4endl << G4endl ; 
1980         }
1981         message << "Direction:" << G4endl << G4endl
1982                 << "v.x() = "   << v.x() << G4endl
1983                 << "v.y() = "   << v.y() << G4endl
1984                 << "v.z() = "   << v.z() << G4endl<< G4endl
1985                 << "Proposed distance :" << G4endl<< G4endl
1986                 << "snxt = "    << snxt/mm << " mm" << G4endl ;
1987         message.precision(oldprc) ;
1988         G4Exception("G4Cons::DistanceToOut(p,v,..)","GeomSolids1002",
1989                     JustWarning, message) ;
1990         break ;
1991     }
1992   }
1993   if (snxt < halfCarTolerance)  { snxt = 0.; }
1994 
1995   return snxt ;
1996 }
1997 
1998 //////////////////////////////////////////////////////////////////
1999 //
2000 // Calculate distance (<=actual) to closest surface of shape from inside
2001 
2002 G4double G4Cons::DistanceToOut(const G4ThreeVector& p) const
2003 {
2004   G4double safe=0.0, rho, safeR1, safeR2, safeZ, safePhi;
2005   G4double tanRMin, secRMin, pRMin;
2006   G4double tanRMax, secRMax, pRMax;
2007 
2008 #ifdef G4CSGDEBUG
2009   if( Inside(p) == kOutside )
2010   {
2011     G4int oldprc=G4cout.precision(16) ;
2012     G4cout << G4endl ;
2013     DumpInfo();
2014     G4cout << "Position:"  << G4endl << G4endl ;
2015     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
2016     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
2017     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
2018     G4cout << "pho at z = "   << std::sqrt( p.x()*p.x()+p.y()*p.y() )/mm
2019            << " mm" << G4endl << G4endl ;
2020     if( (p.x() != 0.) || (p.x() != 0.) )
2021     {
2022       G4cout << "point phi = "   << std::atan2(p.y(),p.x())/degree
2023              << " degrees" << G4endl << G4endl ; 
2024     }
2025     G4cout.precision(oldprc) ;
2026     G4Exception("G4Cons::DistanceToOut(p)", "GeomSolids1002",
2027                 JustWarning, "Point p is outside !?" );
2028   }
2029 #endif
2030 
2031   rho = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
2032   safeZ = fDz - std::fabs(p.z()) ;
2033 
2034   if ((fRmin1 != 0.0) || (fRmin2 != 0.0))
2035   {
2036     tanRMin = (fRmin2 - fRmin1)*0.5/fDz ;
2037     secRMin = std::sqrt(1.0 + tanRMin*tanRMin) ;
2038     pRMin   = tanRMin*p.z() + (fRmin1 + fRmin2)*0.5 ;
2039     safeR1  = (rho - pRMin)/secRMin ;
2040   }
2041   else
2042   {
2043     safeR1 = kInfinity ;
2044   }
2045 
2046   tanRMax = (fRmax2 - fRmax1)*0.5/fDz ;
2047   secRMax = std::sqrt(1.0 + tanRMax*tanRMax) ;
2048   pRMax   = tanRMax*p.z() + (fRmax1+fRmax2)*0.5 ;
2049   safeR2  = (pRMax - rho)/secRMax ;
2050 
2051   if (safeR1 < safeR2)  { safe = safeR1; }
2052   else                  { safe = safeR2; }
2053   if (safeZ < safe)     { safe = safeZ ; }
2054 
2055   // Check if phi divided, Calc distances closest phi plane
2056 
2057   if (!fPhiFullCone)
2058   {
2059     // Above/below central phi of G4Cons?
2060 
2061     if ( (p.y()*cosCPhi - p.x()*sinCPhi) <= 0 )
2062     {
2063       safePhi = -(p.x()*sinSPhi - p.y()*cosSPhi) ;
2064     }
2065     else
2066     {
2067       safePhi = (p.x()*sinEPhi - p.y()*cosEPhi) ;
2068     }
2069     if (safePhi < safe)  { safe = safePhi; }
2070   }
2071   if ( safe < 0 )  { safe = 0; }
2072 
2073   return safe ;
2074 }
2075 
2076 //////////////////////////////////////////////////////////////////////////
2077 //
2078 // GetEntityType
2079 
2080 G4GeometryType G4Cons::GetEntityType() const
2081 {
2082   return {"G4Cons"};
2083 }
2084 
2085 //////////////////////////////////////////////////////////////////////////
2086 //
2087 // Make a clone of the object
2088 //
2089 G4VSolid* G4Cons::Clone() const
2090 {
2091   return new G4Cons(*this);
2092 }
2093 
2094 //////////////////////////////////////////////////////////////////////////
2095 //
2096 // Stream object contents to an output stream
2097 
2098 std::ostream& G4Cons::StreamInfo(std::ostream& os) const
2099 {
2100   G4long oldprc = os.precision(16);
2101   os << "-----------------------------------------------------------\n"
2102      << "    *** Dump for solid - " << GetName() << " ***\n"
2103      << "    ===================================================\n"
2104      << " Solid type: G4Cons\n"
2105      << " Parameters: \n"
2106      << "   inside  -fDz radius: "  << fRmin1/mm << " mm \n"
2107      << "   outside -fDz radius: "  << fRmax1/mm << " mm \n"
2108      << "   inside  +fDz radius: "  << fRmin2/mm << " mm \n"
2109      << "   outside +fDz radius: "  << fRmax2/mm << " mm \n"
2110      << "   half length in Z   : "  << fDz/mm << " mm \n"
2111      << "   starting angle of segment: " << fSPhi/degree << " degrees \n"
2112      << "   delta angle of segment   : " << fDPhi/degree << " degrees \n"
2113      << "-----------------------------------------------------------\n";
2114   os.precision(oldprc);
2115 
2116   return os;
2117 }
2118 
2119 
2120 
2121 /////////////////////////////////////////////////////////////////////////
2122 //
2123 // GetPointOnSurface
2124 
2125 G4ThreeVector G4Cons::GetPointOnSurface() const
2126 {
2127   // declare working variables
2128   //
2129   G4double rone = (fRmax1-fRmax2)/(2.*fDz);
2130   G4double rtwo = (fRmin1-fRmin2)/(2.*fDz);
2131   G4double qone = (fRmax1 == fRmax2) ? 0. : fDz*(fRmax1+fRmax2)/(fRmax1-fRmax2);
2132   G4double qtwo = (fRmin1 == fRmin2) ? 0. : fDz*(fRmin1+fRmin2)/(fRmin1-fRmin2);
2133 
2134   G4double slin   = std::hypot(fRmin1-fRmin2, 2.*fDz);
2135   G4double slout  = std::hypot(fRmax1-fRmax2, 2.*fDz);
2136   G4double Aone   = 0.5*fDPhi*(fRmax2 + fRmax1)*slout; // outer surface
2137   G4double Atwo   = 0.5*fDPhi*(fRmin2 + fRmin1)*slin;  // inner surface
2138   G4double Athree = 0.5*fDPhi*(fRmax1*fRmax1-fRmin1*fRmin1); // base at -Dz
2139   G4double Afour  = 0.5*fDPhi*(fRmax2*fRmax2-fRmin2*fRmin2); // base at +Dz
2140   G4double Afive  = fDz*(fRmax1-fRmin1+fRmax2-fRmin2); // phi section
2141 
2142   G4double phi    = G4RandFlat::shoot(fSPhi,fSPhi+fDPhi);
2143   G4double cosu   = std::cos(phi);
2144   G4double sinu   = std::sin(phi);
2145   G4double rRand1 = GetRadiusInRing(fRmin1, fRmax1);
2146   G4double rRand2 = GetRadiusInRing(fRmin2, fRmax2);
2147   
2148   if ( (fSPhi == 0.) && fPhiFullCone )  { Afive = 0.; }
2149   G4double chose  = G4RandFlat::shoot(0.,Aone+Atwo+Athree+Afour+2.*Afive);
2150 
2151   if( (chose >= 0.) && (chose < Aone) ) // outer surface
2152   {
2153     if(fRmax1 != fRmax2)
2154     {
2155       G4double zRand = G4RandFlat::shoot(-1.*fDz,fDz);
2156       return { rone*cosu*(qone-zRand), rone*sinu*(qone-zRand), zRand };
2157     }
2158     else
2159     {
2160       return { fRmax1*cosu, fRmax2*sinu, G4RandFlat::shoot(-1.*fDz,fDz) };
2161     }
2162   }
2163   else if( (chose >= Aone) && (chose < Aone + Atwo) )  // inner surface
2164   {
2165     if(fRmin1 != fRmin2)
2166     {
2167       G4double zRand = G4RandFlat::shoot(-1.*fDz,fDz);
2168       return { rtwo*cosu*(qtwo-zRand), rtwo*sinu*(qtwo-zRand), zRand };
2169     }
2170     else
2171     {
2172       return { fRmin1*cosu, fRmin2*sinu, G4RandFlat::shoot(-1.*fDz,fDz) };
2173     }
2174   }
2175   else if( (chose >= Aone + Atwo) && (chose < Aone + Atwo + Athree) ) // base at -Dz
2176   {
2177     return {rRand1*cosu, rRand1*sinu, -1*fDz};
2178   }
2179   else if( (chose >= Aone + Atwo + Athree)
2180         && (chose < Aone + Atwo + Athree + Afour) ) // base at +Dz
2181   {
2182     return { rRand2*cosu, rRand2*sinu, fDz };
2183   }
2184   else if( (chose >= Aone + Atwo + Athree + Afour) // SPhi section
2185         && (chose < Aone + Atwo + Athree + Afour + Afive) )
2186   {
2187     G4double zRand  = G4RandFlat::shoot(-1.*fDz,fDz);
2188     rRand1 = G4RandFlat::shoot(fRmin2-((zRand-fDz)/(2.*fDz))*(fRmin1-fRmin2),
2189                                fRmax2-((zRand-fDz)/(2.*fDz))*(fRmax1-fRmax2));
2190     return { rRand1*cosSPhi, rRand1*sinSPhi, zRand };
2191   }
2192   else // SPhi+DPhi section
2193   {
2194     G4double zRand  = G4RandFlat::shoot(-1.*fDz,fDz);
2195     rRand1 = G4RandFlat::shoot(fRmin2-((zRand-fDz)/(2.*fDz))*(fRmin1-fRmin2),
2196                                fRmax2-((zRand-fDz)/(2.*fDz))*(fRmax1-fRmax2)); 
2197     return { rRand1*cosEPhi, rRand1*sinEPhi, zRand };
2198   }
2199 }
2200 
2201 //////////////////////////////////////////////////////////////////////////
2202 //
2203 // Methods for visualisation
2204 
2205 void G4Cons::DescribeYourselfTo (G4VGraphicsScene& scene) const
2206 {
2207   scene.AddSolid (*this);
2208 }
2209 
2210 G4Polyhedron* G4Cons::CreatePolyhedron () const
2211 {
2212   return new G4PolyhedronCons(fRmin1,fRmax1,fRmin2,fRmax2,fDz,fSPhi,fDPhi);
2213 }
2214 
2215 #endif
2216