#include "Riostream.h"
#include "TGeoManager.h"
#include "TGeoVolume.h"
#include "TVirtualGeoPainter.h"
#include "TGeoParaboloid.h"
#include "TVirtualPad.h"
#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"
#include "TMath.h"
ClassImp(TGeoParaboloid)
TGeoParaboloid::TGeoParaboloid()
{
fRlo = 0;
fRhi = 0;
fDz = 0;
fA = 0;
fB = 0;
SetShapeBit(TGeoShape::kGeoParaboloid);
}
TGeoParaboloid::TGeoParaboloid(Double_t rlo, Double_t rhi, Double_t dz)
:TGeoBBox(0,0,0)
{
fRlo = 0;
fRhi = 0;
fDz = 0;
fA = 0;
fB = 0;
SetShapeBit(TGeoShape::kGeoParaboloid);
SetParaboloidDimensions(rlo, rhi, dz);
ComputeBBox();
}
TGeoParaboloid::TGeoParaboloid(const char *name, Double_t rlo, Double_t rhi, Double_t dz)
:TGeoBBox(name, 0, 0, 0)
{
fRlo = 0;
fRhi = 0;
fDz = 0;
fA = 0;
fB = 0;
SetShapeBit(TGeoShape::kGeoParaboloid);
SetParaboloidDimensions(rlo, rhi, dz);
ComputeBBox();
}
TGeoParaboloid::TGeoParaboloid(Double_t *param)
{
SetShapeBit(TGeoShape::kGeoParaboloid);
SetDimensions(param);
ComputeBBox();
}
TGeoParaboloid::~TGeoParaboloid()
{
}
Double_t TGeoParaboloid::Capacity() const
{
Double_t capacity = TMath::Pi()*fDz*(fRlo*fRlo+fRhi*fRhi);
return capacity;
}
void TGeoParaboloid::ComputeBBox()
{
fDX = TMath::Max(fRlo, fRhi);
fDY = fDX;
fDZ = fDz;
}
void TGeoParaboloid::ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm)
{
norm[0] = norm[1] = 0.0;
if (TMath::Abs(point[2]) > fDz) {
norm[2] = TMath::Sign(1., dir[2]);
return;
}
Double_t safz = fDz-TMath::Abs(point[2]);
Double_t r = TMath::Sqrt(point[0]*point[0]+point[1]*point[1]);
Double_t safr = TMath::Abs(r-TMath::Sqrt((point[2]-fB)/fA));
if (safz<safr) {
norm[2] = TMath::Sign(1., dir[2]);
return;
}
Double_t talf = -2.*fA*r;
Double_t calf = 1./TMath::Sqrt(1.+talf*talf);
Double_t salf = talf * calf;
Double_t phi = TMath::ATan2(point[1], point[0]);
norm[0] = salf*TMath::Cos(phi);
norm[1] = salf*TMath::Sin(phi);
norm[2] = calf;
Double_t ndotd = norm[0]*dir[0]+norm[1]*dir[1]+norm[2]*dir[2];
if (ndotd < 0) {
norm[0] = -norm[0];
norm[1] = -norm[1];
norm[2] = -norm[2];
}
}
Bool_t TGeoParaboloid::Contains(const Double_t *point) const
{
if (TMath::Abs(point[2])>fDz) return kFALSE;
Double_t aa = fA*(point[2]-fB);
if (aa < 0) return kFALSE;
Double_t rsq = point[0]*point[0]+point[1]*point[1];
if (aa < fA*fA*rsq) return kFALSE;
return kTRUE;
}
Int_t TGeoParaboloid::DistancetoPrimitive(Int_t px, Int_t py)
{
Int_t n = gGeoManager->GetNsegments();
const Int_t numPoints=n*(n+1)+2;
return ShapeDistancetoPrimitive(numPoints, px, py);
}
Double_t TGeoParaboloid::DistToParaboloid(const Double_t *point, const Double_t *dir, Bool_t in) const
{
Double_t rsq = point[0]*point[0]+point[1]*point[1];
Double_t a = fA * (dir[0]*dir[0] + dir[1]*dir[1]);
Double_t b = 2.*fA*(point[0]*dir[0]+point[1]*dir[1])-dir[2];
Double_t c = fA*rsq + fB - point[2];
Double_t dist = TGeoShape::Big();
if (TMath::Abs(a)<TGeoShape::Tolerance()) {
if (TMath::Abs(b)<TGeoShape::Tolerance()) return dist;
dist = -c/b;
if (dist < 0) return TGeoShape::Big();
return dist;
}
Double_t ainv = 1./a;
Double_t sum = - b*ainv;
Double_t prod = c*ainv;
Double_t delta = sum*sum - 4.*prod;
if (delta<0) return dist;
delta = TMath::Sqrt(delta);
Double_t sone = TMath::Sign(1.,ainv);
Int_t i = -1;
while (i<2) {
dist = 0.5*(sum+i*sone*delta);
i += 2;
if (dist<0) continue;
if (dist<1.E-8) {
Double_t talf = -2.*fA*TMath::Sqrt(rsq);
Double_t phi = TMath::ATan2(point[1], point[0]);
Double_t ndotd = talf*(TMath::Cos(phi)*dir[0]+TMath::Sin(phi)*dir[1])+dir[2];
if (!in) ndotd *= -1;
if (ndotd<0) return dist;
} else return dist;
}
return TGeoShape::Big();
}
Double_t TGeoParaboloid::DistFromInside(const Double_t *point, const Double_t *dir, Int_t iact, Double_t step, Double_t *safe) const
{
if (iact<3 && safe) {
*safe = Safety(point, kTRUE);
if (iact==0) return TGeoShape::Big();
if (iact==1 && step<*safe) return TGeoShape::Big();
}
Double_t dz = TGeoShape::Big();
if (dir[2]<0) {
dz = -(point[2]+fDz)/dir[2];
} else if (dir[2]>0) {
dz = (fDz-point[2])/dir[2];
}
Double_t dpara = DistToParaboloid(point, dir, kTRUE);
return TMath::Min(dz, dpara);
}
Double_t TGeoParaboloid::DistFromOutside(const Double_t *point, const Double_t *dir, Int_t iact, Double_t step, Double_t *safe) const
{
Double_t snxt = TGeoShape::Big();
if (iact<3 && safe) {
*safe = Safety(point, kFALSE);
if (iact==0) return TGeoShape::Big();
if (iact==1 && step<*safe) return TGeoShape::Big();
}
Double_t xnew, ynew, znew;
if (point[2]<=-fDz) {
if (dir[2]<=0) return TGeoShape::Big();
snxt = -(fDz+point[2])/dir[2];
xnew = point[0]+snxt*dir[0];
ynew = point[1]+snxt*dir[1];
if ((xnew*xnew+ynew*ynew) <= fRlo*fRlo) return snxt;
} else if (point[2]>=fDz) {
if (dir[2]>=0) return TGeoShape::Big();
snxt = (fDz-point[2])/dir[2];
xnew = point[0]+snxt*dir[0];
ynew = point[1]+snxt*dir[1];
if ((xnew*xnew+ynew*ynew) <= fRhi*fRhi) return snxt;
}
snxt = DistToParaboloid(point, dir, kFALSE);
if (snxt > 1E20) return snxt;
znew = point[2]+snxt*dir[2];
if (TMath::Abs(znew) <= fDz) return snxt;
return TGeoShape::Big();
}
TGeoVolume *TGeoParaboloid::Divide(TGeoVolume * , const char * , Int_t , Int_t ,
Double_t , Double_t )
{
Error("Divide", "Paraboloid divisions not implemented");
return 0;
}
void TGeoParaboloid::GetBoundingCylinder(Double_t *param) const
{
param[0] = 0.;
param[1] = fDX;
param[1] *= param[1];
param[2] = 0.;
param[3] = 360.;
}
TGeoShape *TGeoParaboloid::GetMakeRuntimeShape(TGeoShape *, TGeoMatrix *) const
{
return 0;
}
void TGeoParaboloid::InspectShape() const
{
printf("*** Shape %s: TGeoParaboloid ***\n", GetName());
printf(" rlo = %11.5f\n", fRlo);
printf(" rhi = %11.5f\n", fRhi);
printf(" dz = %11.5f\n", fDz);
printf(" Bounding box:\n");
TGeoBBox::InspectShape();
}
TBuffer3D *TGeoParaboloid::MakeBuffer3D() const
{
Int_t n = gGeoManager->GetNsegments();
Int_t nbPnts = n*(n+1)+2;
Int_t nbSegs = n*(2*n+3);
Int_t nbPols = n*(n+2);
TBuffer3D* buff = new TBuffer3D(TBuffer3DTypes::kGeneric,
nbPnts, 3*nbPnts, nbSegs, 3*nbSegs, nbPols, 2*n*5 + n*n*6);
if (buff)
{
SetPoints(buff->fPnts);
SetSegsAndPols(*buff);
}
return buff;
}
void TGeoParaboloid::SetSegsAndPols(TBuffer3D &buff) const
{
Int_t indx, i, j;
Int_t n = gGeoManager->GetNsegments();
Int_t c = GetBasicColor();
Int_t nn1 = (n+1)*n+1;
indx = 0;
for (j=0; j<n; j++) {
buff.fSegs[indx++] = c+2;
buff.fSegs[indx++] = 0;
buff.fSegs[indx++] = j+1;
}
for (i=0; i<n+1; i++) {
for (j=0; j<n; j++) {
buff.fSegs[indx++] = c;
buff.fSegs[indx++] = n*i+1+j;
buff.fSegs[indx++] = n*i+1+((j+1)%n);
}
if (i==n) break;
for (j=0; j<n; j++) {
buff.fSegs[indx++] = c;
buff.fSegs[indx++] = n*i+1+j;
buff.fSegs[indx++] = n*(i+1)+1+j;
}
}
for (j=0; j<n; j++) {
buff.fSegs[indx++] = c+1;
buff.fSegs[indx++] = n*n+1+j;
buff.fSegs[indx++] = nn1;
}
indx = 0;
for (j=0; j<n; j++) {
buff.fPols[indx++] = c+2;
buff.fPols[indx++] = 3;
buff.fPols[indx++] = n+j;
buff.fPols[indx++] = (j+1)%n;
buff.fPols[indx++] = j;
}
for (i=0; i<n; i++) {
for (j=0; j<n; j++) {
buff.fPols[indx++] = c;
buff.fPols[indx++] = 4;
buff.fPols[indx++] = (2*i+1)*n+j;
buff.fPols[indx++] = 2*(i+1)*n+j;
buff.fPols[indx++] = (2*i+3)*n+j;
buff.fPols[indx++] = 2*(i+1)*n+((j+1)%n);
}
}
for (j=0; j<n; j++) {
buff.fPols[indx++] = c+1;
buff.fPols[indx++] = 3;
buff.fPols[indx++] = 2*n*(n+1)+j;
buff.fPols[indx++] = 2*n*(n+1)+((j+1)%n);
buff.fPols[indx++] = (2*n+1)*n+j;
}
}
Double_t TGeoParaboloid::Safety(const Double_t *point, Bool_t in) const
{
Double_t safz = fDz-TMath::Abs(point[2]);
if (!in) safz = -safz;
Double_t safr = TGeoShape::Big();
Double_t rsq = point[0]*point[0]+point[1]*point[1];
Double_t z0 = fA*rsq+fB;
Double_t r0sq = (point[2]-fB)/fA;
if (r0sq<0) {
if (in) return 0.;
return safz;
}
Double_t dr = TMath::Sqrt(rsq)-TMath::Sqrt(r0sq);
if (in) {
if (dr>-1.E-8) return 0.;
Double_t dz = TMath::Abs(point[2]-z0);
safr = -dr*dz/TMath::Sqrt(dr*dr+dz*dz);
} else {
if (dr<1.E-8) return safz;
Double_t talf = -2.*fA*TMath::Sqrt(r0sq);
Double_t salf = talf/TMath::Sqrt(1.+talf*talf);
safr = TMath::Abs(dr*salf);
}
if (in) return TMath::Min(safr,safz);
return TMath::Max(safr,safz);
}
void TGeoParaboloid::SetParaboloidDimensions(Double_t rlo, Double_t rhi, Double_t dz)
{
if ((rlo<0) || (rlo<0) || (dz<=0) || TMath::Abs(rlo-rhi)<TGeoShape::Tolerance()) {
SetShapeBit(kGeoRunTimeShape);
Error("SetParaboloidDimensions", "Dimensions of %s invalid: check (rlo>=0) (rhi>=0) (rlo!=rhi) dz>0",GetName());
return;
}
fRlo = rlo;
fRhi = rhi;
fDz = dz;
Double_t dd = 1./(fRhi*fRhi - fRlo*fRlo);
fA = 2.*fDz*dd;
fB = - fDz * (fRlo*fRlo + fRhi*fRhi)*dd;
}
void TGeoParaboloid::SetDimensions(Double_t *param)
{
Double_t rlo = param[0];
Double_t rhi = param[1];
Double_t dz = param[2];
SetParaboloidDimensions(rlo, rhi, dz);
}
void TGeoParaboloid::SetPoints(Double_t *points) const
{
if (!points) return;
Double_t ttmin, ttmax;
ttmin = TMath::ATan2(-fDz, fRlo);
ttmax = TMath::ATan2(fDz, fRhi);
Int_t n = gGeoManager->GetNsegments();
Double_t dtt = (ttmax-ttmin)/n;
Double_t dphi = 360./n;
Double_t tt;
Double_t r, z, delta;
Double_t phi, sph, cph;
Int_t indx = 0;
points[indx++] = 0;
points[indx++] = 0;
points[indx++] = -fDz;
for (Int_t i=0; i<n+1; i++) {
if (i==0) {
r = fRlo;
z = -fDz;
} else if (i==n) {
r = fRhi;
z = fDz;
} else {
tt = TMath::Tan(ttmin + i*dtt);
delta = tt*tt - 4*fA*fB;
r = 0.5*(tt+TMath::Sqrt(delta))/fA;
z = r*tt;
}
for (Int_t j=0; j<n; j++) {
phi = j*dphi*TMath::DegToRad();
sph=TMath::Sin(phi);
cph=TMath::Cos(phi);
points[indx++] = r*cph;
points[indx++] = r*sph;
points[indx++] = z;
}
}
points[indx++] = 0;
points[indx++] = 0;
points[indx++] = fDz;
}
void TGeoParaboloid::GetMeshNumbers(Int_t &nvert, Int_t &nsegs, Int_t &npols) const
{
Int_t n = gGeoManager->GetNsegments();
nvert = n*(n+1)+2;
nsegs = n*(2*n+3);
npols = n*(n+2);
}
Int_t TGeoParaboloid::GetNmeshVertices() const
{
Int_t n = gGeoManager->GetNsegments();
return (n*(n+1)+2);
}
void TGeoParaboloid::SavePrimitive(std::ostream &out, Option_t * )
{
if (TObject::TestBit(kGeoSavePrimitive)) return;
out << " // Shape: " << GetName() << " type: " << ClassName() << std::endl;
out << " rlo = " << fRlo << ";" << std::endl;
out << " rhi = " << fRhi << ";" << std::endl;
out << " dz = " << fDZ << ";" << std::endl;
out << " TGeoShape *" << GetPointerName() << " = new TGeoParaboloid(\"" << GetName() << "\", rlo,rhi,dz);" << std::endl;
TObject::SetBit(TGeoShape::kGeoSavePrimitive);
}
void TGeoParaboloid::SetPoints(Float_t *points) const
{
if (!points) return;
Double_t ttmin, ttmax;
ttmin = TMath::ATan2(-fDz, fRlo);
ttmax = TMath::ATan2(fDz, fRhi);
Int_t n = gGeoManager->GetNsegments();
Double_t dtt = (ttmax-ttmin)/n;
Double_t dphi = 360./n;
Double_t tt;
Double_t r, z, delta;
Double_t phi, sph, cph;
Int_t indx = 0;
points[indx++] = 0;
points[indx++] = 0;
points[indx++] = -fDz;
for (Int_t i=0; i<n+1; i++) {
if (i==0) {
r = fRlo;
z = -fDz;
} else if (i==n) {
r = fRhi;
z = fDz;
} else {
tt = TMath::Tan(ttmin + i*dtt);
delta = tt*tt - 4*fA*fB;
r = 0.5*(tt+TMath::Sqrt(delta))/fA;
z = r*tt;
}
for (Int_t j=0; j<n; j++) {
phi = j*dphi*TMath::DegToRad();
sph=TMath::Sin(phi);
cph=TMath::Cos(phi);
points[indx++] = r*cph;
points[indx++] = r*sph;
points[indx++] = z;
}
}
points[indx++] = 0;
points[indx++] = 0;
points[indx++] = fDz;
}
void TGeoParaboloid::Sizeof3D() const
{
}
const TBuffer3D & TGeoParaboloid::GetBuffer3D(Int_t reqSections, Bool_t localFrame) const
{
static TBuffer3D buffer(TBuffer3DTypes::kGeneric);
TGeoBBox::FillBuffer3D(buffer, reqSections, localFrame);
if (reqSections & TBuffer3D::kRawSizes) {
Int_t n = gGeoManager->GetNsegments();
Int_t nbPnts = n*(n+1)+2;
Int_t nbSegs = n*(2*n+3);
Int_t nbPols = n*(n+2);
if (buffer.SetRawSizes(nbPnts, 3*nbPnts, nbSegs, 3*nbSegs, nbPols, 2*n*5 + n*n*6)) {
buffer.SetSectionsValid(TBuffer3D::kRawSizes);
}
}
if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) {
SetPoints(buffer.fPnts);
if (!buffer.fLocalFrame) {
TransformPoints(buffer.fPnts, buffer.NbPnts());
}
SetSegsAndPols(buffer);
buffer.SetSectionsValid(TBuffer3D::kRaw);
}
return buffer;
}
void TGeoParaboloid::Contains_v(const Double_t *points, Bool_t *inside, Int_t vecsize) const
{
for (Int_t i=0; i<vecsize; i++) inside[i] = Contains(&points[3*i]);
}
void TGeoParaboloid::ComputeNormal_v(const Double_t *points, const Double_t *dirs, Double_t *norms, Int_t vecsize)
{
for (Int_t i=0; i<vecsize; i++) ComputeNormal(&points[3*i], &dirs[3*i], &norms[3*i]);
}
void TGeoParaboloid::DistFromInside_v(const Double_t *points, const Double_t *dirs, Double_t *dists, Int_t vecsize, Double_t* step) const
{
for (Int_t i=0; i<vecsize; i++) dists[i] = DistFromInside(&points[3*i], &dirs[3*i], 3, step[i]);
}
void TGeoParaboloid::DistFromOutside_v(const Double_t *points, const Double_t *dirs, Double_t *dists, Int_t vecsize, Double_t* step) const
{
for (Int_t i=0; i<vecsize; i++) dists[i] = DistFromOutside(&points[3*i], &dirs[3*i], 3, step[i]);
}
void TGeoParaboloid::Safety_v(const Double_t *points, const Bool_t *inside, Double_t *safe, Int_t vecsize) const
{
for (Int_t i=0; i<vecsize; i++) safe[i] = Safety(&points[3*i], inside[i]);
}