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

Last change on this file was 1337, checked in by garnier, 14 years ago

tag geant4.9.4 beta 1 + modifs locales

File size: 19.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// $Id: G4Orb.cc,v 1.31 2009/12/04 15:39:56 grichine Exp $
27// GEANT4 tag $Name: geant4-09-04-beta-01 $
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 rad, pDotV3d; // , tolORMax2, tolIRMax2;
362  G4double c, d2, s = kInfinity;
363
364  const G4double dRmax = 100.*fRmax;
365
366  // General Precalcs
367
368  rad    = std::sqrt(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  c = (rad - fRmax)*(rad + fRmax);
391
392  if( rad > fRmax-fRmaxTolerance*0.5 ) // not inside in terms of Inside(p)
393  {
394    if ( c > fRmaxTolerance*fRmax )
395    {
396      // If outside tolerant boundary of outer G4Orb in terms of c
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 // not outside in terms of c
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    }
434  }
435#ifdef G4CSGDEBUG
436  else // inside ???
437  {
438      G4Exception("G4Orb::DistanceToIn(p,v)", "Notification",
439                  JustWarning, "Point p is inside !?");
440  }
441#endif
442
443  return snxt;
444}
445
446//////////////////////////////////////////////////////////////////////
447//
448// Calculate distance (<= actual) to closest surface of shape from outside
449// - Calculate distance to radial plane
450// - Return 0 if point inside
451
452G4double G4Orb::DistanceToIn( const G4ThreeVector& p ) const
453{
454  G4double safe = 0.0,
455           rad  = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
456  safe = rad - fRmax;
457  if( safe < 0 ) { safe = 0.; }
458  return safe;
459}
460
461/////////////////////////////////////////////////////////////////////
462//
463// Calculate distance to surface of shape from `inside', allowing for tolerance
464//
465
466G4double G4Orb::DistanceToOut( const G4ThreeVector& p,
467                               const G4ThreeVector& v,
468                               const G4bool calcNorm,
469                                     G4bool *validNorm,
470                                     G4ThreeVector *n   ) const
471{
472  G4double snxt = kInfinity;     // ??? snxt is default return value
473  ESide    side = kNull;
474 
475  G4double rad2,pDotV3d; 
476  G4double xi,yi,zi;      // Intersection point
477  G4double c,d2;
478                 
479  rad2    = p.x()*p.x() + p.y()*p.y() + p.z()*p.z();
480  pDotV3d = p.x()*v.x() + p.y()*v.y() + p.z()*v.z();
481   
482  // Radial Intersection from G4Orb::DistanceToIn
483  //
484  // Outer spherical shell intersection
485  // - Only if outside tolerant fRmax
486  // - Check for if inside and outer G4Orb heading through solid (-> 0)
487  // - No intersect -> no intersection with G4Orb
488  //
489  // Shell eqn: x^2+y^2+z^2=RSPH^2
490  //
491  // => (px+svx)^2+(py+svy)^2+(pz+svz)^2=R^2
492  //
493  // => (px^2+py^2+pz^2) +2s(pxvx+pyvy+pzvz)+s^2(vx^2+vy^2+vz^2)=R^2
494  // =>      rad2        +2s(pDotV3d)       +s^2                =R^2
495  //
496  // => s=-pDotV3d+-std::sqrt(pDotV3d^2-(rad2-R^2))
497 
498  const G4double  Rmax_plus = fRmax + fRmaxTolerance*0.5;
499  G4double rad = std::sqrt(rad2);
500
501  if ( rad <= Rmax_plus )
502  {
503    c = (rad - fRmax)*(rad + fRmax);
504
505    if ( c < fRmaxTolerance*fRmax ) 
506    {
507      // Within tolerant Outer radius
508      //
509      // The test is
510      //     rad  - fRmax < 0.5*fRmaxTolerance
511      // =>  rad  < fRmax + 0.5*kRadTol
512      // =>  rad2 < (fRmax + 0.5*kRadTol)^2
513      // =>  rad2 < fRmax^2 + 2.*0.5*fRmax*kRadTol + 0.25*kRadTol*kRadTol
514      // =>  rad2 - fRmax^2    <~    fRmax*kRadTol
515
516      d2 = pDotV3d*pDotV3d - c;
517
518      if( ( c > -fRmaxTolerance*fRmax) &&         // on tolerant surface
519          ( ( pDotV3d >= 0 )   || ( d2 < 0 )) )   // leaving outside from Rmax
520                                                  // not re-entering
521      {
522        if(calcNorm)
523        {
524          *validNorm = true;
525          *n         = G4ThreeVector(p.x()/fRmax,p.y()/fRmax,p.z()/fRmax);
526        }
527        return snxt = 0;
528      }
529      else 
530      {
531        snxt = -pDotV3d + std::sqrt(d2);    // second root since inside Rmax
532        side = kRMax; 
533      }
534    }
535  }
536  else // p is outside ???
537  {
538    G4cout.precision(16);
539    G4cout << G4endl;
540    DumpInfo();
541    G4cout << "Position:"  << G4endl << G4endl;
542    G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
543    G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
544    G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
545    G4cout << "Rp = "<< std::sqrt( p.x()*p.x()+p.y()*p.y()+p.z()*p.z() )/mm << " mm" 
546           << G4endl << G4endl;
547    G4cout << "Direction:" << G4endl << G4endl;
548    G4cout << "v.x() = "   << v.x() << G4endl;
549    G4cout << "v.y() = "   << v.y() << G4endl;
550    G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
551    G4cout << "Proposed distance :" << G4endl << G4endl;
552    G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
553    G4Exception("G4Orb::DistanceToOut(p,v,..)", "Notification",
554                JustWarning, "Logic error: snxt = kInfinity ???");
555  }
556  if (calcNorm)    // Output switch operator
557  {
558    switch( side )
559    {
560      case kRMax:
561        xi=p.x()+snxt*v.x();
562        yi=p.y()+snxt*v.y();
563        zi=p.z()+snxt*v.z();
564        *n=G4ThreeVector(xi/fRmax,yi/fRmax,zi/fRmax);
565        *validNorm=true;
566        break;
567      default:
568        G4cout.precision(16);
569        G4cout << G4endl;
570        DumpInfo();
571        G4cout << "Position:"  << G4endl << G4endl;
572        G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
573        G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
574        G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
575        G4cout << "Direction:" << G4endl << G4endl;
576        G4cout << "v.x() = "   << v.x() << G4endl;
577        G4cout << "v.y() = "   << v.y() << G4endl;
578        G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
579        G4cout << "Proposed distance :" << G4endl << G4endl;
580        G4cout << "snxt = "    << snxt/mm << " mm" << G4endl << G4endl;
581        G4Exception("G4Orb::DistanceToOut(p,v,..)","Notification",JustWarning,
582                    "Undefined side for valid surface normal to solid.");
583        break;
584    }
585  }
586  return snxt;
587}
588
589/////////////////////////////////////////////////////////////////////////
590//
591// Calculate distance (<=actual) to closest surface of shape from inside
592
593G4double G4Orb::DistanceToOut( const G4ThreeVector& p ) const
594{
595  G4double safe=0.0,rad = std::sqrt(p.x()*p.x()+p.y()*p.y()+p.z()*p.z());
596
597#ifdef G4CSGDEBUG
598  if( Inside(p) == kOutside )
599  {
600     G4cout.precision(16);
601     G4cout << G4endl;
602     DumpInfo();
603     G4cout << "Position:"  << G4endl << G4endl;
604     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
605     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
606     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
607     G4Exception("G4Orb::DistanceToOut(p)", "Notification", JustWarning, 
608                 "Point p is outside !?" );
609  }
610#endif
611
612  safe = fRmax - rad;
613  if ( safe < 0. ) safe = 0.;
614  return safe;
615}
616
617//////////////////////////////////////////////////////////////////////////
618//
619// G4EntityType
620
621G4GeometryType G4Orb::GetEntityType() const
622{
623  return G4String("G4Orb");
624}
625
626//////////////////////////////////////////////////////////////////////////
627//
628// Stream object contents to an output stream
629
630std::ostream& G4Orb::StreamInfo( std::ostream& os ) const
631{
632  os << "-----------------------------------------------------------\n"
633     << "    *** Dump for solid - " << GetName() << " ***\n"
634     << "    ===================================================\n"
635     << " Solid type: G4Orb\n"
636     << " Parameters: \n"
637
638     << "    outer radius: " << fRmax/mm << " mm \n"
639     << "-----------------------------------------------------------\n";
640
641  return os;
642}
643
644/////////////////////////////////////////////////////////////////////////
645//
646// GetPointOnSurface
647
648G4ThreeVector G4Orb::GetPointOnSurface() const
649{
650  //  generate a random number from zero to 2pi...
651  //
652  G4double phi      = RandFlat::shoot(0.,2.*pi);
653  G4double cosphi   = std::cos(phi);
654  G4double sinphi   = std::sin(phi);
655 
656  G4double theta    = RandFlat::shoot(0.,pi);
657  G4double costheta = std::cos(theta);
658  G4double sintheta = std::sqrt(1.-sqr(costheta));
659 
660  return G4ThreeVector (fRmax*sintheta*cosphi,
661                        fRmax*sintheta*sinphi, fRmax*costheta); 
662}
663
664////////////////////////////////////////////////////////////////////////
665//
666// Methods for visualisation
667
668void G4Orb::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
669{
670  scene.AddSolid (*this);
671}
672
673G4Polyhedron* G4Orb::CreatePolyhedron () const
674{
675  return new G4PolyhedronSphere (0., fRmax, 0., 2*pi, 0., pi);
676}
677
678G4NURBS* G4Orb::CreateNURBS () const
679{
680  return new G4NURBSbox (fRmax, fRmax, fRmax);       // Box for now!!!
681}
Note: See TracBrowser for help on using the repository browser.