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