source: trunk/source/geometry/solids/CSG/src/G4Orb.cc @ 1253

Last change on this file since 1253 was 1228, checked in by garnier, 15 years ago

update geant4.9.3 tag

File size: 19.3 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// $Id: G4Orb.cc,v 1.30 2009/11/30 10:20:38 gcosmo Exp $
27// GEANT4 tag $Name: geant4-09-03 $
28//
29// class G4Orb
30//
31// Implementation for G4Orb class
32//
33// History:
34//
35// 30.06.04 V.Grichine - bug fixed in DistanceToIn(p,v) on Rmax surface
36// 20.08.03 V.Grichine - created
37//
38//////////////////////////////////////////////////////////////
39
40#include <assert.h>
41
42#include "G4Orb.hh"
43
44#include "G4VoxelLimits.hh"
45#include "G4AffineTransform.hh"
46#include "G4GeometryTolerance.hh"
47
48#include "G4VPVParameterisation.hh"
49
50#include "Randomize.hh"
51
52#include "meshdefs.hh"
53
54#include "G4VGraphicsScene.hh"
55#include "G4Polyhedron.hh"
56#include "G4NURBS.hh"
57#include "G4NURBSbox.hh"
58
59using namespace CLHEP;
60
61// Private enum: Not for external use - used by distanceToOut
62
63enum ESide {kNull,kRMax};
64
65// used by normal
66
67enum ENorm {kNRMax};
68
69
70const G4double G4Orb::fEpsilon = 2.e-11;  // relative tolerance of fRmax
71
72////////////////////////////////////////////////////////////////////////
73//
74// constructor - check positive radius
75//             
76
77G4Orb::G4Orb( const G4String& pName,G4double pRmax )
78: G4CSGSolid(pName)
79{
80
81  G4double kRadTolerance
82    = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
83
84  // Check radius
85  //
86  if (pRmax >= 10*kCarTolerance )
87  {
88    fRmax = pRmax;
89  }
90  else
91  {
92    G4Exception("G4Orb::G4Orb()", "InvalidSetup", FatalException,
93                "Invalid radius > 10*kCarTolerance.");
94  }
95  fRmaxTolerance =  std::max( kRadTolerance, fEpsilon*fRmax);
96
97}
98
99///////////////////////////////////////////////////////////////////////
100//
101// Fake default constructor - sets only member data and allocates memory
102//                            for usage restricted to object persistency.
103//
104G4Orb::G4Orb( __void__& a )
105  : G4CSGSolid(a)
106{
107}
108
109/////////////////////////////////////////////////////////////////////
110//
111// Destructor
112
113G4Orb::~G4Orb()
114{
115}
116
117//////////////////////////////////////////////////////////////////////////
118//
119// Dispatch to parameterisation for replication mechanism dimension
120// computation & modification.
121
122void G4Orb::ComputeDimensions(       G4VPVParameterisation* p,
123                                  const G4int n,
124                                  const G4VPhysicalVolume* pRep)
125{
126  p->ComputeDimensions(*this,n,pRep);
127}
128
129////////////////////////////////////////////////////////////////////////////
130//
131// Calculate extent under transform and specified limit
132
133G4bool G4Orb::CalculateExtent( const EAxis pAxis,
134                               const G4VoxelLimits& pVoxelLimit,
135                               const G4AffineTransform& pTransform,
136                                        G4double& pMin, G4double& pMax ) const
137{
138    // Compute x/y/z mins and maxs for bounding box respecting limits,
139    // with early returns if outside limits. Then switch() on pAxis,
140    // and compute exact x and y limit for x/y case
141     
142    G4double xoffset,xMin,xMax;
143    G4double yoffset,yMin,yMax;
144    G4double zoffset,zMin,zMax;
145
146    G4double diff1,diff2,maxDiff,newMin,newMax;
147    G4double xoff1,xoff2,yoff1,yoff2;
148
149    xoffset=pTransform.NetTranslation().x();
150    xMin=xoffset-fRmax;
151    xMax=xoffset+fRmax;
152
153    if (pVoxelLimit.IsXLimited())
154    {
155      if ( (xMin>pVoxelLimit.GetMaxXExtent()+kCarTolerance)
156        || (xMax<pVoxelLimit.GetMinXExtent()-kCarTolerance) )
157      {
158        return false;
159      }
160      else
161      {
162        if (xMin<pVoxelLimit.GetMinXExtent())
163        {
164          xMin=pVoxelLimit.GetMinXExtent();
165        }
166        if (xMax>pVoxelLimit.GetMaxXExtent())
167        {
168          xMax=pVoxelLimit.GetMaxXExtent();
169        }
170      }
171    }
172    yoffset=pTransform.NetTranslation().y();
173    yMin=yoffset-fRmax;
174    yMax=yoffset+fRmax;
175
176    if (pVoxelLimit.IsYLimited())
177    {
178      if ( (yMin>pVoxelLimit.GetMaxYExtent()+kCarTolerance)
179        || (yMax<pVoxelLimit.GetMinYExtent()-kCarTolerance) )
180      {
181        return false;
182      }
183      else
184      {
185        if (yMin<pVoxelLimit.GetMinYExtent())
186        {
187          yMin=pVoxelLimit.GetMinYExtent();
188        }
189        if (yMax>pVoxelLimit.GetMaxYExtent())
190        {
191          yMax=pVoxelLimit.GetMaxYExtent();
192        }
193      }
194    }
195    zoffset=pTransform.NetTranslation().z();
196    zMin=zoffset-fRmax;
197    zMax=zoffset+fRmax;
198
199    if (pVoxelLimit.IsZLimited())
200    {
201      if ( (zMin>pVoxelLimit.GetMaxZExtent()+kCarTolerance)
202        || (zMax<pVoxelLimit.GetMinZExtent()-kCarTolerance) )
203      {
204        return false;
205      }
206      else
207      {
208        if (zMin<pVoxelLimit.GetMinZExtent())
209        {
210          zMin=pVoxelLimit.GetMinZExtent();
211        }
212        if (zMax>pVoxelLimit.GetMaxZExtent())
213        {
214          zMax=pVoxelLimit.GetMaxZExtent();
215        }
216      }
217    }
218
219    // Known to cut sphere
220
221    switch (pAxis)
222    {
223      case kXAxis:
224        yoff1=yoffset-yMin;
225        yoff2=yMax-yoffset;
226
227        if ( yoff1 >= 0 && yoff2 >= 0 )
228        {
229          // Y limits cross max/min x => no change
230          //
231          pMin=xMin;
232          pMax=xMax;
233        }
234        else
235        {
236          // Y limits don't cross max/min x => compute max delta x,
237          // hence new mins/maxs
238          //
239          diff1=std::sqrt(fRmax*fRmax-yoff1*yoff1);
240          diff2=std::sqrt(fRmax*fRmax-yoff2*yoff2);
241          maxDiff=(diff1>diff2) ? diff1:diff2;
242          newMin=xoffset-maxDiff;
243          newMax=xoffset+maxDiff;
244          pMin=(newMin<xMin) ? xMin : newMin;
245          pMax=(newMax>xMax) ? xMax : newMax;
246        }
247        break;
248      case kYAxis:
249        xoff1=xoffset-xMin;
250        xoff2=xMax-xoffset;
251        if (xoff1>=0&&xoff2>=0)
252        {
253          // X limits cross max/min y => no change
254          //
255          pMin=yMin;
256          pMax=yMax;
257        }
258        else
259        {
260          // X limits don't cross max/min y => compute max delta y,
261          // hence new mins/maxs
262          //
263          diff1=std::sqrt(fRmax*fRmax-xoff1*xoff1);
264          diff2=std::sqrt(fRmax*fRmax-xoff2*xoff2);
265          maxDiff=(diff1>diff2) ? diff1:diff2;
266          newMin=yoffset-maxDiff;
267          newMax=yoffset+maxDiff;
268          pMin=(newMin<yMin) ? yMin : newMin;
269          pMax=(newMax>yMax) ? yMax : newMax;
270        }
271        break;
272      case kZAxis:
273        pMin=zMin;
274        pMax=zMax;
275        break;
276      default:
277        break;
278    }
279    pMin -= fRmaxTolerance;
280    pMax += fRmaxTolerance;
281
282    return true; 
283 
284}
285
286///////////////////////////////////////////////////////////////////////////
287//
288// Return whether point inside/outside/on surface
289// Split into radius checks
290//
291
292EInside G4Orb::Inside( const G4ThreeVector& p ) const
293{
294  G4double rad2,tolRMax;
295  EInside in;
296
297
298  rad2 = p.x()*p.x()+p.y()*p.y()+p.z()*p.z() ;
299
300  G4double rad = std::sqrt(rad2);
301
302  // G4double rad = std::sqrt(rad2);
303  // Check radial surface
304  // sets `in'
305 
306  tolRMax = fRmax - fRmaxTolerance*0.5 ;
307   
308  if ( rad <= tolRMax )  { in = kInside ; }
309  else
310  {
311    tolRMax = fRmax + fRmaxTolerance*0.5 ;       
312    if ( rad <= tolRMax )  { in = kSurface ; }
313    else                   { in = kOutside ; }
314  }
315  return in;
316}
317
318/////////////////////////////////////////////////////////////////////
319//
320// Return unit normal of surface closest to p
321// - note if point on z axis, ignore phi divided sides
322// - unsafe if point close to z axis a rmin=0 - no explicit checks
323
324G4ThreeVector G4Orb::SurfaceNormal( const G4ThreeVector& p ) const
325{
326  ENorm side = kNRMax;
327  G4ThreeVector norm;
328  G4double rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
329
330  switch (side)
331  {
332    case kNRMax: 
333      norm = G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
334      break;
335   default:
336      DumpInfo();
337#ifdef G4CSGDEBUG
338      G4Exception("G4Orb::SurfaceNormal()", "Notification", JustWarning,
339                  "Undefined side for valid surface normal to solid.");
340#endif
341      break;   
342  } 
343
344  return norm;
345}
346
347///////////////////////////////////////////////////////////////////////////////
348//
349// Calculate distance to shape from outside, along normalised vector
350// - return kInfinity if no intersection, or intersection distance <= tolerance
351//
352// -> If point is outside outer radius, compute intersection with rmax
353//        - if no intersection return
354//        - if  valid phi,theta return intersection Dist
355
356G4double G4Orb::DistanceToIn( const G4ThreeVector& p,
357                              const G4ThreeVector& v  ) const
358{
359  G4double snxt = kInfinity ;      // snxt = default return value
360
361  G4double rad2, pDotV3d, tolORMax2, tolIRMax2 ;
362  G4double c, d2, s = kInfinity ;
363
364  const G4double dRmax = 100.*fRmax;
365
366  // General Precalcs
367
368  rad2    = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() ;
369  pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ;
370
371  // Radial Precalcs
372
373  tolORMax2 = (fRmax+fRmaxTolerance*0.5)*(fRmax+fRmaxTolerance*0.5) ;
374  tolIRMax2 = (fRmax-fRmaxTolerance*0.5)*(fRmax-fRmaxTolerance*0.5) ;
375
376  // Outer spherical shell intersection
377  // - Only if outside tolerant fRmax
378  // - Check for if inside and outer G4Orb heading through solid (-> 0)
379  // - No intersect -> no intersection with G4Orb
380  //
381  // Shell eqn: x^2+y^2+z^2 = RSPH^2
382  //
383  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
384  //
385  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
386  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
387  //
388  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
389
390
391  G4double rad = std::sqrt(rad2);
392  c = (rad - fRmax)*(rad + fRmax);
393
394  if ( c > fRmaxTolerance*fRmax )
395  {
396    // If outside tolerant boundary of outer G4Orb
397    // [ should be std::sqrt(rad2) - fRmax > fRmaxTolerance*0.5 ]
398
399    d2 = pDotV3d*pDotV3d - c ;
400
401    if ( d2 >= 0 )
402    {
403      s = -pDotV3d - std::sqrt(d2) ;
404      if ( s >= 0 )
405      {
406        if ( s>dRmax ) // Avoid rounding errors due to precision issues seen on
407        {              // 64 bits systems. Split long distances and recompute
408          G4double fTerm = s-std::fmod(s,dRmax);
409          s = fTerm + DistanceToIn(p+fTerm*v,v);
410        } 
411        return snxt = s;
412      }
413    }
414    else    // No intersection with G4Orb
415    {
416      return snxt = kInfinity;
417    }
418  }
419  else
420  {
421    if ( c > -fRmaxTolerance*fRmax )  // on surface 
422    {
423      d2 = pDotV3d*pDotV3d - c ;             
424      if ( (d2 < fRmaxTolerance*fRmax) || (pDotV3d >= 0) )
425      {
426        return snxt = kInfinity;
427      }
428      else
429      {
430        return snxt = 0.;
431      }
432    }
433#ifdef G4CSGDEBUG
434    else // inside ???
435    {
436      G4Exception("G4Orb::DistanceToIn(p,v)", "Notification",
437                  JustWarning, "Point p is inside !?");
438    }
439#endif
440  }
441  return snxt;
442}
443
444//////////////////////////////////////////////////////////////////////
445//
446// Calculate distance (<= actual) to closest surface of shape from outside
447// - Calculate distance to radial plane
448// - Return 0 if point inside
449
450G4double G4Orb::DistanceToIn( const G4ThreeVector& p ) const
451{
452  G4double safe = 0.0,
453           rad  = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
454  safe = rad - fRmax;
455  if( safe < 0 ) { safe = 0.; }
456  return safe;
457}
458
459/////////////////////////////////////////////////////////////////////
460//
461// Calculate distance to surface of shape from `inside', allowing for tolerance
462//
463
464G4double G4Orb::DistanceToOut( const G4ThreeVector& p,
465                               const G4ThreeVector& v,
466                               const G4bool calcNorm,
467                                     G4bool *validNorm,
468                                     G4ThreeVector *n   ) const
469{
470  G4double snxt = kInfinity;     // ??? snxt is default return value
471  ESide    side = kNull;
472 
473  G4double rad2,pDotV3d; 
474  G4double xi,yi,zi;      // Intersection point
475  G4double c,d2;
476                 
477  rad2    = p.x()*p.x() + p.y()*p.y() + p.z()*p.z();
478  pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z();
479   
480  // Radial Intersection from G4Orb::DistanceToIn
481  //
482  // Outer spherical shell intersection
483  // - Only if outside tolerant fRmax
484  // - Check for if inside and outer G4Orb heading through solid (-> 0)
485  // - No intersect -> no intersection with G4Orb
486  //
487  // Shell eqn: x^2+y^2+z^2=RSPH^2
488  //
489  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
490  //
491  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
492  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
493  //
494  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
495 
496  const G4double  Rmax_plus = fRmax + fRmaxTolerance*0.5;
497  G4double rad = std::sqrt(rad2);
498
499  if ( rad <= Rmax_plus )
500  {
501    c = (rad - fRmax)*(rad + fRmax);
502
503    if ( c < fRmaxTolerance*fRmax ) 
504    {
505      // Within tolerant Outer radius
506      //
507      // The test is
508      //     rad  - fRmax < 0.5*fRmaxTolerance
509      // =>  rad  < fRmax + 0.5*kRadTol
510      // =>  rad2 < (fRmax + 0.5*kRadTol)^2
511      // =>  rad2 < fRmax^2 + 2.*0.5*fRmax*kRadTol + 0.25*kRadTol*kRadTol
512      // =>  rad2 - fRmax^2    <~    fRmax*kRadTol
513
514      d2 = pDotV3d*pDotV3d - c;
515
516      if( ( c > -fRmaxTolerance*fRmax) &&         // on tolerant surface
517          ( ( pDotV3d >= 0 )   || ( d2 < 0 )) )   // leaving outside from Rmax
518                                                  // not re-entering
519      {
520        if(calcNorm)
521        {
522          *validNorm = true ;
523          *n         = G4ThreeVector(p.x()/fRmax,p.y()/fRmax,p.z()/fRmax) ;
524        }
525        return snxt = 0;
526      }
527      else 
528      {
529        snxt = -pDotV3d + std::sqrt(d2);    // second root since inside Rmax
530        side = kRMax ; 
531      }
532    }
533  }
534  else // p is outside ???
535  {
536    G4cout.precision(16);
537    G4cout << G4endl;
538    DumpInfo();
539    G4cout << "Position:"  << G4endl << G4endl;
540    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
541    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
542    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
543    G4cout << "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm << " mm" 
544           << G4endl << G4endl;
545    G4cout << "Direction:" << G4endl << G4endl;
546    G4cout << "v.x() = "   << v.x() << G4endl;
547    G4cout << "v.y() = "   << v.y() << G4endl;
548    G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
549    G4cout << "Proposed distance :" << G4endl << G4endl;
550    G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
551    G4Exception("G4Orb::DistanceToOut(p,v,..)", "Notification",
552                JustWarning, "Logic error: snxt = kInfinity ???");
553  }
554  if (calcNorm)    // Output switch operator
555  {
556    switch( side )
557    {
558      case kRMax:
559        xi=p.x()+snxt*v.x();
560        yi=p.y()+snxt*v.y();
561        zi=p.z()+snxt*v.z();
562        *n=G4ThreeVector(xi/fRmax,yi/fRmax,zi/fRmax);
563        *validNorm=true;
564        break;
565      default:
566        G4cout.precision(16);
567        G4cout << G4endl;
568        DumpInfo();
569        G4cout << "Position:"  << G4endl << G4endl;
570        G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
571        G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
572        G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
573        G4cout << "Direction:" << G4endl << G4endl;
574        G4cout << "v.x() = "   << v.x() << G4endl;
575        G4cout << "v.y() = "   << v.y() << G4endl;
576        G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
577        G4cout << "Proposed distance :" << G4endl << G4endl;
578        G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
579        G4Exception("G4Orb::DistanceToOut(p,v,..)","Notification",JustWarning,
580                    "Undefined side for valid surface normal to solid.");
581        break;
582    }
583  }
584  return snxt;
585}
586
587/////////////////////////////////////////////////////////////////////////
588//
589// Calculate distance (<=actual) to closest surface of shape from inside
590
591G4double G4Orb::DistanceToOut( const G4ThreeVector& p ) const
592{
593  G4double safe=0.0,rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
594
595#ifdef G4CSGDEBUG
596  if( Inside(p) == kOutside )
597  {
598     G4cout.precision(16) ;
599     G4cout << G4endl ;
600     DumpInfo();
601     G4cout << "Position:"  << G4endl << G4endl ;
602     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
603     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
604     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
605     G4Exception("G4Orb::DistanceToOut(p)", "Notification", JustWarning, 
606                 "Point p is outside !?" );
607  }
608#endif
609
610  safe = fRmax - rad;
611  if ( safe < 0. ) safe = 0.;
612  return safe;
613}
614
615//////////////////////////////////////////////////////////////////////////
616//
617// G4EntityType
618
619G4GeometryType G4Orb::GetEntityType() const
620{
621  return G4String("G4Orb");
622}
623
624//////////////////////////////////////////////////////////////////////////
625//
626// Stream object contents to an output stream
627
628std::ostream& G4Orb::StreamInfo( std::ostream& os ) const
629{
630  os << "-----------------------------------------------------------\n"
631     << "    *** Dump for solid - " << GetName() << " ***\n"
632     << "    ===================================================\n"
633     << " Solid type: G4Orb\n"
634     << " Parameters: \n"
635
636     << "    outer radius: " << fRmax/mm << " mm \n"
637     << "-----------------------------------------------------------\n";
638
639  return os;
640}
641
642/////////////////////////////////////////////////////////////////////////
643//
644// GetPointOnSurface
645
646G4ThreeVector G4Orb::GetPointOnSurface() const
647{
648  //  generate a random number from zero to 2pi...
649  //
650  G4double phi      = RandFlat::shoot(0.,2.*pi);
651  G4double cosphi   = std::cos(phi);
652  G4double sinphi   = std::sin(phi);
653 
654  G4double theta    = RandFlat::shoot(0.,pi);
655  G4double costheta = std::cos(theta);
656  G4double sintheta = std::sqrt(1.-sqr(costheta));
657 
658  return G4ThreeVector (fRmax*sintheta*cosphi,
659                        fRmax*sintheta*sinphi, fRmax*costheta); 
660}
661
662////////////////////////////////////////////////////////////////////////
663//
664// Methods for visualisation
665
666void G4Orb::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
667{
668  scene.AddSolid (*this);
669}
670
671G4Polyhedron* G4Orb::CreatePolyhedron () const
672{
673  return new G4PolyhedronSphere (0., fRmax, 0., 2*pi, 0., pi);
674}
675
676G4NURBS* G4Orb::CreateNURBS () const
677{
678  return new G4NURBSbox (fRmax, fRmax, fRmax);       // Box for now!!!
679}
Note: See TracBrowser for help on using the repository browser.