source: trunk/source/geometry/solids/specific/src/G4EllipticalCone.cc @ 1294

Last change on this file since 1294 was 1228, checked in by garnier, 14 years ago

update geant4.9.3 tag

File size: 28.6 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: G4EllipticalCone.cc,v 1.16 2008/04/25 08:45:26 gcosmo Exp $
27// GEANT4 tag $Name: geant4-09-03 $
28//
29// Implementation of G4EllipticalCone class
30//
31// This code implements an Elliptical Cone given explicitly by the
32// equation:
33//   x^2/a^2 + y^2/b^2 = (z-h)^2
34// and specified by the parameters (a,b,h) and a cut parallel to the
35// xy plane above z = 0.
36//
37// Author: Dionysios Anninos
38//
39// --------------------------------------------------------------------
40
41#include "globals.hh"
42
43#include "G4EllipticalCone.hh"
44
45#include "G4ClippablePolygon.hh"
46#include "G4SolidExtentList.hh"
47#include "G4VoxelLimits.hh"
48#include "G4AffineTransform.hh"
49#include "G4GeometryTolerance.hh"
50
51#include "meshdefs.hh"
52
53#include "Randomize.hh"
54
55#include "G4VGraphicsScene.hh"
56#include "G4Polyhedron.hh"
57#include "G4NURBS.hh"
58#include "G4NURBSbox.hh"
59#include "G4VisExtent.hh"
60
61//#define G4SPECSDEBUG 1   
62
63using namespace CLHEP;
64
65//////////////////////////////////////////////////////////////////////
66//
67// Constructor - check parameters
68//
69G4EllipticalCone::G4EllipticalCone(const G4String& pName,
70                                         G4double  pxSemiAxis,
71                                         G4double  pySemiAxis,
72                                         G4double  pzMax,
73                                         G4double  pzTopCut)
74  : G4VSolid(pName), fpPolyhedron(0), fCubicVolume(0.), fSurfaceArea(0.),
75    zTopCut(0.)
76{
77
78  kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
79
80  // Check Semi-Axis
81  //
82  if ( (pxSemiAxis > 0.) && (pySemiAxis > 0.) && (pzMax > 0.) )
83  {
84     SetSemiAxis( pxSemiAxis, pySemiAxis, pzMax );
85  }
86  else
87  {
88     G4cerr << "ERROR - G4EllipticalCone::G4EllipticalCone(): "
89            << GetName() << G4endl
90            << "        Invalid semi-axis or height!" << G4endl;
91     G4Exception("G4EllipticalCone::G4EllipticalCone()", "InvalidSetup",
92                 FatalException, "Invalid semi-axis or height.");
93  }
94
95  if ( pzTopCut > 0 )
96  {
97     SetZCut(pzTopCut);
98  }
99  else
100  {
101     G4cerr << "ERROR - G4EllipticalCone::G4EllipticalCone(): "
102            << GetName() << G4endl
103            << "        Invalid z-coordinate for cutting plane !" << G4endl;
104     G4Exception("G4EllipticalCone::G4EllipticalCone()", "InvalidSetup",
105                 FatalException, "Invalid z-coordinate for cutting plane.");
106  }
107}
108
109///////////////////////////////////////////////////////////////////////////////
110//
111// Fake default constructor - sets only member data and allocates memory
112//                            for usage restricted to object persistency.
113//
114G4EllipticalCone::G4EllipticalCone( __void__& a )
115  : G4VSolid(a), fpPolyhedron(0), fCubicVolume(0.), fSurfaceArea(0.),
116    zTopCut(0.)
117{
118}
119
120///////////////////////////////////////////////////////////////////////////////
121//
122// Destructor
123//
124G4EllipticalCone::~G4EllipticalCone()
125{
126}
127
128///////////////////////////////////////////////////////////////////////////////
129//
130// Calculate extent under transform and specified limit
131//
132G4bool
133G4EllipticalCone::CalculateExtent( const EAxis axis,
134                                   const G4VoxelLimits &voxelLimit,
135                                   const G4AffineTransform &transform,
136                                         G4double &min, G4double &max ) const
137{
138  G4SolidExtentList  extentList( axis, voxelLimit );
139 
140  //
141  // We are going to divide up our elliptical face into small pieces
142  //
143 
144  //
145  // Choose phi size of our segment(s) based on constants as
146  // defined in meshdefs.hh
147  //
148  G4int numPhi = kMaxMeshSections;
149  G4double sigPhi = twopi/numPhi;
150 
151  //
152  // We have to be careful to keep our segments completely outside
153  // of the elliptical surface. To do so we imagine we have
154  // a simple (unit radius) circular cross section (as in G4Tubs)
155  // and then "stretch" the dimensions as necessary to fit the ellipse.
156  //
157  G4double rFudge = 1.0/std::cos(0.5*sigPhi);
158  G4double dxFudgeBot = xSemiAxis*2.*zheight*rFudge,
159           dyFudgeBot = ySemiAxis*2.*zheight*rFudge;
160  G4double dxFudgeTop = xSemiAxis*(zheight-zTopCut)*rFudge,
161           dyFudgeTop = ySemiAxis*(zheight-zTopCut)*rFudge;
162 
163  //
164  // As we work around the elliptical surface, we build
165  // a "phi" segment on the way, and keep track of two
166  // additional polygons for the two ends.
167  //
168  G4ClippablePolygon endPoly1, endPoly2, phiPoly;
169 
170  G4double phi = 0, 
171           cosPhi = std::cos(phi),
172           sinPhi = std::sin(phi);
173  G4ThreeVector v0( dxFudgeTop*cosPhi, dyFudgeTop*sinPhi, +zTopCut ),
174                v1( dxFudgeBot*cosPhi, dyFudgeBot*sinPhi, -zTopCut ),
175                w0, w1;
176  transform.ApplyPointTransform( v0 );
177  transform.ApplyPointTransform( v1 );
178  do
179  {
180    phi += sigPhi;
181    if (numPhi == 1) phi = 0;  // Try to avoid roundoff
182    cosPhi = std::cos(phi), 
183    sinPhi = std::sin(phi);
184   
185    w0 = G4ThreeVector( dxFudgeTop*cosPhi, dyFudgeTop*sinPhi, +zTopCut );
186    w1 = G4ThreeVector( dxFudgeBot*cosPhi, dyFudgeBot*sinPhi, -zTopCut );
187    transform.ApplyPointTransform( w0 );
188    transform.ApplyPointTransform( w1 );
189   
190    //
191    // Add a point to our z ends
192    //
193    endPoly1.AddVertexInOrder( v0 );
194    endPoly2.AddVertexInOrder( v1 );
195   
196    //
197    // Build phi polygon
198    //
199    phiPoly.ClearAllVertices();
200   
201    phiPoly.AddVertexInOrder( v0 );
202    phiPoly.AddVertexInOrder( v1 );
203    phiPoly.AddVertexInOrder( w1 );
204    phiPoly.AddVertexInOrder( w0 );
205   
206    if (phiPoly.PartialClip( voxelLimit, axis ))
207    {
208      //
209      // Get unit normal
210      //
211      phiPoly.SetNormal( (v1-v0).cross(w0-v0).unit() );
212     
213      extentList.AddSurface( phiPoly );
214    }
215
216    //
217    // Next vertex
218    //   
219    v0 = w0;
220    v1 = w1;
221  } while( --numPhi > 0 );
222
223  //
224  // Process the end pieces
225  //
226  if (endPoly1.PartialClip( voxelLimit, axis ))
227  {
228    static const G4ThreeVector normal(0,0,+1);
229    endPoly1.SetNormal( transform.TransformAxis(normal) );
230    extentList.AddSurface( endPoly1 );
231  }
232 
233  if (endPoly2.PartialClip( voxelLimit, axis ))
234  {
235    static const G4ThreeVector normal(0,0,-1);
236    endPoly2.SetNormal( transform.TransformAxis(normal) );
237    extentList.AddSurface( endPoly2 );
238  }
239 
240  //
241  // Return min/max value
242  //
243  return extentList.GetExtent( min, max );
244}
245
246////////////////////////////////////////////////////////////////////////
247//
248// Return whether point inside/outside/on surface
249// Split into radius, phi, theta checks
250// Each check modifies `in', or returns as approprate
251//
252EInside G4EllipticalCone::Inside(const G4ThreeVector& p) const
253{
254  G4double rad2oo,  // outside surface outer tolerance
255           rad2oi;  // outside surface inner tolerance
256 
257  EInside in;
258
259  // check this side of z cut first, because that's fast
260  //
261
262  if ( (p.z() < -zTopCut - 0.5*kCarTolerance)
263    || (p.z() > zTopCut + 0.5*kCarTolerance ) )
264  {
265    return in = kOutside; 
266  }
267
268  rad2oo= sqr(p.x()/( xSemiAxis + 0.5*kRadTolerance ))
269        + sqr(p.y()/( ySemiAxis + 0.5*kRadTolerance ));
270
271  if ( rad2oo > sqr( zheight-p.z() ) )
272  {
273    return in = kOutside; 
274  }
275
276  //  rad2oi= sqr( p.x()*(1.0 + 0.5*kRadTolerance/(xSemiAxis*xSemiAxis)) )
277  //      + sqr( p.y()*(1.0 + 0.5*kRadTolerance/(ySemiAxis*ySemiAxis)) );
278  rad2oi = sqr(p.x()/( xSemiAxis - 0.5*kRadTolerance ))
279        + sqr(p.y()/( ySemiAxis - 0.5*kRadTolerance ));
280     
281  if (rad2oi < sqr( zheight-p.z() ) )
282  {
283    in = ( ( p.z() < -zTopCut + 0.5*kRadTolerance )
284        || ( p.z() >  zTopCut - 0.5*kRadTolerance ) ) ? kSurface : kInside;
285  }
286  else 
287  {
288    in = kSurface;
289  }
290
291  return in;
292}
293
294/////////////////////////////////////////////////////////////////////////
295//
296// Return unit normal of surface closest to p not protected against p=0
297//
298G4ThreeVector G4EllipticalCone::SurfaceNormal( const G4ThreeVector& p) const
299{
300
301  G4double rx = sqr(p.x()/xSemiAxis), 
302           ry = sqr(p.y()/ySemiAxis);
303
304  G4double rad = std::sqrt(rx + ry); 
305
306  G4ThreeVector norm;
307
308  if( (p.z() < -zTopCut) && ((rx+ry) < sqr(zTopCut + zheight)) )
309  {
310    return G4ThreeVector( 0., 0., -1. ); 
311  }
312
313  if( (p.z() > (zheight > zTopCut ? zheight : zTopCut)) &&
314      ((rx+ry) < sqr(zheight-zTopCut)) )
315  {
316    return G4ThreeVector( 0., 0., 1. );
317  }
318
319  if( p.z() > rad + 2.*zTopCut - zheight ) 
320  {
321    if ( p.z() > zTopCut )
322    {
323      if( p.x() == 0. ) 
324      {
325        norm = G4ThreeVector( 0., p.y() < 0. ? -1. : 1., 1. ); 
326        return norm /= norm.mag();
327      } 
328      if( p.y() == 0. )
329      {
330        norm = G4ThreeVector( p.x() < 0. ? -1. : 1., 0., 1. ); 
331        return norm /= norm.mag();
332      } 
333     
334      G4double m =  std::fabs(p.x()/p.y());
335      G4double c2 = sqr(zheight-zTopCut)/(1./sqr(xSemiAxis)+sqr(m/ySemiAxis));
336      G4double x  = std::sqrt(c2);
337      G4double y  = m*x;
338       
339      x /= sqr(xSemiAxis);
340      y /= sqr(ySemiAxis);
341     
342      norm = G4ThreeVector( p.x() < 0. ? -x : x, 
343                            p.y() < 0. ? -y : y,
344                            zheight - zTopCut );
345      norm /= norm.mag();
346      norm += G4ThreeVector( 0., 0., 1. );
347      return norm /= norm.mag();     
348    }
349   
350    return G4ThreeVector( 0., 0., 1. );   
351  }
352 
353  if( p.z() < rad - 2.*zTopCut - zheight )
354  {
355    if( p.x() == 0. ) 
356    {
357      norm = G4ThreeVector( 0., p.y() < 0. ? -1. : 1., -1. ); 
358      return norm /= norm.mag();
359    } 
360    if( p.y() == 0. )
361    {
362      norm = G4ThreeVector( p.x() < 0. ? -1. : 1., 0., -1. ); 
363      return norm /= norm.mag();
364    } 
365   
366    G4double m =  std::fabs(p.x()/p.y());
367    G4double c2 = sqr(zheight+zTopCut)/(1./sqr(xSemiAxis)+sqr(m/ySemiAxis));
368    G4double x  = std::sqrt(c2);
369    G4double y  = m*x;
370   
371    x /= sqr(xSemiAxis);
372    y /= sqr(ySemiAxis);
373   
374    norm = G4ThreeVector( p.x() < 0. ? -x : x, 
375                          p.y() < 0. ? -y : y,
376                          zheight - zTopCut );
377    norm /= norm.mag();
378    norm += G4ThreeVector( 0., 0., -1. );
379    return norm /= norm.mag();     
380  }
381   
382  norm  = G4ThreeVector(p.x()/sqr(xSemiAxis), p.y()/sqr(ySemiAxis), rad);
383   
384  G4double m = std::tan(pi/8.);
385  G4double c = -zTopCut - m*(zTopCut + zheight);
386
387  if( p.z() < -m*rad + c )
388    return G4ThreeVector (0.,0.,-1.);
389
390  return norm /= norm.mag();
391}
392
393//////////////////////////////////////////////////////////////////////////
394//
395// Calculate distance to shape from outside, along normalised vector
396// return kInfinity if no intersection, or intersection distance <= tolerance
397//
398G4double G4EllipticalCone::DistanceToIn( const G4ThreeVector& p,
399                                         const G4ThreeVector& v  ) const
400{
401
402  static const G4double halfTol = 0.5*kCarTolerance;
403
404  G4double distMin = kInfinity;
405
406  // code from EllipticalTube
407
408  G4double sigz = p.z()+zTopCut;
409
410  //
411  // Check z = -dz planer surface
412  //
413
414  if (sigz < halfTol)
415  {
416    //
417    // We are "behind" the shape in z, and so can
418    // potentially hit the rear face. Correct direction?
419    //
420    if (v.z() <= 0)
421    {
422      //
423      // As long as we are far enough away, we know we
424      // can't intersect
425      //
426      if (sigz < 0) return kInfinity;
427     
428      //
429      // Otherwise, we don't intersect unless we are
430      // on the surface of the ellipse
431      //
432
433      if ( sqr(p.x()/( xSemiAxis - halfTol ))
434         + sqr(p.y()/( ySemiAxis - halfTol )) <= sqr( zheight+zTopCut ) )
435        return kInfinity;
436
437    }
438    else
439    {
440      //
441      // How far?
442      //
443      G4double s = -sigz/v.z();
444     
445      //
446      // Where does that place us?
447      //
448      G4double xi = p.x() + s*v.x(),
449               yi = p.y() + s*v.y();
450     
451      //
452      // Is this on the surface (within ellipse)?
453      //
454      if ( sqr(xi/xSemiAxis) + sqr(yi/ySemiAxis) <= sqr( zheight + zTopCut ) )
455      {
456        //
457        // Yup. Return s, unless we are on the surface
458        //
459        return (sigz < -halfTol) ? s : 0;
460      }
461      else if (xi/(xSemiAxis*xSemiAxis)*v.x()
462             + yi/(ySemiAxis*ySemiAxis)*v.y() >= 0)
463      {
464        //
465        // Else, if we are traveling outwards, we know
466        // we must miss
467        //
468        //        return kInfinity;
469      }
470    }
471  }
472
473  //
474  // Check z = +dz planer surface
475  //
476
477  sigz = p.z() - zTopCut;
478 
479  if (sigz > -halfTol)
480  {
481    if (v.z() >= 0)
482    {
483
484      if (sigz > 0) return kInfinity;
485
486      if ( sqr(p.x()/( xSemiAxis - halfTol ))
487         + sqr(p.y()/( ySemiAxis - halfTol )) <= sqr( zheight-zTopCut ) )
488        return kInfinity;
489
490    }
491    else {
492      G4double s = -sigz/v.z();
493
494      G4double xi = p.x() + s*v.x(),
495               yi = p.y() + s*v.y();
496
497      if ( sqr(xi/xSemiAxis) + sqr(yi/ySemiAxis) <= sqr( zheight - zTopCut ) )
498      {
499        return (sigz > -halfTol) ? s : 0;
500      }
501      else if (xi/(xSemiAxis*xSemiAxis)*v.x()
502             + yi/(ySemiAxis*ySemiAxis)*v.y() >= 0)
503      {
504        //        return kInfinity;
505      }
506    }
507  }
508
509
510#if 0
511
512  // check to see if Z plane is relevant
513  //
514  if (p.z() < -zTopCut - 0.5*kCarTolerance)
515  {
516    if (v.z() <= 0.0)
517      return distMin;
518
519    G4double lambda = (-zTopCut - p.z())/v.z();
520   
521    if ( sqr((lambda*v.x()+p.x())/xSemiAxis) +
522         sqr((lambda*v.y()+p.y())/ySemiAxis) <=
523         sqr(zTopCut + zheight + 0.5*kRadTolerance) )
524    {
525      return distMin = std::fabs(lambda);   
526    }
527  }
528
529  if (p.z() > zTopCut+0.5*kCarTolerance)
530  {
531    if (v.z() >= 0.0)
532      { return distMin; }
533
534    G4double lambda  = (zTopCut - p.z()) / v.z();
535
536    if ( sqr((lambda*v.x() + p.x())/xSemiAxis) +
537         sqr((lambda*v.y() + p.y())/ySemiAxis) <=
538         sqr(zheight - zTopCut + 0.5*kRadTolerance) )
539      {
540        return distMin = std::fabs(lambda);
541      }
542  }
543 
544  if (p.z() > zTopCut - 0.5*kCarTolerance
545   && p.z() < zTopCut + 0.5*kCarTolerance )
546  {
547    if (v.z() > 0.)
548      { return kInfinity; }
549
550    return distMin = 0.;
551  }
552 
553  if (p.z() < -zTopCut + 0.5*kCarTolerance
554   && p.z() > -zTopCut - 0.5*kCarTolerance)
555  {
556    if (v.z() < 0.)
557      { return distMin = kInfinity; }
558   
559    return distMin = 0.;
560  }
561 
562#endif
563
564  // if we are here then it either intersects or grazes the curved surface
565  // or it does not intersect at all
566  //
567
568  G4double A = sqr(v.x()/xSemiAxis) + sqr(v.y()/ySemiAxis) - sqr(v.z());
569  G4double B = 2*(v.x()*p.x()/sqr(xSemiAxis) + 
570                  v.y()*p.y()/sqr(ySemiAxis) + v.z()*(zheight-p.z()));
571  G4double C = sqr(p.x()/xSemiAxis) + sqr(p.y()/ySemiAxis) - 
572               sqr(zheight - p.z());
573 
574  G4double discr = B*B - 4.*A*C;
575   
576  // if the discriminant is negative it never hits the curved object
577  //
578  if ( discr < -0.5*kCarTolerance )
579    { return distMin; }
580 
581  //case below is when it hits or grazes the surface
582  //
583  if ( (discr >= - 0.5*kCarTolerance ) && (discr < 0.5*kCarTolerance ) )
584  {
585    return distMin = std::fabs(-B/(2.*A)); 
586  }
587 
588  G4double plus  = (-B+std::sqrt(discr))/(2.*A);
589  G4double minus = (-B-std::sqrt(discr))/(2.*A);
590  //  G4double lambda   = std::fabs(plus) < std::fabs(minus) ? plus : minus;
591
592  G4double lambda = 0;
593
594  if ( minus > halfTol && minus < distMin ) 
595  {
596    lambda = minus ;
597    // check normal vector   n * v < 0
598    G4ThreeVector pin = p + lambda*v;
599
600    G4ThreeVector truenorm(pin.x()/(xSemiAxis*xSemiAxis),
601                           pin.y()/(ySemiAxis*ySemiAxis),
602                           - ( pin.z() - zheight ));
603    if ( truenorm*v < 0)
604    {   // yes, going inside the solid
605      distMin = lambda;
606    }
607  }
608   
609  if ( plus > halfTol  && plus < distMin )
610  {
611    lambda = plus ;
612    // check normal vector   n * v < 0
613    G4ThreeVector pin = p + lambda*v;
614
615    G4ThreeVector truenorm(pin.x()/(xSemiAxis*xSemiAxis),
616                           pin.y()/(ySemiAxis*ySemiAxis),
617                           - ( pin.z() - zheight ) );
618    if ( truenorm*v < 0)
619    {   // yes, going inside the solid
620      distMin = lambda;
621    }
622  }
623
624#ifdef G4SPECSDEBUG   
625//  G4cout << "DToIn: plus,minus, lambda = " << plus
626//         << ", " << minus << ", " << lambda << G4endl ;
627//  G4cout << "DToIn: distMin = " << distMin << G4endl ;
628#endif
629
630   
631  return distMin ;
632}
633
634//////////////////////////////////////////////////////////////////////////
635//
636// Calculate distance (<= actual) to closest surface of shape from outside
637// Return 0 if point inside
638//
639G4double G4EllipticalCone::DistanceToIn(const G4ThreeVector& p) const
640{
641  G4double distR, distR2, distZ, maxDim;
642  G4double distRad; 
643
644  // check if the point lies either below z=-zTopCut in bottom elliptical
645  // region or on top within cut elliptical region
646  //
647  if( (p.z() <= -zTopCut) && (sqr(p.x()/xSemiAxis) + sqr(p.y()/ySemiAxis)
648                           <= sqr(zTopCut + zheight + 0.5*kCarTolerance )) )
649  { 
650    //return distZ = std::fabs(zTopCut - p.z());
651     return distZ = std::fabs(zTopCut + p.z());
652  } 
653 
654  if( (p.z() >= zTopCut) && (sqr(p.x()/xSemiAxis)+sqr(p.y()/ySemiAxis)
655                          <= sqr(zheight - zTopCut + kCarTolerance/2.0 )) )
656  {
657    return distZ = std::fabs(p.z() - zTopCut);
658  } 
659 
660  // below we use the following approximation: we take the largest of the
661  // axes and find the shortest distance to the circular (cut) cone of that
662  // radius. 
663  //
664  maxDim = xSemiAxis >= ySemiAxis ? xSemiAxis:ySemiAxis;
665  distRad = std::sqrt(p.x()*p.x()+p.y()*p.y());
666
667  if( p.z() > maxDim*distRad + zTopCut*(1.+maxDim)-sqr(maxDim)*zheight )
668  {
669    distR2 = sqr(p.z() - zTopCut) + sqr(distRad - maxDim*(zheight - zTopCut));
670    return std::sqrt( distR2 );
671  } 
672
673  if( distRad > maxDim*( zheight - p.z() ) )
674  {
675    if( p.z() > maxDim*distRad - (zTopCut*(1.+maxDim)+sqr(maxDim)*zheight) )
676    {
677      G4double zVal = (p.z()-maxDim*(distRad-maxDim*zheight))/(1.+sqr(maxDim));
678      G4double rVal = maxDim*(zheight - zVal);
679      return distR  = std::sqrt(sqr(p.z() - zVal) + sqr(distRad - rVal));
680    }
681  }
682
683  if( distRad <= maxDim*(zheight - p.z()) )
684  {
685    distR2 = sqr(distRad - maxDim*(zheight + zTopCut)) + sqr(p.z() + zTopCut);
686    return std::sqrt( distR2 );   
687  }   
688 
689  return distR = 0;
690}
691
692/////////////////////////////////////////////////////////////////////////
693//
694// Calculate distance to surface of shape from `inside',
695// allowing for tolerance
696//
697G4double G4EllipticalCone::DistanceToOut(const G4ThreeVector& p,
698                                         const G4ThreeVector& v,
699                                         const G4bool calcNorm,
700                                               G4bool *validNorm,
701                                               G4ThreeVector *) const
702{
703  G4double distMin, lambda;
704  enum surface_e {kPlaneSurf, kCurvedSurf, kNoSurf} surface;
705 
706  distMin = kInfinity;
707  surface = kNoSurf;
708
709  if (v.z() < 0.0)
710  {
711    lambda = (-p.z() - zTopCut)/v.z();
712
713    if ( (sqr((p.x() + lambda*v.x())/xSemiAxis) + 
714          sqr((p.y() + lambda*v.y())/ySemiAxis)) < 
715          sqr(zheight + zTopCut + 0.5*kCarTolerance) )
716    {
717      distMin = std::fabs(lambda);
718
719      if (!calcNorm) { return distMin; }
720    } 
721    distMin = std::fabs(lambda);
722    surface = kPlaneSurf;
723  }
724
725  if (v.z() > 0.0)
726  {
727    lambda = (zTopCut - p.z()) / v.z();
728
729    if ( (sqr((p.x() + lambda*v.x())/xSemiAxis)
730        + sqr((p.y() + lambda*v.y())/ySemiAxis) )
731       < (sqr(zheight - zTopCut + 0.5*kCarTolerance)) )
732    {
733      distMin = std::fabs(lambda);
734      if (!calcNorm) { return distMin; }
735    }
736    distMin = std::fabs(lambda);
737    surface = kPlaneSurf;
738  }
739 
740  // if we are here then it either intersects or grazes the
741  // curved surface...
742  //
743  G4double A = sqr(v.x()/xSemiAxis) + sqr(v.y()/ySemiAxis) - sqr(v.z());
744  G4double B = 2.*(v.x()*p.x()/sqr(xSemiAxis) + 
745                   v.y()*p.y()/sqr(ySemiAxis) + v.z()*(zheight-p.z()));
746  G4double C = sqr(p.x()/xSemiAxis) + sqr(p.y()/ySemiAxis)
747             - sqr(zheight - p.z());
748 
749  G4double discr = B*B - 4.*A*C;
750 
751  if ( discr >= - 0.5*kCarTolerance && discr < 0.5*kCarTolerance )
752  { 
753    if(!calcNorm) { return distMin = std::fabs(-B/(2.*A)); }
754  }
755
756  else if ( discr > 0.5*kCarTolerance )
757  {
758    G4double plus  = (-B+std::sqrt(discr))/(2.*A);
759    G4double minus = (-B-std::sqrt(discr))/(2.*A);
760
761    if ( plus > 0.5*kCarTolerance && minus > 0.5*kCarTolerance )
762    {
763      // take the shorter distance
764      //
765      lambda   = std::fabs(plus) < std::fabs(minus) ? plus : minus;
766    }
767    else
768    {
769      // at least one solution is close to zero or negative
770      // so, take small positive solution or zero
771      //
772      lambda   = plus > -0.5*kCarTolerance ? plus : 0;
773    }
774
775    if ( std::fabs(lambda) < distMin )
776    {
777      distMin  = std::fabs(lambda);
778      surface  = kCurvedSurf;
779    }
780  }
781
782  // set normal if requested
783  //
784  if (calcNorm)
785  {
786    if (surface == kNoSurf)
787    {
788      *validNorm = false;
789    }
790    else
791    {
792      *validNorm = true;
793      switch (surface)
794      {
795        case kPlaneSurf:
796        {
797          *n = G4ThreeVector(0.,0.,(v.z() > 0.0 ? 1. : -1.));
798        }
799        break;
800
801        case kCurvedSurf:
802        {
803          G4ThreeVector pexit = p + distMin*v;
804          G4ThreeVector truenorm(pexit.x()/(xSemiAxis*xSemiAxis),
805                                 pexit.y()/(ySemiAxis*ySemiAxis),
806                                 pexit.z() - zheight );
807          truenorm /= truenorm.mag();
808          *n= truenorm;
809        } 
810        break;
811
812        default:
813          G4cout.precision(16);
814          G4cout << G4endl;
815          DumpInfo();
816          G4cout << "Position:"  << G4endl << G4endl;
817          G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl;
818          G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl;
819          G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl;
820          G4cout << "Direction:" << G4endl << G4endl;
821          G4cout << "v.x() = "   << v.x() << G4endl;
822          G4cout << "v.y() = "   << v.y() << G4endl;
823          G4cout << "v.z() = "   << v.z() << G4endl << G4endl;
824          G4cout << "Proposed distance :" << G4endl << G4endl;
825          G4cout << "distMin = "    << distMin/mm << " mm" << G4endl << G4endl;
826          G4Exception("G4EllipticalCone::DistanceToOut(p,v,..)",
827                      "Notification", JustWarning,
828                      "Undefined side for valid surface normal to solid.");
829          break;
830      }
831    }
832  }
833
834  return distMin;
835}
836
837/////////////////////////////////////////////////////////////////////////
838//
839// Calculate distance (<=actual) to closest surface of shape from inside
840//
841G4double G4EllipticalCone::DistanceToOut(const G4ThreeVector& p) const
842{
843  G4double rad,roo,roo1, distR, distZ, distMin=0.;
844  G4double minAxis = xSemiAxis < ySemiAxis ? xSemiAxis : ySemiAxis;
845
846#ifdef G4SPECSDEBUG
847  if( Inside(p) == kOutside )
848  {
849     G4cout.precision(16) ;
850     G4cout << G4endl ;
851     DumpInfo();
852     G4cout << "Position:"  << G4endl << G4endl ;
853     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
854     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
855     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
856     G4Exception("G4Ellipsoid::DistanceToOut(p)", "Notification", JustWarning, 
857                 "Point p is outside !?" );
858  }
859#endif
860   
861  // since we have made the above warning, below we are working assuming p
862  // is inside check how close it is to the circular cone with radius equal
863  // to the smaller of the axes
864  //
865  if( sqr(p.x()/minAxis)+sqr(p.y()/minAxis) < sqr(zheight - p.z()) )
866  {
867    rad     = std::sqrt(sqr(p.x()) + sqr(p.y()));
868    roo     = minAxis*(zheight-p.z()); // radius of cone at z= p.z()
869    roo1    = minAxis*(zheight-zTopCut); // radius of cone at z=+zTopCut
870
871    distZ=zTopCut - std::fabs(p.z()) ;
872    distR=(roo-rad)/(std::sqrt(1+sqr(minAxis)));
873
874    if(rad>roo1)
875    {
876      distMin=(zTopCut-p.z())*(roo-rad)/(roo-roo1);
877      distMin=std::min(distMin,distR);
878    }     
879    distMin=std::min(distR,distZ);
880  }
881
882  return distMin;
883}
884
885//////////////////////////////////////////////////////////////////////////
886//
887// GetEntityType
888//
889G4GeometryType G4EllipticalCone::GetEntityType() const
890{
891  return G4String("G4EllipticalCone");
892}
893
894//////////////////////////////////////////////////////////////////////////
895//
896// Stream object contents to an output stream
897//
898std::ostream& G4EllipticalCone::StreamInfo( std::ostream& os ) const
899{
900  os << "-----------------------------------------------------------\n"
901     << "    *** Dump for solid - " << GetName() << " ***\n"
902     << "    ===================================================\n"
903     << " Solid type: G4EllipticalCone\n"
904     << " Parameters: \n"
905
906     << "    semi-axis x: " << xSemiAxis/mm << " mm \n"
907     << "    semi-axis y: " << ySemiAxis/mm << " mm \n"
908     << "    height    z: " << zheight/mm << " mm \n"
909     << "    half length in  z: " << zTopCut/mm << " mm \n"
910     << "-----------------------------------------------------------\n";
911
912  return os;
913}
914
915/////////////////////////////////////////////////////////////////////////
916//
917// GetPointOnSurface
918//
919// returns quasi-uniformly distributed point on surface of elliptical cone
920//
921G4ThreeVector G4EllipticalCone::GetPointOnSurface() const
922{
923
924  G4double phi, sinphi, cosphi, aOne, aTwo, aThree,
925           chose, zRand, rRand1, rRand2;
926 
927  G4double rOne = std::sqrt(sqr(xSemiAxis)
928                + sqr(ySemiAxis))*(zheight - zTopCut);
929  G4double rTwo = std::sqrt(sqr(xSemiAxis)
930                + sqr(ySemiAxis))*(zheight + zTopCut);
931 
932  aOne   = pi*(rOne + rTwo)*std::sqrt(sqr(rOne - rTwo)+sqr(2.*zTopCut));
933  aTwo   = pi*xSemiAxis*ySemiAxis*sqr(zheight+zTopCut);
934  aThree = pi*xSemiAxis*ySemiAxis*sqr(zheight-zTopCut); 
935
936  phi = RandFlat::shoot(0.,twopi);
937  cosphi = std::cos(phi);
938  sinphi = std::sin(phi);
939 
940  if(zTopCut >= zheight) aThree = 0.;
941
942  chose = RandFlat::shoot(0.,aOne+aTwo+aThree);
943  if((chose>=0.) && (chose<aOne))
944  {
945    zRand = RandFlat::shoot(-zTopCut,zTopCut);
946    return G4ThreeVector(xSemiAxis*(zheight-zRand)*cosphi,
947                         ySemiAxis*(zheight-zRand)*sinphi,zRand);   
948  }
949  else if((chose>=aOne) && (chose<aOne+aTwo))
950  {
951    do
952    {
953      rRand1 = RandFlat::shoot(0.,1.) ;
954      rRand2 = RandFlat::shoot(0.,1.) ;
955    } while ( rRand2 >= rRand1  ) ;
956
957    //    rRand2 = RandFlat::shoot(0.,std::sqrt(1.-sqr(rRand1)));
958    return G4ThreeVector(rRand1*xSemiAxis*(zheight+zTopCut)*cosphi,
959                         rRand1*ySemiAxis*(zheight+zTopCut)*sinphi, -zTopCut);
960
961  }
962  // else
963  //
964
965  do
966  {
967    rRand1 = RandFlat::shoot(0.,1.) ;
968    rRand2 = RandFlat::shoot(0.,1.) ;
969  } while ( rRand2 >= rRand1  ) ;
970
971  return G4ThreeVector(rRand1*xSemiAxis*(zheight-zTopCut)*cosphi,
972                       rRand1*ySemiAxis*(zheight-zTopCut)*sinphi, zTopCut);
973}
974
975//
976// Methods for visualisation
977//
978
979void G4EllipticalCone::DescribeYourselfTo (G4VGraphicsScene& scene) const
980{
981  scene.AddSolid(*this);
982}
983
984G4VisExtent G4EllipticalCone::GetExtent() const
985{
986  // Define the sides of the box into which the solid instance would fit.
987  //
988  G4double maxDim;
989  maxDim = xSemiAxis > ySemiAxis ? xSemiAxis : ySemiAxis;
990  maxDim = maxDim > zTopCut ? maxDim : zTopCut;
991 
992  return G4VisExtent (-maxDim, maxDim,
993                      -maxDim, maxDim,
994                      -maxDim, maxDim);
995}
996
997G4NURBS* G4EllipticalCone::CreateNURBS () const
998{
999  // Box for now!!!
1000  //
1001  return new G4NURBSbox(xSemiAxis, ySemiAxis,zheight);
1002}
1003
1004G4Polyhedron* G4EllipticalCone::CreatePolyhedron () const
1005{
1006  return new G4PolyhedronEllipticalCone(xSemiAxis, ySemiAxis, zheight, zTopCut);
1007}
1008
1009G4Polyhedron* G4EllipticalCone::GetPolyhedron () const
1010{
1011  if ( (!fpPolyhedron)
1012    || (fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
1013        fpPolyhedron->GetNumberOfRotationSteps()) )
1014    {
1015      delete fpPolyhedron;
1016      fpPolyhedron = CreatePolyhedron();
1017    }
1018  return fpPolyhedron;
1019}
Note: See TracBrowser for help on using the repository browser.