source: trunk/source/geometry/solids/CSG/src/G4Tubs.cc @ 1358

Last change on this file since 1358 was 1337, checked in by garnier, 14 years ago

tag geant4.9.4 beta 1 + modifs locales

File size: 54.8 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: G4Tubs.cc,v 1.79 2009/06/30 10:10:11 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-04-beta-01 $
29//
30//
31// class G4Tubs
32//
33// History:
34//
35// 02.08.07 T.Nikitina: bug fixed in DistanceToOut(p,v,..) for negative value under sqrt
36//                      for the case: p on the surface and v is tangent to the surface
37// 11.05.07 T.Nikitina: bug fixed in DistanceToOut(p,v,..) for phi < 2pi
38// 03.05.05 V.Grichine: SurfaceNormal(p) according to J. Apostolakis proposal
39// 16.03.05 V.Grichine: SurfaceNormal(p) with edges/corners for boolean
40// 20.07.01 V.Grichine: bug fixed in Inside(p)
41// 20.02.01 V.Grichine: bug fixed in Inside(p) and CalculateExtent was
42//                      simplified base on G4Box::CalculateExtent
43// 07.12.00 V.Grichine: phi-section algorithm was changed in Inside(p)
44// 28.11.00 V.Grichine: bug fixed in Inside(p)
45// 31.10.00 V.Grichine: assign sr, sphi in Distance ToOut(p,v,...)
46// 08.08.00 V.Grichine: more stable roots of 2-equation in DistanceToOut(p,v,..)
47// 02.08.00 V.Grichine: point is outside check in Distance ToOut(p)
48// 17.05.00 V.Grichine: bugs (#76,#91) fixed in Distance ToOut(p,v,...)
49// 31.03.00 V.Grichine: bug fixed in Inside(p)
50// 19.11.99 V.Grichine: side = kNull in DistanceToOut(p,v,...)
51// 13.10.99 V.Grichine: bugs fixed in DistanceToIn(p,v)
52// 28.05.99 V.Grichine: bugs fixed in DistanceToOut(p,v,...)
53// 25.05.99 V.Grichine: bugs fixed in DistanceToIn(p,v)
54// 23.03.99 V.Grichine: bug fixed in DistanceToIn(p,v)
55// 09.10.98 V.Grichine: modifications in DistanceToOut(p,v,...)
56// 18.06.98 V.Grichine: n-normalisation in DistanceToOut(p,v)
57//
58// 1994-95  P.Kent:     implementation
59//
60/////////////////////////////////////////////////////////////////////////
61
62#include "G4Tubs.hh"
63
64#include "G4VoxelLimits.hh"
65#include "G4AffineTransform.hh"
66#include "G4GeometryTolerance.hh"
67
68#include "G4VPVParameterisation.hh"
69
70#include "Randomize.hh"
71
72#include "meshdefs.hh"
73
74#include "G4VGraphicsScene.hh"
75#include "G4Polyhedron.hh"
76#include "G4NURBS.hh"
77#include "G4NURBStube.hh"
78#include "G4NURBScylinder.hh"
79#include "G4NURBStubesector.hh"
80
81using namespace CLHEP;
82
83/////////////////////////////////////////////////////////////////////////
84//
85// Constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
86//             - note if pdphi>2PI then reset to 2PI
87
88G4Tubs::G4Tubs( const G4String &pName,
89                      G4double pRMin, G4double pRMax,
90                      G4double pDz,
91                      G4double pSPhi, G4double pDPhi )
92  : G4CSGSolid(pName), fSPhi(0), fDPhi(0)
93{
94
95  kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
96  kAngTolerance = G4GeometryTolerance::GetInstance()->GetAngularTolerance();
97
98  if (pDz>0) // Check z-len
99  {
100    fDz = pDz ;
101  }
102  else
103  {
104    G4cerr << "ERROR - G4Tubs()::G4Tubs()" << G4endl
105           << "        Negative Z half-length (" << pDz << ") in solid: "
106           << GetName() << G4endl;
107    G4Exception("G4Tubs::G4Tubs()", "InvalidSetup", FatalException,
108                "Invalid Z half-length");
109  }
110  if ( (pRMin < pRMax) && (pRMin >= 0) ) // Check radii
111  {
112    fRMin = pRMin ; 
113    fRMax = pRMax ;
114  }
115  else
116  {
117    G4cerr << "ERROR - G4Tubs()::G4Tubs()" << G4endl
118           << "        Invalid values for radii in solid " << GetName()
119           << G4endl
120           << "        pRMin = " << pRMin << ", pRMax = " << pRMax << G4endl;
121    G4Exception("G4Tubs::G4Tubs()", "InvalidSetup", FatalException,
122                "Invalid radii.");
123  }
124
125  // Check angles
126
127  CheckPhiAngles(pSPhi, pDPhi);
128}
129
130///////////////////////////////////////////////////////////////////////
131//
132// Fake default constructor - sets only member data and allocates memory
133//                            for usage restricted to object persistency.
134//
135G4Tubs::G4Tubs( __void__& a )
136  : G4CSGSolid(a)
137{
138}
139
140//////////////////////////////////////////////////////////////////////////
141//
142// Destructor
143
144G4Tubs::~G4Tubs()
145{
146}
147
148/////////////////////////////////////////////////////////////////////////
149//
150// Dispatch to parameterisation for replication mechanism dimension
151// computation & modification.
152
153void G4Tubs::ComputeDimensions(       G4VPVParameterisation* p,
154                                const G4int n,
155                                const G4VPhysicalVolume* pRep )
156{
157  p->ComputeDimensions(*this,n,pRep) ;
158}
159
160////////////////////////////////////////////////////////////////////////
161//
162// Calculate extent under transform and specified limit
163
164G4bool G4Tubs::CalculateExtent( const EAxis              pAxis,
165                                const G4VoxelLimits&     pVoxelLimit,
166                                const G4AffineTransform& pTransform,
167                                      G4double&          pMin, 
168                                      G4double&          pMax    ) const
169{
170
171  if ( (!pTransform.IsRotated()) && (fDPhi == twopi) && (fRMin == 0) )
172  {
173    // Special case handling for unrotated solid tubes
174    // Compute x/y/z mins and maxs fro bounding box respecting limits,
175    // with early returns if outside limits. Then switch() on pAxis,
176    // and compute exact x and y limit for x/y case
177     
178    G4double xoffset, xMin, xMax;
179    G4double yoffset, yMin, yMax;
180    G4double zoffset, zMin, zMax;
181
182    G4double diff1, diff2, maxDiff, newMin, newMax;
183    G4double xoff1, xoff2, yoff1, yoff2, delta;
184
185    xoffset = pTransform.NetTranslation().x();
186    xMin = xoffset - fRMax;
187    xMax = xoffset + fRMax;
188
189    if (pVoxelLimit.IsXLimited())
190    {
191      if ( (xMin > pVoxelLimit.GetMaxXExtent())
192        || (xMax < pVoxelLimit.GetMinXExtent()) )
193      {
194        return false;
195      }
196      else
197      {
198        if (xMin < pVoxelLimit.GetMinXExtent())
199        {
200          xMin = pVoxelLimit.GetMinXExtent();
201        }
202        if (xMax > pVoxelLimit.GetMaxXExtent())
203        {
204          xMax = pVoxelLimit.GetMaxXExtent();
205        }
206      }
207    }
208    yoffset = pTransform.NetTranslation().y();
209    yMin    = yoffset - fRMax;
210    yMax    = yoffset + fRMax;
211
212    if ( pVoxelLimit.IsYLimited() )
213    {
214      if ( (yMin > pVoxelLimit.GetMaxYExtent())
215        || (yMax < pVoxelLimit.GetMinYExtent()) )
216      {
217        return false;
218      }
219      else
220      {
221        if (yMin < pVoxelLimit.GetMinYExtent())
222        {
223          yMin = pVoxelLimit.GetMinYExtent();
224        }
225        if (yMax > pVoxelLimit.GetMaxYExtent())
226        {
227          yMax=pVoxelLimit.GetMaxYExtent();
228        }
229      }
230    }
231    zoffset = pTransform.NetTranslation().z();
232    zMin    = zoffset - fDz;
233    zMax    = zoffset + fDz;
234
235    if ( pVoxelLimit.IsZLimited() )
236    {
237      if ( (zMin > pVoxelLimit.GetMaxZExtent())
238        || (zMax < pVoxelLimit.GetMinZExtent()) )
239      {
240        return false;
241      }
242      else
243      {
244        if (zMin < pVoxelLimit.GetMinZExtent())
245        {
246          zMin = pVoxelLimit.GetMinZExtent();
247        }
248        if (zMax > pVoxelLimit.GetMaxZExtent())
249        {
250          zMax = pVoxelLimit.GetMaxZExtent();
251        }
252      }
253    }
254    switch ( pAxis )  // Known to cut cylinder
255    {
256      case kXAxis :
257      {
258        yoff1 = yoffset - yMin;
259        yoff2 = yMax    - yoffset;
260
261        if ( (yoff1 >= 0) && (yoff2 >= 0) ) // Y limits cross max/min x
262        {                                   // => no change
263          pMin = xMin;
264          pMax = xMax;
265        }
266        else
267        {
268          // Y limits don't cross max/min x => compute max delta x,
269          // hence new mins/maxs
270
271          delta   = fRMax*fRMax - yoff1*yoff1;
272          diff1   = (delta>0.) ? std::sqrt(delta) : 0.;
273          delta   = fRMax*fRMax - yoff2*yoff2;
274          diff2   = (delta>0.) ? std::sqrt(delta) : 0.;
275          maxDiff = (diff1 > diff2) ? diff1:diff2;
276          newMin  = xoffset - maxDiff;
277          newMax  = xoffset + maxDiff;
278          pMin    = (newMin < xMin) ? xMin : newMin;
279          pMax    = (newMax > xMax) ? xMax : newMax;
280        }   
281        break;
282      }
283      case kYAxis :
284      {
285        xoff1 = xoffset - xMin;
286        xoff2 = xMax - xoffset;
287
288        if ( (xoff1 >= 0) && (xoff2 >= 0) ) // X limits cross max/min y
289        {                                   // => no change
290          pMin = yMin;
291          pMax = yMax;
292        }
293        else
294        {
295          // X limits don't cross max/min y => compute max delta y,
296          // hence new mins/maxs
297
298          delta   = fRMax*fRMax - xoff1*xoff1;
299          diff1   = (delta>0.) ? std::sqrt(delta) : 0.;
300          delta   = fRMax*fRMax - xoff2*xoff2;
301          diff2   = (delta>0.) ? std::sqrt(delta) : 0.;
302          maxDiff = (diff1 > diff2) ? diff1 : diff2;
303          newMin  = yoffset - maxDiff;
304          newMax  = yoffset + maxDiff;
305          pMin    = (newMin < yMin) ? yMin : newMin;
306          pMax    = (newMax > yMax) ? yMax : newMax;
307        }
308        break;
309      }
310      case kZAxis:
311      {
312        pMin = zMin;
313        pMax = zMax;
314        break;
315      }
316      default:
317        break;
318    }
319    pMin -= kCarTolerance;
320    pMax += kCarTolerance;
321    return true;
322  }
323  else // Calculate rotated vertex coordinates
324  {
325    G4int i, noEntries, noBetweenSections4;
326    G4bool existsAfterClip = false;
327    G4ThreeVectorList* vertices = CreateRotatedVertices(pTransform);
328
329    pMin =  kInfinity;
330    pMax = -kInfinity;
331
332    noEntries = vertices->size();
333    noBetweenSections4 = noEntries - 4;
334   
335    for ( i = 0 ; i < noEntries ; i += 4 )
336    {
337      ClipCrossSection(vertices, i, pVoxelLimit, pAxis, pMin, pMax);
338    }
339    for ( i = 0 ; i < noBetweenSections4 ; i += 4 )
340    {
341      ClipBetweenSections(vertices, i, pVoxelLimit, pAxis, pMin, pMax);
342    }
343    if ( (pMin != kInfinity) || (pMax != -kInfinity) )
344    {
345      existsAfterClip = true;
346      pMin -= kCarTolerance; // Add 2*tolerance to avoid precision troubles
347      pMax += kCarTolerance;
348    }
349    else
350    {
351      // Check for case where completely enveloping clipping volume
352      // If point inside then we are confident that the solid completely
353      // envelopes the clipping volume. Hence set min/max extents according
354      // to clipping volume extents along the specified axis.
355
356      G4ThreeVector clipCentre(
357             (pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
358             (pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
359             (pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5 );
360       
361      if ( Inside(pTransform.Inverse().TransformPoint(clipCentre)) != kOutside )
362      {
363        existsAfterClip = true;
364        pMin            = pVoxelLimit.GetMinExtent(pAxis);
365        pMax            = pVoxelLimit.GetMaxExtent(pAxis);
366      }
367    }
368    delete vertices;
369    return existsAfterClip;
370  }
371}
372
373
374///////////////////////////////////////////////////////////////////////////
375//
376// Return whether point inside/outside/on surface
377
378EInside G4Tubs::Inside( const G4ThreeVector& p ) const
379{
380  G4double r2,pPhi,tolRMin,tolRMax;
381  EInside in = kOutside ;
382  static const G4double halfCarTolerance=kCarTolerance*0.5;
383  static const G4double halfRadTolerance=kRadTolerance*0.5;
384  static const G4double halfAngTolerance=kAngTolerance*0.5;
385
386  if (std::fabs(p.z()) <= fDz - halfCarTolerance)
387  {
388    r2 = p.x()*p.x() + p.y()*p.y() ;
389
390    if (fRMin) { tolRMin = fRMin + halfRadTolerance ; }
391    else       { tolRMin = 0 ; }
392
393    tolRMax = fRMax - halfRadTolerance ;
394     
395    if ((r2 >= tolRMin*tolRMin) && (r2 <= tolRMax*tolRMax))
396    {
397      if ( fPhiFullTube )
398      {
399        in = kInside ;
400      }
401      else
402      {
403        // Try inner tolerant phi boundaries (=>inside)
404        // if not inside, try outer tolerant phi boundaries
405
406        if ((tolRMin==0)&&(p.x()<=halfCarTolerance)&&(p.y()<=halfCarTolerance))
407        {
408          in=kSurface;
409        }
410        else
411        {
412          pPhi = std::atan2(p.y(),p.x()) ;
413          if ( pPhi < -halfAngTolerance )  { pPhi += twopi; } // 0<=pPhi<2pi
414
415          if ( fSPhi >= 0 )
416          {
417            if ( (std::abs(pPhi) < halfAngTolerance)
418              && (std::abs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
419            { 
420              pPhi += twopi ; // 0 <= pPhi < 2pi
421            }
422            if ( (pPhi >= fSPhi + halfAngTolerance)
423              && (pPhi <= fSPhi + fDPhi - halfAngTolerance) )
424            {
425              in = kInside ;
426            }
427            else if ( (pPhi >= fSPhi - halfAngTolerance)
428                   && (pPhi <= fSPhi + fDPhi + halfAngTolerance) )
429            {
430              in = kSurface ;
431            }
432          }
433          else  // fSPhi < 0
434          {
435            if ( (pPhi <= fSPhi + twopi - halfAngTolerance)
436              && (pPhi >= fSPhi + fDPhi  + halfAngTolerance) ) {;} //kOutside
437            else if ( (pPhi <= fSPhi + twopi + halfAngTolerance)
438                   && (pPhi >= fSPhi + fDPhi  - halfAngTolerance) )
439            {
440              in = kSurface ;
441            }
442            else
443            {
444              in = kInside ;
445            }
446          }
447        }                   
448      }
449    }
450    else  // Try generous boundaries
451    {
452      tolRMin = fRMin - halfRadTolerance ;
453      tolRMax = fRMax + halfRadTolerance ;
454
455      if ( tolRMin < 0 )  { tolRMin = 0; }
456
457      if ( (r2 >= tolRMin*tolRMin) && (r2 <= tolRMax*tolRMax) )
458      {
459        if (fPhiFullTube || (r2 <=halfRadTolerance*halfRadTolerance) )
460        {                        // Continuous in phi or on z-axis
461          in = kSurface ;
462        }
463        else // Try outer tolerant phi boundaries only
464        {
465          pPhi = std::atan2(p.y(),p.x()) ;
466
467          if ( pPhi < -halfAngTolerance)  { pPhi += twopi; } // 0<=pPhi<2pi
468          if ( fSPhi >= 0 )
469          {
470            if ( (std::abs(pPhi) < halfAngTolerance)
471              && (std::abs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
472            { 
473              pPhi += twopi ; // 0 <= pPhi < 2pi
474            }
475            if ( (pPhi >= fSPhi - halfAngTolerance)
476              && (pPhi <= fSPhi + fDPhi + halfAngTolerance) )
477            {
478              in = kSurface ;
479            }
480          }
481          else  // fSPhi < 0
482          {
483            if ( (pPhi <= fSPhi + twopi - halfAngTolerance)
484              && (pPhi >= fSPhi + fDPhi + halfAngTolerance) ) {;} // kOutside
485            else
486            {
487              in = kSurface ;
488            }
489          }
490        }
491      }
492    }
493  }
494  else if (std::fabs(p.z()) <= fDz + halfCarTolerance)
495  {                                          // Check within tolerant r limits
496    r2      = p.x()*p.x() + p.y()*p.y() ;
497    tolRMin = fRMin - halfRadTolerance ;
498    tolRMax = fRMax + halfRadTolerance ;
499
500    if ( tolRMin < 0 )  { tolRMin = 0; }
501
502    if ( (r2 >= tolRMin*tolRMin) && (r2 <= tolRMax*tolRMax) )
503    {
504      if (fPhiFullTube || (r2 <=halfRadTolerance*halfRadTolerance))
505      {                        // Continuous in phi or on z-axis
506        in = kSurface ;
507      }
508      else // Try outer tolerant phi boundaries
509      {
510        pPhi = std::atan2(p.y(),p.x()) ;
511
512        if ( pPhi < -halfAngTolerance )  { pPhi += twopi; }  // 0<=pPhi<2pi
513        if ( fSPhi >= 0 )
514        {
515          if ( (std::abs(pPhi) < halfAngTolerance)
516            && (std::abs(fSPhi + fDPhi - twopi) < halfAngTolerance) )
517          { 
518            pPhi += twopi ; // 0 <= pPhi < 2pi
519          }
520          if ( (pPhi >= fSPhi - halfAngTolerance)
521            && (pPhi <= fSPhi + fDPhi + halfAngTolerance) )
522          {
523            in = kSurface;
524          }
525        }
526        else  // fSPhi < 0
527        {
528          if ( (pPhi <= fSPhi + twopi - halfAngTolerance)
529            && (pPhi >= fSPhi + fDPhi  + halfAngTolerance) ) {;}
530          else
531          {
532            in = kSurface ;
533          }
534        }     
535      }
536    }
537  }
538  return in;
539}
540
541///////////////////////////////////////////////////////////////////////////
542//
543// Return unit normal of surface closest to p
544// - note if point on z axis, ignore phi divided sides
545// - unsafe if point close to z axis a rmin=0 - no explicit checks
546
547G4ThreeVector G4Tubs::SurfaceNormal( const G4ThreeVector& p ) const
548{
549  G4int noSurfaces = 0;
550  G4double rho, pPhi;
551  G4double distZ, distRMin, distRMax;
552  G4double distSPhi = kInfinity, distEPhi = kInfinity;
553
554  static const G4double halfCarTolerance = 0.5*kCarTolerance;
555  static const G4double halfAngTolerance = 0.5*kAngTolerance;
556
557  G4ThreeVector norm, sumnorm(0.,0.,0.);
558  G4ThreeVector nZ = G4ThreeVector(0, 0, 1.0);
559  G4ThreeVector nR, nPs, nPe;
560
561  rho = std::sqrt(p.x()*p.x() + p.y()*p.y());
562
563  distRMin = std::fabs(rho - fRMin);
564  distRMax = std::fabs(rho - fRMax);
565  distZ    = std::fabs(std::fabs(p.z()) - fDz);
566
567  if (!fPhiFullTube)    // Protected against (0,0,z)
568  {
569    if ( rho > halfCarTolerance )
570    {
571      pPhi = std::atan2(p.y(),p.x());
572   
573      if(pPhi  < fSPhi- halfCarTolerance)           { pPhi += twopi; }
574      else if(pPhi > fSPhi+fDPhi+ halfCarTolerance) { pPhi -= twopi; }
575
576      distSPhi = std::fabs(pPhi - fSPhi);       
577      distEPhi = std::fabs(pPhi - fSPhi - fDPhi); 
578    }
579    else if( !fRMin )
580    {
581      distSPhi = 0.; 
582      distEPhi = 0.; 
583    }
584    nPs = G4ThreeVector(std::sin(fSPhi),-std::cos(fSPhi),0);
585    nPe = G4ThreeVector(-std::sin(fSPhi+fDPhi),std::cos(fSPhi+fDPhi),0);
586  }
587  if ( rho > halfCarTolerance ) { nR = G4ThreeVector(p.x()/rho,p.y()/rho,0); }
588
589  if( distRMax <= halfCarTolerance )
590  {
591    noSurfaces ++;
592    sumnorm += nR;
593  }
594  if( fRMin && (distRMin <= halfCarTolerance) )
595  {
596    noSurfaces ++;
597    sumnorm -= nR;
598  }
599  if( fDPhi < twopi )   
600  {
601    if (distSPhi <= halfAngTolerance) 
602    {
603      noSurfaces ++;
604      sumnorm += nPs;
605    }
606    if (distEPhi <= halfAngTolerance) 
607    {
608      noSurfaces ++;
609      sumnorm += nPe;
610    }
611  }
612  if (distZ <= halfCarTolerance) 
613  {
614    noSurfaces ++;
615    if ( p.z() >= 0.)  { sumnorm += nZ; }
616    else               { sumnorm -= nZ; }
617  }
618  if ( noSurfaces == 0 )
619  {
620#ifdef G4CSGDEBUG
621    G4Exception("G4Tube::SurfaceNormal(p)", "Notification",
622                JustWarning, "Point p is not on surface !?" );
623    G4cout.precision(20);
624    G4cout<< "G4Tubs::SN ( "<<p.x()<<", "<<p.y()<<", "<<p.z()<<" ); "
625          << G4endl << G4endl;
626#endif
627     norm = ApproxSurfaceNormal(p);
628  }
629  else if ( noSurfaces == 1 )  { norm = sumnorm; }
630  else                         { norm = sumnorm.unit(); }
631
632  return norm;
633}
634
635/////////////////////////////////////////////////////////////////////////////
636//
637// Algorithm for SurfaceNormal() following the original specification
638// for points not on the surface
639
640G4ThreeVector G4Tubs::ApproxSurfaceNormal( const G4ThreeVector& p ) const
641{ ENorm side ;
642  G4ThreeVector norm ;
643  G4double rho, phi ;
644  G4double distZ, distRMin, distRMax, distSPhi, distEPhi, distMin ;
645
646  rho = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
647
648  distRMin = std::fabs(rho - fRMin) ;
649  distRMax = std::fabs(rho - fRMax) ;
650  distZ    = std::fabs(std::fabs(p.z()) - fDz) ;
651
652  if (distRMin < distRMax) // First minimum
653  {
654    if ( distZ < distRMin )
655    {
656       distMin = distZ ;
657       side    = kNZ ;
658    }
659    else
660    {
661      distMin = distRMin ;
662      side    = kNRMin   ;
663    }
664  }
665  else
666  {
667    if ( distZ < distRMax )
668    {
669      distMin = distZ ;
670      side    = kNZ   ;
671    }
672    else
673    {
674      distMin = distRMax ;
675      side    = kNRMax   ;
676    }
677  }   
678  if (!fPhiFullTube  &&  rho ) // Protected against (0,0,z)
679  {
680    phi = std::atan2(p.y(),p.x()) ;
681
682    if ( phi < 0 )  { phi += twopi; }
683
684    if ( fSPhi < 0 )
685    {
686      distSPhi = std::fabs(phi - (fSPhi + twopi))*rho ;
687    }
688    else
689    {
690      distSPhi = std::fabs(phi - fSPhi)*rho ;
691    }
692    distEPhi = std::fabs(phi - fSPhi - fDPhi)*rho ;
693                                     
694    if (distSPhi < distEPhi) // Find new minimum
695    {
696      if ( distSPhi < distMin )
697      {
698        side = kNSPhi ;
699      }
700    }
701    else
702    {
703      if ( distEPhi < distMin )
704      {
705        side = kNEPhi ;
706      }
707    }
708  }   
709  switch ( side )
710  {
711    case kNRMin : // Inner radius
712    {                     
713      norm = G4ThreeVector(-p.x()/rho, -p.y()/rho, 0) ;
714      break ;
715    }
716    case kNRMax : // Outer radius
717    {                 
718      norm = G4ThreeVector(p.x()/rho, p.y()/rho, 0) ;
719      break ;
720    }
721    case kNZ : //    + or - dz
722    {                             
723      if ( p.z() > 0 )  { norm = G4ThreeVector(0,0,1) ; }
724      else              { norm = G4ThreeVector(0,0,-1); }
725      break ;
726    }
727    case kNSPhi:
728    {
729      norm = G4ThreeVector(std::sin(fSPhi), -std::cos(fSPhi), 0) ;
730      break ;
731    }
732    case kNEPhi:
733    {
734      norm = G4ThreeVector(-std::sin(fSPhi+fDPhi), std::cos(fSPhi+fDPhi), 0) ;
735      break;
736    }
737    default:
738    {
739      DumpInfo();
740      G4Exception("G4Tubs::ApproxSurfaceNormal()", "Notification", JustWarning,
741                  "Undefined side for valid surface normal to solid.");
742      break ;
743    }   
744  }               
745  return norm;
746}
747
748////////////////////////////////////////////////////////////////////
749//
750//
751// Calculate distance to shape from outside, along normalised vector
752// - return kInfinity if no intersection, or intersection distance <= tolerance
753//
754// - Compute the intersection with the z planes
755//        - if at valid r, phi, return
756//
757// -> If point is outer outer radius, compute intersection with rmax
758//        - if at valid phi,z return
759//
760// -> Compute intersection with inner radius, taking largest +ve root
761//        - if valid (in z,phi), save intersction
762//
763//    -> If phi segmented, compute intersections with phi half planes
764//        - return smallest of valid phi intersections and
765//          inner radius intersection
766//
767// NOTE:
768// - 'if valid' implies tolerant checking of intersection points
769
770G4double G4Tubs::DistanceToIn( const G4ThreeVector& p,
771                               const G4ThreeVector& v  ) const
772{
773  G4double snxt = kInfinity ;      // snxt = default return value
774  G4double tolORMin2, tolIRMax2 ;  // 'generous' radii squared
775  G4double tolORMax2, tolIRMin2, tolODz, tolIDz ;
776  const G4double dRmax = 100.*fRMax;
777
778  static const G4double halfCarTolerance = 0.5*kCarTolerance;
779  static const G4double halfRadTolerance = 0.5*kRadTolerance;
780
781  // Intersection point variables
782  //
783  G4double Dist, s, xi, yi, zi, rho2, inum, iden, cosPsi, Comp ;
784  G4double t1, t2, t3, b, c, d ;     // Quadratic solver variables
785 
786  // Calculate tolerant rmin and rmax
787
788  if (fRMin > kRadTolerance)
789  {
790    tolORMin2 = (fRMin - halfRadTolerance)*(fRMin - halfRadTolerance) ;
791    tolIRMin2 = (fRMin + halfRadTolerance)*(fRMin + halfRadTolerance) ;
792  }
793  else
794  {
795    tolORMin2 = 0.0 ;
796    tolIRMin2 = 0.0 ;
797  }
798  tolORMax2 = (fRMax + halfRadTolerance)*(fRMax + halfRadTolerance) ;
799  tolIRMax2 = (fRMax - halfRadTolerance)*(fRMax - halfRadTolerance) ;
800
801  // Intersection with Z surfaces
802
803  tolIDz = fDz - halfCarTolerance ;
804  tolODz = fDz + halfCarTolerance ;
805
806  if (std::fabs(p.z()) >= tolIDz)
807  {
808    if ( p.z()*v.z() < 0 )    // at +Z going in -Z or visa versa
809    {
810      s = (std::fabs(p.z()) - fDz)/std::fabs(v.z()) ;   // Z intersect distance
811
812      if(s < 0.0)  { s = 0.0; }
813
814      xi   = p.x() + s*v.x() ;                // Intersection coords
815      yi   = p.y() + s*v.y() ;
816      rho2 = xi*xi + yi*yi ;
817
818      // Check validity of intersection
819
820      if ((tolIRMin2 <= rho2) && (rho2 <= tolIRMax2))
821      {
822        if (!fPhiFullTube && rho2)
823        {
824          // Psi = angle made with central (average) phi of shape
825          //
826          inum   = xi*cosCPhi + yi*sinCPhi ;
827          iden   = std::sqrt(rho2) ;
828          cosPsi = inum/iden ;
829          if (cosPsi >= cosHDPhiIT)  { return s ; }
830        }
831        else
832        {
833          return s ;
834        }
835      }
836    }
837    else
838    {
839      if ( snxt<halfCarTolerance )  { snxt=0; }
840      return snxt ;  // On/outside extent, and heading away
841                     // -> cannot intersect
842    }
843  }
844
845  // -> Can not intersect z surfaces
846  //
847  // Intersection with rmax (possible return) and rmin (must also check phi)
848  //
849  // Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
850  //
851  // Intersects with x^2+y^2=R^2
852  //
853  // Hence (v.x^2+v.y^2)t^2+ 2t(p.x*v.x+p.y*v.y)+p.x^2+p.y^2-R^2=0
854  //            t1                t2                t3
855
856  t1 = 1.0 - v.z()*v.z() ;
857  t2 = p.x()*v.x() + p.y()*v.y() ;
858  t3 = p.x()*p.x() + p.y()*p.y() ;
859
860  if ( t1 > 0 )        // Check not || to z axis
861  {
862    b = t2/t1 ;
863    c = t3 - fRMax*fRMax ;
864    if ((t3 >= tolORMax2) && (t2<0))   // This also handles the tangent case
865    {
866      // Try outer cylinder intersection
867      //          c=(t3-fRMax*fRMax)/t1;
868
869      c /= t1 ;
870      d = b*b - c ;
871
872      if (d >= 0)  // If real root
873      {
874        s = -b - std::sqrt(d) ;
875        if (s >= 0)  // If 'forwards'
876        {
877          if ( s>dRmax ) // Avoid rounding errors due to precision issues on
878          {              // 64 bits systems. Split long distances and recompute
879            G4double fTerm = s-std::fmod(s,dRmax);
880            s = fTerm + DistanceToIn(p+fTerm*v,v);
881          } 
882          // Check z intersection
883          //
884          zi = p.z() + s*v.z() ;
885          if (std::fabs(zi)<=tolODz)
886          {
887            // Z ok. Check phi intersection if reqd
888            //
889            if (fPhiFullTube)
890            {
891              return s ;
892            }
893            else
894            {
895              xi     = p.x() + s*v.x() ;
896              yi     = p.y() + s*v.y() ;
897              cosPsi = (xi*cosCPhi + yi*sinCPhi)/fRMax ;
898              if (cosPsi >= cosHDPhiIT)  { return s ; }
899            }
900          }  //  end if std::fabs(zi)
901        }    //  end if (s>=0)
902      }      //  end if (d>=0)
903    }        //  end if (r>=fRMax)
904    else 
905    {
906      // Inside outer radius :
907      // check not inside, and heading through tubs (-> 0 to in)
908
909      if ((t3 > tolIRMin2) && (t2 < 0) && (std::fabs(p.z()) <= tolIDz))
910      {
911        // Inside both radii, delta r -ve, inside z extent
912
913        if (!fPhiFullTube)
914        {
915          inum   = p.x()*cosCPhi + p.y()*sinCPhi ;
916          iden   = std::sqrt(t3) ;
917          cosPsi = inum/iden ;
918          if (cosPsi >= cosHDPhiIT)
919          {
920            // In the old version, the small negative tangent for the point
921            // on surface was not taken in account, and returning 0.0 ...
922            // New version: check the tangent for the point on surface and
923            // if no intersection, return kInfinity, if intersection instead
924            // return s.
925            //
926            c = t3-fRMax*fRMax; 
927            if ( c<=0.0 )
928            {
929              return 0.0;
930            }
931            else
932            {
933              c = c/t1 ;
934              d = b*b-c;
935              if ( d>=0.0 )
936              {
937                snxt = c/(-b+std::sqrt(d)); // using safe solution
938                                            // for quadratic equation
939                if ( snxt < halfCarTolerance ) { snxt=0; }
940                return snxt ;
941              }     
942              else
943              {
944                return kInfinity;
945              }
946            }
947          } 
948        }
949        else
950        {   
951          // In the old version, the small negative tangent for the point
952          // on surface was not taken in account, and returning 0.0 ...
953          // New version: check the tangent for the point on surface and
954          // if no intersection, return kInfinity, if intersection instead
955          // return s.
956          //
957          c = t3 - fRMax*fRMax; 
958          if ( c<=0.0 )
959          {
960            return 0.0;
961          }
962          else
963          {
964            c = c/t1 ;
965            d = b*b-c;
966            if ( d>=0.0 )
967            {
968              snxt= c/(-b+std::sqrt(d)); // using safe solution
969                                         // for quadratic equation
970              if ( snxt < halfCarTolerance ) { snxt=0; }
971              return snxt ;
972            }     
973            else
974            {
975              return kInfinity;
976            }
977          }
978        } // end if   (!fPhiFullTube)
979      }   // end if   (t3>tolIRMin2)
980    }     // end if   (Inside Outer Radius)
981    if ( fRMin )    // Try inner cylinder intersection
982    {
983      c = (t3 - fRMin*fRMin)/t1 ;
984      d = b*b - c ;
985      if ( d >= 0.0 )  // If real root
986      {
987        // Always want 2nd root - we are outside and know rmax Hit was bad
988        // - If on surface of rmin also need farthest root
989
990        s = -b + std::sqrt(d) ;
991        if (s >= -halfCarTolerance)  // check forwards
992        {
993          // Check z intersection
994          //
995          if(s < 0.0)  { s = 0.0; }
996          if ( s>dRmax ) // Avoid rounding errors due to precision issues seen
997          {              // 64 bits systems. Split long distances and recompute
998            G4double fTerm = s-std::fmod(s,dRmax);
999            s = fTerm + DistanceToIn(p+fTerm*v,v);
1000          } 
1001          zi = p.z() + s*v.z() ;
1002          if (std::fabs(zi) <= tolODz)
1003          {
1004            // Z ok. Check phi
1005            //
1006            if ( fPhiFullTube )
1007            {
1008              return s ; 
1009            }
1010            else
1011            {
1012              xi     = p.x() + s*v.x() ;
1013              yi     = p.y() + s*v.y() ;
1014              cosPsi = (xi*cosCPhi + yi*sinCPhi)/fRMin ;
1015              if (cosPsi >= cosHDPhiIT)
1016              {
1017                // Good inner radius isect
1018                // - but earlier phi isect still possible
1019
1020                snxt = s ;
1021              }
1022            }
1023          }        //    end if std::fabs(zi)
1024        }          //    end if (s>=0)
1025      }            //    end if (d>=0)
1026    }              //    end if (fRMin)
1027  }
1028
1029  // Phi segment intersection
1030  //
1031  // o Tolerant of points inside phi planes by up to kCarTolerance*0.5
1032  //
1033  // o NOTE: Large duplication of code between sphi & ephi checks
1034  //         -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
1035  //            intersection check <=0 -> >=0
1036  //         -> use some form of loop Construct ?
1037  //
1038  if ( !fPhiFullTube )
1039  {
1040    // First phi surface (Starting phi)
1041    //
1042    Comp    = v.x()*sinSPhi - v.y()*cosSPhi ;
1043                   
1044    if ( Comp < 0 )  // Component in outwards normal dirn
1045    {
1046      Dist = (p.y()*cosSPhi - p.x()*sinSPhi) ;
1047
1048      if ( Dist < halfCarTolerance )
1049      {
1050        s = Dist/Comp ;
1051
1052        if (s < snxt)
1053        {
1054          if ( s < 0 )  { s = 0.0; }
1055          zi = p.z() + s*v.z() ;
1056          if ( std::fabs(zi) <= tolODz )
1057          {
1058            xi   = p.x() + s*v.x() ;
1059            yi   = p.y() + s*v.y() ;
1060            rho2 = xi*xi + yi*yi ;
1061
1062            if ( ( (rho2 >= tolIRMin2) && (rho2 <= tolIRMax2) )
1063              || ( (rho2 >  tolORMin2) && (rho2 <  tolIRMin2)
1064                && ( v.y()*cosSPhi - v.x()*sinSPhi >  0 )
1065                && ( v.x()*cosSPhi + v.y()*sinSPhi >= 0 )     )
1066              || ( (rho2 > tolIRMax2) && (rho2 < tolORMax2)
1067                && (v.y()*cosSPhi - v.x()*sinSPhi > 0)
1068                && (v.x()*cosSPhi + v.y()*sinSPhi < 0) )    )
1069            {
1070              // z and r intersections good
1071              // - check intersecting with correct half-plane
1072              //
1073              if ((yi*cosCPhi-xi*sinCPhi) <= halfCarTolerance) { snxt = s; }
1074            }
1075          }
1076        }
1077      }   
1078    }
1079     
1080    // Second phi surface (Ending phi)
1081
1082    Comp    = -(v.x()*sinEPhi - v.y()*cosEPhi) ;
1083       
1084    if (Comp < 0 )  // Component in outwards normal dirn
1085    {
1086      Dist = -(p.y()*cosEPhi - p.x()*sinEPhi) ;
1087
1088      if ( Dist < halfCarTolerance )
1089      {
1090        s = Dist/Comp ;
1091
1092        if (s < snxt)
1093        {
1094          if ( s < 0 )  { s = 0; }
1095          zi = p.z() + s*v.z() ;
1096          if ( std::fabs(zi) <= tolODz )
1097          {
1098            xi   = p.x() + s*v.x() ;
1099            yi   = p.y() + s*v.y() ;
1100            rho2 = xi*xi + yi*yi ;
1101            if ( ( (rho2 >= tolIRMin2) && (rho2 <= tolIRMax2) )
1102                || ( (rho2 > tolORMin2)  && (rho2 < tolIRMin2)
1103                  && (v.x()*sinEPhi - v.y()*cosEPhi >  0)
1104                  && (v.x()*cosEPhi + v.y()*sinEPhi >= 0) )
1105                || ( (rho2 > tolIRMax2) && (rho2 < tolORMax2)
1106                  && (v.x()*sinEPhi - v.y()*cosEPhi > 0)
1107                  && (v.x()*cosEPhi + v.y()*sinEPhi < 0) ) )
1108            {
1109              // z and r intersections good
1110              // - check intersecting with correct half-plane
1111              //
1112              if ( (yi*cosCPhi-xi*sinCPhi) >= 0 ) { snxt = s; }
1113            }                         //?? >=-halfCarTolerance
1114          }
1115        }
1116      }
1117    }         //  Comp < 0
1118  }           //  !fPhiFullTube
1119  if ( snxt<halfCarTolerance )  { snxt=0; }
1120  return snxt ;
1121}
1122 
1123//////////////////////////////////////////////////////////////////
1124//
1125// Calculate distance to shape from outside, along normalised vector
1126// - return kInfinity if no intersection, or intersection distance <= tolerance
1127//
1128// - Compute the intersection with the z planes
1129//        - if at valid r, phi, return
1130//
1131// -> If point is outer outer radius, compute intersection with rmax
1132//        - if at valid phi,z return
1133//
1134// -> Compute intersection with inner radius, taking largest +ve root
1135//        - if valid (in z,phi), save intersction
1136//
1137//    -> If phi segmented, compute intersections with phi half planes
1138//        - return smallest of valid phi intersections and
1139//          inner radius intersection
1140//
1141// NOTE:
1142// - Precalculations for phi trigonometry are Done `just in time'
1143// - `if valid' implies tolerant checking of intersection points
1144//   Calculate distance (<= actual) to closest surface of shape from outside
1145// - Calculate distance to z, radial planes
1146// - Only to phi planes if outside phi extent
1147// - Return 0 if point inside
1148
1149G4double G4Tubs::DistanceToIn( const G4ThreeVector& p ) const
1150{
1151  G4double safe=0.0, rho, safe1, safe2, safe3 ;
1152  G4double safePhi, cosPsi ;
1153
1154  rho   = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
1155  safe1 = fRMin - rho ;
1156  safe2 = rho - fRMax ;
1157  safe3 = std::fabs(p.z()) - fDz ;
1158
1159  if ( safe1 > safe2 ) { safe = safe1; }
1160  else                 { safe = safe2; }
1161  if ( safe3 > safe )  { safe = safe3; }
1162
1163  if ( (!fPhiFullTube) && (rho) )
1164  {
1165    // Psi=angle from central phi to point
1166    //
1167    cosPsi = (p.x()*cosCPhi + p.y()*sinCPhi)/rho ;
1168   
1169    if ( cosPsi < std::cos(fDPhi*0.5) )
1170    {
1171      // Point lies outside phi range
1172
1173      if ( (p.y()*cosCPhi - p.x()*sinCPhi) <= 0 )
1174      {
1175        safePhi = std::fabs(p.x()*sinSPhi - p.y()*cosSPhi) ;
1176      }
1177      else
1178      {
1179        safePhi = std::fabs(p.x()*sinEPhi - p.y()*cosEPhi) ;
1180      }
1181      if ( safePhi > safe )  { safe = safePhi; }
1182    }
1183  }
1184  if ( safe < 0 )  { safe = 0; }
1185  return safe ;
1186}
1187
1188//////////////////////////////////////////////////////////////////////////////
1189//
1190// Calculate distance to surface of shape from `inside', allowing for tolerance
1191// - Only Calc rmax intersection if no valid rmin intersection
1192
1193G4double G4Tubs::DistanceToOut( const G4ThreeVector& p,
1194                                const G4ThreeVector& v,
1195                                const G4bool calcNorm,
1196                                      G4bool *validNorm,
1197                                      G4ThreeVector *n    ) const
1198{ 
1199  ESide side=kNull , sider=kNull, sidephi=kNull ;
1200  G4double snxt, sr=kInfinity, sphi=kInfinity, pdist ;
1201  G4double deltaR, t1, t2, t3, b, c, d2, roMin2 ;
1202
1203  static const G4double halfCarTolerance = kCarTolerance*0.5;
1204  static const G4double halfAngTolerance = kAngTolerance*0.5;
1205 
1206  // Vars for phi intersection:
1207
1208  G4double pDistS, compS, pDistE, compE, sphi2, xi, yi, vphi, roi2 ;
1209 
1210  // Z plane intersection
1211
1212  if (v.z() > 0 )
1213  {
1214    pdist = fDz - p.z() ;
1215    if ( pdist > halfCarTolerance )
1216    {
1217      snxt = pdist/v.z() ;
1218      side = kPZ ;
1219    }
1220    else
1221    {
1222      if (calcNorm)
1223      {
1224        *n         = G4ThreeVector(0,0,1) ;
1225        *validNorm = true ;
1226      }
1227      return snxt = 0 ;
1228    }
1229  }
1230  else if ( v.z() < 0 )
1231  {
1232    pdist = fDz + p.z() ;
1233
1234    if ( pdist > halfCarTolerance )
1235    {
1236      snxt = -pdist/v.z() ;
1237      side = kMZ ;
1238    }
1239    else
1240    {
1241      if (calcNorm)
1242      {
1243        *n         = G4ThreeVector(0,0,-1) ;
1244        *validNorm = true ;
1245      }
1246      return snxt = 0.0 ;
1247    }
1248  }
1249  else
1250  {
1251    snxt = kInfinity ;    // Travel perpendicular to z axis
1252    side = kNull;
1253  }
1254
1255  // Radial Intersections
1256  //
1257  // Find intersection with cylinders at rmax/rmin
1258  // Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
1259  //
1260  // Intersects with x^2+y^2=R^2
1261  //
1262  // Hence (v.x^2+v.y^2)t^2+ 2t(p.x*v.x+p.y*v.y)+p.x^2+p.y^2-R^2=0
1263  //
1264  //            t1                t2                    t3
1265
1266  t1   = 1.0 - v.z()*v.z() ;      // since v normalised
1267  t2   = p.x()*v.x() + p.y()*v.y() ;
1268  t3   = p.x()*p.x() + p.y()*p.y() ;
1269
1270  if ( snxt > 10*(fDz+fRMax) )  { roi2 = 2*fRMax*fRMax; }
1271  else  { roi2 = snxt*snxt*t1 + 2*snxt*t2 + t3; }        // radius^2 on +-fDz
1272
1273  if ( t1 > 0 ) // Check not parallel
1274  {
1275    // Calculate sr, r exit distance
1276     
1277    if ( (t2 >= 0.0) && (roi2 > fRMax*(fRMax + kRadTolerance)) )
1278    {
1279      // Delta r not negative => leaving via rmax
1280
1281      deltaR = t3 - fRMax*fRMax ;
1282
1283      // NOTE: Should use rho-fRMax<-kRadTolerance*0.5
1284      // - avoid sqrt for efficiency
1285
1286      if ( deltaR < -kRadTolerance*fRMax )
1287      {
1288        b     = t2/t1 ;
1289        c     = deltaR/t1 ;
1290        d2    = b*b-c;
1291        if( d2 >= 0 ) { sr = -b + std::sqrt(d2); }
1292        else          { sr = 0.; }
1293        sider = kRMax ;
1294      }
1295      else
1296      {
1297        // On tolerant boundary & heading outwards (or perpendicular to)
1298        // outer radial surface -> leaving immediately
1299
1300        if ( calcNorm ) 
1301        {
1302          *n         = G4ThreeVector(p.x()/fRMax,p.y()/fRMax,0) ;
1303          *validNorm = true ;
1304        }
1305        return snxt = 0 ; // Leaving by rmax immediately
1306      }
1307    }             
1308    else if ( t2 < 0. ) // i.e.  t2 < 0; Possible rmin intersection
1309    {
1310      roMin2 = t3 - t2*t2/t1 ; // min ro2 of the plane of movement
1311
1312      if ( fRMin && (roMin2 < fRMin*(fRMin - kRadTolerance)) )
1313      {
1314        deltaR = t3 - fRMin*fRMin ;
1315        b      = t2/t1 ;
1316        c      = deltaR/t1 ;
1317        d2     = b*b - c ;
1318
1319        if ( d2 >= 0 )   // Leaving via rmin
1320        {
1321          // NOTE: SHould use rho-rmin>kRadTolerance*0.5
1322          // - avoid sqrt for efficiency
1323
1324          if (deltaR > kRadTolerance*fRMin)
1325          {
1326            sr    = -b-std::sqrt(d2) ;
1327            sider = kRMin ;
1328          }
1329          else
1330          {
1331            if ( calcNorm ) { *validNorm = false; }  // Concave side
1332            return snxt = 0.0;
1333          }
1334        }
1335        else    // No rmin intersect -> must be rmax intersect
1336        {
1337          deltaR = t3 - fRMax*fRMax ;
1338          c     = deltaR/t1 ;
1339          d2    = b*b-c;
1340          if( d2 >=0. )
1341          {
1342            sr     = -b + std::sqrt(d2) ;
1343            sider  = kRMax ;
1344          }
1345          else // Case: On the border+t2<kRadTolerance
1346               //       (v is perpendicular to the surface)
1347          {
1348            if (calcNorm)
1349            {
1350              *n = G4ThreeVector(p.x()/fRMax,p.y()/fRMax,0) ;
1351              *validNorm = true ;
1352            }
1353            return snxt = 0.0;
1354          }
1355        }
1356      }
1357      else if ( roi2 > fRMax*(fRMax + kRadTolerance) )
1358           // No rmin intersect -> must be rmax intersect
1359      {
1360        deltaR = t3 - fRMax*fRMax ;
1361        b      = t2/t1 ;
1362        c      = deltaR/t1;
1363        d2     = b*b-c;
1364        if( d2 >= 0 )
1365        {
1366          sr     = -b + std::sqrt(d2) ;
1367          sider  = kRMax ;
1368        }
1369        else // Case: On the border+t2<kRadTolerance
1370             //       (v is perpendicular to the surface)
1371        {
1372          if (calcNorm)
1373          {
1374            *n = G4ThreeVector(p.x()/fRMax,p.y()/fRMax,0) ;
1375            *validNorm = true ;
1376          }
1377          return snxt = 0.0;
1378        }
1379      }
1380    }
1381   
1382    // Phi Intersection
1383
1384    if ( !fPhiFullTube )
1385    {
1386      // add angle calculation with correction
1387      // of the difference in domain of atan2 and Sphi
1388      //
1389      vphi = std::atan2(v.y(),v.x()) ;
1390     
1391      if ( vphi < fSPhi - halfAngTolerance  )             { vphi += twopi; }
1392      else if ( vphi > fSPhi + fDPhi + halfAngTolerance ) { vphi -= twopi; }
1393
1394
1395      if ( p.x() || p.y() )  // Check if on z axis (rho not needed later)
1396      {
1397        // pDist -ve when inside
1398
1399        pDistS = p.x()*sinSPhi - p.y()*cosSPhi ;
1400        pDistE = -p.x()*sinEPhi + p.y()*cosEPhi ;
1401
1402        // Comp -ve when in direction of outwards normal
1403
1404        compS   = -sinSPhi*v.x() + cosSPhi*v.y() ;
1405        compE   =  sinEPhi*v.x() - cosEPhi*v.y() ;
1406       
1407        sidephi = kNull;
1408       
1409        if( ( (fDPhi <= pi) && ( (pDistS <= halfCarTolerance)
1410                              && (pDistE <= halfCarTolerance) ) )
1411         || ( (fDPhi >  pi) && !((pDistS >  halfCarTolerance)
1412                              && (pDistE >  halfCarTolerance) ) )  )
1413        {
1414          // Inside both phi *full* planes
1415         
1416          if ( compS < 0 )
1417          {
1418            sphi = pDistS/compS ;
1419           
1420            if (sphi >= -halfCarTolerance)
1421            {
1422              xi = p.x() + sphi*v.x() ;
1423              yi = p.y() + sphi*v.y() ;
1424             
1425              // Check intersecting with correct half-plane
1426              // (if not -> no intersect)
1427              //
1428              if( (std::abs(xi)<=kCarTolerance)&&(std::abs(yi)<=kCarTolerance) )
1429              {
1430                sidephi = kSPhi;
1431                if (((fSPhi-halfAngTolerance)<=vphi)
1432                   &&((fSPhi+fDPhi+halfAngTolerance)>=vphi))
1433                {
1434                  sphi = kInfinity;
1435                }
1436              }
1437              else if ( yi*cosCPhi-xi*sinCPhi >=0 )
1438              {
1439                sphi = kInfinity ;
1440              }
1441              else
1442              {
1443                sidephi = kSPhi ;
1444                if ( pDistS > -halfCarTolerance )
1445                {
1446                  sphi = 0.0 ; // Leave by sphi immediately
1447                }   
1448              }       
1449            }
1450            else
1451            {
1452              sphi = kInfinity ;
1453            }
1454          }
1455          else
1456          {
1457            sphi = kInfinity ;
1458          }
1459
1460          if ( compE < 0 )
1461          {
1462            sphi2 = pDistE/compE ;
1463           
1464            // Only check further if < starting phi intersection
1465            //
1466            if ( (sphi2 > -halfCarTolerance) && (sphi2 < sphi) )
1467            {
1468              xi = p.x() + sphi2*v.x() ;
1469              yi = p.y() + sphi2*v.y() ;
1470             
1471              if ((std::abs(xi)<=kCarTolerance)&&(std::abs(yi)<=kCarTolerance))
1472              {
1473                // Leaving via ending phi
1474                //
1475                if( !((fSPhi-halfAngTolerance <= vphi)
1476                     &&(fSPhi+fDPhi+halfAngTolerance >= vphi)) )
1477                {
1478                  sidephi = kEPhi ;
1479                  if ( pDistE <= -halfCarTolerance )  { sphi = sphi2 ; }
1480                  else                                { sphi = 0.0 ;   }
1481                }
1482              } 
1483              else    // Check intersecting with correct half-plane
1484
1485              if ( (yi*cosCPhi-xi*sinCPhi) >= 0)
1486              {
1487                // Leaving via ending phi
1488                //
1489                sidephi = kEPhi ;
1490                if ( pDistE <= -halfCarTolerance ) { sphi = sphi2 ; }
1491                else                               { sphi = 0.0 ;   }
1492              }
1493            }
1494          }
1495        }
1496        else
1497        {
1498          sphi = kInfinity ;
1499        }
1500      }
1501      else
1502      {
1503        // On z axis + travel not || to z axis -> if phi of vector direction
1504        // within phi of shape, Step limited by rmax, else Step =0
1505               
1506        if ( (fSPhi - halfAngTolerance <= vphi)
1507           && (vphi <= fSPhi + fDPhi + halfAngTolerance ) )
1508        {
1509          sphi = kInfinity ;
1510        }
1511        else
1512        {
1513          sidephi = kSPhi ; // arbitrary
1514          sphi    = 0.0 ;
1515        }
1516      }
1517      if (sphi < snxt)  // Order intersecttions
1518      {
1519        snxt = sphi ;
1520        side = sidephi ;
1521      }
1522    }
1523    if (sr < snxt)  // Order intersections
1524    {
1525      snxt = sr ;
1526      side = sider ;
1527    }
1528  }
1529  if (calcNorm)
1530  {
1531    switch(side)
1532    {
1533      case kRMax:
1534        // Note: returned vector not normalised
1535        // (divide by fRMax for unit vector)
1536        //
1537        xi = p.x() + snxt*v.x() ;
1538        yi = p.y() + snxt*v.y() ;
1539        *n = G4ThreeVector(xi/fRMax,yi/fRMax,0) ;
1540        *validNorm = true ;
1541        break ;
1542
1543      case kRMin:
1544        *validNorm = false ;  // Rmin is inconvex
1545        break ;
1546
1547      case kSPhi:
1548        if ( fDPhi <= pi )
1549        {
1550          *n         = G4ThreeVector(sinSPhi,-cosSPhi,0) ;
1551          *validNorm = true ;
1552        }
1553        else
1554        {
1555          *validNorm = false ;
1556        }
1557        break ;
1558
1559      case kEPhi:
1560        if (fDPhi <= pi)
1561        {
1562          *n = G4ThreeVector(-sinEPhi,cosEPhi,0) ;
1563          *validNorm = true ;
1564        }
1565        else
1566        {
1567          *validNorm = false ;
1568        }
1569        break ;
1570
1571      case kPZ:
1572        *n         = G4ThreeVector(0,0,1) ;
1573        *validNorm = true ;
1574        break ;
1575
1576      case kMZ:
1577        *n         = G4ThreeVector(0,0,-1) ;
1578        *validNorm = true ;
1579        break ;
1580
1581      default:
1582        G4cout.precision(16) ;
1583        G4cout << G4endl ;
1584        DumpInfo();
1585        G4cout << "Position:"  << G4endl << G4endl ;
1586        G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
1587        G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
1588        G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
1589        G4cout << "Direction:" << G4endl << G4endl ;
1590        G4cout << "v.x() = "   << v.x() << G4endl ;
1591        G4cout << "v.y() = "   << v.y() << G4endl ;
1592        G4cout << "v.z() = "   << v.z() << G4endl << G4endl ;
1593        G4cout << "Proposed distance :" << G4endl << G4endl ;
1594        G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl ;
1595        G4Exception("G4Tubs::DistanceToOut(p,v,..)","Notification",JustWarning,
1596                    "Undefined side for valid surface normal to solid.");
1597        break ;
1598    }
1599  }
1600  if ( snxt<halfCarTolerance )  { snxt=0 ; }
1601
1602  return snxt ;
1603}
1604
1605//////////////////////////////////////////////////////////////////////////
1606//
1607// Calculate distance (<=actual) to closest surface of shape from inside
1608
1609G4double G4Tubs::DistanceToOut( const G4ThreeVector& p ) const
1610{
1611  G4double safe=0.0, rho, safeR1, safeR2, safeZ, safePhi ;
1612  rho = std::sqrt(p.x()*p.x() + p.y()*p.y()) ;
1613
1614#ifdef G4CSGDEBUG
1615  if( Inside(p) == kOutside )
1616  {
1617    G4cout.precision(16) ;
1618    G4cout << G4endl ;
1619    DumpInfo();
1620    G4cout << "Position:"  << G4endl << G4endl ;
1621    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
1622    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
1623    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
1624    G4Exception("G4Tubs::DistanceToOut(p)", "Notification", JustWarning, 
1625                 "Point p is outside !?");
1626  }
1627#endif
1628
1629  if ( fRMin )
1630  {
1631    safeR1 = rho   - fRMin ;
1632    safeR2 = fRMax - rho ;
1633 
1634    if ( safeR1 < safeR2 ) { safe = safeR1 ; }
1635    else                   { safe = safeR2 ; }
1636  }
1637  else
1638  {
1639    safe = fRMax - rho ;
1640  }
1641  safeZ = fDz - std::fabs(p.z()) ;
1642
1643  if ( safeZ < safe )  { safe = safeZ ; }
1644
1645  // Check if phi divided, Calc distances closest phi plane
1646  //
1647  if ( !fPhiFullTube )
1648  {
1649    if ( p.y()*cosCPhi-p.x()*sinCPhi <= 0 )
1650    {
1651      safePhi = -(p.x()*sinSPhi - p.y()*cosSPhi) ;
1652    }
1653    else
1654    {
1655      safePhi = (p.x()*sinEPhi - p.y()*cosEPhi) ;
1656    }
1657    if (safePhi < safe)  { safe = safePhi ; }
1658  }
1659  if ( safe < 0 )  { safe = 0 ; }
1660
1661  return safe ; 
1662}
1663
1664/////////////////////////////////////////////////////////////////////////
1665//
1666// Create a List containing the transformed vertices
1667// Ordering [0-3] -fDz cross section
1668//          [4-7] +fDz cross section such that [0] is below [4],
1669//                                             [1] below [5] etc.
1670// Note:
1671//  Caller has deletion resposibility
1672//  Potential improvement: For last slice, use actual ending angle
1673//                         to avoid rounding error problems.
1674
1675G4ThreeVectorList*
1676G4Tubs::CreateRotatedVertices( const G4AffineTransform& pTransform ) const
1677{
1678  G4ThreeVectorList* vertices ;
1679  G4ThreeVector vertex0, vertex1, vertex2, vertex3 ;
1680  G4double meshAngle, meshRMax, crossAngle,
1681           cosCrossAngle, sinCrossAngle, sAngle;
1682  G4double rMaxX, rMaxY, rMinX, rMinY, meshRMin ;
1683  G4int crossSection, noCrossSections;
1684
1685  // Compute no of cross-sections necessary to mesh tube
1686  //
1687  noCrossSections = G4int(fDPhi/kMeshAngleDefault) + 1 ;
1688
1689  if ( noCrossSections < kMinMeshSections )
1690  {
1691    noCrossSections = kMinMeshSections ;
1692  }
1693  else if (noCrossSections>kMaxMeshSections)
1694  {
1695    noCrossSections = kMaxMeshSections ;
1696  }
1697  // noCrossSections = 4 ;
1698
1699  meshAngle = fDPhi/(noCrossSections - 1) ;
1700  // meshAngle = fDPhi/(noCrossSections) ;
1701
1702  meshRMax  = (fRMax+100*kCarTolerance)/std::cos(meshAngle*0.5) ;
1703  meshRMin = fRMin - 100*kCarTolerance ; 
1704 
1705  // If complete in phi, set start angle such that mesh will be at fRMax
1706  // on the x axis. Will give better extent calculations when not rotated.
1707
1708  if (fPhiFullTube && (fSPhi == 0) )  { sAngle = -meshAngle*0.5 ; }
1709  else                                { sAngle =  fSPhi ; }
1710   
1711  vertices = new G4ThreeVectorList();
1712  vertices->reserve(noCrossSections*4);
1713   
1714  if ( vertices )
1715  {
1716    for (crossSection = 0 ; crossSection < noCrossSections ; crossSection++ )
1717    {
1718      // Compute coordinates of cross section at section crossSection
1719
1720      crossAngle    = sAngle + crossSection*meshAngle ;
1721      cosCrossAngle = std::cos(crossAngle) ;
1722      sinCrossAngle = std::sin(crossAngle) ;
1723
1724      rMaxX = meshRMax*cosCrossAngle ;
1725      rMaxY = meshRMax*sinCrossAngle ;
1726
1727      if(meshRMin <= 0.0)
1728      {
1729        rMinX = 0.0 ;
1730        rMinY = 0.0 ;
1731      }
1732      else
1733      {
1734        rMinX = meshRMin*cosCrossAngle ;
1735        rMinY = meshRMin*sinCrossAngle ;
1736      }
1737      vertex0 = G4ThreeVector(rMinX,rMinY,-fDz) ;
1738      vertex1 = G4ThreeVector(rMaxX,rMaxY,-fDz) ;
1739      vertex2 = G4ThreeVector(rMaxX,rMaxY,+fDz) ;
1740      vertex3 = G4ThreeVector(rMinX,rMinY,+fDz) ;
1741
1742      vertices->push_back(pTransform.TransformPoint(vertex0)) ;
1743      vertices->push_back(pTransform.TransformPoint(vertex1)) ;
1744      vertices->push_back(pTransform.TransformPoint(vertex2)) ;
1745      vertices->push_back(pTransform.TransformPoint(vertex3)) ;
1746    }
1747  }
1748  else
1749  {
1750    DumpInfo();
1751    G4Exception("G4Tubs::CreateRotatedVertices()",
1752                "FatalError", FatalException,
1753                "Error in allocation of vertices. Out of memory !");
1754  }
1755  return vertices ;
1756}
1757
1758//////////////////////////////////////////////////////////////////////////
1759//
1760// Stream object contents to an output stream
1761
1762G4GeometryType G4Tubs::GetEntityType() const
1763{
1764  return G4String("G4Tubs");
1765}
1766
1767//////////////////////////////////////////////////////////////////////////
1768//
1769// Stream object contents to an output stream
1770
1771std::ostream& G4Tubs::StreamInfo( std::ostream& os ) const
1772{
1773  os << "-----------------------------------------------------------\n"
1774     << "    *** Dump for solid - " << GetName() << " ***\n"
1775     << "    ===================================================\n"
1776     << " Solid type: G4Tubs\n"
1777     << " Parameters: \n"
1778     << "    inner radius : " << fRMin/mm << " mm \n"
1779     << "    outer radius : " << fRMax/mm << " mm \n"
1780     << "    half length Z: " << fDz/mm << " mm \n"
1781     << "    starting phi : " << fSPhi/degree << " degrees \n"
1782     << "    delta phi    : " << fDPhi/degree << " degrees \n"
1783     << "-----------------------------------------------------------\n";
1784
1785  return os;
1786}
1787
1788/////////////////////////////////////////////////////////////////////////
1789//
1790// GetPointOnSurface
1791
1792G4ThreeVector G4Tubs::GetPointOnSurface() const
1793{
1794  G4double xRand, yRand, zRand, phi, cosphi, sinphi, chose,
1795           aOne, aTwo, aThr, aFou;
1796  G4double rRand;
1797
1798  aOne = 2.*fDz*fDPhi*fRMax;
1799  aTwo = 2.*fDz*fDPhi*fRMin;
1800  aThr = 0.5*fDPhi*(fRMax*fRMax-fRMin*fRMin);
1801  aFou = 2.*fDz*(fRMax-fRMin);
1802
1803  phi    = RandFlat::shoot(fSPhi, fSPhi+fDPhi);
1804  cosphi = std::cos(phi);
1805  sinphi = std::sin(phi);
1806
1807  rRand  = RandFlat::shoot(fRMin,fRMax);
1808 
1809  if( (fSPhi == 0) && (fDPhi == twopi) ) { aFou = 0; }
1810 
1811  chose  = RandFlat::shoot(0.,aOne+aTwo+2.*aThr+2.*aFou);
1812
1813  if( (chose >=0) && (chose < aOne) )
1814  {
1815    xRand = fRMax*cosphi;
1816    yRand = fRMax*sinphi;
1817    zRand = RandFlat::shoot(-1.*fDz,fDz);
1818    return G4ThreeVector  (xRand, yRand, zRand);
1819  }
1820  else if( (chose >= aOne) && (chose < aOne + aTwo) )
1821  {
1822    xRand = fRMin*cosphi;
1823    yRand = fRMin*sinphi;
1824    zRand = RandFlat::shoot(-1.*fDz,fDz);
1825    return G4ThreeVector  (xRand, yRand, zRand);
1826  }
1827  else if( (chose >= aOne + aTwo) && (chose < aOne + aTwo + aThr) )
1828  {
1829    xRand = rRand*cosphi;
1830    yRand = rRand*sinphi;
1831    zRand = fDz;
1832    return G4ThreeVector  (xRand, yRand, zRand);
1833  }
1834  else if( (chose >= aOne + aTwo + aThr) && (chose < aOne + aTwo + 2.*aThr) )
1835  {
1836    xRand = rRand*cosphi;
1837    yRand = rRand*sinphi;
1838    zRand = -1.*fDz;
1839    return G4ThreeVector  (xRand, yRand, zRand);
1840  }
1841  else if( (chose >= aOne + aTwo + 2.*aThr)
1842        && (chose < aOne + aTwo + 2.*aThr + aFou) )
1843  {
1844    xRand = rRand*std::cos(fSPhi);
1845    yRand = rRand*std::sin(fSPhi);
1846    zRand = RandFlat::shoot(-1.*fDz,fDz);
1847    return G4ThreeVector  (xRand, yRand, zRand);
1848  }
1849  else
1850  {
1851    xRand = rRand*std::cos(fSPhi+fDPhi);
1852    yRand = rRand*std::sin(fSPhi+fDPhi);
1853    zRand = RandFlat::shoot(-1.*fDz,fDz);
1854    return G4ThreeVector  (xRand, yRand, zRand);
1855  }
1856}
1857
1858///////////////////////////////////////////////////////////////////////////
1859//
1860// Methods for visualisation
1861
1862void G4Tubs::DescribeYourselfTo ( G4VGraphicsScene& scene ) const 
1863{
1864  scene.AddSolid (*this) ;
1865}
1866
1867G4Polyhedron* G4Tubs::CreatePolyhedron () const 
1868{
1869  return new G4PolyhedronTubs (fRMin, fRMax, fDz, fSPhi, fDPhi) ;
1870}
1871
1872G4NURBS* G4Tubs::CreateNURBS () const 
1873{
1874  G4NURBS* pNURBS ;
1875  if (fRMin != 0) 
1876  {
1877    if (fPhiFullTube) 
1878    {
1879      pNURBS = new G4NURBStube (fRMin,fRMax,fDz) ;
1880    }
1881    else 
1882    {
1883      pNURBS = new G4NURBStubesector (fRMin,fRMax,fDz,fSPhi,fSPhi+fDPhi) ;
1884    }
1885  }
1886  else 
1887  {
1888    if (fPhiFullTube) 
1889    {
1890      pNURBS = new G4NURBScylinder (fRMax,fDz) ;
1891    }
1892    else 
1893    {
1894      const G4double epsilon = 1.e-4 ; // Cylinder sector not yet available!
1895      pNURBS = new G4NURBStubesector (epsilon,fRMax,fDz,fSPhi,fSPhi+fDPhi) ;
1896    }
1897  }
1898  return pNURBS ;
1899}
Note: See TracBrowser for help on using the repository browser.