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

Last change on this file since 1202 was 1058, checked in by garnier, 15 years ago

file release beta

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-02-ref-02 $
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.