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

Last change on this file since 1202 was 1058, checked in by garnier, 15 years ago

file release beta

File size: 18.9 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.24 2007/05/18 07:38:01 gcosmo Exp $
27// GEANT4 tag $Name: geant4-09-02-ref-02 $
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  // Check radial surface
302  // sets `in'
303 
304  tolRMax = fRmax - fRmaxTolerance*0.5 ;
305   
306  if ( rad2 <= tolRMax*tolRMax )  in = kInside ;
307  else
308  {
309    tolRMax = fRmax + fRmaxTolerance*0.5 ;       
310    if ( rad2 <= tolRMax*tolRMax ) in = kSurface ;
311    else                           in = kOutside ;
312  }
313  return in;
314}
315
316/////////////////////////////////////////////////////////////////////
317//
318// Return unit normal of surface closest to p
319// - note if point on z axis, ignore phi divided sides
320// - unsafe if point close to z axis a rmin=0 - no explicit checks
321
322G4ThreeVector G4Orb::SurfaceNormal( const G4ThreeVector& p ) const
323{
324  ENorm side = kNRMax;
325  G4ThreeVector norm;
326  G4double rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
327
328  switch (side)
329  {
330    case kNRMax: 
331      norm = G4ThreeVector(p.x()/rad,p.y()/rad,p.z()/rad);
332      break;
333   default:
334      DumpInfo();
335#ifdef G4CSGDEBUG
336      G4Exception("G4Orb::SurfaceNormal()", "Notification", JustWarning,
337                  "Undefined side for valid surface normal to solid.");
338#endif
339      break;   
340  } 
341
342  return norm;
343}
344
345///////////////////////////////////////////////////////////////////////////////
346//
347// Calculate distance to shape from outside, along normalised vector
348// - return kInfinity if no intersection, or intersection distance <= tolerance
349//
350// -> If point is outside outer radius, compute intersection with rmax
351//        - if no intersection return
352//        - if  valid phi,theta return intersection Dist
353
354G4double G4Orb::DistanceToIn( const G4ThreeVector& p,
355                              const G4ThreeVector& v  ) const
356{
357  G4double snxt = kInfinity ;      // snxt = default return value
358
359  G4double rad2, pDotV3d, tolORMax2, tolIRMax2 ;
360  G4double c, d2, s = kInfinity ;
361
362  // General Precalcs
363
364  rad2    = p.x()*p.x() + p.y()*p.y() + p.z()*p.z() ;
365  pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z() ;
366
367  // Radial Precalcs
368
369  tolORMax2 = (fRmax+fRmaxTolerance*0.5)*(fRmax+fRmaxTolerance*0.5) ;
370  tolIRMax2 = (fRmax-fRmaxTolerance*0.5)*(fRmax-fRmaxTolerance*0.5) ;
371
372  // Outer spherical shell intersection
373  // - Only if outside tolerant fRmax
374  // - Check for if inside and outer G4Orb heading through solid (-> 0)
375  // - No intersect -> no intersection with G4Orb
376  //
377  // Shell eqn: x^2+y^2+z^2 = RSPH^2
378  //
379  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
380  //
381  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
382  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
383  //
384  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
385
386  c = rad2 - fRmax*fRmax ;
387
388  if ( c > fRmaxTolerance*fRmax )
389  {
390    // If outside tolerant boundary of outer G4Orb
391    // [ should be std::sqrt(rad2) - fRmax > fRmaxTolerance*0.5 ]
392
393    d2 = pDotV3d*pDotV3d - c ;
394
395    if ( d2 >= 0 )
396    {
397      s = -pDotV3d - std::sqrt(d2) ;
398
399      if (s >= 0 ) return snxt = s;
400           
401    }
402    else    // No intersection with G4Orb
403    {
404      return snxt = kInfinity;
405    }
406  }
407  else
408  {
409    if ( c > -fRmaxTolerance*fRmax )  // on surface 
410    {
411      d2 = pDotV3d*pDotV3d - c ;             
412      //  if ( pDotV3d >= 0 ) return snxt = kInfinity;
413      if ( d2 < fRmaxTolerance*fRmax || pDotV3d >= 0 ) return snxt = kInfinity;
414      else                return snxt = 0.;
415    }
416    else // inside ???
417    {
418      G4Exception("G4Orb::DistanceToIn(p,v)", "Notification",
419                  JustWarning, "Point p is inside !?");
420    }
421  }
422  return snxt;
423}
424
425//////////////////////////////////////////////////////////////////////
426//
427// Calculate distance (<= actual) to closest surface of shape from outside
428// - Calculate distance to radial plane
429// - Return 0 if point inside
430
431G4double G4Orb::DistanceToIn( const G4ThreeVector& p ) const
432{
433  G4double safe=0.0, rad  = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
434                 safe = rad - fRmax;
435  if( safe < 0 ) safe = 0. ;
436  return safe;
437}
438
439/////////////////////////////////////////////////////////////////////
440//
441// Calculate distance to surface of shape from `inside', allowing for tolerance
442//
443
444G4double G4Orb::DistanceToOut( const G4ThreeVector& p,
445                               const G4ThreeVector& v,
446                               const G4bool calcNorm,
447                                     G4bool *validNorm,
448                                     G4ThreeVector *n   ) const
449{
450  G4double snxt = kInfinity;     // ??? snxt is default return value
451  ESide    side = kNull;
452 
453  G4double rad2,pDotV3d; 
454  G4double xi,yi,zi;      // Intersection point
455  G4double c,d2;
456                 
457  rad2    = p.x()*p.x() + p.y()*p.y() + p.z()*p.z();
458  pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z();
459   
460  // Radial Intersection from G4Orb::DistanceToIn
461  //
462  // Outer spherical shell intersection
463  // - Only if outside tolerant fRmax
464  // - Check for if inside and outer G4Orb heading through solid (-> 0)
465  // - No intersect -> no intersection with G4Orb
466  //
467  // Shell eqn: x^2+y^2+z^2=RSPH^2
468  //
469  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
470  //
471  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
472  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
473  //
474  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
475 
476  const G4double  Rmax_plus = fRmax + fRmaxTolerance*0.5;
477
478  if( rad2 <= Rmax_plus*Rmax_plus )
479  {
480    c = rad2-fRmax*fRmax ;
481
482    if ( c < fRmaxTolerance*fRmax) 
483    {
484      // Within tolerant Outer radius
485      //
486      // The test is
487      //     rad  - fRmax < 0.5*fRmaxTolerance
488      // =>  rad  < fRmax + 0.5*kRadTol
489      // =>  rad2 < (fRmax + 0.5*kRadTol)^2
490      // =>  rad2 < fRmax^2 + 2.*0.5*fRmax*kRadTol + 0.25*kRadTol*kRadTol
491      // =>  rad2 - fRmax^2    <~    fRmax*kRadTol
492
493      d2 = pDotV3d*pDotV3d - c;
494
495      if( ( c > -fRmaxTolerance*fRmax) &&         // on tolerant surface
496          ( ( pDotV3d >= 0 )   || ( d2 < 0 )) )   // leaving outside from Rmax
497                                                  // not re-entering
498      {
499        if(calcNorm)
500        {
501          *validNorm = true ;
502          *n         = G4ThreeVector(p.x()/fRmax,p.y()/fRmax,p.z()/fRmax) ;
503        }
504        return snxt = 0;
505      }
506      else 
507      {
508        snxt = -pDotV3d + std::sqrt(d2);    // second root since inside Rmax
509        side = kRMax ; 
510      }
511    }
512  }
513  else // p is outside ???
514  {
515    G4cout.precision(16);
516    G4cout << G4endl;
517    DumpInfo();
518    G4cout << "Position:"  << G4endl << G4endl;
519    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
520    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
521    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
522    G4cout << "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm << " mm" 
523           << G4endl << G4endl;
524    G4cout << "Direction:" << G4endl << G4endl;
525    G4cout << "v.x() = "   << v.x() << G4endl;
526    G4cout << "v.y() = "   << v.y() << G4endl;
527    G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
528    G4cout << "Proposed distance :" << G4endl << G4endl;
529    G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
530    G4Exception("G4Orb::DistanceToOut(p,v,..)", "Notification",
531                JustWarning, "Logic error: snxt = kInfinity ???");
532  }
533  if (calcNorm)    // Output switch operator
534  {
535    switch( side )
536    {
537      case kRMax:
538        xi=p.x()+snxt*v.x();
539        yi=p.y()+snxt*v.y();
540        zi=p.z()+snxt*v.z();
541        *n=G4ThreeVector(xi/fRmax,yi/fRmax,zi/fRmax);
542        *validNorm=true;
543        break;
544      default:
545        G4cout.precision(16);
546        G4cout << G4endl;
547        DumpInfo();
548        G4cout << "Position:"  << G4endl << G4endl;
549        G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
550        G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
551        G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
552        G4cout << "Direction:" << G4endl << G4endl;
553        G4cout << "v.x() = "   << v.x() << G4endl;
554        G4cout << "v.y() = "   << v.y() << G4endl;
555        G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
556        G4cout << "Proposed distance :" << G4endl << G4endl;
557        G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
558        G4Exception("G4Orb::DistanceToOut(p,v,..)","Notification",JustWarning,
559                    "Undefined side for valid surface normal to solid.");
560        break;
561    }
562  }
563  return snxt;
564}
565
566/////////////////////////////////////////////////////////////////////////
567//
568// Calculate distance (<=actual) to closest surface of shape from inside
569
570G4double G4Orb::DistanceToOut( const G4ThreeVector& p ) const
571{
572  G4double safe=0.0,rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
573
574#ifdef G4CSGDEBUG
575  if( Inside(p) == kOutside )
576  {
577     G4cout.precision(16) ;
578     G4cout << G4endl ;
579     DumpInfo();
580     G4cout << "Position:"  << G4endl << G4endl ;
581     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
582     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
583     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
584     G4Exception("G4Orb::DistanceToOut(p)", "Notification", JustWarning, 
585                 "Point p is outside !?" );
586  }
587#endif
588
589  safe = fRmax - rad;
590  if ( safe < 0. ) safe = 0.;
591  return safe;
592}
593
594//////////////////////////////////////////////////////////////////////////
595//
596// G4EntityType
597
598G4GeometryType G4Orb::GetEntityType() const
599{
600  return G4String("G4Orb");
601}
602
603//////////////////////////////////////////////////////////////////////////
604//
605// Stream object contents to an output stream
606
607std::ostream& G4Orb::StreamInfo( std::ostream& os ) const
608{
609  os << "-----------------------------------------------------------\n"
610     << "    *** Dump for solid - " << GetName() << " ***\n"
611     << "    ===================================================\n"
612     << " Solid type: G4Orb\n"
613     << " Parameters: \n"
614
615     << "    outer radius: " << fRmax/mm << " mm \n"
616     << "-----------------------------------------------------------\n";
617
618  return os;
619}
620
621/////////////////////////////////////////////////////////////////////////
622//
623// GetPointOnSurface
624
625G4ThreeVector G4Orb::GetPointOnSurface() const
626{
627  //  generate a random number from zero to 2pi...
628  //
629  G4double phi      = RandFlat::shoot(0.,2.*pi);
630  G4double cosphi   = std::cos(phi);
631  G4double sinphi   = std::sin(phi);
632 
633  G4double theta    = RandFlat::shoot(0.,pi);
634  G4double costheta = std::cos(theta);
635  G4double sintheta = std::sqrt(1.-sqr(costheta));
636 
637  return G4ThreeVector (fRmax*sintheta*cosphi,
638                        fRmax*sintheta*sinphi, fRmax*costheta); 
639}
640
641////////////////////////////////////////////////////////////////////////
642//
643// Methods for visualisation
644
645void G4Orb::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
646{
647  scene.AddSolid (*this);
648}
649
650G4Polyhedron* G4Orb::CreatePolyhedron () const
651{
652  return new G4PolyhedronSphere (0., fRmax, 0., 2*pi, 0., pi);
653}
654
655G4NURBS* G4Orb::CreateNURBS () const
656{
657  return new G4NURBSbox (fRmax, fRmax, fRmax);       // Box for now!!!
658}
Note: See TracBrowser for help on using the repository browser.