source: trunk/source/geometry/solids/BREPS/src/G4BREPSolid.cc

Last change on this file was 1337, checked in by garnier, 15 years ago

tag geant4.9.4 beta 1 + modifs locales

File size: 38.3 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: G4BREPSolid.cc,v 1.37 2008/03/13 14:18:57 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-04-beta-01 $
29//
30// ----------------------------------------------------------------------
31// GEANT 4 class source file
32//
33// G4BREPSolid.cc
34//
35// ----------------------------------------------------------------------
36
37#include "G4BREPSolid.hh"
38#include "G4VoxelLimits.hh"
39#include "G4AffineTransform.hh"
40#include "G4VGraphicsScene.hh"
41#include "G4Polyhedron.hh"
42#include "G4NURBSbox.hh"
43#include "G4BoundingBox3D.hh"
44#include "G4FPlane.hh"
45#include "G4BSplineSurface.hh"
46#include "G4ToroidalSurface.hh"
47#include "G4SphericalSurface.hh"
48
49G4Ray G4BREPSolid::Track;
50G4double G4BREPSolid::ShortestDistance= kInfinity;
51G4int G4BREPSolid::NumberOfSolids=0;
52
53G4BREPSolid::G4BREPSolid(const G4String& name)
54 : G4VSolid(name),
55 Box(0), Convex(0), AxisBox(0), PlaneSolid(0), place(0), bbox(0),
56 intersectionDistance(kInfinity), active(1), startInside(0),
57 nb_of_surfaces(0), SurfaceVec(0), solidname(name),
58 fStatistics(1000000), fCubVolEpsilon(0.001), fAreaAccuracy(-1.),
59 fCubicVolume(0.), fSurfaceArea(0.), fpPolyhedron(0)
60{
61}
62
63G4BREPSolid::G4BREPSolid( const G4String& name ,
64 G4Surface** srfVec ,
65 G4int numberOfSrfs )
66 : G4VSolid(name),
67 Box(0), Convex(0), AxisBox(0), PlaneSolid(0), place(0), bbox(0),
68 intersectionDistance(kInfinity), active(1), startInside(0),
69 nb_of_surfaces(numberOfSrfs), SurfaceVec(srfVec),
70 fStatistics(1000000), fCubVolEpsilon(0.001), fAreaAccuracy(-1.),
71 fCubicVolume(0.), fSurfaceArea(0.), fpPolyhedron(0)
72{
73 Initialize();
74}
75
76G4BREPSolid::G4BREPSolid( __void__& a )
77 : G4VSolid(a),
78 Box(0), Convex(0), AxisBox(0), PlaneSolid(0), place(0), bbox(0),
79 intersectionDistance(kInfinity), active(1), startInside(0),
80 nb_of_surfaces(0), SurfaceVec(0),
81 fStatistics(1000000), fCubVolEpsilon(0.001), fAreaAccuracy(-1.),
82 fCubicVolume(0.), fSurfaceArea(0.), fpPolyhedron(0)
83{
84}
85
86G4BREPSolid::~G4BREPSolid()
87{
88 if(place)
89 delete place;
90
91 if(bbox)
92 delete bbox;
93
94 for(G4int a=0;a<nb_of_surfaces;a++)
95 delete SurfaceVec[a];
96
97 if( nb_of_surfaces > 0 && SurfaceVec != 0 )
98 delete [] SurfaceVec;
99
100 delete fpPolyhedron;
101}
102
103void G4BREPSolid::Initialize()
104{
105 if(active)
106 {
107 // Compute bounding box for solids and surfaces
108 // Convert concave planes to convex
109 //
110 ShortestDistance= kInfinity;
111 IsBox();
112 CheckSurfaceNormals();
113
114 if(!Box || !AxisBox)
115 IsConvex();
116
117 CalcBBoxes();
118 }
119}
120
121G4String G4BREPSolid::GetEntityType() const
122{
123 return "Closed_Shell";
124}
125
126void G4BREPSolid::Reset() const
127{
128 ((G4BREPSolid*)this)->active=1;
129 ((G4BREPSolid*)this)->intersectionDistance=kInfinity;
130 ((G4BREPSolid*)this)->startInside=0;
131
132 for(register G4int a=0;a<nb_of_surfaces;a++)
133 SurfaceVec[a]->Reset();
134
135 ShortestDistance = kInfinity;
136}
137
138void G4BREPSolid::CheckSurfaceNormals()
139{
140 if(!PlaneSolid)
141 return; // All faces must be planar
142
143 Convex=1;
144
145 // Checks that the normals of the surfaces point outwards.
146 // If not, turns the Normal to point out.
147
148 // Loop through each face and check the G4Vector3D of the Normal
149 //
150 G4Surface* srf;
151 G4Point3D V;
152
153 G4int PointNum=0;
154 G4int SrfNum = 0;
155 G4double YValue=0;
156 G4Point3D Pt;
157
158 G4int a, b;
159 for(a=0; a<nb_of_surfaces; a++)
160 {
161 // Find vertex point containing extreme y value
162 //
163 srf = SurfaceVec[a];
164 G4int Points = srf->GetNumberOfPoints();
165
166 for(b =0; b<Points; b++)
167 {
168 Pt = (G4Point3D)srf->GetPoint(b);
169 if(YValue < Pt.y())
170 {
171 YValue = Pt.y();
172 PointNum = b; // Save point number
173 SrfNum = a; // Save srf number
174 }
175 }
176 }
177
178 // Move the selected face to the first in the List
179 //
180 srf = SurfaceVec[SrfNum];
181
182 // Start handling the surfaces in order and compare
183 // the neighbouring ones and turn their normals if they
184 // point inwards
185 //
186 G4Point3D Pt1;
187 G4Point3D Pt2;
188 G4Point3D Pt3;
189 G4Point3D Pt4;
190
191 G4Vector3D N1;
192 G4Vector3D N2;
193 G4Vector3D N3;
194 G4Vector3D N4;
195
196 G4int* ConnectedList = new G4int[nb_of_surfaces];
197
198 for(a=0; a<nb_of_surfaces; a++)
199 ConnectedList[a]=0;
200
201 G4Surface* ConnectedSrf;
202
203 for(a=0; a<nb_of_surfaces-1; a++)
204 {
205 if(ConnectedList[a] == 0)
206 break;
207 else
208 ConnectedList[a]=1;
209
210 srf = SurfaceVec[a];
211 G4int SrfPoints = srf->GetNumberOfPoints();
212 N1 = (srf->Norm())->GetDir();
213
214 for(b=a+1; b<nb_of_surfaces; b++)
215 {
216 if(ConnectedList[b] == 1)
217 break;
218 else
219 ConnectedList[b]=1;
220
221 // Get next in List
222 //
223 ConnectedSrf = SurfaceVec[b];
224
225 // Check if it is connected to srf by looping through the points.
226 //
227 G4int ConnSrfPoints = ConnectedSrf->GetNumberOfPoints();
228
229 for(G4int c=0;c<SrfPoints;c++)
230 {
231 Pt1 = srf->GetPoint(c);
232
233 for(G4int d=0;d<ConnSrfPoints;d++)
234 {
235 // Find common points
236 //
237 Pt2 = (ConnectedSrf)->GetPoint(d);
238
239 if( Pt1 == Pt2 )
240 {
241 // Common point found. Compare normals.
242 //
243 N2 = ((ConnectedSrf)->Norm())->GetDir();
244
245 // Check cross product.
246 //
247 G4Vector3D CP1 = G4Vector3D( N1.cross(N2) );
248 G4double CrossProd1 = CP1.x()+CP1.y()+CP1.z();
249
250 // Create the other normals
251 //
252 if(c==0)
253 Pt3 = srf->GetPoint(c+1);
254 else
255 Pt3 = srf->GetPoint(0);
256
257 N3 = (Pt1-Pt3);
258
259 if(d==0)
260 Pt4 = (ConnectedSrf)->GetPoint(d+1);
261 else
262 Pt4 = (ConnectedSrf)->GetPoint(0);
263
264 N4 = (Pt1-Pt4);
265
266 G4Vector3D CP2 = G4Vector3D( N3.cross(N4) );
267 G4double CrossProd2 = CP2.x()+CP2.y()+CP2.z();
268
269 G4cout << "\nCroosProd2: " << CrossProd2;
270
271 if( (CrossProd1 < 0 && CrossProd2 < 0) ||
272 (CrossProd1 > 0 && CrossProd2 > 0) )
273 {
274 // Turn Normal
275 //
276 (ConnectedSrf)->Norm()
277 ->SetDir(-1 * (ConnectedSrf)->Norm()->GetDir());
278
279 // Take the CrossProd1 again as the other Normal was turned.
280 //
281 CP1 = N1.cross(N2);
282 CrossProd1 = CP1.x()+CP1.y()+CP1.z();
283 }
284 if(CrossProd1 > 0)
285 Convex=0;
286 }
287 }
288 }
289 }
290 }
291
292 delete []ConnectedList;
293}
294
295G4int G4BREPSolid::IsBox()
296{
297 // This is done by checking that the solid consists of 6 planes.
298 // Then the type is checked to be planar face by face.
299 // For each G4Plane the Normal is computed. The dot product
300 // of one face Normal and each other face Normal is computed.
301 // One result should be 1 and the rest 0 in order to the solid
302 // to be a box.
303
304 Box=0;
305 G4Surface* srf1, *srf2;
306 register G4int a;
307
308 // Compute the Normal for the planes
309 //
310 for(a=0; a < nb_of_surfaces;a++)
311 {
312 srf1 = SurfaceVec[a];
313
314 if(srf1->MyType()==1)
315 (srf1)->Project(); // Compute the projection
316 else
317 {
318 PlaneSolid=0;
319 return 0;
320 }
321 }
322
323 // Check that all faces are planar
324 //
325 for(a=0; a < nb_of_surfaces;a++)
326 {
327 srf1 = SurfaceVec[a];
328
329 if (srf1->MyType()!=1)
330 return 0;
331 }
332
333 PlaneSolid = 1;
334
335 // Check that the amount of faces is correct
336 //
337 if(nb_of_surfaces!=6) return 0;
338
339 G4Point3D Pt;
340 G4int Points;
341 G4int Sides=0;
342 G4int Opposite=0;
343
344 srf1 = SurfaceVec[0];
345 Points = (srf1)->GetNumberOfPoints();
346
347 if(Points!=4)
348 return 0;
349
350 G4Vector3D Normal1 = (srf1->Norm())->GetDir();
351 G4double Result;
352
353 for(G4int b=1; b < nb_of_surfaces;b++)
354 {
355 srf2 = SurfaceVec[b];
356 G4Vector3D Normal2 = ((srf2)->Norm())->GetDir();
357 Result = std::fabs(Normal1 * Normal2);
358
359 if((Result != 0) && (Result != 1))
360 return 0;
361 else
362 {
363 if(!(G4int)Result)
364 Sides++;
365 else
366 if(((G4int)Result) == 1)
367 Opposite++;
368 }
369 }
370
371 if((Opposite != 1) && (Sides != nb_of_surfaces-2))
372 return 0;
373
374 G4Vector3D x_axis(1,0,0);
375 G4Vector3D y_axis(0,1,0);
376
377 if(((std::fabs(x_axis * Normal1) == 1) && (std::fabs(y_axis * Normal1) == 0)) ||
378 ((std::fabs(x_axis * Normal1) == 0) && (std::fabs(y_axis * Normal1) == 1)) ||
379 ((std::fabs(x_axis * Normal1) == 0) && (std::fabs(y_axis * Normal1) == 0)))
380 AxisBox=1;
381 else
382 Box=1;
383
384 return 1;
385}
386
387G4bool G4BREPSolid::IsConvex()
388{
389 if(!PlaneSolid)
390 return 0; // All faces must be planar
391
392 // This is not robust. There can be concave solids
393 // where the concavity comes for example from three triangles.
394
395 // Additional checking 20.8. For each face the connecting faces are
396 // found and the cross product computed between the face and each
397 // connecting face. If the result changes value at any point the
398 // solid is concave.
399
400 G4Surface* Srf;
401 G4Surface* ConnectedSrf;
402 G4int Result;
403 Convex = 1;
404
405 G4int a, b, c, d;
406 for(a=0;a<nb_of_surfaces;a++)
407 {
408 Srf = SurfaceVec[a];
409
410 // Primary test. Test wether any one of the faces
411 // is concave -> solid is concave. This is not enough to
412 // distinguish all the cases of concavity.
413 //
414 Result = Srf->IsConvex();
415
416 if(Result != -1)
417 {
418 Convex = 0;
419 return 0;
420 }
421 }
422
423 Srf = SurfaceVec[0];
424 G4Point3D Pt1;
425 G4Point3D Pt2;
426
427 G4int ConnectingPoints=0;
428
429 G4Vector3D N1;
430 G4Vector3D N2;
431
432 // L. Broglia
433 // The number of connecting points can be
434 // (nb_of_surfaces-1) * nb_of_surfaces (loop a & loop b)
435
436 // G4int* ConnectedList = new G4int[nb_of_surfaces];
437 G4int* ConnectedList = new G4int[(nb_of_surfaces-1) * nb_of_surfaces];
438
439 for(a=0; a<nb_of_surfaces; a++)
440 {
441 ConnectedList[a]=0;
442 }
443
444 G4int Connections=0;
445
446 for(a=0; a<nb_of_surfaces-1; a++)
447 {
448 Srf = SurfaceVec[a];
449 G4int SrfPoints = Srf->GetNumberOfPoints();
450 Result=0;
451
452 for(b=0; b<nb_of_surfaces; b++)
453 {
454 if(b==a)
455 b++;
456
457 if(b==nb_of_surfaces)
458 break;
459
460 // Get next in List
461 //
462 ConnectedSrf = SurfaceVec[b];
463
464 // Check if it is connected to Srf by looping through the points.
465 //
466 G4int ConnSrfPoints = ConnectedSrf->GetNumberOfPoints();
467
468 for(c=0; c<SrfPoints; c++)
469 {
470 const G4Point3D& Pts1 =Srf->GetPoint(c);
471
472 for(d=0; d<ConnSrfPoints; d++)
473 {
474 // Find common points
475 //
476 const G4Point3D& Pts2 = ConnectedSrf->GetPoint(d);
477 if(Pts1 == Pts2)
478 ConnectingPoints++;
479 }
480 if(ConnectingPoints > 0)
481 break;
482 }
483
484 if( ConnectingPoints > 0 )
485 {
486 Connections++;
487 ConnectedList[Connections]=b;
488 }
489 ConnectingPoints=0;
490 }
491 }
492
493 // If connected, check for concavity.
494 // Get surfaces from ConnectedList and compare their normals
495 //
496 for(c=0; c<Connections; c++)
497 {
498 G4int Left=0;
499 G4int Right =0;
500 G4int tmp = ConnectedList[c];
501
502 Srf = SurfaceVec[tmp];
503 ConnectedSrf = SurfaceVec[tmp+1];
504
505 // Get normals
506 //
507 N1 = Srf->Norm()->GetDir();
508 N2 = ConnectedSrf->Norm()->GetDir();
509
510 // Check cross product
511 //
512 G4Vector3D CP = G4Vector3D( N1.cross(N2) );
513 G4double CrossProd = CP.x()+CP.y()+CP.z();
514 if( CrossProd > 0 )
515 Left++;
516 if(CrossProd < 0)
517 Right++;
518 if(Left&&Right)
519 {
520 Convex = 0;
521 return 0;
522 }
523 Connections=0;
524 }
525
526 Convex=1;
527
528 // L. Broglia
529 // Problems with this delete when there are many solids to create
530 // delete [] ConnectedList;
531
532 return 1;
533}
534
535G4bool G4BREPSolid::CalculateExtent(const EAxis pAxis,
536 const G4VoxelLimits& pVoxelLimit,
537 const G4AffineTransform& pTransform,
538 G4double& pMin, G4double& pMax) const
539{
540 G4Point3D Min = bbox->GetBoxMin();
541 G4Point3D Max = bbox->GetBoxMax();
542
543 if (!pTransform.IsRotated())
544 {
545 // Special case handling for unrotated boxes
546 // Compute x/y/z mins and maxs respecting limits, with early returns
547 // if outside limits. Then switch() on pAxis
548 //
549 G4double xoffset,xMin,xMax;
550 G4double yoffset,yMin,yMax;
551 G4double zoffset,zMin,zMax;
552
553 xoffset=pTransform.NetTranslation().x();
554 xMin=xoffset+Min.x();
555 xMax=xoffset+Max.x();
556 if (pVoxelLimit.IsXLimited())
557 {
558 if (xMin>pVoxelLimit.GetMaxXExtent()
559 ||xMax<pVoxelLimit.GetMinXExtent())
560 {
561 return false;
562 }
563 else
564 {
565 if (xMin<pVoxelLimit.GetMinXExtent())
566 {
567 xMin=pVoxelLimit.GetMinXExtent();
568 }
569 if (xMax>pVoxelLimit.GetMaxXExtent())
570 {
571 xMax=pVoxelLimit.GetMaxXExtent();
572 }
573 }
574 }
575
576 yoffset=pTransform.NetTranslation().y();
577 yMin=yoffset+Min.y();
578 yMax=yoffset+Max.y();
579 if (pVoxelLimit.IsYLimited())
580 {
581 if (yMin>pVoxelLimit.GetMaxYExtent()
582 ||yMax<pVoxelLimit.GetMinYExtent())
583 {
584 return false;
585 }
586 else
587 {
588 if (yMin<pVoxelLimit.GetMinYExtent())
589 {
590 yMin=pVoxelLimit.GetMinYExtent();
591 }
592 if (yMax>pVoxelLimit.GetMaxYExtent())
593 {
594 yMax=pVoxelLimit.GetMaxYExtent();
595 }
596 }
597 }
598
599 zoffset=pTransform.NetTranslation().z();
600 zMin=zoffset+Min.z();
601 zMax=zoffset+Max.z();
602 if (pVoxelLimit.IsZLimited())
603 {
604 if (zMin>pVoxelLimit.GetMaxZExtent()
605 ||zMax<pVoxelLimit.GetMinZExtent())
606 {
607 return false;
608 }
609 else
610 {
611 if (zMin<pVoxelLimit.GetMinZExtent())
612 {
613 zMin=pVoxelLimit.GetMinZExtent();
614 }
615 if (zMax>pVoxelLimit.GetMaxZExtent())
616 {
617 zMax=pVoxelLimit.GetMaxZExtent();
618 }
619 }
620 }
621
622 switch (pAxis)
623 {
624 case kXAxis:
625 pMin=xMin;
626 pMax=xMax;
627 break;
628 case kYAxis:
629 pMin=yMin;
630 pMax=yMax;
631 break;
632 case kZAxis:
633 pMin=zMin;
634 pMax=zMax;
635 break;
636 default:
637 break;
638 }
639 pMin-=kCarTolerance;
640 pMax+=kCarTolerance;
641
642 return true;
643 }
644 else
645 {
646 // General rotated case - create and clip mesh to boundaries
647
648 G4bool existsAfterClip=false;
649 G4ThreeVectorList *vertices;
650
651 pMin=+kInfinity;
652 pMax=-kInfinity;
653
654 // Calculate rotated vertex coordinates
655 //
656 vertices=CreateRotatedVertices(pTransform);
657 ClipCrossSection(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
658 ClipCrossSection(vertices,4,pVoxelLimit,pAxis,pMin,pMax);
659 ClipBetweenSections(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
660
661 if ( (pMin!=kInfinity) || (pMax!=-kInfinity) )
662 {
663 existsAfterClip=true;
664
665 // Add 2*tolerance to avoid precision troubles
666 //
667 pMin-=kCarTolerance;
668 pMax+=kCarTolerance;
669 }
670 else
671 {
672 // Check for case where completely enveloping clipping volume.
673 // If point inside then we are confident that the solid completely
674 // envelopes the clipping volume. Hence set min/max extents according
675 // to clipping volume extents along the specified axis.
676 //
677 G4ThreeVector clipCentre(
678 (pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
679 (pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
680 (pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5);
681
682 if (Inside(pTransform.Inverse().TransformPoint(clipCentre))!=kOutside)
683 {
684 existsAfterClip=true;
685 pMin=pVoxelLimit.GetMinExtent(pAxis);
686 pMax=pVoxelLimit.GetMaxExtent(pAxis);
687 }
688 }
689 delete vertices;
690 return existsAfterClip;
691 }
692}
693
694G4ThreeVectorList*
695G4BREPSolid::CreateRotatedVertices(const G4AffineTransform& pTransform) const
696{
697 G4Point3D Min = bbox->GetBoxMin();
698 G4Point3D Max = bbox->GetBoxMax();
699
700 G4ThreeVectorList *vertices;
701 vertices=new G4ThreeVectorList();
702 vertices->reserve(8);
703
704 if (vertices)
705 {
706 G4ThreeVector vertex0(Min.x(),Min.y(),Min.z());
707 G4ThreeVector vertex1(Max.x(),Min.y(),Min.z());
708 G4ThreeVector vertex2(Max.x(),Max.y(),Min.z());
709 G4ThreeVector vertex3(Min.x(),Max.y(),Min.z());
710 G4ThreeVector vertex4(Min.x(),Min.y(),Max.z());
711 G4ThreeVector vertex5(Max.x(),Min.y(),Max.z());
712 G4ThreeVector vertex6(Max.x(),Max.y(),Max.z());
713 G4ThreeVector vertex7(Min.x(),Max.y(),Max.z());
714
715 vertices->push_back(pTransform.TransformPoint(vertex0));
716 vertices->push_back(pTransform.TransformPoint(vertex1));
717 vertices->push_back(pTransform.TransformPoint(vertex2));
718 vertices->push_back(pTransform.TransformPoint(vertex3));
719 vertices->push_back(pTransform.TransformPoint(vertex4));
720 vertices->push_back(pTransform.TransformPoint(vertex5));
721 vertices->push_back(pTransform.TransformPoint(vertex6));
722 vertices->push_back(pTransform.TransformPoint(vertex7));
723 }
724 else
725 {
726 G4Exception("G4BREPSolid::CreateRotatedVertices()", "FatalError",
727 FatalException, "Out of memory - Cannot allocate vertices!");
728 }
729 return vertices;
730}
731
732EInside G4BREPSolid::Inside(register const G4ThreeVector& Pt) const
733{
734 // This function finds if the point Pt is inside,
735 // outside or on the surface of the solid
736
737 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
738
739 G4Vector3D v(1, 0, 0.01);
740 G4Vector3D Pttmp(Pt);
741 G4Vector3D Vtmp(v);
742 G4Ray r(Pttmp, Vtmp);
743
744 // Check if point is inside the PCone bounding box
745 //
746 if( !GetBBox()->Inside(Pttmp) )
747 return kOutside;
748
749 // Set the surfaces to active again
750 //
751 Reset();
752
753 // Test if the bounding box of each surface is intersected
754 // by the ray. If not, the surface become deactive.
755 //
756 TestSurfaceBBoxes(r);
757
758 G4int hits=0, samehit=0;
759
760 for(G4int a=0; a < nb_of_surfaces; a++)
761 {
762 if(SurfaceVec[a]->IsActive())
763 {
764 // Count the number of intersections. If this number is odd,
765 // the start of the ray is inside the volume bounded by the surfaces,
766 // so increment the number of intersection by 1 if the point is not
767 // on the surface and if this intersection was not found before.
768 //
769 if( (SurfaceVec[a]->Intersect(r)) & 1 )
770 {
771 // Test if the point is on the surface
772 //
773 if(SurfaceVec[a]->GetDistance() < sqrHalfTolerance)
774 return kSurface;
775
776 // Test if this intersection was found before
777 //
778 for(G4int i=0; i<a; i++)
779 if(SurfaceVec[a]->GetDistance() == SurfaceVec[i]->GetDistance())
780 {
781 samehit++;
782 break;
783 }
784
785 // Count the number of surfaces intersected by the ray
786 //
787 if(!samehit)
788 hits++;
789 }
790 }
791 }
792
793 // If the number of surfaces intersected is odd,
794 // the point is inside the solid
795 //
796 if(hits&1)
797 return kInside;
798 else
799 return kOutside;
800}
801
802G4ThreeVector G4BREPSolid::SurfaceNormal(const G4ThreeVector& Pt) const
803{
804 // This function calculates the normal of the surface at a point on the
805 // surface. If the point is not on the surface the result is undefined.
806 // Note : the sense of the normal depends on the sense of the surface.
807
808 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
809 G4int iplane;
810
811 // Find on which surface the point is
812 //
813 for(iplane = 0; iplane < nb_of_surfaces; iplane++)
814 {
815 if(SurfaceVec[iplane]->HowNear(Pt) < sqrHalfTolerance)
816 // the point is on this surface
817 break;
818 }
819
820 // Calculate the normal at this point
821 //
822 G4ThreeVector norm = SurfaceVec[iplane]->SurfaceNormal(Pt);
823
824 return norm.unit();
825}
826
827G4double G4BREPSolid::DistanceToIn(const G4ThreeVector& Pt) const
828{
829 // Calculates the shortest distance ("safety") from a point
830 // outside the solid to any boundary of this solid.
831 // Return 0 if the point is already inside.
832
833 G4double *dists = new G4double[nb_of_surfaces];
834 G4int a;
835
836 // Set the surfaces to active again
837 //
838 Reset();
839
840 // Compute the shortest distance of the point to each surface.
841 // Be careful : it's a signed value
842 //
843 for(a=0; a< nb_of_surfaces; a++)
844 dists[a] = SurfaceVec[a]->HowNear(Pt);
845
846 G4double Dist = kInfinity;
847
848 // If dists[] is positive, the point is outside, so take the shortest of
849 // the shortest positive distances dists[] can be equal to 0 : point on
850 // a surface.
851 // ( Problem with the G4FPlane : there is no inside and no outside...
852 // So, to test if the point is inside to return 0, utilize the Inside()
853 // function. But I don't know if it is really needed because dToIn is
854 // called only if the point is outside )
855 //
856 for(a = 0; a < nb_of_surfaces; a++)
857 if( std::fabs(Dist) > std::fabs(dists[a]) )
858 //if( dists[a] >= 0)
859 Dist = dists[a];
860
861 delete[] dists;
862
863 if(Dist == kInfinity)
864 return 0; // the point is inside the solid or on a surface
865 else
866 return std::fabs(Dist);
867}
868
869G4double G4BREPSolid::DistanceToIn(register const G4ThreeVector& Pt,
870 register const G4ThreeVector& V ) const
871{
872 // Calculates the distance from a point outside the solid
873 // to the solid's boundary along a specified direction vector.
874 //
875 // Note : Intersections with boundaries less than the tolerance must be
876 // ignored if the direction is away from the boundary.
877
878 G4int a;
879
880 // Set the surfaces to active again
881 //
882 Reset();
883
884 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
885 G4Vector3D Pttmp(Pt);
886 G4Vector3D Vtmp(V);
887 G4Ray r(Pttmp, Vtmp);
888
889 // Test if the bounding box of each surface is intersected
890 // by the ray. If not, the surface become deactive.
891 //
892 TestSurfaceBBoxes(r);
893
894 ShortestDistance = kInfinity;
895
896 for(a=0; a< nb_of_surfaces; a++)
897 {
898 if( SurfaceVec[a]->IsActive() )
899 {
900 // Test if the ray intersects the surface
901 //
902 if( SurfaceVec[a]->Intersect(r) )
903 {
904 G4double surfDistance = SurfaceVec[a]->GetDistance();
905
906 // If more than 1 surface is intersected, take the nearest one
907 //
908 if( surfDistance < ShortestDistance )
909 {
910 if( surfDistance > sqrHalfTolerance )
911 {
912 ShortestDistance = surfDistance;
913 }
914 else
915 {
916 // The point is within the boundary. It is ignored it if
917 // the direction is away from the boundary
918 //
919 G4Vector3D Norm = SurfaceVec[a]->SurfaceNormal(Pttmp);
920
921 if( (Norm * Vtmp) < 0 )
922 {
923 ShortestDistance = surfDistance;
924 }
925 }
926 }
927 }
928 }
929 }
930
931 // Be careful !
932 // SurfaceVec->Distance is in fact the squared distance
933 //
934 if(ShortestDistance != kInfinity)
935 return std::sqrt(ShortestDistance);
936 else
937 return kInfinity; // No intersection
938}
939
940G4double G4BREPSolid::DistanceToOut(register const G4ThreeVector& P,
941 register const G4ThreeVector& D,
942 const G4bool,
943 G4bool *validNorm,
944 G4ThreeVector* ) const
945{
946 // Calculates the distance from a point inside the solid to the solid's
947 // boundary along a specified direction vector.
948 // Returns 0 if the point is already outside.
949 //
950 // Note : If the shortest distance to a boundary is less than the tolerance,
951 // it is ignored. This allows for a point within a tolerant boundary
952 // to leave immediately.
953
954 // Set the surfaces to active again
955 //
956 Reset();
957
958 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
959 G4Vector3D Ptv = P;
960 G4int a;
961
962 if(validNorm)
963 *validNorm=false;
964
965 G4Vector3D Pttmp(Ptv);
966 G4Vector3D Vtmp(D);
967
968 G4Ray r(Pttmp, Vtmp);
969
970 // Test if the bounding box of each surface is intersected
971 // by the ray. If not, the surface become deactive.
972 //
973 TestSurfaceBBoxes(r);
974
975 ShortestDistance = kInfinity;
976
977 for(a=0; a< nb_of_surfaces; a++)
978 {
979 if(SurfaceVec[a]->IsActive())
980 {
981 // Test if the ray intersect the surface
982 //
983 if( (SurfaceVec[a]->Intersect(r)) )
984 {
985 // If more than 1 surface is intersected, take the nearest one
986 //
987 G4double surfDistance = SurfaceVec[a]->GetDistance();
988 if( surfDistance < ShortestDistance )
989 {
990 if( surfDistance > sqrHalfTolerance )
991 {
992 ShortestDistance = surfDistance;
993 }
994 else
995 {
996 // The point is within the boundary: ignore it
997 }
998 }
999 }
1000 }
1001 }
1002
1003 // Be careful !
1004 // SurfaceVec->Distance is in fact the squared distance
1005 //
1006 if(ShortestDistance != kInfinity)
1007 return std::sqrt(ShortestDistance);
1008 else
1009 return 0.0; // No intersection is found, the point is outside
1010}
1011
1012G4double G4BREPSolid::DistanceToOut(const G4ThreeVector& Pt)const
1013{
1014 // Calculates the shortest distance ("safety") from a point
1015 // inside the solid to any boundary of this solid.
1016 // Returns 0 if the point is already outside.
1017
1018 G4double *dists = new G4double[nb_of_surfaces];
1019 G4int a;
1020
1021 // Set the surfaces to active again
1022 //
1023 Reset();
1024
1025 // Compute the shortest distance of the point to each surfaces
1026 // Be careful : it's a signed value
1027 //
1028 for(a=0; a< nb_of_surfaces; a++)
1029 dists[a] = SurfaceVec[a]->HowNear(Pt);
1030
1031 G4double Dist = kInfinity;
1032
1033 // If dists[] is negative, the point is inside so take the shortest of the
1034 // shortest negative distances dists[] can be equal to 0 : point on a
1035 // surface
1036 // ( Problem with the G4FPlane : there is no inside and no outside...
1037 // So, to test if the point is outside to return 0, utilize the Inside()
1038 // function. But I don`t know if it is really needed because dToOut is
1039 // called only if the point is inside )
1040 //
1041 for(a = 0; a < nb_of_surfaces; a++)
1042 if( std::fabs(Dist) > std::fabs(dists[a]) )
1043 //if( dists[a] <= 0)
1044 Dist = dists[a];
1045
1046 delete[] dists;
1047
1048 if(Dist == kInfinity)
1049 return 0; // The point is ouside the solid or on a surface
1050 else
1051 return std::fabs(Dist);
1052}
1053
1054void G4BREPSolid::DescribeYourselfTo (G4VGraphicsScene& scene) const
1055{
1056 scene.AddSolid (*this);
1057}
1058
1059G4Polyhedron* G4BREPSolid::CreatePolyhedron () const
1060{
1061 // Approximate implementation, just a box ...
1062
1063 G4Point3D Min = bbox->GetBoxMin();
1064 G4Point3D Max = bbox->GetBoxMax();
1065
1066 return new G4PolyhedronBox (Max.x(), Max.y(), Max.z());
1067}
1068
1069G4NURBS* G4BREPSolid::CreateNURBS () const
1070{
1071 // Approximate implementation, just a box ...
1072
1073 G4Point3D Min = bbox->GetBoxMin();
1074 G4Point3D Max = bbox->GetBoxMax();
1075
1076 return new G4NURBSbox (Max.x(), Max.y(), Max.z());
1077}
1078
1079void G4BREPSolid::CalcBBoxes()
1080{
1081 // First initialization. Calculates the bounding boxes
1082 // for the surfaces and for the solid.
1083
1084 G4Surface* srf;
1085 G4Point3D min, max;
1086
1087 if(active)
1088 {
1089 min = PINFINITY;
1090 max = -PINFINITY;
1091
1092 for(G4int a = 0;a < nb_of_surfaces;a++)
1093 {
1094 // Get first in List
1095 //
1096 srf = SurfaceVec[a];
1097 G4int convex=1;
1098 G4int concavepoint=-1;
1099
1100 if (srf->MyType() == 1)
1101 {
1102 concavepoint = srf->IsConvex();
1103 convex = srf->GetConvex();
1104 }
1105
1106 // Make bbox for face
1107 //
1108 // if(convex && Concavepoint==-1)
1109 {
1110 srf->CalcBBox();
1111 G4Point3D box_min = srf->GetBBox()->GetBoxMin();
1112 G4Point3D box_max = srf->GetBBox()->GetBoxMax();
1113
1114 // Find max and min of face bboxes to make solids bbox.
1115
1116 // replace by Extend
1117 // max < box_max
1118 //
1119 if(max.x() < box_max.x()) max.setX(box_max.x());
1120 if(max.y() < box_max.y()) max.setY(box_max.y());
1121 if(max.z() < box_max.z()) max.setZ(box_max.z());
1122
1123 // min > box_min
1124 //
1125 if(min.x() > box_min.x()) min.setX(box_min.x());
1126 if(min.y() > box_min.y()) min.setY(box_min.y());
1127 if(min.z() > box_min.z()) min.setZ(box_min.z());
1128 }
1129 }
1130 bbox = new G4BoundingBox3D(min, max);
1131 return;
1132 }
1133 G4cerr << "ERROR - G4BREPSolid::CalcBBoxes()" << G4endl
1134 << " No bbox calculated for solid. Error." << G4endl;
1135}
1136
1137void G4BREPSolid::RemoveHiddenFaces(register const G4Ray& rayref,
1138 G4int In ) const
1139{
1140 // Deactivates the planar faces that are on the "back" side of a solid.
1141 // B-splines are not handled by this function. Also cases where the ray
1142 // starting point is Inside the bbox of the solid are ignored as we don't
1143 // know if the starting point is Inside the actual solid except for
1144 // axis-oriented box-like solids.
1145
1146 register G4Surface* srf;
1147 register const G4Vector3D& RayDir = rayref.GetDir();
1148 register G4double Result;
1149 G4int a;
1150
1151 // In all other cases the ray starting point is outside the solid
1152 //
1153 if(!In)
1154 for(a=0; a<nb_of_surfaces; a++)
1155 {
1156 // Deactivates the solids faces that are hidden
1157 //
1158 srf = SurfaceVec[a];
1159 if(srf->MyType()==1)
1160 {
1161 const G4Vector3D& Normal = (srf->Norm())->GetDir();
1162 Result = (RayDir * Normal);
1163
1164 if( Result >= 0 )
1165 srf->Deactivate();
1166 }
1167 }
1168 else
1169 for(a=0; a<nb_of_surfaces; a++)
1170 {
1171 // Deactivates the AxisBox type solids faces whose normals
1172 // point in the G4Vector3D opposite to the rays G4Vector3D
1173 // i.e. are behind the ray starting point as in this case the
1174 // ray starts from Inside the solid.
1175 //
1176 srf = SurfaceVec[a];
1177 if(srf->MyType()==1)
1178 {
1179 const G4Vector3D& Normal = (srf->Norm())->GetDir();
1180 Result = (RayDir * Normal);
1181
1182 if( Result < 0 )
1183 srf->Deactivate();
1184 }
1185 }
1186}
1187
1188void G4BREPSolid::TestSurfaceBBoxes(register const G4Ray& rayref) const
1189{
1190 register G4Surface* srf;
1191 G4int active_srfs = nb_of_surfaces;
1192
1193 // Do the bbox tests to all surfaces in List
1194 // for planar faces the intersection is instead evaluated.
1195 //
1196 G4int intersection=0;
1197
1198 for(G4int a=0;a<nb_of_surfaces;a++)
1199 {
1200 // Get first in List
1201 //
1202 srf = SurfaceVec[a];
1203
1204 if(srf->IsActive())
1205 {
1206 // Get type
1207 //
1208 if(srf->MyType() != 1) // 1 == planar face
1209 {
1210 if(srf->GetBBox()->Test(rayref))
1211 srf->SetDistance(bbox->GetDistance());
1212 else
1213 {
1214 // Test failed. Flag as inactive.
1215 //
1216 srf->Deactivate();
1217 active_srfs--;
1218 }
1219 }
1220 else
1221 {
1222 // Type was convex planar face
1223 intersection = srf->Intersect(rayref);
1224
1225 if(!intersection)
1226 active_srfs--;
1227 }
1228 }
1229 else
1230 active_srfs--;
1231 }
1232
1233 if(!active_srfs) Active(0);
1234}
1235
1236
1237G4int G4BREPSolid::Intersect(register const G4Ray& rayref) const
1238{
1239 // Gets the roughly calculated closest intersection point for
1240 // a b_spline & accurate point for others.
1241
1242 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
1243
1244 register G4Surface* srf;
1245 G4double HitDistance = -1;
1246 const G4Point3D& RayStart = rayref.GetStart();
1247 const G4Point3D& RayDir = rayref.GetDir();
1248
1249 G4int result=1;
1250
1251 // Sort List of active surfaces according to
1252 // bbox distances to ray starting point
1253 //
1254 QuickSort(SurfaceVec, 0, nb_of_surfaces-1);
1255 G4int Number=0;
1256
1257 // Start handling active surfaces in order
1258 //
1259 for(register G4int a=0;a<nb_of_surfaces;a++)
1260 {
1261 srf = SurfaceVec[a];
1262 G4int included = 0;
1263
1264 if(srf->IsActive())
1265 {
1266 result = srf->Intersect(rayref);
1267 if(result)
1268 {
1269 // Get the evaluated point on the surface
1270 //
1271 const G4Point3D& closest_point = srf->GetClosestHit();
1272
1273 // Test for DistanceToIn(pt, vec)
1274 // if d = 0 and vec.norm > 0, do not see the surface
1275 //
1276 if( !( (srf->GetDistance() < sqrHalfTolerance) ||
1277 (RayDir.dot(srf->SurfaceNormal(closest_point)) > 0) ) )
1278 {
1279
1280 if(srf->MyType()==1)
1281 HitDistance = srf->GetDistance();
1282 else
1283 {
1284 // Check if the evaluated point is in front of the
1285 // bbox of the next surface.
1286 //
1287 HitDistance = RayStart.distance2(closest_point);
1288 }
1289 }
1290 }
1291 else // No hit
1292 {
1293 included = 1;
1294 srf->Deactivate();
1295 }
1296 }
1297 Number++;
1298 }
1299
1300 if(HitDistance < 0)
1301 return 0;
1302
1303 QuickSort(SurfaceVec, 0, nb_of_surfaces-1);
1304
1305 if(!(SurfaceVec[0]->IsActive()))
1306 return 0;
1307
1308 ((G4BREPSolid*)this)->intersection_point = SurfaceVec[0]->GetClosestHit();
1309 bbox->SetDistance(HitDistance);
1310
1311 return 1;
1312}
1313
1314G4int G4BREPSolid::FinalEvaluation(register const G4Ray& rayref,
1315 G4int ToIn ) const
1316{
1317 const G4double sqrHalfTolerance = kCarTolerance*kCarTolerance*0.25;
1318 register G4Surface* srf;
1319 G4double Dist=0;
1320
1321 ((G4BREPSolid*)this)->intersectionDistance = kInfinity;
1322
1323 for(register G4int a=0;a<nb_of_surfaces;a++)
1324 {
1325 srf = SurfaceVec[a];
1326
1327 if(srf->IsActive())
1328 {
1329 const G4Point3D& srf_intersection = srf->Evaluation(rayref);
1330
1331 // Compute hit point distance from ray starting point
1332 //
1333 if(srf->MyType() != 1)
1334 {
1335 G4Point3D start = rayref.GetStart();
1336 Dist = srf_intersection.distance2(start);
1337 }
1338 else
1339 Dist = srf->GetDistance();
1340
1341 // Skip point wich are on the surface i.e. within tolerance of the
1342 // surface. Special handling for DistanceToIn & reflections
1343 //
1344 if(Dist < sqrHalfTolerance)
1345 {
1346 if(ToIn)
1347 {
1348 const G4Vector3D& Dir = rayref.GetDir();
1349 const G4Point3D& Hit = srf->GetClosestHit();
1350 const G4Vector3D& Norm = srf->SurfaceNormal(Hit);
1351
1352 if(( Dir * Norm ) >= 0)
1353 {
1354 Dist = kInfinity;
1355 srf->Deactivate();
1356 }
1357
1358 // else continue with the distance, even though < tolerance
1359 }
1360 else
1361 {
1362 Dist = kInfinity;
1363 srf->Deactivate();
1364 }
1365 }
1366
1367 // If more than one surfaces are evaluated till the
1368 // final stage, only the closest point is taken
1369 //
1370 if(Dist < intersectionDistance)
1371 {
1372 // Check that Hit is in the direction of the ray
1373 // from the starting point
1374 //
1375 const G4Point3D& Pt = rayref.GetStart();
1376 const G4Vector3D& Dir = rayref.GetDir();
1377
1378 G4Point3D TestPoint = (0.00001*Dir) + Pt;
1379 G4double TestDistance = srf_intersection.distance2(TestPoint);
1380
1381 if(TestDistance > Dist)
1382 {
1383 // Hit behind ray starting point, no intersection
1384 //
1385 Dist = kInfinity;
1386 srf->Deactivate();
1387 }
1388 else
1389 {
1390 ((G4BREPSolid*)this)->intersectionDistance = Dist;
1391 ((G4BREPSolid*)this)->intersection_point = srf_intersection;
1392 }
1393
1394 // Check that the intersection is closer than the
1395 // next surfaces approximated point
1396 //
1397 if(srf->IsActive())
1398 {
1399 if(a+1<nb_of_surfaces)
1400 {
1401 const G4Vector3D& Dir = rayref.GetDir();
1402 const G4Point3D& Hit = srf->GetClosestHit();
1403 const G4Vector3D& Norm = srf->SurfaceNormal(Hit);
1404
1405 // L. Broglia
1406 //if(( Dir * Norm ) >= 0)
1407 if(( Dir * Norm ) < 0)
1408 {
1409 Dist = kInfinity;
1410 srf->Deactivate();
1411 }
1412
1413 // else continue with the distance, even though < tolerance
1414
1415 ShortestDistance = Dist;
1416 }
1417 else
1418 {
1419 ShortestDistance = Dist;
1420 return 1;
1421 }
1422 }
1423 }
1424 }
1425 else // if srf NOT active
1426 {
1427 /* if(intersectionDistance < kInfinity)
1428 return 1;
1429 return 0;*/
1430 }
1431 }
1432 if(intersectionDistance < kInfinity)
1433 return 1;
1434
1435 return 0;
1436}
1437
1438G4Point3D G4BREPSolid::Scope() const
1439{
1440 G4Point3D scope;
1441 G4Point3D Max = bbox->GetBoxMax();
1442 G4Point3D Min = bbox->GetBoxMin();
1443
1444 scope.setX(std::fabs(Max.x()) - std::fabs(Min.x()));
1445 scope.setY(std::fabs(Max.y()) - std::fabs(Min.y()));
1446 scope.setZ(std::fabs(Max.z()) - std::fabs(Min.z()));
1447
1448 return scope;
1449}
1450
1451std::ostream& G4BREPSolid::StreamInfo(std::ostream& os) const
1452{
1453 os << "-----------------------------------------------------------\n"
1454 << " *** Dump for solid - " << GetName() << " ***\n"
1455 << " ===================================================\n"
1456 << " Solid type: " << GetEntityType() << "\n"
1457 << " Parameters: \n"
1458 << " Number of solids: " << NumberOfSolids << "\n"
1459 << "-----------------------------------------------------------\n";
1460
1461 return os;
1462}
1463
1464G4Polyhedron* G4BREPSolid::GetPolyhedron () const
1465{
1466 if (!fpPolyhedron ||
1467 fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
1468 fpPolyhedron->GetNumberOfRotationSteps())
1469 {
1470 delete fpPolyhedron;
1471 fpPolyhedron = CreatePolyhedron();
1472 }
1473 return fpPolyhedron;
1474}
Note: See TracBrowser for help on using the repository browser.