Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/geometry/solids/Boolean/src/G4MultiUnion.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 // Implementation of G4MultiUnion class
 27 //
 28 // 19.10.12 M.Gayer - Original implementation from USolids module
 29 // 06.04.17 G.Cosmo - Adapted implementation in Geant4 for VecGeom migration
 30 // --------------------------------------------------------------------
 31 
 32 #include <iostream>
 33 #include <sstream>
 34 
 35 #include "G4MultiUnion.hh"
 36 #include "Randomize.hh"
 37 #include "G4GeometryTolerance.hh"
 38 #include "G4BoundingEnvelope.hh"
 39 #include "G4AffineTransform.hh"
 40 #include "G4DisplacedSolid.hh"
 41 
 42 #include "G4VGraphicsScene.hh"
 43 #include "G4Polyhedron.hh"
 44 #include "G4PolyhedronArbitrary.hh"
 45 #include "HepPolyhedronProcessor.h"
 46 
 47 #include "G4BooleanSolid.hh"
 48 
 49 #include "G4AutoLock.hh"
 50 
 51 namespace
 52 {
 53   G4Mutex polyhedronMutex = G4MUTEX_INITIALIZER;
 54 }
 55 
 56 //______________________________________________________________________________
 57 G4MultiUnion::G4MultiUnion(const G4String& name)
 58   : G4VSolid(name)
 59 {
 60   SetName(name);
 61   fSolids.clear();
 62   fTransformObjs.clear();
 63   kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
 64 }
 65 
 66 //______________________________________________________________________________
 67 G4MultiUnion::~G4MultiUnion()
 68 = default;
 69 
 70 //______________________________________________________________________________
 71 void G4MultiUnion::AddNode(G4VSolid& solid, const G4Transform3D& trans)
 72 {
 73   fSolids.push_back(&solid);
 74   fTransformObjs.push_back(trans);  // Store a local copy of transformations
 75 }
 76 
 77 //______________________________________________________________________________
 78 void G4MultiUnion::AddNode(G4VSolid* solid, const G4Transform3D& trans)
 79 {
 80   fSolids.push_back(solid);
 81   fTransformObjs.push_back(trans);  // Store a local copy of transformations
 82 }
 83 
 84 //______________________________________________________________________________
 85 G4VSolid* G4MultiUnion::Clone() const
 86 {
 87   return new G4MultiUnion(*this);
 88 }
 89 
 90 // Copy constructor
 91 //______________________________________________________________________________
 92 G4MultiUnion::G4MultiUnion(const G4MultiUnion& rhs)
 93   : G4VSolid(rhs), fCubicVolume(rhs.fCubicVolume),
 94     fSurfaceArea(rhs.fSurfaceArea),
 95     kRadTolerance(rhs.kRadTolerance), fAccurate(rhs.fAccurate)
 96 {
 97 }
 98 
 99 // Fake default constructor for persistency
