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