source: trunk/source/geometry/solids/specific/src/G4PolyhedraSide.cc @ 836

Last change on this file since 836 was 831, checked in by garnier, 16 years ago

import all except CVS

File size: 31.4 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//
27// $Id: G4PolyhedraSide.cc,v 1.13 2007/05/31 13:52:48 gcosmo Exp $
28// GEANT4 tag $Name:  $
29//
30//
31// --------------------------------------------------------------------
32// GEANT 4 class source file
33//
34//
35// G4PolyhedraSide.cc
36//
37// Implemenation of the face representing one segmented side of a Polyhedra
38//
39// --------------------------------------------------------------------
40
41#include "G4PolyhedraSide.hh"
42#include "G4IntersectingCone.hh"
43#include "G4ClippablePolygon.hh"
44#include "G4AffineTransform.hh"
45#include "G4SolidExtentList.hh"
46#include "G4GeometryTolerance.hh"
47
48//
49// Constructor
50//
51// Values for r1,z1 and r2,z2 should be specified in clockwise
52// order in (r,z).
53//
54G4PolyhedraSide::G4PolyhedraSide( const G4PolyhedraSideRZ *prevRZ,
55                                  const G4PolyhedraSideRZ *tail,
56                                  const G4PolyhedraSideRZ *head,
57                                  const G4PolyhedraSideRZ *nextRZ,
58                                        G4int theNumSide, 
59                                        G4double thePhiStart, 
60                                        G4double thePhiTotal, 
61                                        G4bool thePhiIsOpen,
62                                        G4bool isAllBehind )
63{
64
65  kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
66
67  //
68  // Record values
69  //
70  r[0] = tail->r; z[0] = tail->z;
71  r[1] = head->r; z[1] = head->z;
72 
73  G4double phiTotal;
74 
75  //
76  // Set phi to our convention
77  //
78  startPhi = thePhiStart;
79  while (startPhi < 0.0) startPhi += twopi;
80 
81  phiIsOpen = thePhiIsOpen;
82  phiTotal = (phiIsOpen) ? thePhiTotal : twopi;
83 
84  allBehind = isAllBehind;
85   
86  //
87  // Make our intersecting cone
88  //
89  cone = new G4IntersectingCone( r, z );
90 
91  //
92  // Construct side plane vector set
93  //
94  numSide = theNumSide;
95  deltaPhi = phiTotal/theNumSide;
96  endPhi = startPhi+phiTotal;
97 
98  vecs = new G4PolyhedraSideVec[numSide];
99 
100  edges = new G4PolyhedraSideEdge[phiIsOpen ? numSide+1 : numSide];
101 
102  //
103  // ...this is where we start
104  //
105  G4double phi = startPhi;
106  G4ThreeVector a1( r[0]*std::cos(phi), r[0]*std::sin(phi), z[0] ),
107          b1( r[1]*std::cos(phi), r[1]*std::sin(phi), z[1] ),
108          c1( prevRZ->r*std::cos(phi), prevRZ->r*std::sin(phi), prevRZ->z ),
109          d1( nextRZ->r*std::cos(phi), nextRZ->r*std::sin(phi), nextRZ->z ),
110          a2, b2, c2, d2;
111  G4PolyhedraSideEdge *edge = edges;
112         
113  G4PolyhedraSideVec *vec = vecs;
114  do
115  {
116    //
117    // ...this is where we are going
118    //
119    phi += deltaPhi;
120    a2 = G4ThreeVector( r[0]*std::cos(phi), r[0]*std::sin(phi), z[0] );
121    b2 = G4ThreeVector( r[1]*std::cos(phi), r[1]*std::sin(phi), z[1] );
122    c2 = G4ThreeVector( prevRZ->r*std::cos(phi), prevRZ->r*std::sin(phi), prevRZ->z );
123    d2 = G4ThreeVector( nextRZ->r*std::cos(phi), nextRZ->r*std::sin(phi), nextRZ->z );
124   
125    G4ThreeVector tt; 
126   
127    //
128    // ...build some relevant vectors.
129    //    the point is to sacrifice a little memory with precalcs
130    //    to gain speed
131    //
132    vec->center = 0.25*( a1 + a2 + b1 + b2 );
133   
134    tt = b2 + b1 - a2 - a1;
135    vec->surfRZ = tt.unit();
136    if (vec==vecs) lenRZ = 0.25*tt.mag();
137   
138    tt = b2 - b1 + a2 - a1;
139    vec->surfPhi = tt.unit();
140    if (vec==vecs)
141    {
142      lenPhi[0] = 0.25*tt.mag();
143      tt = b2 - b1;
144      lenPhi[1] = (0.5*tt.mag()-lenPhi[0])/lenRZ;
145    }
146   
147    tt = vec->surfPhi.cross(vec->surfRZ);
148    vec->normal = tt.unit();
149   
150    //
151    // ...edge normals are the average of the normals of
152    //    the two faces they connect.
153    //
154    // ...edge normals are necessary if we are to accurately
155    //    decide if a point is "inside" a face. For non-convex
156    //    shapes, it is absolutely necessary to know information
157    //    on adjacent faces to accurate determine this.
158    //
159    // ...we don't need them for the phi edges, since that
160    //    information is taken care of internally. The r/z edges,
161    //    however, depend on the adjacent G4PolyhedraSide.
162    //
163    G4ThreeVector a12, adj;
164   
165    a12 = a2-a1;
166
167    adj = 0.5*(c1+c2-a1-a2);
168    adj = adj.cross(a12); 
169    adj = adj.unit() + vec->normal;       
170    vec->edgeNorm[0] = adj.unit();
171   
172    a12 = b1-b2;
173    adj = 0.5*(d1+d2-b1-b2);
174    adj = adj.cross(a12); 
175    adj = adj.unit() + vec->normal;       
176    vec->edgeNorm[1] = adj.unit();
177   
178    //
179    // ...the corners are crucial. It is important that
180    //    they are calculated consistently for adjacent
181    //    G4PolyhedraSides, to avoid gaps caused by roundoff.
182    //
183    vec->edges[0] = edge;
184    edge->corner[0] = a1;
185    edge->corner[1] = b1;
186    edge++;
187    vec->edges[1] = edge;
188
189    a1 = a2;
190    b1 = b2;
191    c1 = c2;
192    d1 = d2;
193  } while( ++vec < vecs+numSide );
194 
195  //
196  // Clean up hanging edge
197  //
198  if (phiIsOpen)
199  {
200    edge->corner[0] = a2;
201    edge->corner[1] = b2;
202  }
203  else
204  {
205    vecs[numSide-1].edges[1] = edges;
206  }
207 
208  //
209  // Go back and fill in remaining fields in edges
210  //
211  vec = vecs;
212  G4PolyhedraSideVec *prev = vecs+numSide-1;
213  do
214  {
215    edge = vec->edges[0];    // The edge between prev and vec
216   
217    //
218    // Okay: edge normal is average of normals of adjacent faces
219    //
220    G4ThreeVector eNorm = vec->normal + prev->normal;
221    edge->normal = eNorm.unit(); 
222   
223    //
224    // Vertex normal is average of norms of adjacent surfaces (all four)
225    // However, vec->edgeNorm is unit vector in some direction
226    // as the sum of normals of adjacent PolyhedraSide with vec.
227    // The normalization used for this vector should be the same
228    // for vec and prev.
229    //
230    eNorm = vec->edgeNorm[0] + prev->edgeNorm[0];
231    edge->cornNorm[0] = eNorm.unit();
232 
233    eNorm = vec->edgeNorm[1] + prev->edgeNorm[1];
234    edge->cornNorm[1] = eNorm.unit();
235  } while( prev=vec, ++vec < vecs + numSide );
236 
237  if (phiIsOpen)
238  {
239    // G4double rFact = std::cos(0.5*deltaPhi);
240    //
241    // If phi is open, we need to patch up normals of the
242    // first and last edges and their corresponding
243    // vertices.
244    //
245    // We use vectors that are in the plane of the
246    // face. This should be safe.
247    //
248    vec = vecs;
249   
250    G4ThreeVector normvec = vec->edges[0]->corner[0]
251                          - vec->edges[0]->corner[1];
252    normvec = normvec.cross(vec->normal);
253    if (normvec.dot(vec->surfPhi) > 0) normvec = -normvec;
254
255    vec->edges[0]->normal = normvec.unit();
256   
257    vec->edges[0]->cornNorm[0] = (vec->edges[0]->corner[0]
258                                - vec->center).unit();
259    vec->edges[0]->cornNorm[1] = (vec->edges[0]->corner[1]
260                                - vec->center).unit();
261   
262    //
263    // Repeat for ending phi
264    //
265    vec = vecs + numSide - 1;
266   
267    normvec = vec->edges[1]->corner[0] - vec->edges[1]->corner[1];
268    normvec = normvec.cross(vec->normal);
269    if (normvec.dot(vec->surfPhi) < 0) normvec = -normvec;
270
271    vec->edges[1]->normal = normvec.unit();
272   
273    vec->edges[1]->cornNorm[0] = (vec->edges[1]->corner[0]
274                                - vec->center).unit();
275    vec->edges[1]->cornNorm[1] = (vec->edges[1]->corner[1]
276                                - vec->center).unit();
277  }
278 
279  //
280  // edgeNorm is the factor one multiplies the distance along vector phi
281  // on the surface of one of our sides in order to calculate the distance
282  // from the edge. (see routine DistanceAway)
283  //
284  edgeNorm = 1.0/std::sqrt( 1.0 + lenPhi[1]*lenPhi[1] );
285}
286
287
288//
289// Fake default constructor - sets only member data and allocates memory
290//                            for usage restricted to object persistency.
291//
292G4PolyhedraSide::G4PolyhedraSide( __void__&)
293  : cone(0), vecs(0), edges(0)
294{
295}
296
297
298//
299// Destructor
300// 
301G4PolyhedraSide::~G4PolyhedraSide()
302{
303  delete cone;
304  delete [] vecs;
305  delete [] edges;
306}
307
308
309//
310// Copy constructor
311//
312G4PolyhedraSide::G4PolyhedraSide( const G4PolyhedraSide &source )
313  : G4VCSGface()
314{
315  CopyStuff( source );
316}
317
318
319//
320// Assignment operator
321//
322G4PolyhedraSide& G4PolyhedraSide::operator=( const G4PolyhedraSide &source )
323{
324  if (this == &source) return *this;
325 
326  delete cone;
327  delete [] vecs;
328  delete [] edges;
329 
330  CopyStuff( source );
331
332  return *this;
333}
334
335
336//
337// CopyStuff
338//
339void G4PolyhedraSide::CopyStuff( const G4PolyhedraSide &source )
340{
341  //
342  // The simple stuff
343  //
344  numSide    = source.numSide;
345  r[0]    = source.r[0];
346  r[1]    = source.r[1];
347  z[0]    = source.z[0];
348  z[1]    = source.z[1];
349  startPhi  = source.startPhi;
350  deltaPhi  = source.deltaPhi;
351  endPhi    = source.endPhi;
352  phiIsOpen = source.phiIsOpen;
353  allBehind = source.allBehind;
354 
355  lenRZ     = source.lenRZ;
356  lenPhi[0] = source.lenPhi[0];
357  lenPhi[1] = source.lenPhi[1];
358  edgeNorm  = source.edgeNorm;
359
360  kCarTolerance = source.kCarTolerance;
361 
362  cone = new G4IntersectingCone( *source.cone );
363
364  //
365  // Duplicate edges
366  //
367  G4int  numEdges = phiIsOpen ? numSide+1 : numSide;
368  edges = new G4PolyhedraSideEdge[numEdges];
369 
370  G4PolyhedraSideEdge *edge = edges,
371          *sourceEdge = source.edges;
372  do
373  {
374    *edge = *sourceEdge;
375  } while( ++sourceEdge, ++edge < edges + numEdges);
376
377  //
378  // Duplicate vecs
379  //
380  vecs = new G4PolyhedraSideVec[numSide];
381 
382  G4PolyhedraSideVec *vec = vecs,
383         *sourceVec = source.vecs;
384  do
385  {
386    *vec = *sourceVec;
387    vec->edges[0] = edges + (sourceVec->edges[0] - source.edges);
388    vec->edges[1] = edges + (sourceVec->edges[1] - source.edges);
389  } while( ++sourceVec, ++vec < vecs + numSide );
390}
391 
392
393//
394// Intersect
395//
396// Decide if a line intersects the face.
397//
398// Arguments:
399//  p    = (in) starting point of line segment
400//  v    = (in) direction of line segment (assumed a unit vector)
401//  A, B    = (in) 2d transform variables (see note top of file)
402//  normSign  = (in) desired sign for dot product with normal (see below)
403//  surfTolerance  = (in) minimum distance from the surface
404//  vecs    = (in) Vector set array
405//  distance  = (out) distance to surface furfilling all requirements
406//  distFromSurface = (out) distance from the surface
407//  thisNormal  = (out) normal vector of the intersecting surface
408//
409// Return value:
410//  true if an intersection is found. Otherwise, output parameters are
411//  undefined.
412//
413// Notes:
414// * normSign: if we are "inside" the shape and only want to find out how far
415//   to leave the shape, we only want to consider intersections with surfaces in
416//   which the trajectory is leaving the shape. Since the normal vectors to the
417//   surface always point outwards from the inside, this means we want the dot
418//   product of the trajectory direction v and the normal of the side normals[i]
419//   to be positive. Thus, we should specify normSign as +1.0. Otherwise, if
420//   we are outside and want to go in, normSign should be set to -1.0.
421//   Don't set normSign to zero, or you will get no intersections!
422//
423// * surfTolerance: see notes on argument "surfTolerance" in routine
424//   "IntersectSidePlane".
425//   ----HOWEVER---- We should *not* apply this surface tolerance if the
426//   starting point is not within phi or z of the surface. Specifically,
427//   if the starting point p angle in x/y places it on a separate side from the
428//   intersection or if the starting point p is outside the z bounds of the
429//   segment, surfTolerance must be ignored or we should *always* accept the
430//   intersection!
431//   This is simply because the sides do not have infinite extent.
432//     
433//
434G4bool G4PolyhedraSide::Intersect( const G4ThreeVector &p,
435                                   const G4ThreeVector &v, 
436                                         G4bool outgoing,
437                                         G4double surfTolerance,
438                                         G4double &distance,
439                                         G4double &distFromSurface,
440                                         G4ThreeVector &normal,
441                                         G4bool &isAllBehind )
442{
443  G4double normSign = outgoing ? +1 : -1;
444 
445  //
446  // ------------------TO BE IMPLEMENTED---------------------
447  // Testing the intersection of individual phi faces is
448  // pretty straight forward. The simple thing therefore is to
449  // form a loop and check them all in sequence.
450  //
451  // But, I worry about one day someone making
452  // a polygon with a thousands sides. A linear search
453  // would not be ideal in such a case.
454  //
455  // So, it would be nice to be able to quickly decide
456  // which face would be intersected. One can make a very
457  // good guess by using the intersection with a cone.
458  // However, this is only reliable in 99% of the cases.
459  //
460  // My solution: make a decent guess as to the one or
461  // two potential faces might get intersected, and then
462  // test them. If we have the wrong face, use the test
463  // to make a better guess.
464  //
465  // Since we might have two guesses, form a queue of
466  // potential intersecting faces. Keep an array of
467  // already tested faces to avoid doing one more than
468  // once.
469  //
470  // Result: at worst, an iterative search. On average,
471  // a little more than two tests would be required.
472  //
473  G4ThreeVector q = p + v;
474 
475  G4int face = 0;
476  G4PolyhedraSideVec *vec = vecs;
477  do
478  {
479    //
480    // Correct normal?
481    //
482    G4double dotProd = normSign*v.dot(vec->normal);
483    if (dotProd <= 0) continue;
484 
485    //
486    // Is this face in front of the point along the trajectory?
487    //
488    G4ThreeVector delta = p - vec->center;
489    distFromSurface = -normSign*delta.dot(vec->normal);
490   
491    if (distFromSurface < -surfTolerance) continue;
492   
493    //
494    //                            phi
495    //      c -------- d           ^
496    //      |          |           |
497    //      a -------- b           +---> r/z
498    //
499    //
500    // Do we remain on this particular segment?
501    //
502    G4ThreeVector qc = q - vec->edges[1]->corner[0];
503    G4ThreeVector qd = q - vec->edges[1]->corner[1];
504   
505    if (normSign*qc.cross(qd).dot(v) < 0) continue;
506   
507    G4ThreeVector qa = q - vec->edges[0]->corner[0];
508    G4ThreeVector qb = q - vec->edges[0]->corner[1];
509   
510    if (normSign*qa.cross(qb).dot(v) > 0) continue;
511   
512    //
513    // We found the one and only segment we might be intersecting.
514    // Do we remain within r/z bounds?
515    //
516   
517    if (r[0] > 1/kInfinity && normSign*qa.cross(qc).dot(v) < 0) return false;
518    if (r[1] > 1/kInfinity && normSign*qb.cross(qd).dot(v) > 0) return false;
519   
520    //
521    // We allow the face to be slightly behind the trajectory
522    // (surface tolerance) only if the point p is within
523    // the vicinity of the face
524    //
525    if (distFromSurface < 0)
526    {
527      G4ThreeVector ps = p - vec->center; 
528     
529      G4double rz = ps.dot(vec->surfRZ);
530      if (std::fabs(rz) > lenRZ+surfTolerance) return false; 
531
532      G4double pp = ps.dot(vec->surfPhi);
533      if (std::fabs(pp) > lenPhi[0] + lenPhi[1]*rz + surfTolerance) return false;
534    }
535     
536
537    //
538    // Intersection found. Return answer.
539    //
540    distance = distFromSurface/dotProd;
541    normal = vec->normal;
542    isAllBehind = allBehind;
543    return true;
544  } while( ++vec, ++face < numSide );
545
546  //
547  // Oh well. Better luck next time.
548  //
549  return false;
550}
551
552
553G4double G4PolyhedraSide::Distance( const G4ThreeVector &p, G4bool outgoing )
554{
555  G4double normSign = outgoing ? -1 : +1;
556 
557  //
558  // Try the closest phi segment first
559  //
560  G4int iPhi = ClosestPhiSegment( p.phi() );
561 
562  G4ThreeVector pdotc = p - vecs[iPhi].center;
563  G4double normDist = pdotc.dot(vecs[iPhi].normal);
564 
565  if (normSign*normDist > -0.5*kCarTolerance)
566  {
567    return DistanceAway( p, vecs[iPhi], &normDist );
568  }
569
570  //
571  // Now we have an interesting problem... do we try to find the
572  // closest facing side??
573  //
574  // Considered carefully, the answer is no. We know that if we
575  // are asking for the distance out, we are supposed to be inside,
576  // and vice versa.
577  //
578 
579  return kInfinity;
580}
581
582
583//
584// Inside
585//
586EInside G4PolyhedraSide::Inside( const G4ThreeVector &p,
587                                       G4double tolerance, 
588                                       G4double *bestDistance )
589{
590  //
591  // Which phi segment is closest to this point?
592  //
593  G4int iPhi = ClosestPhiSegment( p.phi() );
594 
595  G4double norm;
596 
597  //
598  // Get distance to this segment
599  //
600  *bestDistance = DistanceToOneSide( p, vecs[iPhi], &norm );
601 
602  //
603  // Use distance along normal to decide return value
604  //
605  if ( (std::fabs(norm) < tolerance) && (*bestDistance < 2.0*tolerance) )
606    return kSurface;
607  else if (norm < 0)
608    return kInside;
609  else 
610    return kOutside;
611}
612
613
614//
615// Normal
616//
617G4ThreeVector G4PolyhedraSide::Normal( const G4ThreeVector &p,
618                                             G4double *bestDistance )
619{
620  //
621  // Which phi segment is closest to this point?
622  //
623  G4int iPhi = ClosestPhiSegment( p.phi() );
624
625  //
626  // Get distance to this segment
627  //
628  G4double norm;
629  *bestDistance = DistanceToOneSide( p, vecs[iPhi], &norm );
630
631  return vecs[iPhi].normal;
632}
633
634
635//
636// Extent
637//
638G4double G4PolyhedraSide::Extent( const G4ThreeVector axis )
639{
640  if (axis.perp2() < DBL_MIN)
641  {
642    //
643    // Special case
644    //
645    return axis.z() < 0 ? -cone->ZLo() : cone->ZHi();
646  }
647
648  G4int iPhi, i1, i2;
649  G4double best;
650  G4ThreeVector *list[4];
651 
652  //
653  // Which phi segment, if any, does the axis belong to
654  //
655  iPhi = PhiSegment( axis.phi() );
656 
657  if (iPhi < 0)
658  {
659    //
660    // No phi segment? Check front edge of first side and
661    // last edge of second side
662    //
663    i1 = 0; i2 = numSide-1;
664  }
665  else
666  {
667    //
668    // Check all corners of matching phi side
669    //
670    i1 = iPhi; i2 = iPhi;
671  }
672 
673  list[0] = vecs[i1].edges[0]->corner;
674  list[1] = vecs[i1].edges[0]->corner+1;
675  list[2] = vecs[i2].edges[1]->corner;
676  list[3] = vecs[i2].edges[1]->corner+1;
677       
678  //
679  // Who's biggest?
680  //
681  best = -kInfinity;
682  G4ThreeVector **vec = list;
683  do
684  {
685    G4double answer = (*vec)->dot(axis);
686    if (answer > best) best = answer;
687  } while( ++vec < list+4 );
688 
689  return best;
690}
691
692
693//
694// CalculateExtent
695//
696// See notes in G4VCSGface
697//
698void G4PolyhedraSide::CalculateExtent( const EAxis axis, 
699                                       const G4VoxelLimits &voxelLimit,
700                                       const G4AffineTransform &transform,
701                                             G4SolidExtentList &extentList )
702{
703  //
704  // Loop over all sides
705  //
706  G4PolyhedraSideVec *vec = vecs;
707  do
708  {
709    //
710    // Fill our polygon with the four corners of
711    // this side, after the specified transformation
712    //
713    G4ClippablePolygon polygon;
714   
715    polygon.AddVertexInOrder(transform.
716                             TransformPoint(vec->edges[0]->corner[0]));
717    polygon.AddVertexInOrder(transform.
718                             TransformPoint(vec->edges[0]->corner[1]));
719    polygon.AddVertexInOrder(transform.
720                             TransformPoint(vec->edges[1]->corner[1]));
721    polygon.AddVertexInOrder(transform.
722                             TransformPoint(vec->edges[1]->corner[0]));
723   
724    //
725    // Get extent
726    // 
727    if (polygon.PartialClip( voxelLimit, axis ))
728    {
729      //
730      // Get dot product of normal along target axis
731      //
732      polygon.SetNormal( transform.TransformAxis(vec->normal) );
733
734      extentList.AddSurface( polygon );
735    }
736  } while( ++vec < vecs+numSide );
737 
738  return;
739}
740
741
742//
743// IntersectSidePlane
744//
745// Decide if a line correctly intersects one side plane of our segment.
746// It is assumed that the correct side has been chosen, and thus only
747// the z bounds (of the entire segment) are checked.
748//
749// normSign - To be multiplied against normal:
750//            = +1.0 normal is unchanged
751//            = -1.0 normal is reversed (now points inward)
752//
753// Arguments:
754//  p    - (in) Point
755//  v    - (in) Direction
756//  vec    - (in) Description record of the side plane
757//  normSign  - (in) Sign (+/- 1) to apply to normal
758//  surfTolerance  - (in) Surface tolerance (generally > 0, see below)
759//  distance  - (out) Distance along v to intersection
760//  distFromSurface - (out) Distance from surface normal
761//
762// Notes:
763//   surfTolerance  - Used to decide if a point is behind the surface,
764//        a point is allow to be -surfTolerance behind the
765//        surface (as measured along the normal), but *only*
766//        if the point is within the r/z bounds + surfTolerance
767//        of the segment.
768//
769G4bool G4PolyhedraSide::IntersectSidePlane( const G4ThreeVector &p,
770                                            const G4ThreeVector &v,
771                                            const G4PolyhedraSideVec vec,
772                                                  G4double normSign, 
773                                                  G4double surfTolerance,
774                                                  G4double &distance,
775                                                  G4double &distFromSurface )
776{
777  //
778  // Correct normal? Here we have straight sides, and can safely ignore
779  // intersections where the dot product with the normal is zero.
780  //
781  G4double dotProd = normSign*v.dot(vec.normal);
782 
783  if (dotProd <= 0) return false;
784 
785  //
786  // Calculate distance to surface. If the side is too far
787  // behind the point, we must reject it.
788  //
789  G4ThreeVector delta = p - vec.center;
790  distFromSurface = -normSign*delta.dot(vec.normal);
791   
792  if (distFromSurface < -surfTolerance) return false;
793
794  //
795  // Calculate precise distance to intersection with the side
796  // (along the trajectory, not normal to the surface)
797  //
798  distance = distFromSurface/dotProd;
799 
800  //
801  // Do we fall off the r/z extent of the segment?
802  //
803  // Calculate this very, very carefully! Why?
804  //         1. If a RZ end is at R=0, you can't miss!
805  //         2. If you just fall off in RZ, the answer must
806  //            be consistent with adjacent G4PolyhedraSide faces.
807  // (2) implies that only variables used by other G4PolyhedraSide
808  // faces may be used, which includes only: p, v, and the edge corners.
809  // It also means that one side is a ">" or "<", which the other
810  // must be ">=" or "<=". Fortunately, this isn't a new problem.
811  // The solution below I borrowed from Joseph O'Rourke,
812  // "Computational Geometry in C (Second Edition)"
813  // See: http://cs.smith.edu/~orourke/
814  //
815  G4ThreeVector ic = p + distance*v - vec.center;
816  G4double atRZ = vec.surfRZ.dot(ic);
817 
818  if (atRZ < 0)
819  {
820    if (r[0]==0) return true;    // Can't miss!
821   
822    if (atRZ < -lenRZ*1.2) return false;  // Forget it! Missed by a mile.
823   
824    G4ThreeVector q = p + v;   
825    G4ThreeVector qa = q - vec.edges[0]->corner[0],
826                  qb = q - vec.edges[1]->corner[0];
827    G4ThreeVector qacb = qa.cross(qb);
828    if (normSign*qacb.dot(v) < 0) return false;
829   
830    if (distFromSurface < 0)
831    {
832      if (atRZ < -lenRZ-surfTolerance) return false;
833    }
834  }
835  else if (atRZ > 0)
836  {
837    if (r[1]==0) return true;    // Can't miss!
838   
839    if (atRZ > lenRZ*1.2) return false;  // Missed by a mile
840   
841    G4ThreeVector q = p + v;   
842    G4ThreeVector qa = q - vec.edges[0]->corner[1],
843                  qb = q - vec.edges[1]->corner[1];
844    G4ThreeVector qacb = qa.cross(qb);
845    if (normSign*qacb.dot(v) >= 0) return false;
846   
847    if (distFromSurface < 0)
848    {
849      if (atRZ > lenRZ+surfTolerance) return false;
850    }
851  }
852
853  return true;
854}
855
856
857//
858// LineHitsSegments
859//
860// Calculate which phi segments a line intersects in three dimensions.
861// No check is made as to whether the intersections are within the z bounds of
862// the segment.
863//
864G4int G4PolyhedraSide::LineHitsSegments( const G4ThreeVector &p,
865                                         const G4ThreeVector &v,
866                                               G4int *i1, G4int *i2 )
867{
868  G4double s1, s2;
869  //
870  // First, decide if and where the line intersects the cone
871  //
872  G4int n = cone->LineHitsCone( p, v, &s1, &s2 );
873 
874  if (n==0) return 0;
875 
876  //
877  // Try first intersection.
878  //
879  *i1 = PhiSegment( std::atan2( p.y() + s1*v.y(), p.x() + s1*v.x() ) );
880  if (n==1)
881  {
882    return (*i1 < 0) ? 0 : 1;
883  }
884 
885  //
886  // Try second intersection
887  //
888  *i2 = PhiSegment( std::atan2( p.y() + s2*v.y(), p.x() + s2*v.x() ) );
889  if (*i1 == *i2) return 0;
890 
891  if (*i1 < 0)
892  {
893    if (*i2 < 0) return 0;
894    *i1 = *i2;
895    return 1;
896  }
897
898  if (*i2 < 0) return 1;
899 
900  return 2;
901}
902
903
904//
905// ClosestPhiSegment
906//
907// Decide which phi segment is closest in phi to the point.
908// The result is the same as PhiSegment if there is no phi opening.
909//
910G4int G4PolyhedraSide::ClosestPhiSegment( G4double phi0 )
911{
912  G4int iPhi = PhiSegment( phi0 );
913  if (iPhi >= 0) return iPhi;
914 
915  //
916  // Boogers! The points falls inside the phi segment.
917  // Look for the closest point: the start, or  end
918  //
919  G4double phi = phi0;
920 
921  while( phi < startPhi ) phi += twopi;
922  G4double d1 = phi-endPhi;
923
924  while( phi > startPhi ) phi -= twopi;
925  G4double d2 = startPhi-phi;
926 
927  return (d2 < d1) ? 0 : numSide-1;
928}
929
930
931//
932// PhiSegment
933//
934// Decide which phi segment an angle belongs to, counting from zero.
935// A value of -1 indicates that the phi value is outside the shape
936// (only possible if phiTotal < 360 degrees).
937//
938G4int G4PolyhedraSide::PhiSegment( G4double phi0 )
939{
940  //
941  // How far are we from phiStart? Come up with a positive answer
942  // that is less than 2*PI
943  //
944  G4double phi = phi0 - startPhi;
945  while( phi < 0      ) phi += twopi;
946  while( phi > twopi ) phi -= twopi;
947
948  //
949  // Divide
950  //
951  G4int answer = (G4int)(phi/deltaPhi);
952 
953  if (answer >= numSide)
954  {
955    if (phiIsOpen)
956    {
957      return -1;  // Looks like we missed
958    }
959    else
960    {
961      answer = numSide-1;  // Probably just roundoff
962    }
963  }
964 
965  return answer;
966}
967
968
969//
970// DistanceToOneSide
971//
972// Arguments:
973//  p   - (in) Point to check
974//  vec   - (in) vector set of this side
975//  normDist - (out) distance normal to the side or edge, as appropriate, signed
976// Return value = total distance from the side
977//
978G4double G4PolyhedraSide::DistanceToOneSide( const G4ThreeVector &p,
979                                             const G4PolyhedraSideVec &vec,
980                                                   G4double *normDist )
981{
982  G4ThreeVector pc = p - vec.center;
983 
984  //
985  // Get normal distance
986  //
987  *normDist = vec.normal.dot(pc);
988
989  //
990  // Add edge penalty
991  //
992  return DistanceAway( p, vec, normDist );
993}
994
995
996//
997// DistanceAway
998//
999// Add distance from side edges, if necesssary, to total distance,
1000// and updates normDist appropriate depending on edge normals.
1001//
1002G4double G4PolyhedraSide::DistanceAway( const G4ThreeVector &p,
1003                                        const G4PolyhedraSideVec &vec,
1004                                              G4double *normDist )
1005{
1006  G4double distOut2;
1007  G4ThreeVector pc = p - vec.center;
1008  G4double distFaceNorm = *normDist;
1009 
1010  //
1011  // Okay, are we inside bounds?
1012  //
1013  G4double pcDotRZ  = pc.dot(vec.surfRZ);
1014  G4double pcDotPhi = pc.dot(vec.surfPhi);
1015 
1016  //
1017  // Go through all permutations.
1018  //                                                   Phi
1019  //               |              |                     ^
1020  //           B   |      H       |   E                 |
1021  //        ------[1]------------[3]-----               |
1022  //               |XXXXXXXXXXXXXX|                     +----> RZ
1023  //           C   |XXXXXXXXXXXXXX|   F
1024  //               |XXXXXXXXXXXXXX|
1025  //        ------[0]------------[2]----
1026  //           A   |      G       |   D
1027  //               |              |
1028  //
1029  // It's real messy, but at least it's quick
1030  //
1031 
1032  if (pcDotRZ < -lenRZ)
1033  {
1034    G4double lenPhiZ = lenPhi[0] - lenRZ*lenPhi[1];
1035    G4double distOutZ = pcDotRZ+lenRZ;
1036    //
1037    // Below in RZ
1038    //
1039    if (pcDotPhi < -lenPhiZ)
1040    {
1041      //
1042      // ...and below in phi. Find distance to point (A)
1043      //
1044      G4double distOutPhi = pcDotPhi+lenPhiZ;
1045      distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
1046      G4ThreeVector pa = p - vec.edges[0]->corner[0];
1047      *normDist = pa.dot(vec.edges[0]->cornNorm[0]);
1048    }
1049    else if (pcDotPhi > lenPhiZ)
1050    {
1051      //
1052      // ...and above in phi. Find distance to point (B)
1053      //
1054      G4double distOutPhi = pcDotPhi-lenPhiZ;
1055      distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
1056      G4ThreeVector pb = p - vec.edges[1]->corner[0];
1057      *normDist = pb.dot(vec.edges[1]->cornNorm[0]);
1058    }
1059    else
1060    {
1061      //
1062      // ...and inside in phi. Find distance to line (C)
1063      //
1064      G4ThreeVector pa = p - vec.edges[0]->corner[0];
1065      distOut2 = distOutZ*distOutZ;
1066      *normDist = pa.dot(vec.edgeNorm[0]);
1067    }
1068  }
1069  else if (pcDotRZ > lenRZ)
1070  {
1071    G4double lenPhiZ = lenPhi[0] + lenRZ*lenPhi[1];
1072    G4double distOutZ = pcDotRZ-lenRZ;
1073    //
1074    // Above in RZ
1075    //
1076    if (pcDotPhi < -lenPhiZ)
1077    {
1078      //
1079      // ...and below in phi. Find distance to point (D)
1080      //
1081      G4double distOutPhi = pcDotPhi+lenPhiZ;
1082      distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
1083      G4ThreeVector pd = p - vec.edges[0]->corner[1];
1084      *normDist = pd.dot(vec.edges[0]->cornNorm[1]);
1085    }
1086    else if (pcDotPhi > lenPhiZ)
1087    {
1088      //
1089      // ...and above in phi. Find distance to point (E)
1090      //
1091      G4double distOutPhi = pcDotPhi-lenPhiZ;
1092      distOut2 = distOutPhi*distOutPhi + distOutZ*distOutZ;
1093      G4ThreeVector pe = p - vec.edges[1]->corner[1];
1094      *normDist = pe.dot(vec.edges[1]->cornNorm[1]);
1095    }
1096    else
1097    {
1098      //
1099      // ...and inside in phi. Find distance to line (F)
1100      //
1101      distOut2 = distOutZ*distOutZ;
1102      G4ThreeVector pd = p - vec.edges[0]->corner[1];
1103      *normDist = pd.dot(vec.edgeNorm[1]);
1104    }
1105  }
1106  else
1107  {
1108    G4double lenPhiZ = lenPhi[0] + pcDotRZ*lenPhi[1];
1109    //
1110    // We are inside RZ bounds
1111    //
1112    if (pcDotPhi < -lenPhiZ)
1113    {
1114      //
1115      // ...and below in phi. Find distance to line (G)
1116      //
1117      G4double distOut = edgeNorm*(pcDotPhi+lenPhiZ);
1118      distOut2 = distOut*distOut;
1119      G4ThreeVector pd = p - vec.edges[0]->corner[1];
1120      *normDist = pd.dot(vec.edges[0]->normal);
1121    }
1122    else if (pcDotPhi > lenPhiZ)
1123    {
1124      //
1125      // ...and above in phi. Find distance to line (H)
1126      //
1127      G4double distOut = edgeNorm*(pcDotPhi-lenPhiZ);
1128      distOut2 = distOut*distOut;
1129      G4ThreeVector pe = p - vec.edges[1]->corner[1];
1130      *normDist = pe.dot(vec.edges[1]->normal);
1131    }
1132    else
1133    {
1134      //
1135      // Inside bounds! No penalty.
1136      //
1137      return std::fabs(distFaceNorm);
1138    }
1139  }
1140  return std::sqrt( distFaceNorm*distFaceNorm + distOut2 );
1141}
Note: See TracBrowser for help on using the repository browser.