source: trunk/source/geometry/solids/CSG/src/G4Sphere.cc @ 836

Last change on this file since 836 was 831, checked in by garnier, 16 years ago

import all except CVS

File size: 82.6 KB
Line 
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//
27// $Id: G4Sphere.cc,v 1.57.2.1 2008/04/23 09:05:23 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-01-patch-02 $
29//
30// class G4Sphere
31//
32// Implementation for G4Sphere class
33//
34// History:
35//
36// 22.07.05 O.Link    : Added check for intersection with double cone
37// 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
38// 16.09.04 V.Grichine: bug fixed in SurfaceNormal(p), theta normals
39// 16.07.04 V.Grichine: bug fixed in DistanceToOut(p,v), Rmin go outside
40// 02.06.04 V.Grichine: bug fixed in DistanceToIn(p,v), on Rmax,Rmin go inside
41// 30.10.03 J.Apostolakis: new algorithm in Inside for SPhi-sections
42// 29.10.03 J.Apostolakis: fix in Inside for SPhi-0.5*kAngTol < phi < SPhi, SPhi<0
43// 19.06.02 V.Grichine: bug fixed in Inside(p), && -> && fDTheta - kAngTolerance
44// 30.01.02 V.Grichine: bug fixed in Inside(p), && -> || at l.451
45// 06.03.00 V.Grichine: modifications in Distance ToOut(p,v,...)
46// 18.11.99 V.Grichine: side = kNull in Distance ToOut(p,v,...)
47// 25.11.98 V.Grichine: bug fixed in DistanceToIn(p,v), phi intersections
48// 12.11.98 V.Grichine: bug fixed in DistanceToIn(p,v), theta intersections
49// 09.10.98 V.Grichine: modifications in Distance ToOut(p,v,...)
50// 17.09.96 V.Grichine: final modifications to commit
51// 28.03.94 P.Kent: old C++ code converted to tolerant geometry
52// --------------------------------------------------------------------
53
54#include <assert.h>
55
56#include "G4Sphere.hh"
57
58#include "G4VoxelLimits.hh"
59#include "G4AffineTransform.hh"
60#include "G4GeometryTolerance.hh"
61
62#include "G4VPVParameterisation.hh"
63
64#include "Randomize.hh"
65
66#include "meshdefs.hh"
67
68#include "G4VGraphicsScene.hh"
69#include "G4VisExtent.hh"
70#include "G4Polyhedron.hh"
71#include "G4NURBS.hh"
72#include "G4NURBSbox.hh"
73
74using namespace CLHEP;
75
76// Private enum: Not for external use - used by distanceToOut
77
78enum ESide {kNull,kRMin,kRMax,kSPhi,kEPhi,kSTheta,kETheta};
79
80// used by normal
81
82enum ENorm {kNRMin,kNRMax,kNSPhi,kNEPhi,kNSTheta,kNETheta};
83
84////////////////////////////////////////////////////////////////////////
85//
86// constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
87//             - note if pDPhi>2PI then reset to 2PI
88
89G4Sphere::G4Sphere( const G4String& pName,
90                          G4double pRmin, G4double pRmax,
91                          G4double pSPhi, G4double pDPhi,
92                          G4double pSTheta, G4double pDTheta )
93  : G4CSGSolid(pName)
94{
95  fEpsilon = 1.0e-14;
96
97  kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
98  kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
99
100  // Check radii
101
102  if (pRmin<pRmax&&pRmin>=0)
103  {
104    fRmin=pRmin; fRmax=pRmax;
105  }
106  else
107  {
108    G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
109           << "        Invalide values for radii ! - "
110           << "        pRmin = " << pRmin << ", pRmax = " << pRmax << G4endl;
111    G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
112                "Invalid radii");
113  }
114
115  // Check angles
116
117  if (pDPhi>=twopi)
118  {
119    fDPhi=twopi;
120  }
121  else if (pDPhi>0)
122  {
123    fDPhi=pDPhi;
124  }
125  else
126  {
127    G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
128           << "        Negative Z delta-Phi ! - "
129           << pDPhi << G4endl;
130    G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
131                "Invalid DPhi.");
132  }
133
134  // Convert fSPhi to 0-2PI
135
136  if (pSPhi<0)
137  {
138    fSPhi=twopi-std::fmod(std::fabs(pSPhi),twopi);
139  }
140  else
141  {
142    fSPhi=std::fmod(pSPhi,twopi);
143  }
144
145  // Sphere is placed such that fSPhi+fDPhi>twopi !
146  // fSPhi could be < 0 !!?
147  //
148  if (fSPhi+fDPhi>twopi) fSPhi-=twopi;
149
150  // Check theta angles
151
152  if (pSTheta<0 || pSTheta>pi)
153  {
154    G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl;
155    G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
156                "stheta outside 0-PI range.");
157  }
158  else
159  {
160    fSTheta=pSTheta;
161  }
162
163  if (pDTheta+pSTheta>=pi)
164  {
165    fDTheta=pi-pSTheta;
166  }
167  else if (pDTheta>0)
168  {
169    fDTheta=pDTheta;
170  }
171  else
172  {
173    G4cerr << "ERROR - G4Sphere()::G4Sphere(): " << GetName() << G4endl
174           << "        Negative delta-Theta ! - "
175           << pDTheta << G4endl;
176    G4Exception("G4Sphere::G4Sphere()", "InvalidSetup", FatalException,
177                "Invalid pDTheta.");
178  }
179}
180
181///////////////////////////////////////////////////////////////////////
182//
183// Fake default constructor - sets only member data and allocates memory
184//                            for usage restricted to object persistency.
185//
186G4Sphere::G4Sphere( __void__& a )
187  : G4CSGSolid(a)
188{
189}
190
191/////////////////////////////////////////////////////////////////////
192//
193// Destructor
194
195G4Sphere::~G4Sphere()
196{
197}
198
199//////////////////////////////////////////////////////////////////////////
200//
201// Dispatch to parameterisation for replication mechanism dimension
202// computation & modification.
203
204void G4Sphere::ComputeDimensions(       G4VPVParameterisation* p,
205                                  const G4int n,
206                                  const G4VPhysicalVolume* pRep)
207{
208  p->ComputeDimensions(*this,n,pRep);
209}
210
211////////////////////////////////////////////////////////////////////////////
212//
213// Calculate extent under transform and specified limit
214
215G4bool G4Sphere::CalculateExtent( const EAxis pAxis,
216                                  const G4VoxelLimits& pVoxelLimit,
217                                  const G4AffineTransform& pTransform,
218                                        G4double& pMin, G4double& pMax ) const
219{
220  if ( fDPhi==twopi && fDTheta==pi)  // !pTransform.IsRotated() &&
221  {
222    // Special case handling for solid spheres-shells
223    // (rotation doesn't influence).
224    // Compute x/y/z mins and maxs for bounding box respecting limits,
225    // with early returns if outside limits. Then switch() on pAxis,
226    // and compute exact x and y limit for x/y case
227     
228    G4double xoffset,xMin,xMax;
229    G4double yoffset,yMin,yMax;
230    G4double zoffset,zMin,zMax;
231
232    G4double diff1,diff2,maxDiff,newMin,newMax;
233    G4double xoff1,xoff2,yoff1,yoff2;
234
235    xoffset=pTransform.NetTranslation().x();
236    xMin=xoffset-fRmax;
237    xMax=xoffset+fRmax;
238    if (pVoxelLimit.IsXLimited())
239    {
240      if ( (xMin>pVoxelLimit.GetMaxXExtent()+kCarTolerance)
241        || (xMax<pVoxelLimit.GetMinXExtent()-kCarTolerance) )
242      {
243        return false;
244      }
245      else
246      {
247        if (xMin<pVoxelLimit.GetMinXExtent())
248        {
249          xMin=pVoxelLimit.GetMinXExtent();
250        }
251        if (xMax>pVoxelLimit.GetMaxXExtent())
252        {
253          xMax=pVoxelLimit.GetMaxXExtent();
254        }
255      }
256    }
257
258    yoffset=pTransform.NetTranslation().y();
259    yMin=yoffset-fRmax;
260    yMax=yoffset+fRmax;
261    if (pVoxelLimit.IsYLimited())
262    {
263      if ( (yMin>pVoxelLimit.GetMaxYExtent()+kCarTolerance)
264        || (yMax<pVoxelLimit.GetMinYExtent()-kCarTolerance) )
265      {
266        return false;
267      }
268      else
269      {
270        if (yMin<pVoxelLimit.GetMinYExtent())
271        {
272          yMin=pVoxelLimit.GetMinYExtent();
273        }
274        if (yMax>pVoxelLimit.GetMaxYExtent())
275        {
276          yMax=pVoxelLimit.GetMaxYExtent();
277        }
278      }
279    }
280
281    zoffset=pTransform.NetTranslation().z();
282    zMin=zoffset-fRmax;
283    zMax=zoffset+fRmax;
284    if (pVoxelLimit.IsZLimited())
285    {
286      if ( (zMin>pVoxelLimit.GetMaxZExtent()+kCarTolerance)
287        || (zMax<pVoxelLimit.GetMinZExtent()-kCarTolerance) )
288      {
289        return false;
290      }
291      else
292      {
293        if (zMin<pVoxelLimit.GetMinZExtent())
294        {
295          zMin=pVoxelLimit.GetMinZExtent();
296        }
297        if (zMax>pVoxelLimit.GetMaxZExtent())
298        {
299          zMax=pVoxelLimit.GetMaxZExtent();
300        }
301      }
302    }
303
304    // Known to cut sphere
305
306    switch (pAxis)
307    {
308      case kXAxis:
309        yoff1=yoffset-yMin;
310        yoff2=yMax-yoffset;
311        if (yoff1>=0&&yoff2>=0)
312        {
313          // Y limits cross max/min x => no change
314          //
315          pMin=xMin;
316          pMax=xMax;
317        }
318        else
319        {
320          // Y limits don't cross max/min x => compute max delta x,
321          // hence new mins/maxs
322          //
323          diff1=std::sqrt(fRmax*fRmax-yoff1*yoff1);
324          diff2=std::sqrt(fRmax*fRmax-yoff2*yoff2);
325          maxDiff=(diff1>diff2) ? diff1:diff2;
326          newMin=xoffset-maxDiff;
327          newMax=xoffset+maxDiff;
328          pMin=(newMin<xMin) ? xMin : newMin;
329          pMax=(newMax>xMax) ? xMax : newMax;
330        }
331        break;
332      case kYAxis:
333        xoff1=xoffset-xMin;
334        xoff2=xMax-xoffset;
335        if (xoff1>=0&&xoff2>=0)
336        {
337          // X limits cross max/min y => no change
338          //
339          pMin=yMin;
340          pMax=yMax;
341        }
342        else
343        {
344          // X limits don't cross max/min y => compute max delta y,
345          // hence new mins/maxs
346          //
347          diff1=std::sqrt(fRmax*fRmax-xoff1*xoff1);
348          diff2=std::sqrt(fRmax*fRmax-xoff2*xoff2);
349          maxDiff=(diff1>diff2) ? diff1:diff2;
350          newMin=yoffset-maxDiff;
351          newMax=yoffset+maxDiff;
352          pMin=(newMin<yMin) ? yMin : newMin;
353          pMax=(newMax>yMax) ? yMax : newMax;
354        }
355        break;
356      case kZAxis:
357        pMin=zMin;
358        pMax=zMax;
359        break;
360      default:
361        break;
362    }
363    pMin-=kCarTolerance;
364    pMax+=kCarTolerance;
365
366    return true; 
367  }
368  else       // Transformed cutted sphere
369  {
370    G4int i,j,noEntries,noBetweenSections;
371    G4bool existsAfterClip=false;
372
373    // Calculate rotated vertex coordinates
374
375    G4ThreeVectorList* vertices;
376    G4int  noPolygonVertices ;
377    vertices=CreateRotatedVertices(pTransform,noPolygonVertices);
378
379    pMin=+kInfinity;
380    pMax=-kInfinity;
381
382    noEntries=vertices->size();  // noPolygonVertices*noPhiCrossSections
383    noBetweenSections=noEntries-noPolygonVertices;
384
385    G4ThreeVectorList ThetaPolygon ;
386    for (i=0;i<noEntries;i+=noPolygonVertices)
387    {
388      for(j=0;j<(noPolygonVertices/2)-1;j++)
389      {
390        ThetaPolygon.push_back((*vertices)[i+j]) ;     
391        ThetaPolygon.push_back((*vertices)[i+j+1]) ;     
392        ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-2-j]) ;     
393        ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-1-j]) ;     
394        CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
395        ThetaPolygon.clear() ;
396      }
397    }
398    for (i=0;i<noBetweenSections;i+=noPolygonVertices)
399    {
400      for(j=0;j<noPolygonVertices-1;j++)
401      {
402        ThetaPolygon.push_back((*vertices)[i+j]) ;     
403        ThetaPolygon.push_back((*vertices)[i+j+1]) ;     
404        ThetaPolygon.push_back((*vertices)[i+noPolygonVertices+j+1]) ;     
405        ThetaPolygon.push_back((*vertices)[i+noPolygonVertices+j]) ;     
406        CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
407        ThetaPolygon.clear() ;
408      }
409      ThetaPolygon.push_back((*vertices)[i+noPolygonVertices-1]) ;     
410      ThetaPolygon.push_back((*vertices)[i]) ; 
411      ThetaPolygon.push_back((*vertices)[i+noPolygonVertices]) ;     
412      ThetaPolygon.push_back((*vertices)[i+2*noPolygonVertices-1]) ;     
413      CalculateClippedPolygonExtent(ThetaPolygon,pVoxelLimit,pAxis,pMin,pMax);
414      ThetaPolygon.clear() ;
415    }
416     
417    if (pMin!=kInfinity || pMax!=-kInfinity)
418    {
419      existsAfterClip=true;
420
421      // Add 2*tolerance to avoid precision troubles
422      //
423      pMin-=kCarTolerance;
424      pMax+=kCarTolerance;
425    }
426    else
427    {
428      // Check for case where completely enveloping clipping volume
429      // If point inside then we are confident that the solid completely
430      // envelopes the clipping volume. Hence set min/max extents according
431      // to clipping volume extents along the specified axis.
432
433      G4ThreeVector clipCentre(
434          (pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
435          (pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
436          (pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5);
437       
438      if (Inside(pTransform.Inverse().TransformPoint(clipCentre))!=kOutside)
439      {
440        existsAfterClip=true;
441        pMin=pVoxelLimit.GetMinExtent(pAxis);
442        pMax=pVoxelLimit.GetMaxExtent(pAxis);
443      }
444    }
445    delete vertices;
446    return existsAfterClip;
447  }
448}
449
450///////////////////////////////////////////////////////////////////////////
451//
452// Return whether point inside/outside/on surface
453// Split into radius, phi, theta checks
454// Each check modifies `in', or returns as approprate
455
456EInside G4Sphere::Inside( const G4ThreeVector& p ) const
457{
458  G4double rho,rho2,rad2,tolRMin,tolRMax;
459  G4double pPhi,pTheta;
460  EInside in=kOutside;
461
462  rho2 = p.x()*p.x() + p.y()*p.y() ;
463  rad2 = rho2 + p.z()*p.z() ;
464
465  //  if(rad2 >= 1.369e+19) DBG();
466  //  G4double rad = std::sqrt(rad2);
467  // Check radial surfaces
468  // sets `in'
469
470  if ( fRmin ) tolRMin = fRmin + kRadTolerance*0.5;
471  else         tolRMin = 0 ;
472 
473  tolRMax = fRmax - kRadTolerance*0.5 ;
474  //  const G4double  fractionTolerance = 1.0e-12;
475  const G4double  flexRadMaxTolerance = // kRadTolerance;
476    std::max(kRadTolerance, fEpsilon * fRmax);
477
478  const G4double  Rmax_minus = fRmax - flexRadMaxTolerance*0.5;
479  const G4double  flexRadMinTolerance = std::max(kRadTolerance, 
480                     fEpsilon * fRmin);
481  const G4double  Rmin_plus = (fRmin > 0) ? fRmin + flexRadMinTolerance*0.5 : 0 ;
482   
483if(rad2 <= Rmax_minus*Rmax_minus && rad2 >= Rmin_plus*Rmin_plus) in = kInside ; 
484
485// if ( rad2 <= tolRMax*tolRMax && rad2 >= tolRMin*tolRMin )  in = kInside ;
486  // if ( rad <= tolRMax && rad >= tolRMin )  in = kInside ;
487  else
488  {
489    tolRMax = fRmax + kRadTolerance*0.5 ;
490    tolRMin = fRmin - kRadTolerance*0.5 ;
491
492    if ( tolRMin < 0.0 ) tolRMin = 0.0 ;
493   
494     if ( rad2 <= tolRMax*tolRMax && rad2 >= tolRMin*tolRMin )  in = kSurface ;
495    //  if ( rad <= tolRMax && rad >= tolRMin )  in = kSurface ;
496    else                                                return in = kOutside ;
497  }
498
499  // Phi boundaries   : Do not check if it has no phi boundary!
500  // (in != kOutside). It is new J.Apostolakis proposal of 30.10.03
501
502  if ( ( fDPhi < twopi - kAngTolerance ) &&
503       ( (p.x() != 0.0 ) || (p.y() != 0.0) ) )
504  {
505    pPhi = std::atan2(p.y(),p.x()) ;
506
507    if      ( pPhi < fSPhi - kAngTolerance*0.5  )         pPhi += twopi ; 
508    else if ( pPhi > fSPhi + fDPhi + kAngTolerance*0.5 )  pPhi -= twopi; 
509   
510    if ((pPhi < fSPhi - kAngTolerance*0.5) || 
511        (pPhi > fSPhi + fDPhi + kAngTolerance*0.5) )  return in = kOutside ;
512   
513    else if (in == kInside)  // else it's kSurface anyway already
514    {
515      if ( (pPhi < fSPhi + kAngTolerance*0.5) || 
516           (pPhi > fSPhi + fDPhi - kAngTolerance*0.5) )      in = kSurface ;       
517    }
518  }
519
520  // Theta bondaries
521  // (in!=kOutside)
522 
523  if ( (rho2 || p.z()) && fDTheta < pi - kAngTolerance*0.5 )
524  {
525    rho    = std::sqrt(rho2);
526    pTheta = std::atan2(rho,p.z());
527
528    if ( in == kInside )
529    {
530      if ( (pTheta < fSTheta + kAngTolerance*0.5)
531        || (pTheta > fSTheta + fDTheta - kAngTolerance*0.5) )
532      {
533        if ( (pTheta >= fSTheta - kAngTolerance*0.5)
534          && (pTheta <= fSTheta + fDTheta + kAngTolerance*0.5) )
535        {
536          in = kSurface ;
537        }
538        else
539        {
540          in = kOutside ;
541        }
542      }
543    }
544    else
545    {
546      if ( (pTheta < fSTheta - kAngTolerance*0.5)
547        || (pTheta > fSTheta + fDTheta + kAngTolerance*0.5) )
548      {
549        in = kOutside ;
550      }
551    }
552  }
553  return in;
554}
555
556/////////////////////////////////////////////////////////////////////
557//
558// Return unit normal of surface closest to p
559// - note if point on z axis, ignore phi divided sides
560// - unsafe if point close to z axis a rmin=0 - no explicit checks
561
562G4ThreeVector G4Sphere::SurfaceNormal( const G4ThreeVector& p ) const
563{
564  G4int noSurfaces = 0; 
565  G4double rho, rho2, rad, pTheta, pPhi=0.;
566  G4double distRMin = kInfinity;
567  G4double distSPhi = kInfinity, distEPhi = kInfinity;
568  G4double distSTheta = kInfinity, distETheta = kInfinity;
569  G4double delta = 0.5*kCarTolerance, dAngle = 0.5*kAngTolerance;
570  G4ThreeVector nR, nPs, nPe, nTs, nTe, nZ(0.,0.,1.);
571  G4ThreeVector norm, sumnorm(0.,0.,0.);
572
573  rho2 = p.x()*p.x()+p.y()*p.y();
574  rad  = std::sqrt(rho2+p.z()*p.z());
575  rho  = std::sqrt(rho2);
576
577  G4double    distRMax = std::fabs(rad-fRmax);
578  if (fRmin)  distRMin = std::fabs(rad-fRmin);
579   
580  if ( rho && (fDPhi < twopi || fDTheta < pi) )
581  {
582    pPhi = std::atan2(p.y(),p.x());
583
584    if(pPhi  < fSPhi-dAngle)           pPhi     += twopi;
585    else if(pPhi > fSPhi+fDPhi+dAngle) pPhi     -= twopi;
586  }
587  if ( fDPhi < twopi ) // && rho ) // old limitation against (0,0,z)
588  {
589    if ( rho )
590    {
591      distSPhi = std::fabs( pPhi - fSPhi ); 
592      distEPhi = std::fabs(pPhi-fSPhi-fDPhi); 
593    }
594    else if( !fRmin )
595    {
596      distSPhi = 0.; 
597      distEPhi = 0.; 
598    }
599    nPs = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
600    nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
601  }       
602  if ( fDTheta < pi ) // && rad ) // old limitation against (0,0,0)
603  {
604    if ( rho )
605    {
606      pTheta     = std::atan2(rho,p.z());
607      distSTheta = std::fabs(pTheta-fSTheta); 
608      distETheta = std::fabs(pTheta-fSTheta-fDTheta);
609 
610      nTs = G4ThreeVector(-std::cos(fSTheta)*std::cos(pPhi),
611                        -std::cos(fSTheta)*std::sin(pPhi),
612                         std::sin(fSTheta)               );
613      nTe = G4ThreeVector( std::cos(fSTheta+fDTheta)*std::cos(pPhi),
614                         std::cos(fSTheta+fDTheta)*std::sin(pPhi),
615                        -std::sin(fSTheta+fDTheta)               );   
616    }
617    else if( !fRmin )
618    {
619      if ( fSTheta )                distSTheta = 0.;
620      if ( fSTheta + fDTheta < pi ) distETheta = 0.;
621    }   
622  }
623  if( rad )  nR = G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
624
625  if( distRMax <= delta )
626  {
627    noSurfaces ++;
628    sumnorm += nR;
629  }
630  if( fRmin && distRMin <= delta )
631  {
632    noSurfaces ++;
633    sumnorm -= nR;
634  }
635  if( fDPhi < twopi )   
636  {
637    if (distSPhi <= dAngle)
638    {
639      noSurfaces ++;
640      sumnorm += nPs;
641    }
642    if (distEPhi <= dAngle) 
643    {
644      noSurfaces ++;
645      sumnorm += nPe;
646    }
647  }
648  if ( fDTheta < pi )
649  {
650    if (distSTheta <= dAngle && fSTheta > 0.)
651    {
652      noSurfaces ++;
653      if( rad <= delta && fDPhi >= twopi) sumnorm += nZ;
654      else                                sumnorm += nTs;
655    }
656    if (distETheta <= dAngle && fSTheta+fDTheta < pi) 
657    {
658      noSurfaces ++;
659      if( rad <= delta && fDPhi >= twopi) sumnorm -= nZ;
660      else                                sumnorm += nTe;
661      if(sumnorm.z() == 0.)               sumnorm += nZ;
662    }
663  }
664  if ( noSurfaces == 0 )
665  {
666#ifdef G4CSGDEBUG
667    G4Exception("G4Sphere::SurfaceNormal(p)", "Notification", JustWarning, 
668                "Point p is not on surface !?" ); 
669#endif
670     norm = ApproxSurfaceNormal(p);
671  }
672  else if ( noSurfaces == 1 ) norm = sumnorm;
673  else                        norm = sumnorm.unit();
674  return norm;
675}
676
677
678/////////////////////////////////////////////////////////////////////////////////////////////
679//
680// Algorithm for SurfaceNormal() following the original specification
681// for points not on the surface
682
683G4ThreeVector G4Sphere::ApproxSurfaceNormal( const G4ThreeVector& p ) const
684{
685  ENorm side;
686  G4ThreeVector norm;
687  G4double rho,rho2,rad,pPhi,pTheta;
688  G4double distRMin,distRMax,distSPhi,distEPhi,
689           distSTheta,distETheta,distMin;
690
691  rho2=p.x()*p.x()+p.y()*p.y();
692  rad=std::sqrt(rho2+p.z()*p.z());
693  rho=std::sqrt(rho2);
694
695  //
696  // Distance to r shells
697  //
698
699  distRMax=std::fabs(rad-fRmax);
700  if (fRmin)
701  {
702    distRMin=std::fabs(rad-fRmin);
703     
704    if (distRMin<distRMax)
705    {
706      distMin=distRMin;
707      side=kNRMin;
708    }
709    else
710    {   
711      distMin=distRMax;
712      side=kNRMax;
713    }
714  }
715  else
716  {
717    distMin=distRMax;
718    side=kNRMax;
719  }
720
721  //
722  // Distance to phi planes
723  //
724  // Protected against (0,0,z)
725   
726  pPhi = std::atan2(p.y(),p.x());
727  if (pPhi<0) pPhi += twopi;
728
729  if (fDPhi<twopi&&rho)
730  {
731    if (fSPhi<0)
732    {
733      distSPhi=std::fabs(pPhi-(fSPhi+twopi))*rho;
734    }
735    else
736    {
737      distSPhi=std::fabs(pPhi-fSPhi)*rho;
738    }
739
740    distEPhi=std::fabs(pPhi-fSPhi-fDPhi)*rho;
741
742    // Find new minimum
743    //
744    if (distSPhi<distEPhi)
745    {
746      if (distSPhi<distMin)
747      {
748        distMin=distSPhi;
749        side=kNSPhi;
750      }
751    }
752    else
753    {
754      if (distEPhi<distMin)
755      {
756        distMin=distEPhi;
757        side=kNEPhi;
758      }
759    }
760  }
761
762  //
763  // Distance to theta planes
764  //
765
766  if (fDTheta<pi&&rad)
767  {
768    pTheta=std::atan2(rho,p.z());
769    distSTheta=std::fabs(pTheta-fSTheta)*rad;
770    distETheta=std::fabs(pTheta-fSTheta-fDTheta)*rad;
771
772    // Find new minimum
773    //
774    if (distSTheta<distETheta)
775    {
776      if (distSTheta<distMin)
777      {
778        distMin = distSTheta ;
779        side = kNSTheta ;
780      }
781    }
782    else
783    {
784      if (distETheta<distMin)
785      {
786        distMin = distETheta ;
787        side = kNETheta ;
788      }
789    }
790  }
791
792  switch (side)
793  {
794    case kNRMin:      // Inner radius
795      norm=G4ThreeVector(-p.x()/rad,-p.y()/rad,-p.z()/rad);
796      break;
797    case kNRMax:      // Outer radius
798      norm=G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
799      break;
800    case kNSPhi:
801      norm=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
802      break;
803    case kNEPhi:
804      norm=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
805      break;
806    case kNSTheta:
807      norm=G4ThreeVector(-std::cos(fSTheta)*std::cos(pPhi),
808                         -std::cos(fSTheta)*std::sin(pPhi),
809                          std::sin(fSTheta)            );
810      //  G4cout<<G4endl<<" case kNSTheta:"<<G4endl;
811      //  G4cout<<"pPhi = "<<pPhi<<G4endl;
812      //  G4cout<<"rad  = "<<rad<<G4endl;
813      //  G4cout<<"pho  = "<<rho<<G4endl;
814      //  G4cout<<"p:    "<<p.x()<<"; "<<p.y()<<"; "<<p.z()<<G4endl;
815      //  G4cout<<"norm: "<<norm.x()<<"; "<<norm.y()<<"; "<<norm.z()<<G4endl;
816      break;
817    case kNETheta:
818      norm=G4ThreeVector( std::cos(fSTheta+fDTheta)*std::cos(pPhi),
819                          std::cos(fSTheta+fDTheta)*std::sin(pPhi),
820                         -std::sin(fSTheta+fDTheta)              );
821
822      //  G4cout<<G4endl<<" case kNETheta:"<<G4endl;
823      //  G4cout<<"pPhi = "<<pPhi<<G4endl;
824      //  G4cout<<"rad  = "<<rad<<G4endl;
825      //  G4cout<<"pho  = "<<rho<<G4endl;
826      //  G4cout<<"p:    "<<p.x()<<"; "<<p.y()<<"; "<<p.z()<<G4endl;
827      //  G4cout<<"norm: "<<norm.x()<<"; "<<norm.y()<<"; "<<norm.z()<<G4endl;
828      break;
829    default:
830      DumpInfo();
831      G4Exception("G4Sphere::ApproxSurfaceNormal()", "Notification", JustWarning,
832                  "Undefined side for valid surface normal to solid.");
833      break;   
834  } // end case
835
836  return norm;
837}
838
839///////////////////////////////////////////////////////////////////////////////
840//
841// Calculate distance to shape from outside, along normalised vector
842// - return kInfinity if no intersection, or intersection distance <= tolerance
843//
844// -> If point is outside outer radius, compute intersection with rmax
845//        - if no intersection return
846//        - if  valid phi,theta return intersection Dist
847//
848// -> If shell, compute intersection with inner radius, taking largest +ve root
849//        - if valid phi,theta, save intersection
850//
851// -> If phi segmented, compute intersection with phi half planes
852//        - if valid intersection(r,theta), return smallest intersection of
853//          inner shell & phi intersection
854//
855// -> If theta segmented, compute intersection with theta cones
856//        - if valid intersection(r,phi), return smallest intersection of
857//          inner shell & theta intersection
858//
859//
860// NOTE:
861// - `if valid' (above) implies tolerant checking of intersection points
862//
863// OPT:
864// Move tolIO/ORmin/RMax2 precalcs to where they are needed -
865// not required for most cases.
866// Avoid atan2 for non theta cut G4Sphere.
867
868G4double G4Sphere::DistanceToIn( const G4ThreeVector& p,
869                                 const G4ThreeVector& v  ) const
870{
871  G4double snxt = kInfinity ;      // snxt = default return value
872
873  G4double rho2, rad2, pDotV2d, pDotV3d, pTheta ;
874
875  G4double tolIRMin2, tolORMin2, tolORMax2, tolIRMax2 ;
876  G4double tolSTheta=0., tolETheta=0. ;
877
878  // Intersection point
879
880  G4double xi, yi, zi, rhoi, rhoi2, radi2, iTheta ;
881
882  // Phi intersection
883
884  G4double sinSPhi, cosSPhi, ePhi, sinEPhi, cosEPhi , Comp ; 
885
886  // Phi flag and precalcs
887
888  G4bool segPhi ;       
889  G4double hDPhi, hDPhiOT, hDPhiIT, cPhi, sinCPhi=0., cosCPhi=0. ; 
890  G4double cosHDPhiOT=0., cosHDPhiIT=0. ;
891  G4double Dist, cosPsi ;
892
893  G4bool segTheta ;                             // Theta flag and precals
894  G4double tanSTheta, tanETheta ;
895  G4double tanSTheta2, tanETheta2 ;
896  G4double dist2STheta, dist2ETheta ;
897  G4double t1, t2, b, c, d2, d, s = kInfinity ;
898
899  // General Precalcs
900
901  rho2 = p.x()*p.x() + p.y()*p.y() ;
902  rad2 = rho2 + p.z()*p.z() ;
903  pTheta = std::atan2(std::sqrt(rho2),p.z()) ;
904
905  pDotV2d = p.x()*v.x() + p.y()*v.y() ;
906  pDotV3d = pDotV2d + p.z()*v.z() ;
907
908  // Radial Precalcs
909
910  if (fRmin > kRadTolerance*0.5)
911  {
912    tolORMin2=(fRmin-kRadTolerance*0.5)*(fRmin-kRadTolerance*0.5);
913  }
914  else
915  {
916    tolORMin2 = 0 ;
917  }
918  tolIRMin2 = (fRmin+kRadTolerance*0.5)*(fRmin+kRadTolerance*0.5) ;
919  tolORMax2 = (fRmax+kRadTolerance*0.5)*(fRmax+kRadTolerance*0.5) ;
920  tolIRMax2 = (fRmax-kRadTolerance*0.5)*(fRmax-kRadTolerance*0.5) ;
921
922  // Set phi divided flag and precalcs
923
924  if (fDPhi < twopi)
925  {
926    segPhi = true ;
927    hDPhi = 0.5*fDPhi ;    // half delta phi
928    cPhi = fSPhi + hDPhi ;
929
930    hDPhiOT = hDPhi+0.5*kAngTolerance; // Outer Tolerant half delta phi
931    hDPhiIT = hDPhi-0.5*kAngTolerance;
932
933    sinCPhi    = std::sin(cPhi) ;
934    cosCPhi    = std::cos(cPhi) ;
935    cosHDPhiOT = std::cos(hDPhiOT) ;
936    cosHDPhiIT = std::cos(hDPhiIT) ;
937  }
938  else
939  {
940    segPhi = false ;
941  }
942
943  // Theta precalcs
944   
945  if (fDTheta < pi )
946  {
947    segTheta  = true ;
948    tolSTheta = fSTheta - kAngTolerance*0.5 ;
949    tolETheta = fSTheta + fDTheta + kAngTolerance*0.5 ;
950  }
951  else
952  {
953    segTheta = false ;
954  }
955
956  // Outer spherical shell intersection
957  // - Only if outside tolerant fRmax
958  // - Check for if inside and outer G4Sphere heading through solid (-> 0)
959  // - No intersect -> no intersection with G4Sphere
960  //
961  // Shell eqn: x^2+y^2+z^2=RSPH^2
962  //
963  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
964  //
965  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
966  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
967  //
968  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
969
970  c = rad2 - fRmax*fRmax ;
971  const G4double  flexRadMaxTolerance = // kRadTolerance;
972    std::max(kRadTolerance, fEpsilon * fRmax);
973
974  //  if (c > kRadTolerance*fRmax)
975  if (c > flexRadMaxTolerance*fRmax)
976  {
977    // If outside toleranct boundary of outer G4Sphere
978    // [should be std::sqrt(rad2)-fRmax > kRadTolerance*0.5]
979
980    d2 = pDotV3d*pDotV3d - c ;
981
982    if ( d2 >= 0 )
983    {
984      s = -pDotV3d - std::sqrt(d2) ;
985
986      if (s >= 0 )
987      {
988        xi   = p.x() + s*v.x() ;
989        yi   = p.y() + s*v.y() ;
990        rhoi = std::sqrt(xi*xi + yi*yi) ;
991
992        if (segPhi && rhoi)    // Check phi intersection
993        {
994          cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
995
996          if (cosPsi >= cosHDPhiOT)
997          {
998            if (segTheta)   // Check theta intersection
999            {
1000              zi = p.z() + s*v.z() ;
1001
1002              // rhoi & zi can never both be 0
1003              // (=>intersect at origin =>fRmax=0)
1004              //
1005              iTheta = std::atan2(rhoi,zi) ;
1006              if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
1007              {
1008                return snxt = s ;
1009              }
1010            }
1011            else
1012            {
1013              return snxt=s;
1014            }
1015          }
1016        }
1017        else
1018        {
1019          if (segTheta)    // Check theta intersection
1020          {
1021            zi = p.z() + s*v.z() ;
1022
1023            // rhoi & zi can never both be 0
1024            // (=>intersect at origin => fRmax=0 !)
1025            //
1026            iTheta = std::atan2(rhoi,zi) ;
1027            if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
1028            {
1029              return snxt=s;
1030            }
1031          }
1032          else
1033          {
1034            return snxt = s ;
1035          }
1036        }         
1037      }
1038    }
1039    else    // No intersection with G4Sphere
1040    {
1041      return snxt=kInfinity;
1042    }
1043  }
1044  else
1045  {
1046    // Inside outer radius
1047    // check not inside, and heading through G4Sphere (-> 0 to in)
1048
1049    d2 = pDotV3d*pDotV3d - c ;
1050
1051    // if (rad2 > tolIRMin2 && pDotV3d < 0 )
1052
1053    if (rad2 > tolIRMax2 && ( d2 >= flexRadMaxTolerance*fRmax && pDotV3d < 0 ) )
1054    {
1055      if (segPhi)
1056      {
1057        // Use inner phi tolerant boundary -> if on tolerant
1058        // phi boundaries, phi intersect code handles leaving/entering checks
1059
1060        cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
1061
1062        if (cosPsi>=cosHDPhiIT)
1063        { 
1064          // inside radii, delta r -ve, inside phi
1065
1066          if (segTheta)
1067          {
1068            if ( (pTheta >= tolSTheta + kAngTolerance)
1069              && (pTheta <= tolETheta - kAngTolerance) )
1070            {
1071              return snxt=0;
1072            }
1073          }
1074          else    // strictly inside Theta in both cases
1075          {
1076            return snxt=0;
1077          }
1078        }
1079      }
1080      else
1081      {
1082        if ( segTheta )
1083        {
1084          if ( (pTheta >= tolSTheta + kAngTolerance)
1085            && (pTheta <= tolETheta - kAngTolerance) )
1086          {
1087            return snxt=0;
1088          }
1089        }
1090        else   // strictly inside Theta in both cases
1091        {
1092          return snxt=0;
1093        }
1094      }
1095    }
1096  }
1097
1098  // Inner spherical shell intersection
1099  // - Always farthest root, because would have passed through outer
1100  //   surface first.
1101  // - Tolerant check for if travelling through solid
1102
1103  if (fRmin)
1104  {
1105    c  = rad2 - fRmin*fRmin ;
1106    d2 = pDotV3d*pDotV3d - c ;
1107
1108    // Within tolerance inner radius of inner G4Sphere
1109    // Check for immediate entry/already inside and travelling outwards
1110
1111    // if (c >- kRadTolerance*0.5 && pDotV3d >= 0 && rad2 < tolIRMin2 )
1112
1113    if ( c > -kRadTolerance*0.5 && rad2 < tolIRMin2 && 
1114         ( d2 < fRmin*kCarTolerance || pDotV3d >= 0 ) )
1115    {
1116      if (segPhi)
1117      {
1118        // Use inner phi tolerant boundary -> if on tolerant
1119        // phi boundaries, phi intersect code handles leaving/entering checks
1120
1121        cosPsi = (p.x()*cosCPhi+p.y()*sinCPhi)/std::sqrt(rho2) ;
1122        if (cosPsi >= cosHDPhiIT)
1123        { 
1124          // inside radii, delta r -ve, inside phi
1125          //
1126          if (segTheta)
1127          {
1128            if ( (pTheta >= tolSTheta + kAngTolerance)
1129              && (pTheta <= tolETheta - kAngTolerance) )
1130            {
1131              return snxt=0;
1132            }
1133          }
1134          else
1135          {
1136            return snxt = 0 ;
1137          }
1138        }
1139      }
1140      else
1141      {
1142        if (segTheta)
1143        {
1144          if ( (pTheta >= tolSTheta + kAngTolerance)
1145            && (pTheta <= tolETheta - kAngTolerance) )
1146          {
1147            return snxt = 0 ;
1148          }
1149        }
1150        else
1151        {
1152          return snxt=0;
1153        }
1154      }
1155    }
1156    else   // Not special tolerant case
1157    {
1158      //  d2 = pDotV3d*pDotV3d - c ;
1159
1160      if (d2 >= 0)
1161      {
1162        s = -pDotV3d + std::sqrt(d2) ;
1163        if ( s >= kRadTolerance*0.5 )  // It was >= 0 ??
1164        {
1165          xi   = p.x() + s*v.x() ;
1166          yi   = p.y() + s*v.y() ;
1167          rhoi = std::sqrt(xi*xi+yi*yi) ;
1168
1169          if ( segPhi && rhoi )   // Check phi intersection
1170          {
1171            cosPsi = (xi*cosCPhi + yi*sinCPhi)/rhoi ;
1172
1173            if (cosPsi >= cosHDPhiOT)
1174            {
1175              if (segTheta)  // Check theta intersection
1176              {
1177                zi = p.z() + s*v.z() ;
1178
1179                // rhoi & zi can never both be 0
1180                // (=>intersect at origin =>fRmax=0)
1181                //
1182                iTheta = std::atan2(rhoi,zi) ;
1183                if ( (iTheta >= tolSTheta) && (iTheta<=tolETheta) )
1184                {
1185                  snxt = s ;
1186                }
1187              }
1188              else
1189              {
1190                snxt=s;
1191              }
1192            }
1193          }
1194          else
1195          {
1196            if (segTheta)   // Check theta intersection
1197            {
1198              zi = p.z() + s*v.z() ;
1199
1200              // rhoi & zi can never both be 0
1201              // (=>intersect at origin => fRmax=0 !)
1202              //
1203              iTheta = std::atan2(rhoi,zi) ;
1204              if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
1205              {
1206                snxt = s ;
1207              }
1208            }
1209            else
1210            {
1211              snxt=s;
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 ( segPhi )
1229  {
1230    // First phi surface (`S'tarting phi)
1231
1232    sinSPhi = std::sin(fSPhi) ;
1233    cosSPhi = std::cos(fSPhi) ;
1234
1235    // Comp = Component in outwards normal dirn
1236    //
1237    Comp    = v.x()*sinSPhi - v.y()*cosSPhi  ;
1238                   
1239    if ( Comp < 0 )
1240    {
1241      Dist = p.y()*cosSPhi - p.x()*sinSPhi ;
1242
1243      if (Dist < kCarTolerance*0.5)
1244      {
1245        s = Dist/Comp ;
1246
1247        if (s < snxt)
1248        {
1249          if ( s > 0 )
1250          {
1251            xi    = p.x() + s*v.x() ;
1252            yi    = p.y() + s*v.y() ;
1253            zi    = p.z() + s*v.z() ;
1254            rhoi2 = xi*xi + yi*yi   ;
1255            radi2 = rhoi2 + zi*zi   ;
1256          }
1257          else
1258          {
1259            s     = 0     ;
1260            xi    = p.x() ;
1261            yi    = p.y() ;
1262            zi    = p.z() ;
1263            rhoi2 = rho2  ;
1264            radi2 = rad2  ;
1265          }
1266          if ( (radi2 <= tolORMax2)
1267            && (radi2 >= tolORMin2)
1268            && ((yi*cosCPhi-xi*sinCPhi) <= 0) )
1269          {
1270            // Check theta intersection
1271            // rhoi & zi can never both be 0
1272            // (=>intersect at origin =>fRmax=0)
1273            //
1274            if ( segTheta )
1275            {
1276              iTheta = std::atan2(std::sqrt(rhoi2),zi) ;
1277              if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
1278              {
1279                // r and theta intersections good
1280                // - check intersecting with correct half-plane
1281
1282                if ((yi*cosCPhi-xi*sinCPhi) <= 0)
1283                {
1284                  snxt = s ;
1285                }
1286              }
1287            }
1288            else
1289            {
1290              snxt = s ;
1291            }
1292          }
1293        }
1294      }
1295    }
1296
1297    // Second phi surface (`E'nding phi)
1298
1299    ePhi    = fSPhi + fDPhi ;
1300    sinEPhi = std::sin(ePhi)     ;
1301    cosEPhi = std::cos(ePhi)     ;
1302
1303    // Compnent in outwards normal dirn
1304
1305    Comp    = -( v.x()*sinEPhi-v.y()*cosEPhi ) ;
1306       
1307    if (Comp < 0)
1308    {
1309      Dist = -(p.y()*cosEPhi-p.x()*sinEPhi) ;
1310      if ( Dist < kCarTolerance*0.5 )
1311      {
1312        s = Dist/Comp ;
1313
1314        if ( s < snxt )
1315        {
1316          if (s > 0)
1317          {
1318            xi    = p.x() + s*v.x() ;
1319            yi    = p.y() + s*v.y() ;
1320            zi    = p.z() + s*v.z() ;
1321            rhoi2 = xi*xi + yi*yi   ;
1322            radi2 = rhoi2 + zi*zi   ;
1323          }
1324          else
1325          {
1326            s     = 0     ;
1327            xi    = p.x() ;
1328            yi    = p.y() ;
1329            zi    = p.z() ;
1330            rhoi2 = rho2  ;
1331            radi2 = rad2  ;
1332          } if ( (radi2 <= tolORMax2)
1333            && (radi2 >= tolORMin2)
1334            && ((yi*cosCPhi-xi*sinCPhi) >= 0) )
1335          {
1336            // Check theta intersection
1337            // rhoi & zi can never both be 0
1338            // (=>intersect at origin =>fRmax=0)
1339            //
1340            if ( segTheta )
1341            {
1342              iTheta = std::atan2(std::sqrt(rhoi2),zi) ;
1343              if ( (iTheta >= tolSTheta) && (iTheta <= tolETheta) )
1344              {
1345                // r and theta intersections good
1346                // - check intersecting with correct half-plane
1347
1348                if ((yi*cosCPhi-xi*sinCPhi) >= 0)
1349                {
1350                  snxt = s ;
1351                }
1352              }
1353            }
1354            else
1355            {
1356              snxt = s ;
1357            }
1358          }
1359        }
1360      }
1361    }
1362  }
1363
1364  // Theta segment intersection
1365
1366  if ( segTheta )
1367  {
1368
1369    // Intersection with theta surfaces
1370    // Known failure cases:
1371    // o  Inside tolerance of stheta surface, skim
1372    //    ~parallel to cone and Hit & enter etheta surface [& visa versa]
1373    //
1374    //    To solve: Check 2nd root of etheta surface in addition to stheta
1375    //
1376    // o  start/end theta is exactly pi/2
1377    // Intersections with cones
1378    //
1379    // Cone equation: x^2+y^2=z^2tan^2(t)
1380    //
1381    // => (px+svx)^2+(py+svy)^2=(pz+svz)^2tan^2(t)
1382    //
1383    // => (px^2+py^2-pz^2tan^2(t))+2s(pxvx+pyvy-pzvztan^2(t))
1384    //       + s^2(vx^2+vy^2-vz^2tan^2(t)) = 0
1385    //
1386    // => s^2(1-vz^2(1+tan^2(t))+2s(pdotv2d-pzvztan^2(t))+(rho2-pz^2tan^2(t))=0
1387
1388    tanSTheta  = std::tan(fSTheta)         ;
1389    tanSTheta2 = tanSTheta*tanSTheta  ;
1390    tanETheta  = std::tan(fSTheta+fDTheta) ;
1391    tanETheta2 = tanETheta*tanETheta  ;
1392     
1393    if (fSTheta)
1394    {
1395      dist2STheta = rho2 - p.z()*p.z()*tanSTheta2 ;
1396    }
1397    else
1398    {
1399      dist2STheta = kInfinity ;
1400    }
1401    if ( fSTheta + fDTheta < pi )
1402    {
1403      dist2ETheta=rho2-p.z()*p.z()*tanETheta2;
1404    }
1405      else
1406    {
1407      dist2ETheta=kInfinity;
1408    }     
1409    if ( pTheta < tolSTheta) // dist2STheta<-kRadTolerance*0.5 && dist2ETheta>0)
1410    {
1411      // Inside (theta<stheta-tol) s theta cone
1412      // First root of stheta cone, second if first root -ve
1413
1414      t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
1415      t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
1416       
1417      b  = t2/t1 ;
1418      c  = dist2STheta/t1 ;
1419      d2 = b*b - c ;
1420
1421      if ( d2 >= 0 )
1422      {
1423        d = std::sqrt(d2) ;
1424        s = -b - d ;    // First root
1425
1426        if ( s < 0 )
1427        {
1428          s=-b+d;    // Second root
1429        }
1430        if (s >= 0 && s < snxt)
1431        {
1432          xi    = p.x() + s*v.x() ;
1433          yi    = p.y() + s*v.y() ;
1434          zi    = p.z() + s*v.z() ;
1435          rhoi2 = xi*xi + yi*yi   ;
1436          radi2 = rhoi2 + zi*zi   ;
1437          if ( (radi2 <= tolORMax2)
1438            && (radi2 >= tolORMin2)
1439            && (zi*(fSTheta - halfpi) <= 0) )
1440          {
1441            if ( segPhi && rhoi2 )  // Check phi intersection
1442            {
1443              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1444              if (cosPsi >= cosHDPhiOT)
1445              {
1446                snxt = s ;
1447              }
1448            }
1449            else
1450            {
1451              snxt = s ;
1452            }
1453          }
1454        }
1455      }
1456
1457      // Possible intersection with ETheta cone.
1458      // Second >= 0 root should be considered
1459       
1460      if ( fSTheta + fDTheta < pi )
1461      {
1462        t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
1463        t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
1464       
1465        b  = t2/t1 ;
1466        c  = dist2ETheta/t1 ;
1467        d2 = b*b - c ;
1468
1469        if (d2 >= 0)
1470        {
1471          d = std::sqrt(d2) ;
1472          s = -b + d ;    // Second root
1473
1474          if (s >= 0 && s < snxt)
1475          {
1476            xi    = p.x() + s*v.x() ;
1477            yi    = p.y() + s*v.y() ;
1478            zi    = p.z() + s*v.z() ;
1479            rhoi2 = xi*xi + yi*yi   ;
1480            radi2 = rhoi2 + zi*zi   ;
1481
1482            if ( (radi2 <= tolORMax2)
1483              && (radi2 >= tolORMin2)
1484              && (zi*(fSTheta + fDTheta - halfpi) <= 0) )
1485            {
1486              if (segPhi && rhoi2)   // Check phi intersection
1487              {
1488                cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1489                if (cosPsi >= cosHDPhiOT)
1490                {
1491                  snxt = s ;
1492                }
1493              }
1494              else
1495              {
1496                snxt = s ;
1497              }
1498            }
1499          }
1500        }
1501      }
1502    } 
1503    else if (pTheta > tolETheta) 
1504    { // dist2ETheta<-kRadTolerance*0.5 && dist2STheta>0)
1505      // Inside (theta>etheta+tol) e theta cone
1506      // First root of etheta cone, second if first root `imaginary'
1507
1508      t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
1509      t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
1510       
1511      b  = t2/t1 ;
1512      c  = dist2ETheta/t1 ;
1513      d2 = b*b - c ;
1514
1515      if (d2 >= 0)
1516      {
1517        d = std::sqrt(d2) ;
1518        s = -b - d ;    // First root
1519        if (s < 0)
1520        {
1521          s = -b + d ;           // second root
1522        }
1523        if (s >= 0 && s < snxt)
1524        {
1525          xi    = p.x() + s*v.x() ;
1526          yi    = p.y() + s*v.y() ;
1527          zi    = p.z() + s*v.z() ;
1528          rhoi2 = xi*xi + yi*yi   ;
1529          radi2 = rhoi2 + zi*zi   ;
1530
1531          if ( (radi2 <= tolORMax2)
1532            && (radi2 >= tolORMin2) 
1533            && (zi*(fSTheta + fDTheta - halfpi) <= 0) )
1534          {
1535            if (segPhi && rhoi2)  // Check phi intersection
1536            {
1537              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1538              if (cosPsi >= cosHDPhiOT)
1539              {
1540                snxt = s ;
1541              }
1542            }
1543            else
1544            {
1545              snxt = s ;
1546            }
1547          }
1548        }
1549      }
1550
1551      // Possible intersection with STheta cone.
1552      // Second >= 0 root should be considered
1553       
1554      if ( fSTheta )
1555      {
1556        t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
1557        t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
1558
1559        b  = t2/t1 ;
1560        c  = dist2STheta/t1 ;
1561        d2 = b*b - c ;
1562
1563        if (d2 >= 0)
1564        {
1565          d = std::sqrt(d2) ;
1566          s = -b + d ;    // Second root
1567
1568          if ( (s >= 0) && (s < snxt) )
1569          {
1570            xi    = p.x() + s*v.x() ;
1571            yi    = p.y() + s*v.y() ;
1572            zi    = p.z() + s*v.z() ;
1573            rhoi2 = xi*xi + yi*yi   ;
1574            radi2 = rhoi2 + zi*zi   ;
1575
1576            if ( (radi2 <= tolORMax2)
1577              && (radi2 >= tolORMin2)
1578              && (zi*(fSTheta - halfpi) <= 0) )
1579            {
1580              if (segPhi && rhoi2)   // Check phi intersection
1581              {
1582                cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1583                if (cosPsi >= cosHDPhiOT)
1584                {
1585                  snxt = s ;
1586                }
1587              }
1588              else
1589              {
1590                snxt = s ;
1591              }
1592            }
1593          }
1594        }
1595      } 
1596    }     
1597    else if ( (pTheta <tolSTheta + kAngTolerance)
1598           && (fSTheta > kAngTolerance) )
1599    {
1600      // In tolerance of stheta
1601      // If entering through solid [r,phi] => 0 to in
1602      // else try 2nd root
1603
1604      t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
1605      if ( (t2>=0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta<pi*.5)
1606        || (t2<0  && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta>pi*.5)
1607        || (v.z()<0 && tolIRMin2<rad2 && rad2<tolIRMax2 && fSTheta==pi*.5) )
1608      {
1609        if (segPhi && rho2)  // Check phi intersection
1610        {
1611          cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
1612          if (cosPsi >= cosHDPhiIT)
1613          {
1614            return 0 ;
1615          }
1616        }
1617        else
1618        {
1619          return 0 ;
1620        }
1621      }
1622
1623      // Not entering immediately/travelling through
1624
1625      t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
1626      b  = t2/t1 ;
1627      c  = dist2STheta/t1 ;
1628      d2 = b*b - c ;
1629
1630      if (d2 >= 0)
1631      {
1632        d = std::sqrt(d2) ;
1633        s = -b + d ;
1634        if ( (s >= kCarTolerance*0.5) && (s < snxt) && (fSTheta < pi*0.5) )
1635        {
1636          xi    = p.x() + s*v.x() ;
1637          yi    = p.y() + s*v.y() ;
1638          zi    = p.z() + s*v.z() ;
1639          rhoi2 = xi*xi + yi*yi   ;
1640          radi2 = rhoi2 + zi*zi   ;
1641
1642          if ( (radi2 <= tolORMax2)
1643            && (radi2 >= tolORMin2)
1644            && (zi*(fSTheta - halfpi) <= 0) )
1645          {
1646            if ( segPhi && rhoi2 )    // Check phi intersection
1647            {
1648              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1649              if ( cosPsi >= cosHDPhiOT )
1650              {
1651                snxt = s ;
1652              }
1653            }
1654            else
1655            {
1656              snxt = s ;
1657            }
1658          }
1659        }
1660      }
1661    }   
1662    else if ( (pTheta > tolETheta - kAngTolerance)
1663           && ((fSTheta + fDTheta) < pi-kAngTolerance) )   
1664    {
1665
1666      // In tolerance of etheta
1667      // If entering through solid [r,phi] => 0 to in
1668      // else try 2nd root
1669
1670      t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
1671
1672      if (
1673    (t2<0    && (fSTheta+fDTheta) <pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
1674 || (t2>=0   && (fSTheta+fDTheta) >pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
1675 || (v.z()>0 && (fSTheta+fDTheta)==pi*0.5 && tolIRMin2<rad2 && rad2<tolIRMax2)
1676         )
1677      {
1678        if (segPhi && rho2)   // Check phi intersection
1679        {
1680          cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/std::sqrt(rho2) ;
1681          if (cosPsi >= cosHDPhiIT)
1682          {
1683            return 0 ;
1684          }
1685        }
1686        else
1687        {
1688          return 0 ;
1689        }
1690      }
1691
1692      // Not entering immediately/travelling through
1693
1694      t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
1695      b  = t2/t1 ;
1696      c  = dist2ETheta/t1 ;
1697      d2 = b*b - c ;
1698
1699      if (d2 >= 0)
1700      {
1701        d = std::sqrt(d2) ;
1702        s = -b + d ;
1703       
1704        if ( (s >= kCarTolerance*0.5)
1705          && (s < snxt) && ((fSTheta + fDTheta) > pi*0.5) )
1706        {
1707          xi    = p.x() + s*v.x() ;
1708          yi    = p.y() + s*v.y() ;
1709          zi    = p.z() + s*v.z() ;
1710          rhoi2 = xi*xi + yi*yi   ;
1711          radi2 = rhoi2 + zi*zi   ;
1712
1713          if ( (radi2 <= tolORMax2)
1714            && (radi2 >= tolORMin2)
1715            && (zi*(fSTheta + fDTheta - halfpi) <= 0) )
1716          {
1717            if (segPhi && rhoi2)   // Check phi intersection
1718            {
1719              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1720              if (cosPsi>=cosHDPhiOT)
1721              {
1722                snxt = s ;
1723              }
1724            }
1725            else
1726            {
1727              snxt = s ;
1728            }
1729          }
1730        }
1731      }   
1732    } 
1733    else
1734    {
1735      // stheta+tol<theta<etheta-tol
1736      // For BOTH stheta & etheta check 2nd root for validity [r,phi]
1737
1738      t1 = 1 - v.z()*v.z()*(1 + tanSTheta2) ;
1739      t2 = pDotV2d - p.z()*v.z()*tanSTheta2 ;
1740
1741      b  = t2/t1;
1742      c  = dist2STheta/t1 ;
1743      d2 = b*b - c ;
1744
1745      if (d2 >= 0)
1746      {
1747        d = std::sqrt(d2) ;
1748        s = -b + d ;    // second root
1749
1750        if (s >= 0 && s < snxt)
1751        {
1752          xi    = p.x() + s*v.x() ;
1753          yi    = p.y() + s*v.y() ;
1754          zi    = p.z() + s*v.z() ;
1755          rhoi2 = xi*xi + yi*yi   ;
1756          radi2 = rhoi2 + zi*zi   ;
1757
1758          if ( (radi2 <= tolORMax2)
1759            && (radi2 >= tolORMin2)
1760            && (zi*(fSTheta - halfpi) <= 0) )
1761          {
1762            if (segPhi && rhoi2)   // Check phi intersection
1763            {
1764              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1765              if (cosPsi >= cosHDPhiOT)
1766              {
1767                snxt = s ;
1768              }
1769            }
1770            else
1771            {
1772              snxt = s ;
1773            }
1774          }
1775        }
1776      }       
1777      t1 = 1 - v.z()*v.z()*(1 + tanETheta2) ;
1778      t2 = pDotV2d - p.z()*v.z()*tanETheta2 ;
1779       
1780      b  = t2/t1 ;
1781      c  = dist2ETheta/t1 ;
1782      d2 = b*b - c ;
1783
1784      if (d2 >= 0)
1785      {
1786        d = std::sqrt(d2) ;
1787        s = -b + d;    // second root
1788
1789        if (s >= 0 && s < snxt)
1790        {
1791          xi    = p.x() + s*v.x() ;
1792          yi    = p.y() + s*v.y() ;
1793          zi    = p.z() + s*v.z() ;
1794          rhoi2 = xi*xi + yi*yi   ;
1795          radi2 = rhoi2 + zi*zi   ;
1796
1797          if ( (radi2 <= tolORMax2)
1798            && (radi2 >= tolORMin2)
1799            && (zi*(fSTheta + fDTheta - halfpi) <= 0) )
1800          {
1801            if (segPhi && rhoi2)   // Check phi intersection
1802            {
1803              cosPsi = (xi*cosCPhi + yi*sinCPhi)/std::sqrt(rhoi2) ;
1804              if ( cosPsi >= cosHDPhiOT )
1805              {
1806                snxt=s;
1807              }
1808            }
1809            else
1810            {
1811              snxt = s ;
1812            }
1813          }
1814        }
1815      }
1816    } 
1817  }
1818  return snxt;
1819}
1820
1821//////////////////////////////////////////////////////////////////////
1822//
1823// Calculate distance (<= actual) to closest surface of shape from outside
1824// - Calculate distance to radial planes
1825// - Only to phi planes if outside phi extent
1826// - Only to theta planes if outside theta extent
1827// - Return 0 if point inside
1828
1829G4double G4Sphere::DistanceToIn( const G4ThreeVector& p ) const
1830{
1831  G4double safe=0.0,safeRMin,safeRMax,safePhi,safeTheta;
1832  G4double rho2,rad,rho;
1833  G4double phiC,cosPhiC,sinPhiC,cosPsi,ePhi;
1834  G4double pTheta,dTheta1,dTheta2;
1835  rho2=p.x()*p.x()+p.y()*p.y();
1836  rad=std::sqrt(rho2+p.z()*p.z());
1837  rho=std::sqrt(rho2);
1838
1839  //
1840  // Distance to r shells
1841  //   
1842  if (fRmin)
1843  {
1844    safeRMin=fRmin-rad;
1845    safeRMax=rad-fRmax;
1846    if (safeRMin>safeRMax)
1847    {
1848      safe=safeRMin;
1849    }
1850    else
1851    {
1852      safe=safeRMax;
1853    }
1854  }
1855  else
1856  {
1857    safe=rad-fRmax;
1858  }
1859
1860  //
1861  // Distance to phi extent
1862  //
1863  if (fDPhi<twopi&&rho)
1864  {
1865    phiC=fSPhi+fDPhi*0.5;
1866    cosPhiC=std::cos(phiC);
1867    sinPhiC=std::sin(phiC);
1868
1869    // Psi=angle from central phi to point
1870    //
1871    cosPsi=(p.x()*cosPhiC+p.y()*sinPhiC)/rho;
1872    if (cosPsi<std::cos(fDPhi*0.5))
1873    {
1874      // Point lies outside phi range
1875      //
1876      if ((p.y()*cosPhiC-p.x()*sinPhiC)<=0)
1877      {
1878        safePhi=std::fabs(p.x()*std::sin(fSPhi)-p.y()*std::cos(fSPhi));
1879      }
1880      else
1881      {
1882        ePhi=fSPhi+fDPhi;
1883        safePhi=std::fabs(p.x()*std::sin(ePhi)-p.y()*std::cos(ePhi));
1884      }
1885      if (safePhi>safe) safe=safePhi;
1886    }
1887  }
1888  //
1889  // Distance to Theta extent
1890  //   
1891  if ((rad!=0.0) && (fDTheta<pi))
1892  {
1893    pTheta=std::acos(p.z()/rad);
1894    if (pTheta<0) pTheta+=pi;
1895    dTheta1=fSTheta-pTheta;
1896    dTheta2=pTheta-(fSTheta+fDTheta);
1897    if (dTheta1>dTheta2)
1898    {
1899      if (dTheta1>=0)             // WHY ???????????
1900      {
1901        safeTheta=rad*std::sin(dTheta1);
1902        if (safe<=safeTheta)
1903        {
1904          safe=safeTheta;
1905        }
1906      }
1907    }
1908    else
1909    {
1910      if (dTheta2>=0)
1911      {
1912        safeTheta=rad*std::sin(dTheta2);
1913        if (safe<=safeTheta)
1914        {
1915          safe=safeTheta;
1916        }
1917      }
1918    }
1919  }
1920
1921  if (safe<0) safe=0;
1922  return safe;
1923}
1924
1925/////////////////////////////////////////////////////////////////////
1926//
1927// Calculate distance to surface of shape from `inside', allowing for tolerance
1928// - Only Calc rmax intersection if no valid rmin intersection
1929
1930G4double G4Sphere::DistanceToOut( const G4ThreeVector& p,
1931                                  const G4ThreeVector& v,
1932                                  const G4bool calcNorm,
1933                                        G4bool *validNorm,
1934                                        G4ThreeVector *n   ) const
1935{
1936  G4double snxt = kInfinity;     // snxt is default return value
1937  G4double sphi= kInfinity,stheta= kInfinity;
1938  ESide side=kNull,sidephi=kNull,sidetheta=kNull; 
1939
1940  G4double t1,t2;
1941  G4double b,c,d;
1942
1943  // Variables for phi intersection:
1944
1945  G4double sinSPhi,cosSPhi,ePhi,sinEPhi,cosEPhi;
1946  G4double cPhi,sinCPhi,cosCPhi;
1947  G4double pDistS,compS,pDistE,compE,sphi2,vphi;
1948   
1949  G4double rho2,rad2,pDotV2d,pDotV3d,pTheta;
1950
1951  G4double tolSTheta=0.,tolETheta=0.;
1952  G4double xi,yi,zi;      // Intersection point
1953
1954  // G4double Comp; // Phi intersection
1955
1956  G4bool segPhi;        // Phi flag and precalcs
1957  G4double hDPhi,hDPhiOT,hDPhiIT; 
1958  G4double cosHDPhiOT,cosHDPhiIT;
1959
1960  G4bool segTheta;                             // Theta flag and precals
1961  G4double tanSTheta=0.,tanETheta, rhoSecTheta;
1962  G4double tanSTheta2=0.,tanETheta2=0.;
1963  G4double dist2STheta,dist2ETheta;
1964  G4double d2,s;
1965
1966  // General Precalcs
1967
1968  rho2=p.x()*p.x()+p.y()*p.y();
1969  rad2=rho2+p.z()*p.z();
1970  //  G4double rad=std::sqrt(rad2);
1971
1972  pTheta=std::atan2(std::sqrt(rho2),p.z());
1973
1974  pDotV2d=p.x()*v.x()+p.y()*v.y();
1975  pDotV3d=pDotV2d+p.z()*v.z();
1976
1977  // Set phi divided flag and precalcs
1978
1979  if(fDPhi<twopi)
1980  {
1981    segPhi=true;
1982    hDPhi=0.5*fDPhi;    // half delta phi
1983    cPhi=fSPhi+hDPhi;;
1984    hDPhiOT=hDPhi+0.5*kAngTolerance; // Outer Tolerant half delta phi
1985    hDPhiIT=hDPhi-0.5*kAngTolerance;
1986    sinCPhi=std::sin(cPhi);
1987    cosCPhi=std::cos(cPhi);
1988    cosHDPhiOT=std::cos(hDPhiOT);
1989    cosHDPhiIT=std::cos(hDPhiIT);
1990  }
1991  else
1992  {
1993    segPhi=false;
1994  }
1995
1996  // Theta precalcs
1997   
1998  if (fDTheta < pi)
1999  {
2000    segTheta=true;
2001    tolSTheta=fSTheta-kAngTolerance*0.5;
2002    tolETheta=fSTheta+fDTheta+kAngTolerance*0.5;
2003  }
2004  else
2005  {
2006    segTheta=false;
2007  }
2008   
2009  // Radial Intersections from G4Sphere::DistanceToIn
2010  //
2011  // Outer spherical shell intersection
2012  // - Only if outside tolerant fRmax
2013  // - Check for if inside and outer G4Sphere heading through solid (-> 0)
2014  // - No intersect -> no intersection with G4Sphere
2015  //
2016  // Shell eqn: x^2+y^2+z^2=RSPH^2
2017  //
2018  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
2019  //
2020  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
2021  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
2022  //
2023  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
2024  //
2025  // const G4double  fractionTolerance = 1.0e-12;
2026  const G4double  flexRadMaxTolerance = // kRadTolerance;
2027    std::max(kRadTolerance, fEpsilon * fRmax);
2028
2029  const G4double  Rmax_plus = fRmax + flexRadMaxTolerance*0.5;
2030  const G4double  flexRadMinTolerance = std::max(kRadTolerance, 
2031                     fEpsilon * fRmin);
2032  const G4double  Rmin_minus= (fRmin > 0) ? fRmin-flexRadMinTolerance*0.5 : 0 ;
2033
2034  if(rad2 <= Rmax_plus*Rmax_plus && rad2 >= Rmin_minus*Rmin_minus)
2035    //  if(rad <= Rmax_plus && rad >= Rmin_minus)
2036  {
2037    c = rad2 - fRmax*fRmax;
2038
2039    if (c < flexRadMaxTolerance*fRmax) 
2040    {
2041      // Within tolerant Outer radius
2042      //
2043      // The test is
2044      //     rad  - fRmax < 0.5*kRadTolerance
2045      // =>  rad  < fRmax + 0.5*kRadTol
2046      // =>  rad2 < (fRmax + 0.5*kRadTol)^2
2047      // =>  rad2 < fRmax^2 + 2.*0.5*fRmax*kRadTol + 0.25*kRadTol*kRadTol
2048      // =>  rad2 - fRmax^2    <~    fRmax*kRadTol
2049
2050      d2 = pDotV3d*pDotV3d - c;
2051
2052      if( (c >- flexRadMaxTolerance*fRmax)       // on tolerant surface
2053       && ((pDotV3d >=0) || (d2 < 0)) )          // leaving outside from Rmax
2054                                                 // not re-entering
2055      {
2056        if(calcNorm)
2057        {
2058          *validNorm = true ;
2059          *n         = G4ThreeVector(p.x()/fRmax,p.y()/fRmax,p.z()/fRmax) ;
2060        }
2061        return snxt = 0;
2062      }
2063      else 
2064      {
2065        snxt=-pDotV3d+std::sqrt(d2);    // second root since inside Rmax
2066        side = kRMax ; 
2067      }
2068    }
2069
2070    // Inner spherical shell intersection:
2071    // Always first >=0 root, because would have passed
2072    // from outside of Rmin surface .
2073
2074    if (fRmin)
2075    {
2076      c  = rad2 - fRmin*fRmin;
2077      d2 = pDotV3d*pDotV3d - c;
2078
2079      if (c >- flexRadMinTolerance*fRmin) // 2.0 * (0.5*kRadTolerance) * fRmin
2080      {
2081        if( c < flexRadMinTolerance*fRmin && 
2082            d2 >= flexRadMinTolerance*fRmin && pDotV3d < 0 ) // leaving from Rmin
2083        {
2084          if(calcNorm)
2085          {
2086            *validNorm = false ;   // Rmin surface is concave
2087          }
2088          return snxt = 0 ;
2089        }
2090        else
2091        { 
2092          if (d2 >= 0)
2093          {
2094            s = -pDotV3d-std::sqrt(d2) ;
2095            if (s>=0)     // Always intersect Rmin first
2096            {
2097              snxt = s ;
2098              side = kRMin ;
2099            }
2100          }
2101        }
2102      }
2103    }
2104  }
2105
2106  // Theta segment intersection
2107
2108  if (segTheta)
2109  {
2110    // Intersection with theta surfaces
2111    //
2112    // Known failure cases:
2113    // o  Inside tolerance of stheta surface, skim
2114    //    ~parallel to cone and Hit & enter etheta surface [& visa versa]
2115    //
2116    //    To solve: Check 2nd root of etheta surface in addition to stheta
2117    //
2118    // o  start/end theta is exactly pi/2
2119    //
2120    // Intersections with cones
2121    //
2122    // Cone equation: x^2+y^2=z^2tan^2(t)
2123    //
2124    // => (px+svx)^2+(py+svy)^2=(pz+svz)^2tan^2(t)
2125    //
2126    // => (px^2+py^2-pz^2tan^2(t))+2s(pxvx+pyvy-pzvztan^2(t))
2127    //       + s^2(vx^2+vy^2-vz^2tan^2(t)) = 0
2128    //
2129    // => s^2(1-vz^2(1+tan^2(t))+2s(pdotv2d-pzvztan^2(t))+(rho2-pz^2tan^2(t))=0
2130    //
2131    tanSTheta=std::tan(fSTheta);
2132    tanSTheta2=tanSTheta*tanSTheta;
2133    tanETheta=std::tan(fSTheta+fDTheta);
2134    tanETheta2=tanETheta*tanETheta;
2135     
2136    if (fSTheta)
2137    {
2138      dist2STheta=rho2-p.z()*p.z()*tanSTheta2;
2139    }
2140    else
2141    {
2142      dist2STheta = kInfinity;
2143    }
2144    if (fSTheta + fDTheta < pi)
2145    {
2146      dist2ETheta = rho2-p.z()*p.z()*tanETheta2;
2147    }
2148    else
2149    {
2150      dist2ETheta = kInfinity ;
2151    }
2152    if (pTheta > tolSTheta && pTheta < tolETheta)   // Inside theta 
2153    {
2154      // In tolerance of STheta and possible leaving out to small thetas N-
2155
2156      if(pTheta < tolSTheta + kAngTolerance  && fSTheta > kAngTolerance) 
2157      {
2158        t2=pDotV2d-p.z()*v.z()*tanSTheta2 ; // =(VdotN+)*rhoSecSTheta
2159
2160        if( fSTheta < pi*0.5 && t2 < 0)
2161        {
2162          if(calcNorm) *validNorm = false ;
2163          return snxt = 0 ;
2164        }
2165        else if(fSTheta > pi*0.5 && t2 >= 0)
2166        {
2167          if(calcNorm)
2168          {
2169            rhoSecTheta = std::sqrt(rho2*(1+tanSTheta2)) ;
2170            *validNorm = true ;
2171            *n = G4ThreeVector(-p.x()/rhoSecTheta,   // N-
2172                               -p.y()/rhoSecTheta,
2173                               tanSTheta/std::sqrt(1+tanSTheta2) ) ;
2174          }
2175          return snxt = 0 ;
2176        }
2177        else if( fSTheta == pi*0.5 && v.z() > 0)
2178        {
2179          if(calcNorm)
2180          {
2181            *validNorm = true ;
2182            *n = G4ThreeVector(0,0,1) ;
2183          }
2184          return snxt = 0 ;
2185        }
2186      }
2187
2188      // In tolerance of ETheta and possible leaving out to larger thetas N+
2189
2190      if ( (pTheta  > tolETheta - kAngTolerance)
2191        && (( fSTheta + fDTheta) < pi - kAngTolerance) ) 
2192      {
2193        t2=pDotV2d-p.z()*v.z()*tanETheta2 ;
2194        if((fSTheta+fDTheta)>pi*0.5 && t2<0)
2195        {
2196          if(calcNorm) *validNorm = false ;
2197          return snxt = 0 ;
2198        }
2199        else if( (fSTheta+fDTheta) < pi*0.5 && t2 >= 0 )
2200        {
2201          if(calcNorm)
2202          {
2203            rhoSecTheta = std::sqrt(rho2*(1+tanETheta2)) ;
2204            *validNorm = true ;
2205            *n = G4ThreeVector( p.x()/rhoSecTheta,  // N+
2206                                p.y()/rhoSecTheta,
2207                                -tanETheta/std::sqrt(1+tanETheta2)  ) ; 
2208          }
2209          return snxt = 0 ;
2210        }
2211        else if( ( fSTheta+fDTheta) == pi*0.5 && v.z() < 0 )
2212        {
2213          if(calcNorm)
2214          {
2215            *validNorm = true ;
2216            *n = G4ThreeVector(0,0,-1) ;
2217          }
2218          return snxt = 0 ;
2219        }
2220      }
2221      if( fSTheta > 0 )
2222      {       
2223        // First root of fSTheta cone, second if first root -ve
2224
2225        t1 = 1-v.z()*v.z()*(1+tanSTheta2);
2226        t2 = pDotV2d-p.z()*v.z()*tanSTheta2;
2227       
2228        b  = t2/t1;
2229        c  = dist2STheta/t1;
2230        d2 = b*b - c ;
2231
2232        if ( d2 >= 0 )
2233        {
2234          d = std::sqrt(d2) ;
2235          s = -b - d ;    // First root
2236
2237          if ( s < 0 )
2238          {
2239            s = -b + d ;    // Second root
2240          }
2241          if (s > flexRadMaxTolerance*0.5 )   // && s<sr)
2242          {
2243            // check against double cone solution
2244            zi=p.z()+s*v.z();
2245            if (fSTheta<pi*0.5 && zi<0)
2246            {
2247              s = kInfinity ;  // wrong cone
2248            }
2249            if (fSTheta>pi*0.5 && zi>0)
2250            {
2251              s = kInfinity ;  // wrong cone
2252            }
2253            stheta = s ;
2254            sidetheta = kSTheta ;
2255          }
2256        }
2257      }
2258
2259      // Possible intersection with ETheta cone 
2260     
2261      if (fSTheta + fDTheta < pi)
2262      {
2263        t1 = 1-v.z()*v.z()*(1+tanETheta2);
2264        t2 = pDotV2d-p.z()*v.z()*tanETheta2;       
2265        b  = t2/t1;
2266        c  = dist2ETheta/t1;
2267        d2 = b*b-c ;
2268
2269        if ( d2 >= 0 )
2270        {
2271          d = std::sqrt(d2);
2272          s = -b - d ;          // First root
2273
2274          if ( s < 0 )
2275          {
2276            s=-b+d;    // Second root
2277          }
2278          if (s > flexRadMaxTolerance*0.5 && s < stheta )
2279          {
2280            // check against double cone solution
2281            zi=p.z()+s*v.z();
2282            if (fSTheta+fDTheta<pi*0.5 && zi<0)
2283            {
2284              s = kInfinity ;  // wrong cone
2285            }
2286            if (fSTheta+fDTheta>pi*0.5 && zi>0)
2287            {
2288              s = kInfinity ;  // wrong cone
2289            }
2290            if (s < stheta)
2291            {
2292              stheta = s ;
2293              sidetheta = kETheta ;
2294            }
2295          }
2296        }
2297      }
2298    } 
2299  }
2300
2301  // Phi Intersection
2302   
2303  if ( fDPhi < twopi)
2304  {
2305    sinSPhi=std::sin(fSPhi);
2306    cosSPhi=std::cos(fSPhi);
2307    ePhi=fSPhi+fDPhi;
2308    sinEPhi=std::sin(ePhi);
2309    cosEPhi=std::cos(ePhi);
2310    cPhi=fSPhi+fDPhi*0.5;
2311    sinCPhi=std::sin(cPhi);
2312    cosCPhi=std::cos(cPhi);
2313
2314    if ( p.x()||p.y() ) // Check if on z axis (rho not needed later)
2315    {
2316      // pDist -ve when inside
2317
2318      pDistS=p.x()*sinSPhi-p.y()*cosSPhi;
2319      pDistE=-p.x()*sinEPhi+p.y()*cosEPhi;
2320
2321      // Comp -ve when in direction of outwards normal
2322
2323      compS   = -sinSPhi*v.x()+cosSPhi*v.y() ;
2324      compE   =  sinEPhi*v.x()-cosEPhi*v.y() ;
2325      sidephi = kNull ;
2326
2327      if ( pDistS <= 0 && pDistE <= 0 )
2328      {
2329        // Inside both phi *full* planes
2330
2331        if ( compS < 0 )
2332        {
2333          sphi = pDistS/compS ;
2334          xi   = p.x()+sphi*v.x() ;
2335          yi   = p.y()+sphi*v.y() ;
2336
2337          // Check intersecting with correct half-plane
2338          // (if not -> no intersect)
2339
2340          if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
2341          {
2342            sphi=kInfinity;
2343          }
2344          else
2345          {
2346            sidephi = kSPhi ;
2347            if ( pDistS > -0.5*kCarTolerance) sphi =0 ; // Leave by sphi
2348          }
2349        }
2350        else sphi = kInfinity ;
2351
2352        if ( compE < 0 )
2353        {
2354          sphi2=pDistE/compE ;
2355          if (sphi2 < sphi) // Only check further if < starting phi intersection
2356          {
2357            xi = p.x()+sphi2*v.x() ;
2358            yi = p.y()+sphi2*v.y() ;
2359
2360            // Check intersecting with correct half-plane
2361 
2362            if ((yi*cosCPhi-xi*sinCPhi)>=0) // Leaving via ending phi
2363            {
2364              sidephi = kEPhi ;
2365              if ( pDistE <= -0.5*kCarTolerance )
2366              {
2367                sphi=sphi2;
2368              }
2369              else 
2370              {
2371                sphi = 0 ;
2372              }
2373            }
2374          }
2375        }       
2376      }
2377      else if ( pDistS >= 0 && pDistE >= 0 ) // Outside both *full* phi planes
2378      {
2379        if ( pDistS <= pDistE )
2380        {
2381          sidephi = kSPhi ;
2382        }
2383        else
2384        {
2385          sidephi = kEPhi ;
2386        }
2387        if ( fDPhi > pi )
2388        {
2389          if ( compS < 0 && compE < 0 ) sphi = 0 ;
2390          else                          sphi = kInfinity ;
2391        }
2392        else
2393        {
2394          // if towards both >=0 then once inside (after error)
2395          // will remain inside
2396
2397          if ( compS >= 0 && compE >= 0 )
2398          {
2399            sphi=kInfinity;
2400          }
2401          else
2402          {
2403            sphi=0;
2404          }
2405        }   
2406      }
2407      else if ( pDistS > 0 && pDistE < 0 )
2408      {
2409        // Outside full starting plane, inside full ending plane
2410
2411        if ( fDPhi > pi )
2412        {
2413          if ( compE < 0 )
2414          {
2415            sphi = pDistE/compE ;
2416            xi   = p.x() + sphi*v.x() ;
2417            yi   = p.y() + sphi*v.y() ;
2418
2419            // Check intersection in correct half-plane
2420            // (if not -> not leaving phi extent)
2421            //
2422            if ( ( yi*cosCPhi - xi*sinCPhi ) <= 0 )
2423            {
2424              sphi = kInfinity ;
2425            }
2426            else // Leaving via Ending phi
2427            {
2428              sidephi = kEPhi ;
2429              if ( pDistE > -0.5*kCarTolerance ) sphi = 0. ;
2430            }
2431          }
2432          else
2433          {
2434            sphi = kInfinity ;
2435          }
2436        }
2437        else
2438        {
2439          if ( compS >= 0 )
2440          {
2441            if ( compE < 0 )
2442            {           
2443              sphi = pDistE/compE ;
2444              xi   = p.x() + sphi*v.x() ;
2445              yi   = p.y() + sphi*v.y() ;
2446
2447              // Check intersection in correct half-plane
2448              // (if not -> remain in extent)
2449              //
2450              if ( ( yi*cosCPhi - xi*sinCPhi) <= 0 )
2451              {
2452                sphi=kInfinity;
2453              }
2454              else // otherwise leaving via Ending phi
2455              {
2456                sidephi = kEPhi ;
2457              }
2458            }
2459            else sphi=kInfinity;
2460          }
2461          else // leaving immediately by starting phi
2462          {
2463            sidephi = kSPhi ;
2464            sphi    = 0 ;
2465          }
2466        }
2467      }
2468      else
2469      {
2470        // Must be pDistS < 0 && pDistE > 0
2471        // Inside full starting plane, outside full ending plane
2472
2473        if ( fDPhi > pi )
2474        {
2475          if ( compS < 0 )
2476          {
2477            sphi=pDistS/compS;
2478            xi=p.x()+sphi*v.x();
2479            yi=p.y()+sphi*v.y();
2480
2481            // Check intersection in correct half-plane
2482            // (if not -> not leaving phi extent)
2483            //
2484            if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
2485            {
2486              sphi = kInfinity ;
2487            }
2488           else  // Leaving via Starting phi
2489           {
2490              sidephi = kSPhi ;
2491             if ( pDistS > -0.5*kCarTolerance ) sphi = 0 ;
2492            }
2493          }
2494          else
2495          {
2496            sphi = kInfinity ;
2497          }
2498        }
2499        else
2500        {
2501          if ( compE >= 0 )
2502          {
2503            if ( compS < 0 )
2504            {
2505              sphi = pDistS/compS ;
2506              xi   = p.x()+sphi*v.x() ;
2507              yi   = p.y()+sphi*v.y() ;
2508
2509              // Check intersection in correct half-plane
2510              // (if not -> remain in extent)
2511              //
2512              if ( ( yi*cosCPhi - xi*sinCPhi ) >= 0 )
2513              {
2514                sphi = kInfinity ;
2515              }
2516              else // otherwise leaving via Starting phi
2517              {
2518                sidephi = kSPhi ;
2519              }
2520            }
2521            else
2522            {
2523              sphi = kInfinity ;
2524            }
2525          }
2526          else // leaving immediately by ending
2527          {
2528            sidephi = kEPhi ;
2529            sphi    = 0     ;
2530          }
2531        }
2532      }     
2533    }
2534    else
2535    {
2536      // On z axis + travel not || to z axis -> if phi of vector direction
2537      // within phi of shape, Step limited by rmax, else Step =0
2538
2539      if ( v.x() || v.y() )
2540      {
2541        vphi = std::atan2(v.y(),v.x()) ;
2542        if ( fSPhi < vphi && vphi < fSPhi + fDPhi )
2543        {
2544          sphi=kInfinity;
2545        }
2546        else
2547        {
2548          sidephi = kSPhi ; // arbitrary
2549          sphi    = 0     ;
2550        }
2551      }
2552      else  // travel along z - no phi intersaction
2553      {
2554        sphi = kInfinity ;
2555      }
2556    }
2557    if ( sphi < snxt )  // Order intersecttions
2558    {
2559      snxt = sphi ;
2560      side = sidephi ;
2561    }
2562  }
2563  if (stheta < snxt ) // Order intersections
2564  {
2565    snxt = stheta ;
2566    side = sidetheta ;
2567  }
2568
2569  if (calcNorm)    // Output switch operator
2570  {
2571    switch( side )
2572    {
2573      case kRMax:
2574        xi=p.x()+snxt*v.x();
2575        yi=p.y()+snxt*v.y();
2576        zi=p.z()+snxt*v.z();
2577        *n=G4ThreeVector(xi/fRmax,yi/fRmax,zi/fRmax);
2578        *validNorm=true;
2579        break;
2580      case kRMin:
2581        *validNorm=false;  // Rmin is concave
2582        break;
2583      case kSPhi:
2584        if (fDPhi<=pi)     // Normal to Phi-
2585        {
2586          *n=G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
2587          *validNorm=true;
2588        }
2589        else *validNorm=false;
2590        break ;
2591      case kEPhi:
2592        if (fDPhi<=pi)      // Normal to Phi+
2593        {
2594          *n=G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
2595          *validNorm=true;
2596        }
2597        else *validNorm=false;
2598        break;
2599      case kSTheta:
2600        if( fSTheta == pi*0.5 )
2601        {
2602          *n=G4ThreeVector(0,0,1);
2603          *validNorm=true;
2604        }
2605        else if ( fSTheta > pi )
2606        {
2607          xi=p.x()+snxt*v.x();
2608          yi=p.y()+snxt*v.y();
2609          rhoSecTheta = std::sqrt((xi*xi+yi*yi)*(1+tanSTheta2)) ;
2610          *n = G4ThreeVector(-xi/rhoSecTheta,   // N-
2611                             -yi/rhoSecTheta,
2612                             tanSTheta/std::sqrt(1+tanSTheta2)) ;
2613          *validNorm=true;
2614        }
2615        else *validNorm=false;  // Concave STheta cone
2616        break;
2617      case kETheta:
2618        if( ( fSTheta + fDTheta ) == pi*0.5 )
2619        {
2620          *n         = G4ThreeVector(0,0,-1);
2621          *validNorm = true ;
2622        }
2623        else if ( ( fSTheta + fDTheta ) < pi )
2624        {
2625          xi=p.x()+snxt*v.x();
2626          yi=p.y()+snxt*v.y();
2627          rhoSecTheta = std::sqrt((xi*xi+yi*yi)*(1+tanETheta2)) ;
2628          *n = G4ThreeVector( xi/rhoSecTheta,   // N+
2629                              yi/rhoSecTheta,
2630                              -tanSTheta/std::sqrt(1+tanSTheta2) ) ;
2631          *validNorm=true;
2632        }
2633        else *validNorm=false;   // Concave ETheta cone
2634        break;
2635      default:
2636        G4cout.precision(16);
2637        G4cout << G4endl;
2638        DumpInfo();
2639        G4cout << "Position:"  << G4endl << G4endl;
2640        G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
2641        G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
2642        G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
2643        G4cout << "Direction:" << G4endl << G4endl;
2644        G4cout << "v.x() = "   << v.x() << G4endl;
2645        G4cout << "v.y() = "   << v.y() << G4endl;
2646        G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
2647        G4cout << "Proposed distance :" << G4endl << G4endl;
2648        G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
2649        G4Exception("G4Sphere::DistanceToOut(p,v,..)",
2650                    "Notification", JustWarning,
2651                    "Undefined side for valid surface normal to solid.");
2652        break;
2653    }
2654  }
2655  if (snxt == kInfinity)
2656  {
2657    G4cout.precision(24);
2658    G4cout << G4endl;
2659    DumpInfo();
2660    G4cout << "Position:"  << G4endl << G4endl;
2661    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
2662    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
2663    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
2664    G4cout << "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm << " mm" 
2665           << G4endl << G4endl;
2666    G4cout << "Direction:" << G4endl << G4endl;
2667    G4cout << "v.x() = "   << v.x() << G4endl;
2668    G4cout << "v.y() = "   << v.y() << G4endl;
2669    G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
2670    G4cout << "Proposed distance :" << G4endl << G4endl;
2671    G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
2672    G4Exception("G4Sphere::DistanceToOut(p,v,..)",
2673                "Notification", JustWarning,
2674                "Logic error: snxt = kInfinity  ???");
2675  }
2676
2677  return snxt;
2678}
2679
2680/////////////////////////////////////////////////////////////////////////
2681//
2682// Calcluate distance (<=actual) to closest surface of shape from inside
2683
2684G4double G4Sphere::DistanceToOut( const G4ThreeVector& p ) const
2685{
2686  G4double safe=0.0,safeRMin,safeRMax,safePhi,safeTheta;
2687  G4double rho2,rad,rho;
2688  G4double phiC,cosPhiC,sinPhiC,ePhi;
2689  G4double pTheta,dTheta1,dTheta2;
2690  rho2=p.x()*p.x()+p.y()*p.y();
2691  rad=std::sqrt(rho2+p.z()*p.z());
2692  rho=std::sqrt(rho2);
2693
2694#ifdef G4CSGDEBUG
2695  if( Inside(p) == kOutside )
2696  {
2697     G4cout.precision(16) ;
2698     G4cout << G4endl ;
2699     DumpInfo();
2700     G4cout << "Position:"  << G4endl << G4endl ;
2701     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
2702     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
2703     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
2704     G4Exception("G4Sphere::DistanceToOut(p)",
2705                 "Notification", JustWarning, "Point p is outside !?" );
2706  }
2707#endif
2708
2709  //
2710  // Distance to r shells
2711  //   
2712  if (fRmin)
2713  {
2714    safeRMin=rad-fRmin;
2715    safeRMax=fRmax-rad;
2716    if (safeRMin<safeRMax)
2717    {
2718      safe=safeRMin;
2719    }
2720    else
2721    {
2722      safe=safeRMax;
2723    }
2724  }
2725  else
2726  {
2727    safe=fRmax-rad;
2728  }
2729
2730  //
2731  // Distance to phi extent
2732  //
2733  if (fDPhi<twopi && rho)
2734  {
2735    phiC=fSPhi+fDPhi*0.5;
2736    cosPhiC=std::cos(phiC);
2737    sinPhiC=std::sin(phiC);
2738    if ((p.y()*cosPhiC-p.x()*sinPhiC)<=0)
2739    {
2740      safePhi=-(p.x()*std::sin(fSPhi)-p.y()*std::cos(fSPhi));
2741    }
2742    else
2743    {
2744      ePhi=fSPhi+fDPhi;
2745      safePhi=(p.x()*std::sin(ePhi)-p.y()*std::cos(ePhi));
2746    }
2747    if (safePhi<safe) safe=safePhi;
2748  }
2749
2750  //
2751  // Distance to Theta extent
2752  //   
2753  if (rad)
2754  {
2755    pTheta=std::acos(p.z()/rad);
2756    if (pTheta<0) pTheta+=pi;
2757    dTheta1=pTheta-fSTheta;
2758    dTheta2=(fSTheta+fDTheta)-pTheta;
2759    if (dTheta1<dTheta2)
2760    {
2761      safeTheta=rad*std::sin(dTheta1);
2762      if (safe>safeTheta)
2763      {
2764        safe=safeTheta;
2765      }
2766    }
2767    else
2768    {
2769      safeTheta=rad*std::sin(dTheta2);
2770      if (safe>safeTheta)
2771      {
2772        safe=safeTheta;
2773      }
2774    }
2775  }
2776
2777  if (safe<0) safe=0;
2778    return safe;
2779}
2780
2781//////////////////////////////////////////////////////////////////////////
2782//
2783// Create a List containing the transformed vertices
2784// Ordering [0-3] -fDz cross section
2785//          [4-7] +fDz cross section such that [0] is below [4],
2786//                                             [1] below [5] etc.
2787// Note:
2788//  Caller has deletion resposibility
2789//  Potential improvement: For last slice, use actual ending angle
2790//                         to avoid rounding error problems.
2791
2792G4ThreeVectorList*
2793G4Sphere::CreateRotatedVertices( const G4AffineTransform& pTransform,
2794                                       G4int& noPolygonVertices ) const
2795{
2796  G4ThreeVectorList *vertices;
2797  G4ThreeVector vertex;
2798  G4double meshAnglePhi,meshRMax,crossAnglePhi,
2799           coscrossAnglePhi,sincrossAnglePhi,sAnglePhi;
2800  G4double meshTheta,crossTheta,startTheta;
2801  G4double rMaxX,rMaxY,rMinX,rMinY,rMinZ,rMaxZ;
2802  G4int crossSectionPhi,noPhiCrossSections,crossSectionTheta,noThetaSections;
2803
2804  // Phi cross sections
2805   
2806  noPhiCrossSections=G4int (fDPhi/kMeshAngleDefault)+1;
2807   
2808  if (noPhiCrossSections<kMinMeshSections)
2809  {
2810    noPhiCrossSections=kMinMeshSections;
2811  }
2812  else if (noPhiCrossSections>kMaxMeshSections)
2813  {
2814    noPhiCrossSections=kMaxMeshSections;
2815  }
2816  meshAnglePhi=fDPhi/(noPhiCrossSections-1);
2817   
2818  // If complete in phi, set start angle such that mesh will be at fRMax
2819  // on the x axis. Will give better extent calculations when not rotated.
2820   
2821  if (fDPhi==pi*2.0 && fSPhi==0)
2822  {
2823    sAnglePhi = -meshAnglePhi*0.5;
2824  }
2825    else
2826  {
2827    sAnglePhi=fSPhi;
2828  }   
2829
2830  // Theta cross sections
2831   
2832  noThetaSections = G4int(fDTheta/kMeshAngleDefault)+1;
2833   
2834  if (noThetaSections<kMinMeshSections)
2835  {
2836    noThetaSections=kMinMeshSections;
2837  }
2838  else if (noThetaSections>kMaxMeshSections)
2839  {
2840    noThetaSections=kMaxMeshSections;
2841  }
2842  meshTheta=fDTheta/(noThetaSections-1);
2843   
2844  // If complete in Theta, set start angle such that mesh will be at fRMax
2845  // on the z axis. Will give better extent calculations when not rotated.
2846   
2847  if (fDTheta==pi && fSTheta==0)
2848  {
2849    startTheta = -meshTheta*0.5;
2850  }
2851  else
2852  {
2853    startTheta=fSTheta;
2854  }   
2855
2856  meshRMax = (meshAnglePhi >= meshTheta) ?
2857             fRmax/std::cos(meshAnglePhi*0.5) : fRmax/std::cos(meshTheta*0.5);
2858  G4double* cosCrossTheta = new G4double[noThetaSections];
2859  G4double* sinCrossTheta = new G4double[noThetaSections];   
2860  vertices=new G4ThreeVectorList();
2861  vertices->reserve(noPhiCrossSections*(noThetaSections*2));
2862  if (vertices && cosCrossTheta && sinCrossTheta)
2863  {
2864    for (crossSectionPhi=0;
2865         crossSectionPhi<noPhiCrossSections; crossSectionPhi++)
2866    {
2867      crossAnglePhi=sAnglePhi+crossSectionPhi*meshAnglePhi;
2868      coscrossAnglePhi=std::cos(crossAnglePhi);
2869      sincrossAnglePhi=std::sin(crossAnglePhi);
2870      for (crossSectionTheta=0;
2871           crossSectionTheta<noThetaSections;crossSectionTheta++)
2872      {
2873        // Compute coordinates of cross section at section crossSectionPhi
2874        //
2875        crossTheta=startTheta+crossSectionTheta*meshTheta;
2876        cosCrossTheta[crossSectionTheta]=std::cos(crossTheta);
2877        sinCrossTheta[crossSectionTheta]=std::sin(crossTheta);
2878
2879        rMinX=fRmin*sinCrossTheta[crossSectionTheta]*coscrossAnglePhi;
2880        rMinY=fRmin*sinCrossTheta[crossSectionTheta]*sincrossAnglePhi;
2881        rMinZ=fRmin*cosCrossTheta[crossSectionTheta];
2882       
2883        vertex=G4ThreeVector(rMinX,rMinY,rMinZ);
2884        vertices->push_back(pTransform.TransformPoint(vertex));
2885       
2886      }    // Theta forward
2887   
2888      for (crossSectionTheta=noThetaSections-1;
2889           crossSectionTheta>=0; crossSectionTheta--)
2890      {
2891        rMaxX=meshRMax*sinCrossTheta[crossSectionTheta]*coscrossAnglePhi;
2892        rMaxY=meshRMax*sinCrossTheta[crossSectionTheta]*sincrossAnglePhi;
2893        rMaxZ=meshRMax*cosCrossTheta[crossSectionTheta];
2894       
2895        vertex=G4ThreeVector(rMaxX,rMaxY,rMaxZ);
2896        vertices->push_back(pTransform.TransformPoint(vertex));
2897
2898      }   // Theta back
2899    }   // Phi
2900    noPolygonVertices = noThetaSections*2 ;
2901  }
2902  else
2903  {
2904    DumpInfo();
2905    G4Exception("G4Sphere::CreateRotatedVertices()",
2906                "FatalError", FatalException,
2907                "Error in allocation of vertices. Out of memory !");
2908  }
2909
2910  delete[] cosCrossTheta;
2911  delete[] sinCrossTheta;
2912
2913  return vertices;
2914}
2915
2916//////////////////////////////////////////////////////////////////////////
2917//
2918// G4EntityType
2919
2920G4GeometryType G4Sphere::GetEntityType() const
2921{
2922  return G4String("G4Sphere");
2923}
2924
2925//////////////////////////////////////////////////////////////////////////
2926//
2927// Stream object contents to an output stream
2928
2929std::ostream& G4Sphere::StreamInfo( std::ostream& os ) const
2930{
2931  os << "-----------------------------------------------------------\n"
2932     << "    *** Dump for solid - " << GetName() << " ***\n"
2933     << "    ===================================================\n"
2934     << " Solid type: G4Sphere\n"
2935     << " Parameters: \n"
2936     << "    inner radius: " << fRmin/mm << " mm \n"
2937     << "    outer radius: " << fRmax/mm << " mm \n"
2938     << "    starting phi of segment  : " << fSPhi/degree << " degrees \n"
2939     << "    delta phi of segment     : " << fDPhi/degree << " degrees \n"
2940     << "    starting theta of segment: " << fSTheta/degree << " degrees \n"
2941     << "    delta theta of segment   : " << fDTheta/degree << " degrees \n"
2942     << "-----------------------------------------------------------\n";
2943
2944  return os;
2945}
2946
2947////////////////////////////////////////////////////////////////////////////////
2948//
2949// GetPointOnSurface
2950
2951G4ThreeVector G4Sphere::GetPointOnSurface() const
2952{
2953  G4double zRand, aOne, aTwo, aThr, aFou, aFiv, chose, phi, sinphi, cosphi;
2954  G4double height1, height2, slant1, slant2, costheta, sintheta,theta,rRand;
2955
2956  height1 = (fRmax-fRmin)*std::cos(fSTheta);
2957  height2 = (fRmax-fRmin)*std::cos(fSTheta+fDTheta);
2958  slant1  = std::sqrt(sqr((fRmax - fRmin)*std::sin(fSTheta))
2959                      + height1*height1);
2960  slant2  = std::sqrt(sqr((fRmax - fRmin)*std::sin(fSTheta+fDTheta))
2961                      + height2*height2);
2962  rRand   = RandFlat::shoot(fRmin,fRmax);
2963 
2964  aOne = fRmax*fRmax*fDPhi*(std::cos(fSTheta)-std::cos(fSTheta+fDTheta));
2965  aTwo = fRmin*fRmin*fDPhi*(std::cos(fSTheta)-std::cos(fSTheta+fDTheta));
2966  aThr = fDPhi*((fRmax + fRmin)*std::sin(fSTheta))*slant1;
2967  aFou = fDPhi*((fRmax + fRmin)*std::sin(fSTheta+fDTheta))*slant2;
2968  aFiv = 0.5*fDTheta*(fRmax*fRmax-fRmin*fRmin);
2969 
2970  phi = RandFlat::shoot(fSPhi, fSPhi + fDPhi); 
2971  cosphi = std::cos(phi); 
2972  sinphi = std::sin(phi);
2973  theta = RandFlat::shoot(fSTheta,fSTheta+fDTheta);
2974  costheta = std::cos(theta);
2975  sintheta = std::sqrt(1.-sqr(costheta));
2976
2977  if( ((fSPhi==0) && (fDPhi==2.*pi)) || (fDPhi==2.*pi) ) {aFiv = 0;}
2978  if(fSTheta == 0)  {aThr=0;}
2979  if(fDTheta + fSTheta == pi) {aFou = 0;}
2980  if(fSTheta == 0.5*pi) {aThr = pi*(fRmax*fRmax-fRmin*fRmin);}
2981  if(fSTheta + fDTheta == 0.5*pi) { aFou = pi*(fRmax*fRmax-fRmin*fRmin);}
2982
2983  chose = RandFlat::shoot(0.,aOne+aTwo+aThr+aFou+2.*aFiv);
2984  if( (chose>=0.) && (chose<aOne) )
2985  {
2986    return G4ThreeVector(fRmax*sintheta*cosphi,
2987                         fRmax*sintheta*sinphi, fRmax*costheta);
2988  }
2989  else if( (chose>=aOne) && (chose<aOne+aTwo) )
2990  {
2991    return G4ThreeVector(fRmin*sintheta*cosphi,
2992                         fRmin*sintheta*sinphi, fRmin*costheta);
2993  }
2994  else if( (chose>=aOne+aTwo) && (chose<aOne+aTwo+aThr) )
2995  {
2996    if (fSTheta != 0.5*pi)
2997    {
2998      zRand = RandFlat::shoot(fRmin*std::cos(fSTheta),fRmax*std::cos(fSTheta));
2999      return G4ThreeVector(std::tan(fSTheta)*zRand*cosphi,
3000                           std::tan(fSTheta)*zRand*sinphi,zRand);
3001    }
3002    else
3003    {
3004      return G4ThreeVector(rRand*cosphi, rRand*sinphi, 0.);
3005    }   
3006  }
3007  else if( (chose>=aOne+aTwo+aThr) && (chose<aOne+aTwo+aThr+aFou) )
3008  {
3009    if(fSTheta + fDTheta != 0.5*pi)
3010    {
3011      zRand = RandFlat::shoot(fRmin*std::cos(fSTheta+fDTheta),
3012                              fRmax*std::cos(fSTheta+fDTheta));
3013      return G4ThreeVector  (std::tan(fSTheta+fDTheta)*zRand*cosphi,
3014                             std::tan(fSTheta+fDTheta)*zRand*sinphi,zRand);
3015    }
3016    else
3017    {
3018      return G4ThreeVector(rRand*cosphi, rRand*sinphi, 0.);
3019    }
3020  }
3021  else if( (chose>=aOne+aTwo+aThr+aFou) && (chose<aOne+aTwo+aThr+aFou+aFiv) )
3022  {
3023    return G4ThreeVector(rRand*sintheta*std::cos(fSPhi),
3024                         rRand*sintheta*std::sin(fSPhi),rRand*costheta);
3025  }
3026  else
3027  {
3028    return G4ThreeVector(rRand*sintheta*std::cos(fSPhi+fDPhi),
3029                         rRand*sintheta*std::sin(fSPhi+fDPhi),rRand*costheta);
3030  }
3031}
3032
3033/////////////////////////////////////////////////////////////////////////////
3034//
3035// Methods for visualisation
3036
3037G4VisExtent G4Sphere::GetExtent() const
3038{
3039  return G4VisExtent(-fRmax, fRmax,-fRmax, fRmax,-fRmax, fRmax );
3040}
3041
3042
3043void G4Sphere::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
3044{
3045  scene.AddSolid (*this);
3046}
3047
3048G4Polyhedron* G4Sphere::CreatePolyhedron () const
3049{
3050  return new G4PolyhedronSphere (fRmin, fRmax, fSPhi, fDPhi, fSTheta, fDTheta);
3051}
3052
3053G4NURBS* G4Sphere::CreateNURBS () const
3054{
3055  return new G4NURBSbox (fRmax, fRmax, fRmax);       // Box for now!!!
3056}
Note: See TracBrowser for help on using the repository browser.