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