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