100 //______________________________________________________________________________
101 G4MultiUnion::G4MultiUnion( __void__& a )
102   : G4VSolid(a)
103 {
104 }
105 
106 // Assignment operator
107 //______________________________________________________________________________
108 G4MultiUnion& G4MultiUnion::operator = (const G4MultiUnion& rhs)
109 {
110   // Check assignment to self
111   //
112   if (this == &rhs)
113   {
114     return *this;
115   }
116 
117   // Copy base class data
118   //
119   G4VSolid::operator=(rhs);
120 
121   return *this;
122 }
123 
124 //______________________________________________________________________________
125 G4double G4MultiUnion::GetCubicVolume()
126 {
127   if (fCubicVolume == 0.0)
128   {
129     fCubicVolume = EstimateCubicVolume(1000000, 0.001);
130   }
131   return fCubicVolume;
132 }
133 
134 //______________________________________________________________________________
135 G4double
136 G4MultiUnion::DistanceToInNoVoxels(const G4ThreeVector& aPoint,
137                                    const G4ThreeVector& aDirection) const
138 {
139   G4ThreeVector direction = aDirection.unit();
140   G4ThreeVector localPoint, localDirection;
141   G4double minDistance = kInfinity;
142 
143   std::size_t numNodes = fSolids.size();
144   for (std::size_t i = 0 ; i < numNodes ; ++i)
145   {
146     G4VSolid& solid = *fSolids[i];
147     const G4Transform3D& transform = fTransformObjs[i];
148 
149     localPoint = GetLocalPoint(transform, aPoint);
150     localDirection = GetLocalVector(transform, direction);
151 
152     G4double distance = solid.DistanceToIn(localPoint, localDirection);
153     if (minDistance > distance) minDistance = distance;
154   }
155   return minDistance;
156 }
157 
158 //______________________________________________________________________________
159 G4double G4MultiUnion::DistanceToInCandidates(const G4ThreeVector& aPoint,
160                                               const G4ThreeVector& direction,
161                                               std::vector<G4int>& candidates,
162                                               G4SurfBits& bits) const
163 {
164   std::size_t candidatesCount = candidates.size();
165   G4ThreeVector localPoint, localDirection;
166 
167   G4double minDistance = kInfinity;
168   for (std::size_t i = 0 ; i < candidatesCount; ++i)
169   {
170     G4int candidate = candidates[i];
171     G4VSolid& solid = *fSolids[candidate];
172     const G4Transform3D& transform = fTransformObjs[candidate];
173 
174     localPoint = GetLocalPoint(transform, aPoint);
175     localDirection = GetLocalVector(transform, direction);
176     G4double distance = solid.DistanceToIn(localPoint, localDirection);
177     if (minDistance > distance) minDistance = distance;
178     bits.SetBitNumber(candidate);
179     if (minDistance == 0) break;
180   }
181   return minDistance;
182 }
183 
184 // Algorithm note: we have to look also for all other objects in next voxels,
185 // if the distance is not shorter ... we have to do it because,
186 // for example for objects which starts in first voxel in which they
187 // do not collide with direction line, but in second it collides...
188 // The idea of crossing voxels would be still applicable,
189 // because this way we could exclude from the testing such solids,
190 // which were found that obviously are not good candidates, because
191 // they would return infinity
192 // But if distance is smaller than the shift to next voxel, we can return
193 // it immediately
194 //______________________________________________________________________________
195 G4double G4MultiUnion::DistanceToIn(const G4ThreeVector& aPoint,
196                                     const G4ThreeVector& aDirection) const
197 {
198   G4double minDistance = kInfinity;
199   G4ThreeVector direction = aDirection.unit();
200   G4double shift = fVoxels.DistanceToFirst(aPoint, direction);
201   if (shift == kInfinity) return shift;
202 
203   G4ThreeVector currentPoint = aPoint;
204   if (shift != 0.0) currentPoint += direction * shift;
205 
206   G4SurfBits exclusion(fVoxels.GetBitsPerSlice());
207   std::vector<G4int> candidates, curVoxel(3);
208   fVoxels.GetVoxel(curVoxel, currentPoint);
209 
210   do
211   {
212     {
213       if (fVoxels.GetCandidatesVoxelArray(curVoxel, candidates, &exclusion) != 0)
214       {
215         G4double distance = DistanceToInCandidates(aPoint, direction,
216                                                    candidates, exclusion);
217         if (minDistance > distance) minDistance = distance;
218         if (distance < shift) break;
219       }
220     }
221     shift = fVoxels.DistanceToNext(aPoint, direction, curVoxel);
222   }
223   while (minDistance > shift);
224  
225   return minDistance;
226 }
227 
228 //______________________________________________________________________________
229 G4double G4MultiUnion::DistanceToOutNoVoxels(const G4ThreeVector& aPoint,
230                                              const G4ThreeVector& aDirection,
231                                              G4ThreeVector* aNormal) const
232 {
233   // Computes distance from a point presumably outside the solid to the solid
234   // surface. Ignores first surface if the point is actually inside.
235   // Early return infinity in case the safety to any surface is found greater
236   // than the proposed step aPstep.
237   // The normal vector to the crossed surface is filled only in case the box
238   // is crossed, otherwise aNormal->IsNull() is true.
239 
240   // algorithm:
241   G4ThreeVector direction = aDirection.unit();
242   G4ThreeVector localPoint, localDirection;
243   G4int ignoredSolid = -1;
244   G4double resultDistToOut = 0;
245   G4ThreeVector currentPoint = aPoint;
246 
247   auto numNodes = (G4int)fSolids.size();
248   for (auto i = 0; i < numNodes; ++i)
249   {
250     if (i != ignoredSolid)
251     {
252       G4VSolid& solid = *fSolids[i];
253       const G4Transform3D& transform = fTransformObjs[i];
254       localPoint = GetLocalPoint(transform, currentPoint);
255       localDirection = GetLocalVector(transform, direction);
256       EInside location = solid.Inside(localPoint);
257       if (location != EInside::kOutside)
258       {
259         G4double distance = solid.DistanceToOut(localPoint, localDirection,
260                                                 false, nullptr, aNormal);
261         if (distance < kInfinity)
262         {
263           if (resultDistToOut == kInfinity) resultDistToOut = 0;
264           if (distance > 0)
265           {
266             currentPoint = GetGlobalPoint(transform, localPoint
267                                           + distance*localDirection);
268             resultDistToOut += distance;
269             ignoredSolid = i; // skip the solid which we have just left
270             i = -1; // force the loop to continue from 0
271           }
272         }
273       }
274     }
275   }
276   return resultDistToOut;
277 }
278 
279 //______________________________________________________________________________
280 G4double G4MultiUnion::DistanceToOut(const G4ThreeVector& aPoint,
281                                      const G4ThreeVector& aDirection,
282                                      const G4bool /* calcNorm */,
283                                      G4bool* /* validNorm */,
284                                      G4ThreeVector* aNormal) const
285 {
286   return DistanceToOutVoxels(aPoint, aDirection, aNormal);
287 }
288 
289 //______________________________________________________________________________
290 G4double G4MultiUnion::DistanceToOutVoxels(const G4ThreeVector& aPoint,
291                                            const G4ThreeVector& aDirection,
292                                            G4ThreeVector* aNormal) const
293 {
294   // Computes distance from a point presumably inside the solid to the solid
295   // surface. Ignores first surface along each axis systematically (for points
296   // inside or outside. Early returns zero in case the second surface is behind
297   // the starting point.
298   // o The proposed step is ignored.
299   // o The normal vector to the crossed surface is always filled.
300 
301   // In the case the considered point is located inside the G4MultiUnion
302   // structure, the treatments are as follows:
303   //      - investigation of the candidates for the passed point
304   //      - progressive moving of the point towards the surface, along the
305   //        passed direction
306   //      - processing of the normal
307 
308   G4ThreeVector direction = aDirection.unit();
309   std::vector<G4int> candidates;
310   G4double distance = 0;
311   std::size_t numNodes = 2*fSolids.size();
312   std::size_t count=0;
313 
314   if (fVoxels.GetCandidatesVoxelArray(aPoint, candidates) != 0)
315   {
316     // For normal case for which we presume the point is inside
317     G4ThreeVector localPoint, localDirection, localNormal;
318     G4ThreeVector currentPoint = aPoint;
319     G4SurfBits exclusion(fVoxels.GetBitsPerSlice());
320     G4bool notOutside;
321     G4ThreeVector maxNormal;
322 
323     do
324     {
325       notOutside = false;
326 
327       G4double maxDistance = -kInfinity;
328       G4int maxCandidate = 0;
329       G4ThreeVector maxLocalPoint;
330 
331       std::size_t limit = candidates.size();
332       for (std::size_t i = 0 ; i < limit ; ++i)
333       {
334         G4int candidate = candidates[i];
335         // ignore the current component (that you just got out of) since
336         // numerically the propagated point will be on its surface
337 
338         G4VSolid& solid = *fSolids[candidate];
339         const G4Transform3D& transform = fTransformObjs[candidate];
340 
341         // The coordinates of the point are modified so as to fit the
342         // intrinsic solid local frame:
343         localPoint = GetLocalPoint(transform, currentPoint);
344 
345         // DistanceToOut at least for Trd sometimes return non-zero value
346         // even from points that are outside. Therefore, this condition
347         // must currently be here, otherwise it would not work.
348         // But it means it would be slower.
349 
350         if (solid.Inside(localPoint) != EInside::kOutside)
351         {
352           notOutside = true;
353 
354           localDirection = GetLocalVector(transform, direction);
355 
356           // propagate with solid.DistanceToOut
357           G4double shift = solid.DistanceToOut(localPoint, localDirection,
358                                                false, nullptr, &localNormal);
359           if (maxDistance < shift)
360           {
361             maxDistance = shift;
362             maxCandidate = candidate;
363             maxNormal = localNormal;
364           }
365         }
366       }
367 
368       if (notOutside)
369       {
370         const G4Transform3D& transform = fTransformObjs[maxCandidate];
371 
372         // convert from local normal
373         if (aNormal != nullptr) *aNormal = GetGlobalVector(transform, maxNormal);
374 
375         distance += maxDistance;
376         currentPoint += maxDistance * direction;
377         if(maxDistance == 0.) ++count;
378 
379         // the current component will be ignored
380         exclusion.SetBitNumber(maxCandidate);
381         EInside location = InsideWithExclusion(currentPoint, &exclusion);
382 
383         // perform a Inside
384         // it should be excluded current solid from checking
385         // we have to collect the maximum distance from all given candidates.
386         // such "maximum" candidate should be then used for finding next
387         // candidates
388         if (location == EInside::kOutside)
389         {
390           // else return cumulated distances to outside of the traversed
391           // components
392           break;
393         }
394         // if inside another component, redo 1 to 3 but add the next
395         // DistanceToOut on top of the previous.
396 
397         // and fill the candidates for the corresponding voxel (just
398         // exiting current component along direction)
399         candidates.clear();
400 
401         fVoxels.GetCandidatesVoxelArray(currentPoint, candidates, &exclusion);
402         exclusion.ResetBitNumber(maxCandidate);
403       }
404     }
405     while  ((notOutside) && (count < numNodes));
406   }
407 
408   return distance;
409 }
410 
411 //______________________________________________________________________________
412 EInside G4MultiUnion::InsideWithExclusion(const G4ThreeVector& aPoint,
413                                           G4SurfBits* exclusion) const
414 {
415   // Classify point location with respect to solid:
416   //  o eInside       - inside the solid
417   //  o eSurface      - close to surface within tolerance
418   //  o eOutside      - outside the solid
419 
420   // Hitherto, it is considered that only parallelepipedic nodes
421   // can be added to the container
422 
423   // Implementation using voxelisation techniques:
424   // ---------------------------------------------
425 
426   G4ThreeVector localPoint;
427   EInside location = EInside::kOutside;
428 
429   std::vector<G4int> candidates;
430   std::vector<G4MultiUnionSurface> surfaces;
431 
432   // TODO: test if it works well and if so measure performance
433   // TODO: getPointIndex should not be used, instead GetVoxel + GetVoxelsIndex
434   //       should be used
435   // TODO: than pass result to GetVoxel further to GetCandidatesVoxelArray
436   // TODO: eventually GetVoxel should be inlined here, early exit if any
437   //       binary search is -1
438 
439   G4int limit = fVoxels.GetCandidatesVoxelArray(aPoint, candidates, exclusion);
440   for (G4int i = 0 ; i < limit ; ++i)
441   {
442     G4int candidate = candidates[i];
443     G4VSolid& solid = *fSolids[candidate];
444     const G4Transform3D& transform = fTransformObjs[candidate];
445 
446     // The coordinates of the point are modified so as to fit the intrinsic
447     // solid local frame:
448     localPoint = GetLocalPoint(transform, aPoint);
449     location = solid.Inside(localPoint);
450     if (location == EInside::kInside) return EInside::kInside;
451     else if (location == EInside::kSurface)
452     {
453       G4MultiUnionSurface surface;
454       surface.point = localPoint;
455       surface.solid = &solid;
456       surfaces.push_back(surface);
457     }
458   }
459 
460   ///////////////////////////////////////////////////////////////////////////
461   // Important comment: When two solids touch each other along a flat
462   // surface, the surface points will be considered as kSurface, while points
463   // located around will correspond to kInside (cf. G4UnionSolid)
464 
465   std::size_t size = surfaces.size();
466 
467   if (size == 0)
468   {
469     return EInside::kOutside;
470   }
471 
472   for (std::size_t i = 0; i < size - 1; ++i)
473   {
474     G4MultiUnionSurface& left = surfaces[i];
475     for (std::size_t j = i + 1; j < size; ++j)
476     {
477       G4MultiUnionSurface& right = surfaces[j];
478       G4ThreeVector n, n2;
479       n = left.solid->SurfaceNormal(left.point);
480       n2 = right.solid->SurfaceNormal(right.point);
481       if ((n +  n2).mag2() < 1000 * kRadTolerance)
482         return EInside::kInside;
483     }
484   }
485 
486   return EInside::kSurface;
487 }
488 
489 //______________________________________________________________________________
490 EInside G4MultiUnion::Inside(const G4ThreeVector& aPoint) const
491 {
492   // Classify point location with respect to solid:
493   //  o eInside       - inside the solid
494   //  o eSurface      - close to surface within tolerance
495   //  o eOutside      - outside the solid
496 
497   // Hitherto, it is considered that only parallelepipedic nodes can be
498   // added to the container
499 
500   // Implementation using voxelisation techniques:
501   // ---------------------------------------------
502 
503   //  return InsideIterator(aPoint);
504 
505   EInside location = InsideWithExclusion(aPoint);
506   return location;
507 }
508 
509 //______________________________________________________________________________
510 EInside G4MultiUnion::InsideNoVoxels(const G4ThreeVector& aPoint) const
511 {
512   G4ThreeVector localPoint;
513   EInside location = EInside::kOutside;
514   G4int countSurface = 0;
515 
516   auto numNodes = (G4int)fSolids.size();
517   for (auto i = 0 ; i < numNodes ; ++i)
518   {
519     G4VSolid& solid = *fSolids[i];
520     G4Transform3D transform = GetTransformation(i);
521 
522     // The coordinates of the point are modified so as to fit the
523     // intrinsic solid local frame:
524     localPoint = GetLocalPoint(transform, aPoint);
525 
526     location = solid.Inside(localPoint);
527 
528     if (location == EInside::kSurface)
529       ++countSurface;
530 
531     if (location == EInside::kInside) return EInside::kInside;
532   }
533   if (countSurface != 0) return EInside::kSurface;
534   return EInside::kOutside;
535 }
536 
537 //______________________________________________________________________________
538 void G4MultiUnion::Extent(EAxis aAxis, G4double& aMin, G4double& aMax) const
539 {
540   // Determines the bounding box for the considered instance of "UMultipleUnion"
541   G4ThreeVector min, max;
542 
543   auto numNodes = (G4int)fSolids.size();
544   for (auto i = 0 ; i < numNodes ; ++i)
545   {
546     G4VSolid& solid = *fSolids[i];
547     G4Transform3D transform = GetTransformation(i);
548     solid.BoundingLimits(min, max);
549    
550     TransformLimits(min, max, transform);
551     
552     if (i == 0)
553     {
554       switch (aAxis)
555       {
556         case kXAxis:
557           aMin = min.x();
558           aMax = max.x();
559           break;
560         case kYAxis:
561           aMin = min.y();
562           aMax = max.y();
563           break;
564         case kZAxis:
565           aMin = min.z();
566           aMax = max.z();
567           break;
568         default:
569           break;
570       }
571     }
572     else
573     {
574       // Determine the min/max on the considered axis:
575       switch (aAxis)
576       {
577         case kXAxis:
578           if (min.x() < aMin)
579             aMin = min.x();
580           if (max.x() > aMax)
581             aMax = max.x();
582           break;
583         case kYAxis:
584           if (min.y() < aMin)
585             aMin = min.y();
586           if (max.y() > aMax)
587             aMax = max.y();
588           break;
589         case kZAxis:
590           if (min.z() < aMin)
591             aMin = min.z();
592           if (max.z() > aMax)
593             aMax = max.z();
594           break;
595         default:
596           break;
597       }
598     }
599   }
600 }
601 
602 //______________________________________________________________________________
603 void G4MultiUnion::BoundingLimits(G4ThreeVector& aMin,
604                                   G4ThreeVector& aMax) const
605 {
606    Extent(kXAxis, aMin[0], aMax[0]);
607    Extent(kYAxis, aMin[1], aMax[1]);
608    Extent(kZAxis, aMin[2], aMax[2]);
609 }
610 
611 //______________________________________________________________________________
612 G4bool
613 G4MultiUnion::CalculateExtent(const EAxis pAxis,
614                               const G4VoxelLimits& pVoxelLimit,
615                               const G4AffineTransform& pTransform,
616                                     G4double& pMin, G4double& pMax) const
617 {
618   G4ThreeVector bmin, bmax;
619 
620   // Get bounding box
621   BoundingLimits(bmin,bmax);
622 
623   // Find extent
624   G4BoundingEnvelope bbox(bmin,bmax);
625   return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
626 }
627 
628 //______________________________________________________________________________
629 G4ThreeVector G4MultiUnion::SurfaceNormal(const G4ThreeVector& aPoint) const
630 {
631   // Computes the localNormal on a surface and returns it as a unit vector.
632   // Must return a valid vector. (even if the point is not on the surface).
633   //
634   //   On an edge or corner, provide an average localNormal of all facets within
635   //   tolerance
636   //   NOTE: the tolerance value used in here is not yet the global surface
637   //         tolerance - we will have to revise this value - TODO
638 
639   std::vector<G4int> candidates;
640   G4ThreeVector localPoint, normal, localNormal;
641   G4double safety = kInfinity;
642   G4int node = 0;
643 
644   ///////////////////////////////////////////////////////////////////////////
645   // Important comment: Cases for which the point is located on an edge or
646   // on a vertice remain to be treated
647 
648   // determine weather we are in voxel area
649   if (fVoxels.GetCandidatesVoxelArray(aPoint, candidates) != 0)
650   {
651     std::size_t limit = candidates.size();
652     for (std::size_t i = 0 ; i < limit ; ++i)
653     {
654       G4int candidate = candidates[i];
655       const G4Transform3D& transform = fTransformObjs[candidate];
656 
657       // The coordinates of the point are modified so as to fit the intrinsic
658       // solid local frame:
659       localPoint = GetLocalPoint(transform, aPoint);
660       G4VSolid& solid = *fSolids[candidate];
661       EInside location = solid.Inside(localPoint);
662 
663       if (location == EInside::kSurface)
664       {
665         // normal case when point is on surface, we pick first solid
666         normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
667         return normal.unit();
668       }
669       else
670       {
671         // collect the smallest safety and remember solid node
672         G4double s = (location == EInside::kInside)
673                    ? solid.DistanceToOut(localPoint)
674                    : solid.DistanceToIn(localPoint);
675         if (s < safety)
676         {
677           safety = s;
678           node = candidate;
679         }
680       }
681     }
682     // on none of the solids, the point was not on the surface
683     G4VSolid& solid = *fSolids[node];
684     const G4Transform3D& transform = fTransformObjs[node];
685     localPoint = GetLocalPoint(transform, aPoint);
686 
687     normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
688     return normal.unit();
689   }
690   else
691   {
692     // for the case when point is certainly outside:
693 
694     // find a solid in union with the smallest safety
695     node = SafetyFromOutsideNumberNode(aPoint, safety);
696     G4VSolid& solid = *fSolids[node];
697 
698     const G4Transform3D& transform = fTransformObjs[node];
699     localPoint = GetLocalPoint(transform, aPoint);
700 
701     // evaluate normal for point at this found solid
702     // and transform multi-union coordinates
703     normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
704 
705     return normal.unit();
706   }
707 }
708 
709 //______________________________________________________________________________
710 G4double G4MultiUnion::DistanceToOut(const G4ThreeVector& point) const
711 {
712   // Estimates isotropic distance to the surface of the solid. This must
713   // be either accurate or an underestimate.
714   // Two modes: - default/fast mode, sacrificing accuracy for speed
715   //            - "precise" mode,  requests accurate value if available.
716 
717   std::vector<G4int> candidates;
718   G4ThreeVector localPoint;
719   G4double safetyMin = kInfinity;
720 
721   // In general, the value return by DistanceToIn(p) will not be the exact
722   // but only an undervalue (cf. overlaps)
723   fVoxels.GetCandidatesVoxelArray(point, candidates);
724 
725   std::size_t limit = candidates.size();
726   for (std::size_t i = 0; i < limit; ++i)
727   {
728     G4int candidate = candidates[i];
729 
730     // The coordinates of the point are modified so as to fit the intrinsic
731     // solid local frame:
732     const G4Transform3D& transform = fTransformObjs[candidate];
733     localPoint = GetLocalPoint(transform, point);
734     G4VSolid& solid = *fSolids[candidate];
735     if (solid.Inside(localPoint) == EInside::kInside)
736     {
737       G4double safety = solid.DistanceToOut(localPoint);
738       if (safetyMin > safety) safetyMin = safety;
739     }
740   }
741   if (safetyMin == kInfinity) safetyMin = 0; // we are not inside
742 
743   return safetyMin;
744 }
745 
746 //______________________________________________________________________________
747 G4double G4MultiUnion::DistanceToIn(const G4ThreeVector& point) const
748 {
749   // Estimates the isotropic safety from a point outside the current solid to
750   // any of its surfaces. The algorithm may be accurate or should provide a fast
751   // underestimate.
752 
753   if (!fAccurate)  { return fVoxels.DistanceToBoundingBox(point); }
754 
755   const std::vector<G4VoxelBox>& boxes = fVoxels.GetBoxes();
756   G4double safetyMin = kInfinity;
757   G4ThreeVector localPoint;
758 
759   std::size_t numNodes = fSolids.size();
760   for (std::size_t j = 0; j < numNodes; ++j)
761   {
762     G4ThreeVector dxyz;
763     if (j > 0)
764     {
765       const G4ThreeVector& pos = boxes[j].pos;
766       const G4ThreeVector& hlen = boxes[j].hlen;
767       for (auto i = 0; i <= 2; ++i)
768         // distance to middle point - hlength => distance from point to border
769         // of x,y,z
770         if ((dxyz[i] = std::abs(point[i] - pos[i]) - hlen[i]) > safetyMin)
771           continue;
772 
773       G4double d2xyz = 0.;
774       for (auto i = 0; i <= 2; ++i)
775         if (dxyz[i] > 0) d2xyz += dxyz[i] * dxyz[i];
776 
777       // minimal distance is at least this, but could be even higher. therefore,
778       // we can stop if previous was already lower, let us check if it does any
779       // chance to be better tha previous values...
780       if (d2xyz >= safetyMin * safetyMin)
781       {
782         continue;
783       }
784     }
785     const G4Transform3D& transform = fTransformObjs[j];
786     localPoint = GetLocalPoint(transform, point);
787     G4VSolid& solid = *fSolids[j];
788 
789     G4double safety = solid.DistanceToIn(localPoint);
790     if (safety <= 0) return safety;
791       // it was detected, that the point is not located outside
792     if (safetyMin > safety) safetyMin = safety;
793   }
794   return safetyMin;
795 }
796 
797 //______________________________________________________________________________
798 G4double G4MultiUnion::GetSurfaceArea()
799 {
800   if (fSurfaceArea == 0.0)
801   {
802     fSurfaceArea = EstimateSurfaceArea(1000000, 0.001);
803   }
804   return fSurfaceArea;
805 }
806 
807 //______________________________________________________________________________
808 G4int G4MultiUnion::GetNumOfConstituents() const
809 {
810   G4int num = 0;
811   for (const auto solid : fSolids) { num += solid->GetNumOfConstituents(); }
812   return num;
813 }
814 
815 //______________________________________________________________________________
816 G4bool G4MultiUnion::IsFaceted() const
817 {
818   for (const auto solid : fSolids) { if (!solid->IsFaceted()) return false; }
819   return true;
820 }
821 
822 //______________________________________________________________________________
823 void G4MultiUnion::Voxelize()
824 {
825   fVoxels.Voxelize(fSolids, fTransformObjs);
826 }
827 
828 //______________________________________________________________________________
829 G4int G4MultiUnion::SafetyFromOutsideNumberNode(const G4ThreeVector& aPoint,
830                                                 G4double& safetyMin) const
831 {
832   // Method returning the closest node from a point located outside a
833   // G4MultiUnion.
834   // This is used to compute the normal in the case no candidate has been found.
835 
836   const std::vector<G4VoxelBox>& boxes = fVoxels.GetBoxes();
837   safetyMin = kInfinity;
838   std::size_t safetyNode = 0;
839   G4ThreeVector localPoint;
840 
841   std::size_t numNodes = fSolids.size();
842   for (std::size_t i = 0; i < numNodes; ++i)
843   {
844     G4double d2xyz = 0.;
845     G4double dxyz0 = std::abs(aPoint.x() - boxes[i].pos.x()) - boxes[i].hlen.x();
846     if (dxyz0 > safetyMin) continue;
847     G4double dxyz1 = std::abs(aPoint.y() - boxes[i].pos.y()) - boxes[i].hlen.y();
848     if (dxyz1 > safetyMin) continue;
849     G4double dxyz2 = std::abs(aPoint.z() - boxes[i].pos.z()) - boxes[i].hlen.z();
850     if (dxyz2 > safetyMin) continue;
851 
852     if (dxyz0 > 0) d2xyz += dxyz0 * dxyz0;
853     if (dxyz1 > 0) d2xyz += dxyz1 * dxyz1;
854     if (dxyz2 > 0) d2xyz += dxyz2 * dxyz2;
855     if (d2xyz >= safetyMin * safetyMin) continue;
856 
857     G4VSolid& solid = *fSolids[i];
858     const G4Transform3D& transform = fTransformObjs[i];
859     localPoint = GetLocalPoint(transform, aPoint);
860     fAccurate = true;
861     G4double safety = solid.DistanceToIn(localPoint);
862     fAccurate = false;
863     if (safetyMin > safety)
864     {
865       safetyMin = safety;
866       safetyNode = i;
867     }
868   }
869   return (G4int)safetyNode;
870 }
871 
872 //______________________________________________________________________________
873 void G4MultiUnion::TransformLimits(G4ThreeVector& min, G4ThreeVector& max,
874                                    const G4Transform3D& transformation) const
875 {
876   // The goal of this method is to convert the quantities min and max
877   // (representing the bounding box of a given solid in its local frame)
878   // to the main frame, using "transformation"
879 
880   G4ThreeVector vertices[8] =     // Detemination of the vertices thanks to
881   {                               // the extension of each solid:
882     G4ThreeVector(min.x(), min.y(), min.z()), // 1st vertice:
883     G4ThreeVector(min.x(), max.y(), min.z()), // 2nd vertice:
884     G4ThreeVector(max.x(), max.y(), min.z()),
885     G4ThreeVector(max.x(), min.y(), min.z()),
886     G4ThreeVector(min.x(), min.y(), max.z()),
887     G4ThreeVector(min.x(), max.y(), max.z()),
888     G4ThreeVector(max.x(), max.y(), max.z()),
889     G4ThreeVector(max.x(), min.y(), max.z())
890   };
891 
892   min.set(kInfinity,kInfinity,kInfinity);
893   max.set(-kInfinity,-kInfinity,-kInfinity);
894 
895   // Loop on th vertices
896   G4int limit = sizeof(vertices) / sizeof(G4ThreeVector);
897   for (G4int i = 0 ; i < limit; ++i)
898   {
899     // From local frame to the global one:
900     // Current positions on the three axis:
901     G4ThreeVector current = GetGlobalPoint(transformation, vertices[i]);
902 
903     // If need be, replacement of the min & max values:
904     if (current.x() > max.x()) max.setX(current.x());
905     if (current.x() < min.x()) min.setX(current.x());
906 
907     if (current.y() > max.y()) max.setY(current.y());
908     if (current.y() < min.y()) min.setY(current.y());
909 
910     if (current.z() > max.z()) max.setZ(current.z());
911     if (current.z() < min.z()) min.setZ(current.z());
912   }
913 }
914 
915 // Stream object contents to an output stream
916 //______________________________________________________________________________
917 std::ostream& G4MultiUnion::StreamInfo(std::ostream& os) const
918 {
919   G4long oldprc = os.precision(16);
920   os << "-----------------------------------------------------------\n"
921      << "                *** Dump for solid - " << GetName() << " ***\n"
922      << "                ===================================================\n"
923      << " Solid type: G4MultiUnion\n"
924      << " Parameters: \n";
925      std::size_t numNodes = fSolids.size();
926      for (std::size_t i = 0 ; i < numNodes ; ++i)
927      {
928       G4VSolid& solid = *fSolids[i];
929       solid.StreamInfo(os);
930       const G4Transform3D& transform = fTransformObjs[i];
931       os << " Translation is " << transform.getTranslation() << " \n";
932       os << " Rotation is :" << " \n";
933       os << " " << transform.getRotation() << "\n";
934      }
935   os << "             \n"
936      << "-----------------------------------------------------------\n";
937   os.precision(oldprc);
938 
939   return os;
940 }
941 
942 //______________________________________________________________________________
943 G4ThreeVector G4MultiUnion::GetPointOnSurface() const
944 {
945   G4ThreeVector point;
946 
947   G4long size = fSolids.size();
948 
949   do
950   {
951     G4long rnd = G4RandFlat::shootInt(G4long(0), size);
952     G4VSolid& solid = *fSolids[rnd];
953     point = solid.GetPointOnSurface();
954     const G4Transform3D& transform = fTransformObjs[rnd];
955     point = GetGlobalPoint(transform, point);
956   }
957   while (Inside(point) != EInside::kSurface);
958 
959   return point;
960 }
961 
962 //______________________________________________________________________________
963 void 
964 G4MultiUnion::DescribeYourselfTo ( G4VGraphicsScene& scene ) const 
965 {
966   scene.AddSolid (*this);
967 }
968 
969 //______________________________________________________________________________
970 G4Polyhedron* G4MultiUnion::CreatePolyhedron() const
971 {
972   if (G4BooleanSolid::GetExternalBooleanProcessor() == nullptr)
973   {
974     HepPolyhedronProcessor processor;
975     HepPolyhedronProcessor::Operation operation = HepPolyhedronProcessor::UNION;
976 
977     G4VSolid* solidA = GetSolid(0);
978     const G4Transform3D transform0 = GetTransformation(0);
979     G4DisplacedSolid dispSolidA("placedA", solidA, transform0);
980 
981     auto top = new G4Polyhedron(*dispSolidA.GetPolyhedron());
982 
983     for (G4int i = 1; i < GetNumberOfSolids(); ++i)
984     {
985       G4VSolid* solidB = GetSolid(i);
986       const G4Transform3D transform = GetTransformation(i);
987       G4DisplacedSolid dispSolidB("placedB", solidB, transform);
988       G4Polyhedron* operand = dispSolidB.GetPolyhedron();
989       processor.push_back(operation, *operand);
990     }
991 
992     if (processor.execute(*top))
993     {
994       return top;
995     }
996     else
997     {
998       return nullptr;
999     }
1000   }
1001   else
1002   {
1003     return G4BooleanSolid::GetExternalBooleanProcessor()->Process(this);
1004   }
1005 }
1006 
1007 //______________________________________________________________________________
1008 G4Polyhedron* G4MultiUnion::GetPolyhedron() const
1009 {
1010   if (fpPolyhedron == nullptr ||
1011       fRebuildPolyhedron ||
1012       fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
1013       fpPolyhedron->GetNumberOfRotationSteps())
1014   {
1015     G4AutoLock l(&polyhedronMutex);
1016     delete fpPolyhedron;
1017     fpPolyhedron = CreatePolyhedron();
1018     fRebuildPolyhedron = false;
1019     l.unlock();
1020   }
1021   return fpPolyhedron;
1022 }
1023