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

Last change on this file since 834 was 831, checked in by garnier, 17 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.