Files
geant4/source/geometry/solids/usolids/src/UCons.cc
T
2016-06-10 11:51:14 +02:00

2229 lines
58 KiB
C++

//
// ********************************************************************
// * This Software is part of the AIDA Unified Solids Library package *
// * See: https://aidasoft.web.cern.ch/USolids *
// ********************************************************************
//
// $Id:$
//
// --------------------------------------------------------------------
//
// UCons
//
// 19.10.12 Marek Gayer
// Created from original implementation in Geant4
// --------------------------------------------------------------------
#include "UUtils.hh"
#include <string>
#include <cmath>
#include <sstream>
#include "UCons.hh"
using namespace std;
////////////////////////////////////////////////////////////////////////
//
// Private enum: Not for external use - used by distanceToOut
enum ESide {kNull, kRMin, kRMax, kSPhi, kEPhi, kPZ, kMZ};
// used by normal
enum ENorm {kNRMin, kNRMax, kNSPhi, kNEPhi, kNZ};
//////////////////////////////////////////////////////////////////////////
//
// constructor - check parameters, convert angles so 0<sphi+dpshi<=2_PI
// - note if pDPhi>2PI then reset to 2PI
UCons::UCons(const std::string& pName,
double pRmin1, double pRmax1,
double pRmin2, double pRmax2,
double pDz,
double pSPhi, double pDPhi)
: VUSolid(pName.c_str()), fRmin1(pRmin1), fRmin2(pRmin2),
fRmax1(pRmax1), fRmax2(pRmax2), fDz(pDz), fSPhi(0.), fDPhi(0.)
{
kRadTolerance = frTolerance;
kAngTolerance = faTolerance;
// Check z-len
//
if (pDz < 0)
{
std::ostringstream message;
message << "Invalid Z half-length for Solid: " << GetName() << std::endl
<< " hZ = " << pDz;
UUtils::Exception("UCons::UCons()", "UGeomSolids", FatalErrorInArguments, 1, message.str().c_str());
}
// Check radii
//
if (((pRmin1 >= pRmax1) || (pRmin2 >= pRmax2) || (pRmin1 < 0)) && (pRmin2 < 0))
{
std::ostringstream message;
message << "Invalid values of radii for Solid: " << GetName() << std::endl
<< " pRmin1 = " << pRmin1 << ", pRmin2 = " << pRmin2
<< ", pRmax1 = " << pRmax1 << ", pRmax2 = " << pRmax2;
UUtils::Exception("UCons::UCons()", "UGeomSolids", FatalErrorInArguments, 1, message.str().c_str());
}
if ((pRmin1 == 0.0) && (pRmin2 > 0.0))
{
fRmin1 = 1e3 * kRadTolerance;
}
if ((pRmin2 == 0.0) && (pRmin1 > 0.0))
{
fRmin2 = 1e3 * kRadTolerance;
}
// Check angles
//
CheckPhiAngles(pSPhi, pDPhi);
Initialize();
}
///////////////////////////////////////////////////////////////////////
//
// Fake default constructor - sets only member data and allocates memory
// for usage restricted to object persistency.
//
UCons::UCons(/* __void__& a */)
: VUSolid(""), kRadTolerance(0.), kAngTolerance(0.),
fRmin1(0.), fRmin2(0.), fRmax1(0.), fRmax2(0.), fDz(0.),
fSPhi(0.), fDPhi(0.), sinCPhi(0.), cosCPhi(0.), cosHDPhiOT(0.),
cosHDPhiIT(0.), sinSPhi(0.), cosSPhi(0.), sinEPhi(0.), cosEPhi(0.),
fPhiFullCone(false)
{
Initialize();
}
///////////////////////////////////////////////////////////////////////
//
// Destructor
UCons::~UCons()
{
}
//////////////////////////////////////////////////////////////////////////
//
// Copy constructor
UCons::UCons(const UCons& rhs)
: VUSolid(rhs), kRadTolerance(rhs.kRadTolerance),
kAngTolerance(rhs.kAngTolerance), fRmin1(rhs.fRmin1), fRmin2(rhs.fRmin2),
fRmax1(rhs.fRmax1), fRmax2(rhs.fRmax2), fDz(rhs.fDz), fSPhi(rhs.fSPhi),
fDPhi(rhs.fDPhi), sinCPhi(rhs.sinCPhi), cosCPhi(rhs.cosCPhi),
cosHDPhiOT(rhs.cosHDPhiOT), cosHDPhiIT(rhs.cosHDPhiIT),
sinSPhi(rhs.sinSPhi), cosSPhi(rhs.cosSPhi), sinEPhi(rhs.sinEPhi),
cosEPhi(rhs.cosEPhi), fPhiFullCone(rhs.fPhiFullCone)
{
Initialize();
}
//////////////////////////////////////////////////////////////////////////
//
// Assignment operator
UCons& UCons::operator = (const UCons& rhs)
{
// Check assignment to self
//
if (this == &rhs)
{
return *this;
}
// Copy base class data
//
VUSolid::operator=(rhs);
// Copy data
//
kRadTolerance = rhs.kRadTolerance;
kAngTolerance = rhs.kAngTolerance;
fRmin1 = rhs.fRmin1;
fRmin2 = rhs.fRmin2;
fRmax1 = rhs.fRmax1;
fRmax2 = rhs.fRmax2;
fDz = rhs.fDz;
fSPhi = rhs.fSPhi;
fDPhi = rhs.fDPhi;
sinCPhi = rhs.sinCPhi;
cosCPhi = rhs.cosCPhi;
cosHDPhiOT = rhs.cosHDPhiOT;
cosHDPhiIT = rhs.cosHDPhiIT;
sinSPhi = rhs.sinSPhi;
cosSPhi = rhs.cosSPhi;
sinEPhi = rhs.sinEPhi;
cosEPhi = rhs.cosEPhi;
fPhiFullCone = rhs.fPhiFullCone;
Initialize();
return *this;
}
////////////////////////////////////////////////////////////////////////
//
// Return Unit normal of surface closest to p
// - note if point on z axis, ignore phi divided sides
// - unsafe if point close to z axis a rmin=0 - no explicit checks
bool UCons::Normal(const UVector3& p, UVector3& n) const
{
int noSurfaces = 0;
double rho, pPhi;
double distZ, distRMin, distRMax;
double distSPhi = UUtils::kInfinity, distEPhi = UUtils::kInfinity;
double pRMin, widRMin;
double pRMax, widRMax;
static const double delta = 0.5 * VUSolid::Tolerance();
static const double dAngle = 0.5 * kAngTolerance;
UVector3 norm, sumnorm(0., 0., 0.), nZ = UVector3(0., 0., 1.);
UVector3 nR, nr(0., 0., 0.), nPs, nPe;
distZ = std::fabs(std::fabs(p.z) - fDz);
rho = std::sqrt(p.x * p.x + p.y * p.y);
pRMin = rho - p.z * tanRMin;
widRMin = fRmin2 - fDz * tanRMin;
distRMin = std::fabs(pRMin - widRMin) / secRMin;
pRMax = rho - p.z * tanRMax;
widRMax = fRmax2 - fDz * tanRMax;
distRMax = std::fabs(pRMax - widRMax) / secRMax;
if (!fPhiFullCone) // Protected against (0,0,z)
{
if (rho)
{
pPhi = std::atan2(p.y, p.x);
if (pPhi < fSPhi - delta)
{
pPhi += 2 * UUtils::kPi;
}
else if (pPhi > fSPhi + fDPhi + delta)
{
pPhi -= 2 * UUtils::kPi;
}
distSPhi = std::fabs(pPhi - fSPhi);
distEPhi = std::fabs(pPhi - fSPhi - fDPhi);
}
else if (!(fRmin1) || !(fRmin2))
{
distSPhi = 0.;
distEPhi = 0.;
}
nPs = UVector3(std::sin(fSPhi), -std::cos(fSPhi), 0);
nPe = UVector3(-std::sin(fSPhi + fDPhi), std::cos(fSPhi + fDPhi), 0);
}
if (rho > delta)
{
nR = UVector3(p.x / rho / secRMax, p.y / rho / secRMax, -tanRMax / secRMax);
if (fRmin1 || fRmin2)
{
nr = UVector3(-p.x / rho / secRMin, -p.y / rho / secRMin, tanRMin / secRMin);
}
}
if (distRMax <= delta)
{
noSurfaces ++;
sumnorm += nR;
}
if ((fRmin1 || fRmin2) && (distRMin <= delta))
{
noSurfaces ++;
sumnorm += nr;
}
if (!fPhiFullCone)
{
if (distSPhi <= dAngle)
{
noSurfaces ++;
sumnorm += nPs;
}
if (distEPhi <= dAngle)
{
noSurfaces ++;
sumnorm += nPe;
}
}
if (distZ <= delta)
{
noSurfaces ++;
if (p.z >= 0.)
{
sumnorm += nZ;
}
else
{
sumnorm -= nZ;
}
}
if (noSurfaces == 0)
{
#ifdef UDEBUG
UUtils::Exception("UCons::SurfaceNormal(p)", "GeomSolids1002",
Warning, 1, "Point p is not on surface !?");
#endif
norm = ApproxSurfaceNormal(p);
}
else if (noSurfaces == 1)
{
norm = sumnorm;
}
else
{
norm = sumnorm.Unit();
}
n = norm;
return (bool) noSurfaces;
}
////////////////////////////////////////////////////////////////////////////
//
// Algorithm for SurfaceNormal() following the original specification
// for points not on the surface
UVector3 UCons::ApproxSurfaceNormal(const UVector3& p) const
{
ENorm side;
UVector3 norm;
double rho, phi;
double distZ, distRMin, distRMax, distSPhi, distEPhi, distMin;
double pRMin, widRMin;
double pRMax, widRMax;
distZ = std::fabs(std::fabs(p.z) - fDz);
rho = std::sqrt(p.x * p.x + p.y * p.y);
pRMin = rho - p.z * tanRMin;
widRMin = fRmin2 - fDz * tanRMin;
distRMin = std::fabs(pRMin - widRMin) / secRMin;
pRMax = rho - p.z * tanRMax;
widRMax = fRmax2 - fDz * tanRMax;
distRMax = std::fabs(pRMax - widRMax) / secRMax;
if (distRMin < distRMax) // First minimum
{
if (distZ < distRMin)
{
distMin = distZ;
side = kNZ;
}
else
{
distMin = distRMin;
side = kNRMin;
}
}
else
{
if (distZ < distRMax)
{
distMin = distZ;
side = kNZ;
}
else
{
distMin = distRMax;
side = kNRMax;
}
}
if (!fPhiFullCone && rho) // Protected against (0,0,z)
{
phi = std::atan2(p.y, p.x);
if (phi < 0)
{
phi += 2 * UUtils::kPi;
}
if (fSPhi < 0)
{
distSPhi = std::fabs(phi - (fSPhi + 2 * UUtils::kPi)) * rho;
}
else
{
distSPhi = std::fabs(phi - fSPhi) * rho;
}
distEPhi = std::fabs(phi - fSPhi - fDPhi) * rho;
// Find new minimum
if (distSPhi < distEPhi)
{
if (distSPhi < distMin)
{
side = kNSPhi;
}
}
else
{
if (distEPhi < distMin)
{
side = kNEPhi;
}
}
}
switch (side)
{
case kNRMin: // Inner radius
rho *= secRMin;
norm = UVector3(-p.x / rho, -p.y / rho, tanRMin / secRMin);
break;
case kNRMax: // Outer radius
rho *= secRMax;
norm = UVector3(p.x / rho, p.y / rho, -tanRMax / secRMax);
break;
case kNZ: // +/- dz
if (p.z > 0)
{
norm = UVector3(0, 0, 1);
}
else
{
norm = UVector3(0, 0, -1);
}
break;
case kNSPhi:
norm = UVector3(std::sin(fSPhi), -std::cos(fSPhi), 0);
break;
case kNEPhi:
norm = UVector3(-std::sin(fSPhi + fDPhi), std::cos(fSPhi + fDPhi), 0);
break;
default: // Should never reach this case...
UUtils::Exception("UCons::ApproxSurfaceNormal()",
"GeomSolids1002", Warning, 1,
"Undefined side for valid surface normal to solid.");
break;
}
return norm;
}
////////////////////////////////////////////////////////////////////////
//
// Calculate distance to shape from outside, along normalised vector
// - return UUtils::kInfinity if no intersection, or intersection distance <= tolerance
//
// - Compute the intersection with the z planes
// - if at valid r, phi, return
//
// -> If point is outside cone, compute intersection with rmax1*0.5
// - if at valid phi,z return
// - if inside outer cone, handle case when on tolerant outer cone
// boundary and heading inwards(->0 to in)
//
// -> Compute intersection with inner cone, taking largest +ve root
// - if valid (in z,phi), save intersction
//
// -> If phi segmented, compute intersections with phi half planes
// - return smallest of valid phi intersections and
// inner radius intersection
//
// NOTE:
// - `if valid' implies tolerant checking of intersection points
// - z, phi intersection from Tubs
double UCons::DistanceToIn(const UVector3& p,
const UVector3& v, double /* aPstep */) const
{
double snxt = UUtils::kInfinity;
const double dRmax = 100 * std::max(fRmax1, fRmax2);
static const double halfCarTolerance = VUSolid::Tolerance() * 0.5;
static const double halfRadTolerance = kRadTolerance * 0.5;
double rMaxAv, rMaxOAv; // Data for cones
double rMinAv, rMinOAv;
double rout, rin;
double tolORMin, tolORMin2, tolIRMin, tolIRMin2; // `generous' radii squared
double tolORMax2, tolIRMax, tolIRMax2;
double tolODz, tolIDz;
double Dist, sd, xi, yi, zi, ri = 0., risec, rhoi2, cosPsi; // Intersection point vars
double t1, t2, t3, b, c, d; // Quadratic solver variables
double nt1, nt2, nt3;
double Comp;
UVector3 norm;
// Cone Precalcs
rMinAv = (fRmin1 + fRmin2) * 0.5;
if (rMinAv > halfRadTolerance)
{
rMinOAv = rMinAv - halfRadTolerance;
}
else
{
rMinOAv = 0.0;
}
rMaxAv = (fRmax1 + fRmax2) * 0.5;
rMaxOAv = rMaxAv + halfRadTolerance;
// Intersection with z-surfaces
tolIDz = fDz - halfCarTolerance;
tolODz = fDz + halfCarTolerance;
if (std::fabs(p.z) >= tolIDz)
{
if (p.z * v.z < 0) // at +Z going in -Z or visa versa
{
sd = (std::fabs(p.z) - fDz) / std::fabs(v.z); // Z intersect distance
if (sd < 0.0)
{
sd = 0.0; // negative dist -> zero
}
xi = p.x + sd * v.x; // Intersection coords
yi = p.y + sd * v.y;
rhoi2 = xi * xi + yi * yi ;
// Check validity of intersection
// Calculate (outer) tolerant radi^2 at intersecion
if (v.z > 0)
{
tolORMin = fRmin1 - halfRadTolerance * secRMin;
tolIRMin = fRmin1 + halfRadTolerance * secRMin;
tolIRMax = fRmax1 - halfRadTolerance * secRMin;
tolORMax2 = (fRmax1 + halfRadTolerance * secRMax) *
(fRmax1 + halfRadTolerance * secRMax);
}
else
{
tolORMin = fRmin2 - halfRadTolerance * secRMin;
tolIRMin = fRmin2 + halfRadTolerance * secRMin;
tolIRMax = fRmax2 - halfRadTolerance * secRMin;
tolORMax2 = (fRmax2 + halfRadTolerance * secRMax) *
(fRmax2 + halfRadTolerance * secRMax);
}
if (tolORMin > 0)
{
tolORMin2 = tolORMin * tolORMin;
tolIRMin2 = tolIRMin * tolIRMin;
}
else
{
tolORMin2 = 0.0;
tolIRMin2 = 0.0;
}
if (tolIRMax > 0)
{
tolIRMax2 = tolIRMax * tolIRMax;
}
else
{
tolIRMax2 = 0.0;
}
if ((tolIRMin2 <= rhoi2) && (rhoi2 <= tolIRMax2))
{
if (!fPhiFullCone && rhoi2)
{
// Psi = angle made with central (average) phi of shape
cosPsi = (xi * cosCPhi + yi * sinCPhi) / std::sqrt(rhoi2);
if (cosPsi >= cosHDPhiIT)
{
return sd;
}
}
else
{
return sd;
}
}
}
else // On/outside extent, and heading away -> cannot intersect
{
return snxt;
}
}
// ----> Can not intersect z surfaces
// Intersection with outer cone (possible return) and
// inner cone (must also check phi)
//
// Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
//
// Intersects with x^2+y^2=(a*z+b)^2
//
// where a=tanRMax or tanRMin
// b=rMaxAv or rMinAv
//
// (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0;
// t1 t2 t3
//
// \--------u-------/ \-----------v----------/ \---------w--------/
//
t1 = 1.0 - v.z * v.z;
t2 = p.x * v.x + p.y * v.y;
t3 = p.x * p.x + p.y * p.y;
rin = tanRMin * p.z + rMinAv;
rout = tanRMax * p.z + rMaxAv;
// Outer Cone Intersection
// Must be outside/on outer cone for valid intersection
nt1 = t1 - (tanRMax * v.z) * (tanRMax * v.z);
nt2 = t2 - tanRMax * v.z * rout;
nt3 = t3 - rout * rout;
if (std::fabs(nt1) > kRadTolerance) // Equation quadratic => 2 roots
{
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c ;
if ((nt3 > rout * rout * kRadTolerance * kRadTolerance * secRMax * secRMax)
|| (rout < 0))
{
// If outside real cone (should be rho-rout>kRadTolerance*0.5
// NOT rho^2 etc) saves a std::sqrt() at expense of accuracy
if (d >= 0)
{
if ((rout < 0) && (nt3 <= 0))
{
// Inside `shadow cone' with -ve radius
// -> 2nd root could be on real cone
if (b > 0)
{
sd = c / (-b - std::sqrt(d));
}
else
{
sd = -b + std::sqrt(d);
}
}
else
{
if ((b <= 0) && (c >= 0)) // both >=0, try smaller root
{
sd = c / (-b + std::sqrt(d));
}
else
{
if (c <= 0) // second >=0
{
sd = -b + std::sqrt(d);
}
else // both negative, travel away
{
return UUtils::kInfinity;
}
}
}
if (sd > 0) // If 'forwards'. Check z intersection
{
if (sd > dRmax) // Avoid rounding errors due to precision issues on
{
// 64 bits systems. Split long distances and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
zi = p.z + sd * v.z;
if (std::fabs(zi) <= tolODz)
{
// Z ok. Check phi intersection if reqd
if (fPhiFullCone)
{
return sd;
}
else
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
ri = rMaxAv + zi * tanRMax;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
return sd;
}
}
}
} // end if (sd>0)
}
}
else
{
// Inside outer cone
// check not inside, and heading through UCons (-> 0 to in)
if ((t3 > (rin + halfRadTolerance * secRMin)*
(rin + halfRadTolerance * secRMin))
&& (nt2 < 0) && (d >= 0) && (std::fabs(p.z) <= tolIDz))
{
// Inside cones, delta r -ve, inside z extent
// Point is on the Surface => check Direction using Normal.Dot(v)
xi = p.x;
yi = p.y ;
risec = std::sqrt(xi * xi + yi * yi) * secRMax;
norm = UVector3(xi / risec, yi / risec, -tanRMax / secRMax);
if (!fPhiFullCone)
{
cosPsi = (p.x * cosCPhi + p.y * sinCPhi) / std::sqrt(t3);
if (cosPsi >= cosHDPhiIT)
{
if (norm.Dot(v) <= 0)
{
return 0.0;
}
}
}
else
{
if (norm.Dot(v) <= 0)
{
return 0.0;
}
}
}
}
}
else // Single root case
{
if (std::fabs(nt2) > kRadTolerance)
{
sd = -0.5 * nt3 / nt2;
if (sd < 0)
{
return UUtils::kInfinity; // travel away
}
else // sd >= 0, If 'forwards'. Check z intersection
{
zi = p.z + sd * v.z;
if ((std::fabs(zi) <= tolODz) && (nt2 < 0))
{
// Z ok. Check phi intersection if reqd
if (fPhiFullCone)
{
return sd;
}
else
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
ri = rMaxAv + zi * tanRMax;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
return sd;
}
}
}
}
}
else // travel || cone surface from its origin
{
sd = UUtils::kInfinity;
}
}
// Inner Cone Intersection
// o Space is divided into 3 areas:
// 1) Radius greater than real inner cone & imaginary cone & outside
// tolerance
// 2) Radius less than inner or imaginary cone & outside tolarance
// 3) Within tolerance of real or imaginary cones
// - Extra checks needed for 3's intersections
// => lots of duplicated code
if (rMinAv)
{
nt1 = t1 - (tanRMin * v.z) * (tanRMin * v.z);
nt2 = t2 - tanRMin * v.z * rin;
nt3 = t3 - rin * rin;
if (nt1)
{
if (nt3 > rin * kRadTolerance * secRMin)
{
// At radius greater than real & imaginary cones
// -> 2nd root, with zi check
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d >= 0) // > 0
{
if (b > 0)
{
sd = c / (-b - std::sqrt(d));
}
else
{
sd = -b + std::sqrt(d);
}
if (sd >= 0) // > 0
{
if (sd > dRmax) // Avoid rounding errors due to precision issues on
{
// 64 bits systems. Split long distance and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
zi = p.z + sd * v.z;
if (std::fabs(zi) <= tolODz)
{
if (!fPhiFullCone)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
ri = rMinAv + zi * tanRMin;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
if (sd > halfRadTolerance)
{
snxt = sd;
}
else
{
// Calculate a normal vector in order to check Direction
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
snxt = sd;
}
}
}
}
else
{
if (sd > halfRadTolerance)
{
return sd;
}
else
{
// Calculate a normal vector in order to check Direction
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
return sd;
}
}
}
}
}
}
}
else if (nt3 < -rin * kRadTolerance * secRMin)
{
// Within radius of inner cone (real or imaginary)
// -> Try 2nd root, with checking intersection is with real cone
// -> If check fails, try 1st root, also checking intersection is
// on real cone
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d >= 0) // > 0
{
if (b > 0)
{
sd = c / (-b - std::sqrt(d));
}
else
{
sd = -b + std::sqrt(d);
}
zi = p.z + sd * v.z;
ri = rMinAv + zi * tanRMin;
if (ri > 0)
{
if ((sd >= 0) && (std::fabs(zi) <= tolODz)) // sd > 0
{
if (sd > dRmax) // Avoid rounding errors due to precision issues
{
// seen on 64 bits systems. Split and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
if (!fPhiFullCone)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiOT)
{
if (sd > halfRadTolerance)
{
snxt = sd;
}
else
{
// Calculate a normal vector in order to check Direction
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
snxt = sd;
}
}
}
}
else
{
if (sd > halfRadTolerance)
{
return sd;
}
else
{
// Calculate a normal vector in order to check Direction
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
return sd;
}
}
}
}
}
else
{
if (b > 0)
{
sd = -b - std::sqrt(d);
}
else
{
sd = c / (-b + std::sqrt(d));
}
zi = p.z + sd * v.z;
ri = rMinAv + zi * tanRMin;
if ((sd >= 0) && (ri > 0) && (std::fabs(zi) <= tolODz)) // sd>0
{
if (sd > dRmax) // Avoid rounding errors due to precision issues
{
// seen on 64 bits systems. Split and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
if (!fPhiFullCone)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
if (sd > halfRadTolerance)
{
snxt = sd;
}
else
{
// Calculate a normal vector in order to check Direction
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
snxt = sd;
}
}
}
}
else
{
if (sd > halfRadTolerance)
{
return sd;
}
else
{
// Calculate a normal vector in order to check Direction
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
if (norm.Dot(v) <= 0)
{
return sd;
}
}
}
}
}
}
}
else
{
// Within kRadTol*0.5 of inner cone (real OR imaginary)
// ----> Check not travelling through (=>0 to in)
// ----> if not:
// -2nd root with validity check
if (std::fabs(p.z) <= tolODz)
{
if (nt2 > 0)
{
// Inside inner real cone, heading outwards, inside z range
if (!fPhiFullCone)
{
cosPsi = (p.x * cosCPhi + p.y * sinCPhi) / std::sqrt(t3);
if (cosPsi >= cosHDPhiIT)
{
return 0.0;
}
}
else
{
return 0.0;
}
}
else
{
// Within z extent, but not travelling through
// -> 2nd root or UUtils::kInfinity if 1st root on imaginary cone
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d >= 0) // > 0
{
if (b > 0)
{
sd = -b - std::sqrt(d);
}
else
{
sd = c / (-b + std::sqrt(d));
}
zi = p.z + sd * v.z;
ri = rMinAv + zi * tanRMin;
if (ri > 0) // 2nd root
{
if (b > 0)
{
sd = c / (-b - std::sqrt(d));
}
else
{
sd = -b + std::sqrt(d);
}
zi = p.z + sd * v.z;
if ((sd >= 0) && (std::fabs(zi) <= tolODz)) // sd>0
{
if (sd > dRmax) // Avoid rounding errors due to precision issue
{
// seen on 64 bits systems. Split and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
if (!fPhiFullCone)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
ri = rMinAv + zi * tanRMin;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
snxt = sd;
}
}
else
{
return sd;
}
}
}
else
{
return UUtils::kInfinity;
}
}
}
}
else // 2nd root
{
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d > 0)
{
if (b > 0)
{
sd = c / (-b - std::sqrt(d));
}
else
{
sd = -b + std::sqrt(d);
}
zi = p.z + sd * v.z;
if ((sd >= 0) && (std::fabs(zi) <= tolODz)) // sd>0
{
if (sd > dRmax) // Avoid rounding errors due to precision issues
{
// seen on 64 bits systems. Split and recompute
double fTerm = sd - std::fmod(sd, dRmax);
sd = fTerm + DistanceToIn(p + fTerm * v, v);
}
if (!fPhiFullCone)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
ri = rMinAv + zi * tanRMin;
cosPsi = (xi * cosCPhi + yi * sinCPhi) / ri;
if (cosPsi >= cosHDPhiIT)
{
snxt = sd;
}
}
else
{
return sd;
}
}
}
}
}
}
}
// Phi segment intersection
//
// o Tolerant of points inside phi planes by up to VUSolid::Tolerance()*0.5
//
// o NOTE: Large duplication of code between sphi & ephi checks
// -> only diffs: sphi -> ephi, Comp -> -Comp and half-plane
// intersection check <=0 -> >=0
// -> Should use some form of loop Construct
if (!fPhiFullCone)
{
// First phi surface (starting phi)
Comp = v.x * sinSPhi - v.y * cosSPhi;
if (Comp < 0) // Component in outwards normal dirn
{
Dist = (p.y * cosSPhi - p.x * sinSPhi);
if (Dist < halfCarTolerance)
{
sd = Dist / Comp;
if (sd < snxt)
{
if (sd < 0)
{
sd = 0.0;
}
zi = p.z + sd * v.z;
if (std::fabs(zi) <= tolODz)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
rhoi2 = xi * xi + yi * yi;
tolORMin2 = (rMinOAv + zi * tanRMin) * (rMinOAv + zi * tanRMin);
tolORMax2 = (rMaxOAv + zi * tanRMax) * (rMaxOAv + zi * tanRMax);
if ((rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2))
{
// z and r intersections good - check intersecting with
// correct half-plane
if ((yi * cosCPhi - xi * sinCPhi) <= 0)
{
snxt = sd;
}
}
}
}
}
}
// Second phi surface (Ending phi)
Comp = -(v.x * sinEPhi - v.y * cosEPhi);
if (Comp < 0) // Component in outwards normal dirn
{
Dist = -(p.y * cosEPhi - p.x * sinEPhi);
if (Dist < halfCarTolerance)
{
sd = Dist / Comp;
if (sd < snxt)
{
if (sd < 0)
{
sd = 0.0;
}
zi = p.z + sd * v.z;
if (std::fabs(zi) <= tolODz)
{
xi = p.x + sd * v.x;
yi = p.y + sd * v.y;
rhoi2 = xi * xi + yi * yi;
tolORMin2 = (rMinOAv + zi * tanRMin) * (rMinOAv + zi * tanRMin);
tolORMax2 = (rMaxOAv + zi * tanRMax) * (rMaxOAv + zi * tanRMax);
if ((rhoi2 >= tolORMin2) && (rhoi2 <= tolORMax2))
{
// z and r intersections good - check intersecting with
// correct half-plane
if ((yi * cosCPhi - xi * sinCPhi) >= 0.0)
{
snxt = sd;
}
}
}
}
}
}
}
if (snxt < halfCarTolerance)
{
snxt = 0.;
}
return snxt;
}
//////////////////////////////////////////////////////////////////////////////
//
// Calculate distance (<= actual) to closest surface of shape from outside
// - Calculate distance to z, radial planes
// - Only to phi planes if outside phi extent
// - Return 0 if point inside
double UCons::SafetyFromOutside(const UVector3& p, bool) const
{
double safe = 0.0, rho, safeR1, safeR2, safeZ, safePhi, cosPsi;
double pRMin, pRMax;
rho = std::sqrt(p.x * p.x + p.y * p.y);
safeZ = std::fabs(p.z) - fDz;
if (fRmin1 || fRmin2)
{
pRMin = tanRMin * p.z + (fRmin1 + fRmin2) * 0.5;
safeR1 = (pRMin - rho) / secRMin;
pRMax = tanRMax * p.z + (fRmax1 + fRmax2) * 0.5;
safeR2 = (rho - pRMax) / secRMax;
if (safeR1 > safeR2)
{
safe = safeR1;
}
else
{
safe = safeR2;
}
}
else
{
pRMax = tanRMax * p.z + (fRmax1 + fRmax2) * 0.5;
safe = (rho - pRMax) / secRMax;
}
if (safeZ > safe)
{
safe = safeZ;
}
if (!fPhiFullCone && rho)
{
// Psi=angle from central phi to point
cosPsi = (p.x * cosCPhi + p.y * sinCPhi) / rho;
if (cosPsi < std::cos(fDPhi * 0.5)) // Point lies outside phi range
{
if ((p.y * cosCPhi - p.x * sinCPhi) <= 0.0)
{
safePhi = std::fabs(p.x * std::sin(fSPhi) - p.y * std::cos(fSPhi));
}
else
{
safePhi = std::fabs(p.x * sinEPhi - p.y * cosEPhi);
}
if (safePhi > safe)
{
safe = safePhi;
}
}
}
if (safe < 0.0)
{
safe = 0.0;
}
return safe;
}
///////////////////////////////////////////////////////////////
//
// Calculate distance to surface of shape from 'inside', allowing for tolerance
// - Only Calc rmax intersection if no valid rmin intersection
// double DistanceToOut( const UVector3& p, const UVector3& v, bool calcNorm, bool aConvex, UVector3 *n ) const;
double UCons::DistanceToOut(const UVector3& p,
const UVector3& v,
UVector3& aNormalVector,
bool& aConvex,
double /* aPstep*/) const
{
ESide side = kNull, sider = kNull, sidephi = kNull;
static const double halfCarTolerance = VUSolid::Tolerance() * 0.5;
static const double halfRadTolerance = kRadTolerance * 0.5;
static const double halfAngTolerance = kAngTolerance * 0.5;
double snxt, srd, sphi, pdist;
double rMaxAv; // Data for outer cone
double rMinAv; // Data for inner cone
double t1, t2, t3, rout, rin, nt1, nt2, nt3;
double b, c, d, sr2, sr3;
// Vars for intersection within tolerance
ESide sidetol = kNull;
double slentol = UUtils::kInfinity;
// Vars for phi intersection:
double pDistS, compS, pDistE, compE, sphi2, xi, yi, risec, vphi;
double zi, ri, deltaRoi2;
// Z plane intersection
if (v.z > 0.0)
{
pdist = fDz - p.z;
if (pdist > halfCarTolerance)
{
snxt = pdist / v.z;
side = kPZ;
}
else
{
aNormalVector = UVector3(0, 0, 1);
aConvex = true;
return snxt = 0.0;
}
}
else if (v.z < 0.0)
{
pdist = fDz + p.z;
if (pdist > halfCarTolerance)
{
snxt = -pdist / v.z;
side = kMZ;
}
else
{
aNormalVector = UVector3(0, 0, -1);
aConvex = true;
return snxt = 0.0;
}
}
else // Travel perpendicular to z axis
{
snxt = UUtils::kInfinity;
side = kNull;
}
// Radial Intersections
//
// Intersection with outer cone (possible return) and
// inner cone (must also check phi)
//
// Intersection point (xi,yi,zi) on line x=p.x+t*v.x etc.
//
// Intersects with x^2+y^2=(a*z+b)^2
//
// where a=tanRMax or tanRMin
// b=rMaxAv or rMinAv
//
// (vx^2+vy^2-(a*vz)^2)t^2+2t(pxvx+pyvy-a*vz(a*pz+b))+px^2+py^2-(a*pz+b)^2=0;
// t1 t2 t3
//
// \--------u-------/ \-----------v----------/ \---------w--------/
rMaxAv = (fRmax1 + fRmax2) * 0.5;
t1 = 1.0 - v.z * v.z; // since v normalised
t2 = p.x * v.x + p.y * v.y;
t3 = p.x * p.x + p.y * p.y;
rout = tanRMax * p.z + rMaxAv;
nt1 = t1 - (tanRMax * v.z) * (tanRMax * v.z);
nt2 = t2 - tanRMax * v.z * rout;
nt3 = t3 - rout * rout;
if (v.z > 0.0)
{
deltaRoi2 = snxt * snxt * t1 + 2 * snxt * t2 + t3
- fRmax2 * (fRmax2 + kRadTolerance * secRMax);
}
else if (v.z < 0.0)
{
deltaRoi2 = snxt * snxt * t1 + 2 * snxt * t2 + t3
- fRmax1 * (fRmax1 + kRadTolerance * secRMax);
}
else
{
deltaRoi2 = 1.0;
}
if (nt1 && (deltaRoi2 > 0.0))
{
// Equation quadratic => 2 roots : second root must be leaving
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d >= 0)
{
// Check if on outer cone & heading outwards
// NOTE: Should use rho-rout>-kRadTolerance*0.5
if (nt3 > -halfRadTolerance && nt2 >= 0)
{
risec = std::sqrt(t3) * secRMax;
aConvex = true;
aNormalVector = UVector3(p.x / risec, p.y / risec, -tanRMax / secRMax);
return snxt = 0;
}
else
{
sider = kRMax ;
if (b > 0)
{
srd = -b - std::sqrt(d);
}
else
{
srd = c / (-b + std::sqrt(d));
}
zi = p.z + srd * v.z;
ri = tanRMax * zi + rMaxAv;
if ((ri >= 0) && (-halfRadTolerance <= srd) && (srd <= halfRadTolerance))
{
// An intersection within the tolerance
// we will Store it in case it is good -
//
slentol = srd;
sidetol = kRMax;
}
if ((ri < 0) || (srd < halfRadTolerance))
{
// Safety: if both roots -ve ensure that srd cannot `win'
// distance to out
if (b > 0)
{
sr2 = c / (-b - std::sqrt(d));
}
else
{
sr2 = -b + std::sqrt(d);
}
zi = p.z + sr2 * v.z;
ri = tanRMax * zi + rMaxAv;
if ((ri >= 0) && (sr2 > halfRadTolerance))
{
srd = sr2;
}
else
{
srd = UUtils::kInfinity;
if ((-halfRadTolerance <= sr2) && (sr2 <= halfRadTolerance))
{
// An intersection within the tolerance.
// Storing it in case it is good.
slentol = sr2;
sidetol = kRMax;
}
}
}
}
}
else
{
// No intersection with outer cone & not parallel
// -> already outside, no intersection
risec = std::sqrt(t3) * secRMax;
aConvex = true;
aNormalVector = UVector3(p.x / risec, p.y / risec, -tanRMax / secRMax);
return snxt = 0.0;
}
}
else if (nt2 && (deltaRoi2 > 0.0))
{
// Linear case (only one intersection) => point outside outer cone
risec = std::sqrt(t3) * secRMax;
aConvex = true;
aNormalVector = UVector3(p.x / risec, p.y / risec, -tanRMax / secRMax);
return snxt = 0.0;
}
else
{
// No intersection -> parallel to outer cone
// => Z or inner cone intersection
srd = UUtils::kInfinity;
}
// Check possible intersection within tolerance
if (slentol <= halfCarTolerance)
{
// An intersection within the tolerance was found.
// We must accept it only if the momentum points outwards.
//
// UVector3 ptTol; // The point of the intersection
// ptTol= p + slentol*v;
// ri=tanRMax*zi+rMaxAv;
//
// Calculate a normal vector, as below
xi = p.x + slentol * v.x;
yi = p.y + slentol * v.y;
risec = std::sqrt(xi * xi + yi * yi) * secRMax;
UVector3 norm = UVector3(xi / risec, yi / risec, -tanRMax / secRMax);
if (norm.Dot(v) > 0) // We will leave the Cone immediatelly
{
aNormalVector = norm.Unit();
aConvex = true;
return snxt = 0.0;
}
else // On the surface, but not heading out so we ignore this intersection
{
// (as it is within tolerance).
slentol = UUtils::kInfinity;
}
}
// Inner Cone intersection
if (fRmin1 || fRmin2)
{
nt1 = t1 - (tanRMin * v.z) * (tanRMin * v.z);
if (nt1)
{
rMinAv = (fRmin1 + fRmin2) * 0.5;
rin = tanRMin * p.z + rMinAv;
nt2 = t2 - tanRMin * v.z * rin;
nt3 = t3 - rin * rin;
// Equation quadratic => 2 roots : first root must be leaving
b = nt2 / nt1;
c = nt3 / nt1;
d = b * b - c;
if (d >= 0.0)
{
// NOTE: should be rho-rin<kRadTolerance*0.5,
// but using squared versions for efficiency
if (nt3 < kRadTolerance * (rin + kRadTolerance * 0.25))
{
if (nt2 < 0.0)
{
aConvex = false;
return snxt = 0.0;
}
}
else
{
if (b > 0)
{
sr2 = -b - std::sqrt(d);
}
else
{
sr2 = c / (-b + std::sqrt(d));
}
zi = p.z + sr2 * v.z;
ri = tanRMin * zi + rMinAv;
if ((ri >= 0.0) && (-halfRadTolerance <= sr2) && (sr2 <= halfRadTolerance))
{
// An intersection within the tolerance
// storing it in case it is good.
slentol = sr2;
sidetol = kRMax;
}
if ((ri < 0) || (sr2 < halfRadTolerance))
{
if (b > 0)
{
sr3 = c / (-b - std::sqrt(d));
}
else
{
sr3 = -b + std::sqrt(d);
}
// Safety: if both roots -ve ensure that srd cannot `win'
// distancetoout
if (sr3 > halfRadTolerance)
{
if (sr3 < srd)
{
zi = p.z + sr3 * v.z;
ri = tanRMin * zi + rMinAv;
if (ri >= 0.0)
{
srd = sr3;
sider = kRMin;
}
}
}
else if (sr3 > -halfRadTolerance)
{
// Intersection in tolerance. Store to check if it's good
slentol = sr3;
sidetol = kRMin;
}
}
else if ((sr2 < srd) && (sr2 > halfCarTolerance))
{
srd = sr2;
sider = kRMin;
}
else if (sr2 > -halfCarTolerance)
{
// Intersection in tolerance. Store to check if it's good
slentol = sr2;
sidetol = kRMin;
}
if (slentol <= halfCarTolerance)
{
// An intersection within the tolerance was found.
// We must accept it only if the momentum points outwards.
UVector3 norm;
// Calculate a normal vector, as below
xi = p.x + slentol * v.x;
yi = p.y + slentol * v.y;
if (sidetol == kRMax)
{
risec = std::sqrt(xi * xi + yi * yi) * secRMax;
norm = UVector3(xi / risec, yi / risec, -tanRMax / secRMax);
}
else
{
risec = std::sqrt(xi * xi + yi * yi) * secRMin;
norm = UVector3(-xi / risec, -yi / risec, tanRMin / secRMin);
}
if (norm.Dot(v) > 0)
{
// We will leave the cone immediately
aNormalVector = norm.Unit();
aConvex = true;
return snxt = 0.0;
}
else
{
// On the surface, but not heading out so we ignore this
// intersection (as it is within tolerance).
slentol = UUtils::kInfinity;
}
}
}
}
}
}
// Linear case => point outside inner cone ---> outer cone intersect
//
// Phi Intersection
if (!fPhiFullCone)
{
// add angle calculation with correction
// of the difference in domain of atan2 and Sphi
vphi = std::atan2(v.y, v.x);
if (vphi < fSPhi - halfAngTolerance)
{
vphi += 2 * UUtils::kPi;
}
else if (vphi > fSPhi + fDPhi + halfAngTolerance)
{
vphi -= 2 * UUtils::kPi;
}
if (p.x || p.y) // Check if on z axis (rho not needed later)
{
// pDist -ve when inside
pDistS = p.x * sinSPhi - p.y * cosSPhi;
pDistE = -p.x * sinEPhi + p.y * cosEPhi;
// Comp -ve when in direction of outwards normal
compS = -sinSPhi * v.x + cosSPhi * v.y;
compE = sinEPhi * v.x - cosEPhi * v.y;
sidephi = kNull;
if (((fDPhi <= UUtils::kPi) && ((pDistS <= halfCarTolerance)
&& (pDistE <= halfCarTolerance)))
|| ((fDPhi > UUtils::kPi) && !((pDistS > halfCarTolerance)
&& (pDistE > halfCarTolerance))))
{
// Inside both phi *full* planes
if (compS < 0)
{
sphi = pDistS / compS;
if (sphi >= -halfCarTolerance)
{
xi = p.x + sphi * v.x;
yi = p.y + sphi * v.y;
// Check intersecting with correct half-plane
// (if not -> no intersect)
//
if ((std::fabs(xi) <= VUSolid::Tolerance())
&& (std::fabs(yi) <= VUSolid::Tolerance()))
{
sidephi = kSPhi;
if ((fSPhi - halfAngTolerance <= vphi)
&& (fSPhi + fDPhi + halfAngTolerance >= vphi))
{
sphi = UUtils::kInfinity;
}
}
else if ((yi * cosCPhi - xi * sinCPhi) >= 0)
{
sphi = UUtils::kInfinity;
}
else
{
sidephi = kSPhi;
if (pDistS > -halfCarTolerance)
{
sphi = 0.0; // Leave by sphi immediately
}
}
}
else
{
sphi = UUtils::kInfinity;
}
}
else
{
sphi = UUtils::kInfinity;
}
if (compE < 0)
{
sphi2 = pDistE / compE;
// Only check further if < starting phi intersection
//
if ((sphi2 > -halfCarTolerance) && (sphi2 < sphi))
{
xi = p.x + sphi2 * v.x;
yi = p.y + sphi2 * v.y;
// Check intersecting with correct half-plane
if ((std::fabs(xi) <= VUSolid::Tolerance())
&& (std::fabs(yi) <= VUSolid::Tolerance()))
{
// Leaving via ending phi
if (!((fSPhi - halfAngTolerance <= vphi)
&& (fSPhi + fDPhi + halfAngTolerance >= vphi)))
{
sidephi = kEPhi;
if (pDistE <= -halfCarTolerance)
{
sphi = sphi2;
}
else
{
sphi = 0.0;
}
}
}
else // Check intersecting with correct half-plane
if (yi * cosCPhi - xi * sinCPhi >= 0)
{
// Leaving via ending phi
sidephi = kEPhi;
if (pDistE <= -halfCarTolerance)
{
sphi = sphi2;
}
else
{
sphi = 0.0;
}
}
}
}
}
else
{
sphi = UUtils::kInfinity;
}
}
else
{
// On z axis + travel not || to z axis -> if phi of vector direction
// within phi of shape, Step limited by rmax, else Step =0
if ((fSPhi - halfAngTolerance <= vphi)
&& (vphi <= fSPhi + fDPhi + halfAngTolerance))
{
sphi = UUtils::kInfinity;
}
else
{
sidephi = kSPhi ; // arbitrary
sphi = 0.0;
}
}
if (sphi < snxt) // Order intersecttions
{
snxt = sphi;
side = sidephi;
}
}
if (srd < snxt) // Order intersections
{
snxt = srd ;
side = sider;
}
switch (side)
{
// Note: returned vector not normalised
case kRMax: // (divide by frmax for Unit vector)
xi = p.x + snxt * v.x;
yi = p.y + snxt * v.y;
risec = std::sqrt(xi * xi + yi * yi) * secRMax;
aNormalVector = UVector3(xi / risec, yi / risec, -tanRMax / secRMax);
aConvex = true;
break;
case kRMin:
aConvex = false; // Rmin is inconvex
break;
case kSPhi:
if (fDPhi <= UUtils::kPi)
{
aNormalVector = UVector3(sinSPhi, -cosSPhi, 0);
aConvex = true;
}
else
{
aConvex = false;
}
break;
case kEPhi:
if (fDPhi <= UUtils::kPi)
{
aNormalVector = UVector3(-sinEPhi, cosEPhi, 0);
aConvex = true;
}
else
{
aConvex = false;
}
break;
case kPZ:
aNormalVector = UVector3(0, 0, 1);
aConvex = true;
break;
case kMZ:
aNormalVector = UVector3(0, 0, -1);
aConvex = true;
break;
default:
cout << std::endl;
// DumpInfo();
std::ostringstream message;
int oldprc = message.precision(16);
message << "Undefined side for valid surface normal to solid."
<< std::endl
<< "Position:" << std::endl << std::endl
<< "p.x = " << p.x << " mm" << std::endl
<< "p.y = " << p.y << " mm" << std::endl
<< "p.z = " << p.z << " mm" << std::endl << std::endl
<< "pho at z = " << std::sqrt(p.x * p.x + p.y * p.y)
<< " mm" << std::endl << std::endl;
if (p.x != 0. || p.y != 0.)
{
message << "point phi = " << std::atan2(p.y, p.x) / (UUtils::kPi / 180.0)
<< " degree" << std::endl << std::endl;
}
message << "Direction:" << std::endl << std::endl
<< "v.x = " << v.x << std::endl
<< "v.y = " << v.y << std::endl
<< "v.z = " << v.z << std::endl << std::endl
<< "Proposed distance :" << std::endl << std::endl
<< "snxt = " << snxt << " mm" << std::endl;
message.precision(oldprc);
UUtils::Exception("UCons::DistanceToOut()", "UGeomSolids", Warning, 1, message.str().c_str());
break;
}
if (snxt < halfCarTolerance)
{
snxt = 0.;
}
return snxt;
}
//////////////////////////////////////////////////////////////////
//
// Calculate distance (<=actual) to closest surface of shape from inside
double UCons::SafetyFromInside(const UVector3& p, bool) const
{
double safe = 0.0, rho, safeR1, safeR2, safeZ, safePhi;
double pRMin;
double pRMax;
#ifdef UCSGDEBUG
if (Inside(p) == eOutside)
{
int oldprc = cout.precision(16);
cout << std::endl;
DumpInfo();
cout << "Position:" << std::endl << std::endl;
cout << "p.x = " << p.x << " mm" << std::endl;
cout << "p.y = " << p.y << " mm" << std::endl;
cout << "p.z = " << p.z << " mm" << std::endl << std::endl;
cout << "pho at z = " << std::sqrt(p.x * p.x + p.y * p.y)
<< " mm" << std::endl << std::endl;
if ((p.x != 0.) || (p.x != 0.))
{
cout << "point phi = " << std::atan2(p.y, p.x) / degree
<< " degree" << std::endl << std::endl;
}
cout.precision(oldprc);
UUtils::Exception("UCons::UCons()", "UGeomSolids", Warning, 1, message.str().c_str());
}
#endif
rho = std::sqrt(p.x * p.x + p.y * p.y);
safeZ = fDz - std::fabs(p.z);
if (fRmin1 || fRmin2)
{
pRMin = tanRMin * p.z + (fRmin1 + fRmin2) * 0.5;
safeR1 = (rho - pRMin) / secRMin;
}
else
{
safeR1 = UUtils::kInfinity;
}
pRMax = tanRMax * p.z + (fRmax1 + fRmax2) * 0.5;
safeR2 = (pRMax - rho) / secRMax;
if (safeR1 < safeR2)
{
safe = safeR1;
}
else
{
safe = safeR2;
}
if (safeZ < safe)
{
safe = safeZ;
}
// Check if phi divided, Calc distances closest phi plane
if (!fPhiFullCone)
{
// Above/below central phi of UCons?
if ((p.y * cosCPhi - p.x * sinCPhi) <= 0)
{
safePhi = -(p.x * sinSPhi - p.y * cosSPhi);
}
else
{
safePhi = (p.x * sinEPhi - p.y * cosEPhi);
}
if (safePhi < safe)
{
safe = safePhi;
}
}
if (safe < 0)
{
safe = 0;
}
return safe;
}
//////////////////////////////////////////////////////////////////////////
//
// GetEntityType
UGeometryType UCons::GetEntityType() const
{
return std::string("Cons");
}
//////////////////////////////////////////////////////////////////////////
//
// Make a clone of the object
//
VUSolid* UCons::Clone() const
{
return new UCons(*this);
}
//////////////////////////////////////////////////////////////////////////
//
// Stream object contents to an output stream
std::ostream& UCons::StreamInfo(std::ostream& os) const
{
int oldprc = os.precision(16);
os << "-----------------------------------------------------------\n"
<< " *** Dump for solid - " << GetName() << " ***\n"
<< " ===================================================\n"
<< " Solid type: UCons\n"
<< " Parameters: \n"
<< " inside -fDz radius: " << fRmin1 << " mm \n"
<< " outside -fDz radius: " << fRmax1 << " mm \n"
<< " inside +fDz radius: " << fRmin2 << " mm \n"
<< " outside +fDz radius: " << fRmax2 << " mm \n"
<< " half length in Z : " << fDz << " mm \n"
<< " starting angle of segment: " << fSPhi / (UUtils::kPi / 180.0) << " degrees \n"
<< " delta angle of segment : " << fDPhi / (UUtils::kPi / 180.0) << " degrees \n"
<< "-----------------------------------------------------------\n";
os.precision(oldprc);
return os;
}
/////////////////////////////////////////////////////////////////////////
//
// GetPointOnSurface
UVector3 UCons::GetPointOnSurface() const
{
// declare working variables
//
double Aone, Atwo, Athree, Afour, Afive, slin, slout, phi;
double zRand, cosu, sinu, rRand1, rRand2, chose, rone, rtwo, qone, qtwo;
rone = (fRmax1 - fRmax2) / (2.*fDz);
rtwo = (fRmin1 - fRmin2) / (2.*fDz);
qone = 0.;
qtwo = 0.;
if (fRmax1 != fRmax2)
{
qone = fDz * (fRmax1 + fRmax2) / (fRmax1 - fRmax2);
}
if (fRmin1 != fRmin2)
{
qtwo = fDz * (fRmin1 + fRmin2) / (fRmin1 - fRmin2);
}
slin = std::sqrt(UUtils::sqr(fRmin1 - fRmin2) + UUtils::sqr(2.*fDz));
slout = std::sqrt(UUtils::sqr(fRmax1 - fRmax2) + UUtils::sqr(2.*fDz));
Aone = 0.5 * fDPhi * (fRmax2 + fRmax1) * slout;
Atwo = 0.5 * fDPhi * (fRmin2 + fRmin1) * slin;
Athree = 0.5 * fDPhi * (fRmax1 * fRmax1 - fRmin1 * fRmin1);
Afour = 0.5 * fDPhi * (fRmax2 * fRmax2 - fRmin2 * fRmin2);
Afive = fDz * (fRmax1 - fRmin1 + fRmax2 - fRmin2);
phi = UUtils::Random(fSPhi, fSPhi + fDPhi);
cosu = std::cos(phi);
sinu = std::sin(phi);
rRand1 = UUtils::GetRadiusInRing(fRmin1, fRmin2);
rRand2 = UUtils::GetRadiusInRing(fRmax1, fRmax2);
if ((fSPhi == 0.) && fPhiFullCone)
{
Afive = 0.;
}
chose = UUtils::Random(0., Aone + Atwo + Athree + Afour + 2.*Afive);
if ((chose >= 0.) && (chose < Aone))
{
if (fRmin1 != fRmin2)
{
zRand = UUtils::Random(-1.*fDz, fDz);
return UVector3(rtwo * cosu * (qtwo - zRand),
rtwo * sinu * (qtwo - zRand), zRand);
}
else
{
return UVector3(fRmin1 * cosu, fRmin2 * sinu,
UUtils::Random(-1.*fDz, fDz));
}
}
else if ((chose >= Aone) && (chose <= Aone + Atwo))
{
if (fRmax1 != fRmax2)
{
zRand = UUtils::Random(-1.*fDz, fDz);
return UVector3(rone * cosu * (qone - zRand),
rone * sinu * (qone - zRand), zRand);
}
else
{
return UVector3(fRmax1 * cosu, fRmax2 * sinu,
UUtils::Random(-1.*fDz, fDz));
}
}
else if ((chose >= Aone + Atwo) && (chose < Aone + Atwo + Athree))
{
return UVector3(rRand1 * cosu, rRand1 * sinu, -1 * fDz);
}
else if ((chose >= Aone + Atwo + Athree)
&& (chose < Aone + Atwo + Athree + Afour))
{
return UVector3(rRand2 * cosu, rRand2 * sinu, fDz);
}
else if ((chose >= Aone + Atwo + Athree + Afour)
&& (chose < Aone + Atwo + Athree + Afour + Afive))
{
zRand = UUtils::Random(-1.*fDz, fDz);
rRand1 = UUtils::Random(fRmin2 - ((zRand - fDz) / (2.*fDz)) * (fRmin1 - fRmin2),
fRmax2 - ((zRand - fDz) / (2.*fDz)) * (fRmax1 - fRmax2));
return UVector3(rRand1 * std::cos(fSPhi),
rRand1 * std::sin(fSPhi), zRand);
}
else
{
zRand = UUtils::Random(-1.*fDz, fDz);
rRand1 = UUtils::Random(fRmin2 - ((zRand - fDz) / (2.*fDz)) * (fRmin1 - fRmin2),
fRmax2 - ((zRand - fDz) / (2.*fDz)) * (fRmax1 - fRmax2));
return UVector3(rRand1 * std::cos(fSPhi + fDPhi),
rRand1 * std::sin(fSPhi + fDPhi), zRand);
}
}
void UCons::Extent(UVector3& aMin, UVector3& aMax) const
{
double max = fRmax1 > fRmax2 ? fRmax1 : fRmax2;
aMin = UVector3(-max, -max, -fDz);
aMax = UVector3(max, max, fDz);
}
void UCons::GetParametersList(int, double* aArray) const
{
aArray[0] = GetInnerRadiusMinusZ();
aArray[1] = GetOuterRadiusMinusZ();
aArray[2] = GetInnerRadiusPlusZ();
aArray[3] = GetOuterRadiusPlusZ();
aArray[4] = GetZHalfLength();
aArray[5] = GetStartPhiAngle();
aArray[6] = GetDeltaPhiAngle();
}