#include "TObjArray.h"
#include "TGeoPolygon.h"
#include "TMath.h"
#include "TGeoShape.h"
#include "TGeoManager.h"
#include "TVirtualGeoPainter.h"
ClassImp(TGeoPolygon)
TGeoPolygon::TGeoPolygon()
{
fNvert = 0;
fNconvex = 0;
fInd = 0;
fIndc = 0;
fX = 0;
fY = 0;
fDaughters = 0;
SetConvex(kFALSE);
TObject::SetBit(kGeoFinishPolygon, kFALSE);
}
TGeoPolygon::TGeoPolygon(Int_t nvert)
{
if (nvert<3) {
Fatal("Ctor", "Invalid number of vertices %i", nvert);
return;
}
fNvert = nvert;
fNconvex = 0;
fInd = new Int_t[nvert];
fIndc = 0;
fX = 0;
fY = 0;
fDaughters = 0;
SetConvex(kFALSE);
TObject::SetBit(kGeoFinishPolygon, kFALSE);
SetNextIndex();
}
TGeoPolygon::~TGeoPolygon()
{
if (fInd) delete [] fInd;
if (fIndc) delete [] fIndc;
if (fDaughters) {
fDaughters->Delete();
delete fDaughters;
}
}
Double_t TGeoPolygon::Area() const
{
Int_t ic,i,j;
Double_t area = 0;
for (ic=0; ic<fNvert; ic++) {
i = fInd[ic];
j = fInd[(ic+1)%fNvert];
area += 0.5*(fX[i]*fY[j]-fX[j]*fY[i]);
}
return TMath::Abs(area);
}
Bool_t TGeoPolygon::Contains(const Double_t *point) const
{
Int_t i;
TGeoPolygon *poly;
for (i=0; i<fNconvex; i++)
if (!IsRightSided(point, fIndc[i], fIndc[(i+1)%fNconvex])) return kFALSE;
if (!fDaughters) return kTRUE;
Int_t nd = fDaughters->GetEntriesFast();
for (i=0; i<nd; i++) {
poly = (TGeoPolygon*)fDaughters->UncheckedAt(i);
if (poly->Contains(point)) return kFALSE;
}
return kTRUE;
}
void TGeoPolygon::ConvexCheck()
{
if (fNvert==3) {
SetConvex();
return;
}
Int_t j,k;
Double_t point[2];
for (Int_t i=0; i<fNvert; i++) {
j = (i+1)%fNvert;
k = (i+2)%fNvert;
point[0] = fX[fInd[k]];
point[1] = fY[fInd[k]];
if (!IsRightSided(point, fInd[i], fInd[j])) return;
}
SetConvex();
}
void TGeoPolygon::Draw(Option_t *)
{
if (!gGeoManager) return;
gGeoManager->GetGeomPainter()->DrawPolygon(this);
}
void TGeoPolygon::FinishPolygon()
{
TObject::SetBit(kGeoFinishPolygon);
ConvexCheck();
OutscribedConvex();
if (IsConvex()) {
memcpy(fIndc, fInd, fNvert*sizeof(Int_t));
return;
}
if (IsConvex()) return;
if (!fDaughters) fDaughters = new TObjArray();
TGeoPolygon *poly = 0;
Int_t indconv = 0;
Int_t indnext, indback;
Int_t nskip;
while (indconv < fNconvex) {
indnext = (indconv+1)%fNconvex;
nskip = fIndc[indnext]-fIndc[indconv];
if (nskip<0) nskip+=fNvert;
if (nskip==1) {
indconv++;
continue;
}
poly = new TGeoPolygon(nskip+1);
poly->SetXY(fX,fY);
poly->SetNextIndex(fInd[fIndc[indconv]]);
poly->SetNextIndex(fInd[fIndc[indnext]]);
indback = fIndc[indnext]-1;
if (indback < 0) indback+=fNvert;
while (indback != fIndc[indconv]) {
poly->SetNextIndex(fInd[indback]);
indback--;
if (indback < 0) indback+=fNvert;
}
poly->FinishPolygon();
fDaughters->Add(poly);
indconv++;
}
for (indconv=0; indconv<fNconvex; indconv++) fIndc[indconv] = fInd[fIndc[indconv]];
}
void TGeoPolygon::GetVertices(Double_t *x, Double_t *y) const
{
memcpy(x, fX, fNvert*sizeof(Double_t));
memcpy(y, fY, fNvert*sizeof(Double_t));
}
void TGeoPolygon::GetConvexVertices(Double_t *x, Double_t *y) const
{
for (Int_t ic=0; ic<fNconvex; ic++) {
x[ic] = fX[fIndc[ic]];
y[ic] = fY[fIndc[ic]];
}
}
Bool_t TGeoPolygon::IsRightSided(const Double_t *point, Int_t ind1, Int_t ind2) const
{
Double_t dot = (point[0]-fX[ind1])*(fY[ind2]-fY[ind1]) -
(point[1]-fY[ind1])*(fX[ind2]-fX[ind1]);
if (!IsClockwise()) dot = -dot;
if (dot<-1.E-10) return kFALSE;
return kTRUE;
}
Bool_t TGeoPolygon::IsSegConvex(Int_t i1, Int_t i2) const
{
if (i2<0) i2=(i1+1)%fNvert;
Double_t point[2];
for (Int_t i=0; i<fNvert; i++) {
if (i==i1 || i==i2) continue;
point[0] = fX[fInd[i]];
point[1] = fY[fInd[i]];
if (!IsRightSided(point, fInd[i1], fInd[i2])) return kFALSE;
}
return kTRUE;
}
Bool_t TGeoPolygon::IsIllegalCheck() const
{
if (fNvert<4) return kFALSE;
Bool_t is_illegal = kFALSE;
Double_t x1,y1,x2,y2,x3,y3,x4,y4;
for (Int_t i=0; i<fNvert-2; i++) {
for (Int_t j=i+2; j<fNvert; j++) {
if (i==0 && j==(fNvert-1)) continue;
x1 = fX[i];
y1 = fY[i];
x2 = fX[i+1];
y2 = fY[i+1];
x3 = fX[j];
y3 = fY[j];
x4 = fX[(j+1)%fNvert];
y4 = fY[(j+1)%fNvert];
if (TGeoShape::IsSegCrossing(x1,y1,x2,y2,x3,y3,x4,y4)) {
Error("IsIllegalCheck", "Illegal crossing of segment %d vs. segment %d", i,j);
is_illegal = kTRUE;
}
}
}
return is_illegal;
}
void TGeoPolygon::OutscribedConvex()
{
fNconvex = 0;
Int_t iseg = 0;
Int_t ivnew;
Bool_t conv;
Int_t *indconv = new Int_t[fNvert];
memset(indconv, 0, fNvert*sizeof(Int_t));
while (iseg<fNvert) {
if (!IsSegConvex(iseg)) {
if (iseg+2 > fNvert) break;
ivnew = (iseg+2)%fNvert;
conv = kFALSE;
while (ivnew) {
if (IsSegConvex(iseg, ivnew)) {
conv = kTRUE;
break;
}
ivnew = (ivnew+1)%fNvert;
}
if (!conv) {
iseg++;
continue;
}
} else {
ivnew = (iseg+1)%fNvert;
}
if (!fNconvex) indconv[fNconvex++] = iseg;
else if (indconv[fNconvex-1] != iseg) indconv[fNconvex++] = iseg;
if (iseg<fNvert-1) indconv[fNconvex++] = ivnew;
if (ivnew<iseg) break;
iseg = ivnew;
}
if (!fNconvex) {
delete [] indconv;
Fatal("OutscribedConvex","cannot build outscribed convex");
return;
}
fIndc = new Int_t[fNvert];
memcpy(fIndc, indconv, fNconvex*sizeof(Int_t));
delete [] indconv;
}
Double_t TGeoPolygon::Safety(const Double_t *point, Int_t &isegment) const
{
Int_t i1, i2;
Double_t p1[2], p2[3];
Double_t lsq, ssq, dx, dy, dpx, dpy, u;
Double_t safe=1E30;
Int_t isegmin=0;
for (i1=0; i1<fNvert; i1++) {
if (TGeoShape::IsSameWithinTolerance(safe,0)) {
isegment = isegmin;
return 0.;
}
i2 = (i1+1)%fNvert;
p1[0] = fX[i1];
p1[1] = fY[i1];
p2[0] = fX[i2];
p2[1] = fY[i2];
dx = p2[0] - p1[0];
dy = p2[1] - p1[1];
dpx = point[0] - p1[0];
dpy = point[1] - p1[1];
lsq = dx*dx + dy*dy;
if (TGeoShape::IsSameWithinTolerance(lsq,0)) {
ssq = dpx*dpx + dpy*dpy;
if (ssq < safe) {
safe = ssq;
isegmin = i1;
}
continue;
}
u = (dpx*dx + dpy*dy)/lsq;
if (u>1) {
dpx = point[0]-p2[0];
dpy = point[1]-p2[1];
} else {
if (u>=0) {
dpx -= u*dx;
dpy -= u*dy;
}
}
ssq = dpx*dpx + dpy*dpy;
if (ssq < safe) {
safe = ssq;
isegmin = i1;
}
}
isegment = isegmin;
safe = TMath::Sqrt(safe);
return safe;
}
void TGeoPolygon::SetNextIndex(Int_t index)
{
Int_t i;
if (index <0) {
for (i=0; i<fNvert; i++) fInd[i] = i;
return;
}
if (fNconvex >= fNvert) {
Error("SetNextIndex", "all indices already set");
return;
}
fInd[fNconvex++] = index;
if (fNconvex == fNvert) {
if (!fX || !fY) return;
Double_t area = 0.0;
for (i=0; i<fNvert; i++) area += fX[fInd[i]]*fY[fInd[(i+1)%fNvert]]-fX[fInd[(i+1)%fNvert]]*fY[fInd[i]];
if (area<0) TObject::SetBit(kGeoACW, kFALSE);
else TObject::SetBit(kGeoACW, kTRUE);
}
}
void TGeoPolygon::SetXY(Double_t *x, Double_t *y)
{
Int_t i;
fX = x;
fY = y;
Double_t area = 0.0;
for (i=0; i<fNvert; i++) area += fX[fInd[i]]*fY[fInd[(i+1)%fNvert]]-fX[fInd[(i+1)%fNvert]]*fY[fInd[i]];
if (area<0) TObject::SetBit(kGeoACW, kFALSE);
else TObject::SetBit(kGeoACW, kTRUE);
if (!fDaughters) return;
TGeoPolygon *poly;
Int_t nd = fDaughters->GetEntriesFast();
for (i=0; i<nd; i++) {
poly = (TGeoPolygon*)fDaughters->At(i);
if (poly) poly->SetXY(x,y);
}
}