Files
geant4/source/geometry/solids/Boolean/src/G4MultiUnion.cc
T
2024-12-06 11:11:40 +01:00

1023 lines
34 KiB
C++

//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
// Implementation of G4MultiUnion class
//
// 19.10.12 M.Gayer - Original implementation from USolids module
// 06.04.17 G.Cosmo - Adapted implementation in Geant4 for VecGeom migration
// --------------------------------------------------------------------
#include <iostream>
#include <sstream>
#include "G4MultiUnion.hh"
#include "Randomize.hh"
#include "G4GeometryTolerance.hh"
#include "G4BoundingEnvelope.hh"
#include "G4AffineTransform.hh"
#include "G4DisplacedSolid.hh"
#include "G4VGraphicsScene.hh"
#include "G4Polyhedron.hh"
#include "G4PolyhedronArbitrary.hh"
#include "HepPolyhedronProcessor.h"
#include "G4BooleanSolid.hh"
#include "G4AutoLock.hh"
namespace
{
G4Mutex polyhedronMutex = G4MUTEX_INITIALIZER;
}
//______________________________________________________________________________
G4MultiUnion::G4MultiUnion(const G4String& name)
: G4VSolid(name)
{
SetName(name);
fSolids.clear();
fTransformObjs.clear();
kRadTolerance = G4GeometryTolerance::GetInstance()->GetRadialTolerance();
}
//______________________________________________________________________________
G4MultiUnion::~G4MultiUnion()
= default;
//______________________________________________________________________________
void G4MultiUnion::AddNode(G4VSolid& solid, const G4Transform3D& trans)
{
fSolids.push_back(&solid);
fTransformObjs.push_back(trans); // Store a local copy of transformations
}
//______________________________________________________________________________
void G4MultiUnion::AddNode(G4VSolid* solid, const G4Transform3D& trans)
{
fSolids.push_back(solid);
fTransformObjs.push_back(trans); // Store a local copy of transformations
}
//______________________________________________________________________________
G4VSolid* G4MultiUnion::Clone() const
{
return new G4MultiUnion(*this);
}
// Copy constructor
//______________________________________________________________________________
G4MultiUnion::G4MultiUnion(const G4MultiUnion& rhs)
: G4VSolid(rhs), fCubicVolume(rhs.fCubicVolume),
fSurfaceArea(rhs.fSurfaceArea),
kRadTolerance(rhs.kRadTolerance), fAccurate(rhs.fAccurate)
{
}
// Fake default constructor for persistency
//______________________________________________________________________________
G4MultiUnion::G4MultiUnion( __void__& a )
: G4VSolid(a)
{
}
// Assignment operator
//______________________________________________________________________________
G4MultiUnion& G4MultiUnion::operator = (const G4MultiUnion& rhs)
{
// Check assignment to self
//
if (this == &rhs)
{
return *this;
}
// Copy base class data
//
G4VSolid::operator=(rhs);
return *this;
}
//______________________________________________________________________________
G4double G4MultiUnion::GetCubicVolume()
{
if (fCubicVolume == 0.0)
{
fCubicVolume = EstimateCubicVolume(1000000, 0.001);
}
return fCubicVolume;
}
//______________________________________________________________________________
G4double
G4MultiUnion::DistanceToInNoVoxels(const G4ThreeVector& aPoint,
const G4ThreeVector& aDirection) const
{
G4ThreeVector direction = aDirection.unit();
G4ThreeVector localPoint, localDirection;
G4double minDistance = kInfinity;
std::size_t numNodes = fSolids.size();
for (std::size_t i = 0 ; i < numNodes ; ++i)
{
G4VSolid& solid = *fSolids[i];
const G4Transform3D& transform = fTransformObjs[i];
localPoint = GetLocalPoint(transform, aPoint);
localDirection = GetLocalVector(transform, direction);
G4double distance = solid.DistanceToIn(localPoint, localDirection);
if (minDistance > distance) minDistance = distance;
}
return minDistance;
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToInCandidates(const G4ThreeVector& aPoint,
const G4ThreeVector& direction,
std::vector<G4int>& candidates,
G4SurfBits& bits) const
{
std::size_t candidatesCount = candidates.size();
G4ThreeVector localPoint, localDirection;
G4double minDistance = kInfinity;
for (std::size_t i = 0 ; i < candidatesCount; ++i)
{
G4int candidate = candidates[i];
G4VSolid& solid = *fSolids[candidate];
const G4Transform3D& transform = fTransformObjs[candidate];
localPoint = GetLocalPoint(transform, aPoint);
localDirection = GetLocalVector(transform, direction);
G4double distance = solid.DistanceToIn(localPoint, localDirection);
if (minDistance > distance) minDistance = distance;
bits.SetBitNumber(candidate);
if (minDistance == 0) break;
}
return minDistance;
}
// Algorithm note: we have to look also for all other objects in next voxels,
// if the distance is not shorter ... we have to do it because,
// for example for objects which starts in first voxel in which they
// do not collide with direction line, but in second it collides...
// The idea of crossing voxels would be still applicable,
// because this way we could exclude from the testing such solids,
// which were found that obviously are not good candidates, because
// they would return infinity
// But if distance is smaller than the shift to next voxel, we can return
// it immediately
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToIn(const G4ThreeVector& aPoint,
const G4ThreeVector& aDirection) const
{
G4double minDistance = kInfinity;
G4ThreeVector direction = aDirection.unit();
G4double shift = fVoxels.DistanceToFirst(aPoint, direction);
if (shift == kInfinity) return shift;
G4ThreeVector currentPoint = aPoint;
if (shift != 0.0) currentPoint += direction * shift;
G4SurfBits exclusion(fVoxels.GetBitsPerSlice());
std::vector<G4int> candidates, curVoxel(3);
fVoxels.GetVoxel(curVoxel, currentPoint);
do
{
{
if (fVoxels.GetCandidatesVoxelArray(curVoxel, candidates, &exclusion) != 0)
{
G4double distance = DistanceToInCandidates(aPoint, direction,
candidates, exclusion);
if (minDistance > distance) minDistance = distance;
if (distance < shift) break;
}
}
shift = fVoxels.DistanceToNext(aPoint, direction, curVoxel);
}
while (minDistance > shift);
return minDistance;
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToOutNoVoxels(const G4ThreeVector& aPoint,
const G4ThreeVector& aDirection,
G4ThreeVector* aNormal) const
{
// Computes distance from a point presumably outside the solid to the solid
// surface. Ignores first surface if the point is actually inside.
// Early return infinity in case the safety to any surface is found greater
// than the proposed step aPstep.
// The normal vector to the crossed surface is filled only in case the box
// is crossed, otherwise aNormal->IsNull() is true.
// algorithm:
G4ThreeVector direction = aDirection.unit();
G4ThreeVector localPoint, localDirection;
G4int ignoredSolid = -1;
G4double resultDistToOut = 0;
G4ThreeVector currentPoint = aPoint;
auto numNodes = (G4int)fSolids.size();
for (auto i = 0; i < numNodes; ++i)
{
if (i != ignoredSolid)
{
G4VSolid& solid = *fSolids[i];
const G4Transform3D& transform = fTransformObjs[i];
localPoint = GetLocalPoint(transform, currentPoint);
localDirection = GetLocalVector(transform, direction);
EInside location = solid.Inside(localPoint);
if (location != EInside::kOutside)
{
G4double distance = solid.DistanceToOut(localPoint, localDirection,
false, nullptr, aNormal);
if (distance < kInfinity)
{
if (resultDistToOut == kInfinity) resultDistToOut = 0;
if (distance > 0)
{
currentPoint = GetGlobalPoint(transform, localPoint
+ distance*localDirection);
resultDistToOut += distance;
ignoredSolid = i; // skip the solid which we have just left
i = -1; // force the loop to continue from 0
}
}
}
}
}
return resultDistToOut;
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToOut(const G4ThreeVector& aPoint,
const G4ThreeVector& aDirection,
const G4bool /* calcNorm */,
G4bool* /* validNorm */,
G4ThreeVector* aNormal) const
{
return DistanceToOutVoxels(aPoint, aDirection, aNormal);
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToOutVoxels(const G4ThreeVector& aPoint,
const G4ThreeVector& aDirection,
G4ThreeVector* aNormal) const
{
// Computes distance from a point presumably inside the solid to the solid
// surface. Ignores first surface along each axis systematically (for points
// inside or outside. Early returns zero in case the second surface is behind
// the starting point.
// o The proposed step is ignored.
// o The normal vector to the crossed surface is always filled.
// In the case the considered point is located inside the G4MultiUnion
// structure, the treatments are as follows:
// - investigation of the candidates for the passed point
// - progressive moving of the point towards the surface, along the
// passed direction
// - processing of the normal
G4ThreeVector direction = aDirection.unit();
std::vector<G4int> candidates;
G4double distance = 0;
std::size_t numNodes = 2*fSolids.size();
std::size_t count=0;
if (fVoxels.GetCandidatesVoxelArray(aPoint, candidates) != 0)
{
// For normal case for which we presume the point is inside
G4ThreeVector localPoint, localDirection, localNormal;
G4ThreeVector currentPoint = aPoint;
G4SurfBits exclusion(fVoxels.GetBitsPerSlice());
G4bool notOutside;
G4ThreeVector maxNormal;
do
{
notOutside = false;
G4double maxDistance = -kInfinity;
G4int maxCandidate = 0;
G4ThreeVector maxLocalPoint;
std::size_t limit = candidates.size();
for (std::size_t i = 0 ; i < limit ; ++i)
{
G4int candidate = candidates[i];
// ignore the current component (that you just got out of) since
// numerically the propagated point will be on its surface
G4VSolid& solid = *fSolids[candidate];
const G4Transform3D& transform = fTransformObjs[candidate];
// The coordinates of the point are modified so as to fit the
// intrinsic solid local frame:
localPoint = GetLocalPoint(transform, currentPoint);
// DistanceToOut at least for Trd sometimes return non-zero value
// even from points that are outside. Therefore, this condition
// must currently be here, otherwise it would not work.
// But it means it would be slower.
if (solid.Inside(localPoint) != EInside::kOutside)
{
notOutside = true;
localDirection = GetLocalVector(transform, direction);
// propagate with solid.DistanceToOut
G4double shift = solid.DistanceToOut(localPoint, localDirection,
false, nullptr, &localNormal);
if (maxDistance < shift)
{
maxDistance = shift;
maxCandidate = candidate;
maxNormal = localNormal;
}
}
}
if (notOutside)
{
const G4Transform3D& transform = fTransformObjs[maxCandidate];
// convert from local normal
if (aNormal != nullptr) *aNormal = GetGlobalVector(transform, maxNormal);
distance += maxDistance;
currentPoint += maxDistance * direction;
if(maxDistance == 0.) ++count;
// the current component will be ignored
exclusion.SetBitNumber(maxCandidate);
EInside location = InsideWithExclusion(currentPoint, &exclusion);
// perform a Inside
// it should be excluded current solid from checking
// we have to collect the maximum distance from all given candidates.
// such "maximum" candidate should be then used for finding next
// candidates
if (location == EInside::kOutside)
{
// else return cumulated distances to outside of the traversed
// components
break;
}
// if inside another component, redo 1 to 3 but add the next
// DistanceToOut on top of the previous.
// and fill the candidates for the corresponding voxel (just
// exiting current component along direction)
candidates.clear();
fVoxels.GetCandidatesVoxelArray(currentPoint, candidates, &exclusion);
exclusion.ResetBitNumber(maxCandidate);
}
}
while ((notOutside) && (count < numNodes));
}
return distance;
}
//______________________________________________________________________________
EInside G4MultiUnion::InsideWithExclusion(const G4ThreeVector& aPoint,
G4SurfBits* exclusion) const
{
// Classify point location with respect to solid:
// o eInside - inside the solid
// o eSurface - close to surface within tolerance
// o eOutside - outside the solid
// Hitherto, it is considered that only parallelepipedic nodes
// can be added to the container
// Implementation using voxelisation techniques:
// ---------------------------------------------
G4ThreeVector localPoint;
EInside location = EInside::kOutside;
std::vector<G4int> candidates;
std::vector<G4MultiUnionSurface> surfaces;
// TODO: test if it works well and if so measure performance
// TODO: getPointIndex should not be used, instead GetVoxel + GetVoxelsIndex
// should be used
// TODO: than pass result to GetVoxel further to GetCandidatesVoxelArray
// TODO: eventually GetVoxel should be inlined here, early exit if any
// binary search is -1
G4int limit = fVoxels.GetCandidatesVoxelArray(aPoint, candidates, exclusion);
for (G4int i = 0 ; i < limit ; ++i)
{
G4int candidate = candidates[i];
G4VSolid& solid = *fSolids[candidate];
const G4Transform3D& transform = fTransformObjs[candidate];
// The coordinates of the point are modified so as to fit the intrinsic
// solid local frame:
localPoint = GetLocalPoint(transform, aPoint);
location = solid.Inside(localPoint);
if (location == EInside::kInside) return EInside::kInside;
else if (location == EInside::kSurface)
{
G4MultiUnionSurface surface;
surface.point = localPoint;
surface.solid = &solid;
surfaces.push_back(surface);
}
}
///////////////////////////////////////////////////////////////////////////
// Important comment: When two solids touch each other along a flat
// surface, the surface points will be considered as kSurface, while points
// located around will correspond to kInside (cf. G4UnionSolid)
std::size_t size = surfaces.size();
if (size == 0)
{
return EInside::kOutside;
}
for (std::size_t i = 0; i < size - 1; ++i)
{
G4MultiUnionSurface& left = surfaces[i];
for (std::size_t j = i + 1; j < size; ++j)
{
G4MultiUnionSurface& right = surfaces[j];
G4ThreeVector n, n2;
n = left.solid->SurfaceNormal(left.point);
n2 = right.solid->SurfaceNormal(right.point);
if ((n + n2).mag2() < 1000 * kRadTolerance)
return EInside::kInside;
}
}
return EInside::kSurface;
}
//______________________________________________________________________________
EInside G4MultiUnion::Inside(const G4ThreeVector& aPoint) const
{
// Classify point location with respect to solid:
// o eInside - inside the solid
// o eSurface - close to surface within tolerance
// o eOutside - outside the solid
// Hitherto, it is considered that only parallelepipedic nodes can be
// added to the container
// Implementation using voxelisation techniques:
// ---------------------------------------------
// return InsideIterator(aPoint);
EInside location = InsideWithExclusion(aPoint);
return location;
}
//______________________________________________________________________________
EInside G4MultiUnion::InsideNoVoxels(const G4ThreeVector& aPoint) const
{
G4ThreeVector localPoint;
EInside location = EInside::kOutside;
G4int countSurface = 0;
auto numNodes = (G4int)fSolids.size();
for (auto i = 0 ; i < numNodes ; ++i)
{
G4VSolid& solid = *fSolids[i];
G4Transform3D transform = GetTransformation(i);
// The coordinates of the point are modified so as to fit the
// intrinsic solid local frame:
localPoint = GetLocalPoint(transform, aPoint);
location = solid.Inside(localPoint);
if (location == EInside::kSurface)
++countSurface;
if (location == EInside::kInside) return EInside::kInside;
}
if (countSurface != 0) return EInside::kSurface;
return EInside::kOutside;
}
//______________________________________________________________________________
void G4MultiUnion::Extent(EAxis aAxis, G4double& aMin, G4double& aMax) const
{
// Determines the bounding box for the considered instance of "UMultipleUnion"
G4ThreeVector min, max;
auto numNodes = (G4int)fSolids.size();
for (auto i = 0 ; i < numNodes ; ++i)
{
G4VSolid& solid = *fSolids[i];
G4Transform3D transform = GetTransformation(i);
solid.BoundingLimits(min, max);
TransformLimits(min, max, transform);
if (i == 0)
{
switch (aAxis)
{
case kXAxis:
aMin = min.x();
aMax = max.x();
break;
case kYAxis:
aMin = min.y();
aMax = max.y();
break;
case kZAxis:
aMin = min.z();
aMax = max.z();
break;
default:
break;
}
}
else
{
// Determine the min/max on the considered axis:
switch (aAxis)
{
case kXAxis:
if (min.x() < aMin)
aMin = min.x();
if (max.x() > aMax)
aMax = max.x();
break;
case kYAxis:
if (min.y() < aMin)
aMin = min.y();
if (max.y() > aMax)
aMax = max.y();
break;
case kZAxis:
if (min.z() < aMin)
aMin = min.z();
if (max.z() > aMax)
aMax = max.z();
break;
default:
break;
}
}
}
}
//______________________________________________________________________________
void G4MultiUnion::BoundingLimits(G4ThreeVector& aMin,
G4ThreeVector& aMax) const
{
Extent(kXAxis, aMin[0], aMax[0]);
Extent(kYAxis, aMin[1], aMax[1]);
Extent(kZAxis, aMin[2], aMax[2]);
}
//______________________________________________________________________________
G4bool
G4MultiUnion::CalculateExtent(const EAxis pAxis,
const G4VoxelLimits& pVoxelLimit,
const G4AffineTransform& pTransform,
G4double& pMin, G4double& pMax) const
{
G4ThreeVector bmin, bmax;
// Get bounding box
BoundingLimits(bmin,bmax);
// Find extent
G4BoundingEnvelope bbox(bmin,bmax);
return bbox.CalculateExtent(pAxis,pVoxelLimit,pTransform,pMin,pMax);
}
//______________________________________________________________________________
G4ThreeVector G4MultiUnion::SurfaceNormal(const G4ThreeVector& aPoint) const
{
// Computes the localNormal on a surface and returns it as a unit vector.
// Must return a valid vector. (even if the point is not on the surface).
//
// On an edge or corner, provide an average localNormal of all facets within
// tolerance
// NOTE: the tolerance value used in here is not yet the global surface
// tolerance - we will have to revise this value - TODO
std::vector<G4int> candidates;
G4ThreeVector localPoint, normal, localNormal;
G4double safety = kInfinity;
G4int node = 0;
///////////////////////////////////////////////////////////////////////////
// Important comment: Cases for which the point is located on an edge or
// on a vertice remain to be treated
// determine weather we are in voxel area
if (fVoxels.GetCandidatesVoxelArray(aPoint, candidates) != 0)
{
std::size_t limit = candidates.size();
for (std::size_t i = 0 ; i < limit ; ++i)
{
G4int candidate = candidates[i];
const G4Transform3D& transform = fTransformObjs[candidate];
// The coordinates of the point are modified so as to fit the intrinsic
// solid local frame:
localPoint = GetLocalPoint(transform, aPoint);
G4VSolid& solid = *fSolids[candidate];
EInside location = solid.Inside(localPoint);
if (location == EInside::kSurface)
{
// normal case when point is on surface, we pick first solid
normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
return normal.unit();
}
else
{
// collect the smallest safety and remember solid node
G4double s = (location == EInside::kInside)
? solid.DistanceToOut(localPoint)
: solid.DistanceToIn(localPoint);
if (s < safety)
{
safety = s;
node = candidate;
}
}
}
// on none of the solids, the point was not on the surface
G4VSolid& solid = *fSolids[node];
const G4Transform3D& transform = fTransformObjs[node];
localPoint = GetLocalPoint(transform, aPoint);
normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
return normal.unit();
}
else
{
// for the case when point is certainly outside:
// find a solid in union with the smallest safety
node = SafetyFromOutsideNumberNode(aPoint, safety);
G4VSolid& solid = *fSolids[node];
const G4Transform3D& transform = fTransformObjs[node];
localPoint = GetLocalPoint(transform, aPoint);
// evaluate normal for point at this found solid
// and transform multi-union coordinates
normal = GetGlobalVector(transform, solid.SurfaceNormal(localPoint));
return normal.unit();
}
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToOut(const G4ThreeVector& point) const
{
// Estimates isotropic distance to the surface of the solid. This must
// be either accurate or an underestimate.
// Two modes: - default/fast mode, sacrificing accuracy for speed
// - "precise" mode, requests accurate value if available.
std::vector<G4int> candidates;
G4ThreeVector localPoint;
G4double safetyMin = kInfinity;
// In general, the value return by DistanceToIn(p) will not be the exact
// but only an undervalue (cf. overlaps)
fVoxels.GetCandidatesVoxelArray(point, candidates);
std::size_t limit = candidates.size();
for (std::size_t i = 0; i < limit; ++i)
{
G4int candidate = candidates[i];
// The coordinates of the point are modified so as to fit the intrinsic
// solid local frame:
const G4Transform3D& transform = fTransformObjs[candidate];
localPoint = GetLocalPoint(transform, point);
G4VSolid& solid = *fSolids[candidate];
if (solid.Inside(localPoint) == EInside::kInside)
{
G4double safety = solid.DistanceToOut(localPoint);
if (safetyMin > safety) safetyMin = safety;
}
}
if (safetyMin == kInfinity) safetyMin = 0; // we are not inside
return safetyMin;
}
//______________________________________________________________________________
G4double G4MultiUnion::DistanceToIn(const G4ThreeVector& point) const
{
// Estimates the isotropic safety from a point outside the current solid to
// any of its surfaces. The algorithm may be accurate or should provide a fast
// underestimate.
if (!fAccurate) { return fVoxels.DistanceToBoundingBox(point); }
const std::vector<G4VoxelBox>& boxes = fVoxels.GetBoxes();
G4double safetyMin = kInfinity;
G4ThreeVector localPoint;
std::size_t numNodes = fSolids.size();
for (std::size_t j = 0; j < numNodes; ++j)
{
G4ThreeVector dxyz;
if (j > 0)
{
const G4ThreeVector& pos = boxes[j].pos;
const G4ThreeVector& hlen = boxes[j].hlen;
for (auto i = 0; i <= 2; ++i)
// distance to middle point - hlength => distance from point to border
// of x,y,z
if ((dxyz[i] = std::abs(point[i] - pos[i]) - hlen[i]) > safetyMin)
continue;
G4double d2xyz = 0.;
for (auto i = 0; i <= 2; ++i)
if (dxyz[i] > 0) d2xyz += dxyz[i] * dxyz[i];
// minimal distance is at least this, but could be even higher. therefore,
// we can stop if previous was already lower, let us check if it does any
// chance to be better tha previous values...
if (d2xyz >= safetyMin * safetyMin)
{
continue;
}
}
const G4Transform3D& transform = fTransformObjs[j];
localPoint = GetLocalPoint(transform, point);
G4VSolid& solid = *fSolids[j];
G4double safety = solid.DistanceToIn(localPoint);
if (safety <= 0) return safety;
// it was detected, that the point is not located outside
if (safetyMin > safety) safetyMin = safety;
}
return safetyMin;
}
//______________________________________________________________________________
G4double G4MultiUnion::GetSurfaceArea()
{
if (fSurfaceArea == 0.0)
{
fSurfaceArea = EstimateSurfaceArea(1000000, 0.001);
}
return fSurfaceArea;
}
//______________________________________________________________________________
G4int G4MultiUnion::GetNumOfConstituents() const
{
G4int num = 0;
for (const auto solid : fSolids) { num += solid->GetNumOfConstituents(); }
return num;
}
//______________________________________________________________________________
G4bool G4MultiUnion::IsFaceted() const
{
for (const auto solid : fSolids) { if (!solid->IsFaceted()) return false; }
return true;
}
//______________________________________________________________________________
void G4MultiUnion::Voxelize()
{
fVoxels.Voxelize(fSolids, fTransformObjs);
}
//______________________________________________________________________________
G4int G4MultiUnion::SafetyFromOutsideNumberNode(const G4ThreeVector& aPoint,
G4double& safetyMin) const
{
// Method returning the closest node from a point located outside a
// G4MultiUnion.
// This is used to compute the normal in the case no candidate has been found.
const std::vector<G4VoxelBox>& boxes = fVoxels.GetBoxes();
safetyMin = kInfinity;
std::size_t safetyNode = 0;
G4ThreeVector localPoint;
std::size_t numNodes = fSolids.size();
for (std::size_t i = 0; i < numNodes; ++i)
{
G4double d2xyz = 0.;
G4double dxyz0 = std::abs(aPoint.x() - boxes[i].pos.x()) - boxes[i].hlen.x();
if (dxyz0 > safetyMin) continue;
G4double dxyz1 = std::abs(aPoint.y() - boxes[i].pos.y()) - boxes[i].hlen.y();
if (dxyz1 > safetyMin) continue;
G4double dxyz2 = std::abs(aPoint.z() - boxes[i].pos.z()) - boxes[i].hlen.z();
if (dxyz2 > safetyMin) continue;
if (dxyz0 > 0) d2xyz += dxyz0 * dxyz0;
if (dxyz1 > 0) d2xyz += dxyz1 * dxyz1;
if (dxyz2 > 0) d2xyz += dxyz2 * dxyz2;
if (d2xyz >= safetyMin * safetyMin) continue;
G4VSolid& solid = *fSolids[i];
const G4Transform3D& transform = fTransformObjs[i];
localPoint = GetLocalPoint(transform, aPoint);
fAccurate = true;
G4double safety = solid.DistanceToIn(localPoint);
fAccurate = false;
if (safetyMin > safety)
{
safetyMin = safety;
safetyNode = i;
}
}
return (G4int)safetyNode;
}
//______________________________________________________________________________
void G4MultiUnion::TransformLimits(G4ThreeVector& min, G4ThreeVector& max,
const G4Transform3D& transformation) const
{
// The goal of this method is to convert the quantities min and max
// (representing the bounding box of a given solid in its local frame)
// to the main frame, using "transformation"
G4ThreeVector vertices[8] = // Detemination of the vertices thanks to
{ // the extension of each solid:
G4ThreeVector(min.x(), min.y(), min.z()), // 1st vertice:
G4ThreeVector(min.x(), max.y(), min.z()), // 2nd vertice:
G4ThreeVector(max.x(), max.y(), min.z()),
G4ThreeVector(max.x(), min.y(), min.z()),
G4ThreeVector(min.x(), min.y(), max.z()),
G4ThreeVector(min.x(), max.y(), max.z()),
G4ThreeVector(max.x(), max.y(), max.z()),
G4ThreeVector(max.x(), min.y(), max.z())
};
min.set(kInfinity,kInfinity,kInfinity);
max.set(-kInfinity,-kInfinity,-kInfinity);
// Loop on th vertices
G4int limit = sizeof(vertices) / sizeof(G4ThreeVector);
for (G4int i = 0 ; i < limit; ++i)
{
// From local frame to the global one:
// Current positions on the three axis:
G4ThreeVector current = GetGlobalPoint(transformation, vertices[i]);
// If need be, replacement of the min & max values:
if (current.x() > max.x()) max.setX(current.x());
if (current.x() < min.x()) min.setX(current.x());
if (current.y() > max.y()) max.setY(current.y());
if (current.y() < min.y()) min.setY(current.y());
if (current.z() > max.z()) max.setZ(current.z());
if (current.z() < min.z()) min.setZ(current.z());
}
}
// Stream object contents to an output stream
//______________________________________________________________________________
std::ostream& G4MultiUnion::StreamInfo(std::ostream& os) const
{
G4long oldprc = os.precision(16);
os << "-----------------------------------------------------------\n"
<< " *** Dump for solid - " << GetName() << " ***\n"
<< " ===================================================\n"
<< " Solid type: G4MultiUnion\n"
<< " Parameters: \n";
std::size_t numNodes = fSolids.size();
for (std::size_t i = 0 ; i < numNodes ; ++i)
{
G4VSolid& solid = *fSolids[i];
solid.StreamInfo(os);
const G4Transform3D& transform = fTransformObjs[i];
os << " Translation is " << transform.getTranslation() << " \n";
os << " Rotation is :" << " \n";
os << " " << transform.getRotation() << "\n";
}
os << " \n"
<< "-----------------------------------------------------------\n";
os.precision(oldprc);
return os;
}
//______________________________________________________________________________
G4ThreeVector G4MultiUnion::GetPointOnSurface() const
{
G4ThreeVector point;
G4long size = fSolids.size();
do
{
G4long rnd = G4RandFlat::shootInt(G4long(0), size);
G4VSolid& solid = *fSolids[rnd];
point = solid.GetPointOnSurface();
const G4Transform3D& transform = fTransformObjs[rnd];
point = GetGlobalPoint(transform, point);
}
while (Inside(point) != EInside::kSurface);
return point;
}
//______________________________________________________________________________
void
G4MultiUnion::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
{
scene.AddSolid (*this);
}
//______________________________________________________________________________
G4Polyhedron* G4MultiUnion::CreatePolyhedron() const
{
if (G4BooleanSolid::GetExternalBooleanProcessor() == nullptr)
{
HepPolyhedronProcessor processor;
HepPolyhedronProcessor::Operation operation = HepPolyhedronProcessor::UNION;
G4VSolid* solidA = GetSolid(0);
const G4Transform3D transform0 = GetTransformation(0);
G4DisplacedSolid dispSolidA("placedA", solidA, transform0);
auto top = new G4Polyhedron(*dispSolidA.GetPolyhedron());
for (G4int i = 1; i < GetNumberOfSolids(); ++i)
{
G4VSolid* solidB = GetSolid(i);
const G4Transform3D transform = GetTransformation(i);
G4DisplacedSolid dispSolidB("placedB", solidB, transform);
G4Polyhedron* operand = dispSolidB.GetPolyhedron();
processor.push_back(operation, *operand);
}
if (processor.execute(*top))
{
return top;
}
else
{
return nullptr;
}
}
else
{
return G4BooleanSolid::GetExternalBooleanProcessor()->Process(this);
}
}
//______________________________________________________________________________
G4Polyhedron* G4MultiUnion::GetPolyhedron() const
{
if (fpPolyhedron == nullptr ||
fRebuildPolyhedron ||
fpPolyhedron->GetNumberOfRotationStepsAtTimeOfCreation() !=
fpPolyhedron->GetNumberOfRotationSteps())
{
G4AutoLock l(&polyhedronMutex);
delete fpPolyhedron;
fpPolyhedron = CreatePolyhedron();
fRebuildPolyhedron = false;
l.unlock();
}
return fpPolyhedron;
}