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

Last change on this file since 1086 was 1058, checked in by garnier, 17 years ago

file release beta

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