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