    // @(#)root/g3d:$Name:  $:$Id: TPCON.cxx,v 1.3 2002/11/11 11:21:16 brun Exp $
    // Author: Nenad Buncic   29/09/95

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    #include "TPCON.h"
    #include "TNode.h"
    #include "TVirtualPad.h"
    #include "TVirtualGL.h"

    ClassImp(TPCON)

    //______________________________________________________________________________
    //

    // PCON is a polycone. It has the following parameters:
    //
    //     - name       name of the shape
    //     - title      shape's title
    //     - material  (see TMaterial)
    //     - phi1       the azimuthal angle phi at which the volume begins (angles
    //                  are counted counterclockwise)
    //     - dphi       opening angle of the volume, which extends from
    //                  phi1 to phi1+dphi
    //     - nz         number of planes perpendicular to the z axis where
    //                  the dimension of the section is given -- this number
    //                  should be at least 2
    //     - rmin       array of dimension nz with minimum radius at a given plane
    //     - rmax       array of dimension nz with maximum radius at a given plane
    //     - z          array of dimension nz with z position of given plane

    //______________________________________________________________________________
     TPCON::TPCON()
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*PCON shape default constructor*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                      ==============================

        fRmin  = 0;
        fRmax  = 0;
        fDz    = 0;
        fCoTab = 0;
        fSiTab = 0;
    }

    //______________________________________________________________________________
     TPCON::TPCON(const char *name, const char *title, const char *material, Float_t phi1, Float_t dphi1, Int_t nz)
          : TShape(name, title,material)
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*PCON shape normal constructor*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                      =============================
    //*-*  Parameters of the nz positions must be entered via TPCON::DefineSection.

        if (nz < 2 ) {
            Error(name, "number of z planes for %s must be at least two !", name);
            return;
        }
        fPhi1  = phi1;
        fDphi1 = dphi1;
        fNz    = nz;
        fNdiv  = 0;
        fRmin  = new Float_t [nz+1];
        fRmax  = new Float_t [nz+1];
        fDz    = new Float_t [nz+1];

        fCoTab = 0;
        fSiTab = 0;

        while (fDphi1 > 360) fDphi1 -= 360;

        MakeTableOfCoSin();
    }

    //______________________________________________________________________________
     void TPCON::MakeTableOfCoSin()
    {
        const Double_t PI  = TMath::ATan(1) * 4.0;
        const Double_t ragrad  = PI/180.0;

        Int_t n = GetNumberOfDivisions () + 1;
        if (fCoTab)
            delete [] fCoTab; // Delete the old tab if any
            fCoTab = new Double_t [n];
        if (!fCoTab ) return;

        if (fSiTab)
            delete [] fSiTab; // Delete the old tab if any
        fSiTab = new Double_t [n];
        if (!fSiTab ) return;

        Double_t range   = Double_t(fDphi1 * ragrad);
        Double_t phi1    = Double_t(fPhi1  * ragrad);
        Double_t angstep = range/(n-1);

        FillTableOfCoSin(phi1,angstep,n);
    }
    //______________________________________________________________________________
     TPCON::~TPCON()
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*PCON shape default destructor*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                      =============================

        if (fRmin) delete [] fRmin;
        if (fRmax) delete [] fRmax;
        if (fDz)   delete [] fDz;
        if (fSiTab) delete [] fSiTab;
        if (fCoTab) delete [] fCoTab;

        fRmin = 0;
        fRmax = 0;
        fDz   = 0;
        fCoTab = 0;
        fSiTab = 0;

    }

    //______________________________________________________________________________
     void TPCON::DefineSection(Int_t secNum, Float_t z, Float_t rmin, Float_t rmax)
    {
    //*-*-*-*-*-*-*-*-*-*Defines section secNum of the polycone*-*-*-*-*-*-*-*-*-*-*
    //*-*                ======================================
    //
    //     - rmin  radius of the inner circle in the cross-section
    //
    //     - rmax  radius of the outer circle in the cross-section
    //
    //     - z     z coordinate of the section

        if ((secNum < 0) || (secNum >= fNz)) return;

        fRmin[secNum] = rmin;
        fRmax[secNum] = rmax;
        fDz[secNum]   = z;
    }

    //______________________________________________________________________________
     Int_t TPCON::DistancetoPrimitive(Int_t px, Int_t py)
    {
    //*-*-*-*-*-*-*-*Compute distance from point px,py to a PCON*-*-*-*-*-*-*
    //*-*            ===========================================
    //*-*
    //*-*  Compute the closest distance of approach from point px,py to each
    //*-*  computed outline point of the PCON.
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
       Int_t n = GetNumberOfDivisions()+1;
       Int_t numPoints = fNz*2*n;
       return ShapeDistancetoPrimitive(numPoints,px,py);
    }

    //______________________________________________________________________________
     void  TPCON::FillTableOfCoSin(Double_t phi, Double_t angstep,Int_t n)
    {
      // File the table of cos and sin to prepare drawing
      Double_t ph = phi-angstep;
      for (Int_t j = 0; j < n; j++)
      {
        ph += angstep;
        fCoTab[j] = TMath::Cos(ph);
        fSiTab[j] = TMath::Sin(ph);
      }
    }
    //______________________________________________________________________________
     void TPCON::Paint(Option_t *option)
    {
    //*-*-*-*-*-*-*-*Paint this 3-D shape with its current attributes*-*-*-*-*-*-*-*
    //*-*            ================================================

       Int_t i, j;
       if (fNz < 2) return;
       const Int_t n = GetNumberOfDivisions()+1;

       Int_t numpoints = fNz*2*n;
       if (numpoints <= 0) return;
       //*-* Allocate memory for points *-*

       Float_t *points = new Float_t[3*numpoints];
       if (!points) return;
       SetPoints(points);

       Bool_t rangeView = strcmp(option,"range")==0 ? kTRUE : kFALSE;
       if (!rangeView && gPad->GetView3D()) PaintGLPoints(points);

     //==  for (i = 0; i < numpoints; i++)
     //==          gNode->Local2Master(&points[3*i],&points[3*i]);

       Bool_t specialCase = kFALSE;

       if (fDphi1 == 360)           //mark this as a very special case, when
            specialCase = kTRUE;     //we have to draw this PCON like a TUBE

        X3DBuffer *buff = new X3DBuffer;
        if (buff) {
            buff->numPoints = numpoints;
            buff->numSegs   = 4*(fNz*n-1+(specialCase == kTRUE));
            buff->numPolys  = 2*(fNz*n-1+(specialCase == kTRUE));
        }

    //*-* Allocate memory for points *-*

        buff->points = points;

        Int_t c = ((GetLineColor() % 8) - 1) * 4;     // Basic colors: 0, 1, ... 7
        if (c < 0) c = 0;

    //*-* Allocate memory for segments *-*

        Int_t indx, indx2, k;
        indx = indx2 = 0;

        buff->segs = new Int_t[buff->numSegs*3];
        if (buff->segs) {

            //inside & outside circles, number of segments: 2*fNz*(n-1)
            //             special case number of segments: 2*fNz*n
            for (i = 0; i < fNz*2; i++) {
                indx2 = i*n;
                for (j = 1; j < n; j++) {
                    buff->segs[indx++] = c;
                    buff->segs[indx++] = indx2+j-1;
                    buff->segs[indx++] = indx2+j;
                }
                if (specialCase) {
                    buff->segs[indx++] = c;
                    buff->segs[indx++] = indx2+j-1;
                    buff->segs[indx++] = indx2;
                }
            }

            //bottom & top lines, number of segments: 2*n
            for (i = 0; i < 2; i++) {
                indx2 = i*(fNz-1)*2*n;
                for (j = 0; j < n; j++) {
                    buff->segs[indx++] = c;
                    buff->segs[indx++] = indx2+j;
                    buff->segs[indx++] = indx2+n+j;
                }
            }

            //inside & outside cilindres, number of segments: 2*(fNz-1)*n
            for (i = 0; i < (fNz-1); i++) {

                //inside cilinder
                indx2 = i*n*2;
                for (j = 0; j < n; j++) {
                    buff->segs[indx++] = c+2;
                    buff->segs[indx++] = indx2+j;
                    buff->segs[indx++] = indx2+n*2+j;
                }
                //outside cilinder
                indx2 = i*n*2+n;
                for (j = 0; j < n; j++) {
                    buff->segs[indx++] = c+3;
                    buff->segs[indx++] = indx2+j;
                    buff->segs[indx++] = indx2+n*2+j;
                }
            }

            //left & right sections, number of segments: 2*(fNz-2)
            //          special case number of segments: 0
            if (!specialCase) {
                for (i = 1; i < (fNz-1); i++) {
                    for (j = 0; j < 2; j++) {
                        buff->segs[indx++] = c;
                        buff->segs[indx++] =  2*i    * n + j*(n-1);
                        buff->segs[indx++] = (2*i+1) * n + j*(n-1);
                    }
                }
            }
        }

        Int_t m = n - 1 + (specialCase == kTRUE);

    //*-* Allocate memory for polygons *-*

        indx = 0;

        buff->polys = new Int_t[buff->numPolys*6];

        if (buff->polys) {

            //bottom & top, number of polygons: 2*(n-1)
            // special case number of polygons: 2*n
            for (i = 0; i < 2; i++) {
                for (j = 0; j < n-1; j++) {
                    buff->polys[indx++] = c+3;
                    buff->polys[indx++] = 4;
                    buff->polys[indx++] = 2*fNz*m+i*n+j;
                    buff->polys[indx++] = i*(fNz*2-2)*m+m+j;
                    buff->polys[indx++] = 2*fNz*m+i*n+j+1;
                    buff->polys[indx++] = i*(fNz*2-2)*m+j;
                }
                if (specialCase) {
                    buff->polys[indx++] = c+3;
                    buff->polys[indx++] = 4;
                    buff->polys[indx++] = 2*fNz*m+i*n+j;
                    buff->polys[indx++] = i*(fNz*2-2)*m+m+j;
                    buff->polys[indx++] = 2*fNz*m+i*n;
                    buff->polys[indx++] = i*(fNz*2-2)*m+j;
                }
            }

            //inside & outside, number of polygons: (fNz-1)*2*(n-1)
            for (k = 0; k < (fNz-1); k++) {
                for (i = 0; i < 2; i++) {
                    for (j = 0; j < n-1; j++) {
                        buff->polys[indx++] = c+i;
                        buff->polys[indx++] = 4;
                        buff->polys[indx++] = (2*k+i*1)*m+j;
                        buff->polys[indx++] = fNz*2*m+(2*k+i*1+2)*n+j;
                        buff->polys[indx++] = (2*k+i*1+2)*m+j;
                        buff->polys[indx++] = fNz*2*m+(2*k+i*1+2)*n+j+1;
                    }
                    if (specialCase) {
                        buff->polys[indx++] = c+i;
                        buff->polys[indx++] = 4;
                        buff->polys[indx++] = (2*k+i*1)*m+j;
                        buff->polys[indx++] = fNz*2*m+(2*k+i*1+2)*n+j;
                        buff->polys[indx++] = (2*k+i*1+2)*m+j;
                        buff->polys[indx++] = fNz*2*m+(2*k+i*1+2)*n;
                    }
                }
            }

            //left & right sections, number of polygons: 2*(fNz-1)
            //          special case number of polygons: 0
            if (!specialCase) {
                indx2 = fNz*2*(n-1);
                for (k = 0; k < (fNz-1); k++) {
                    for (i = 0; i < 2; i++) {
                        buff->polys[indx++] = c+2;
                        buff->polys[indx++] = 4;
                        buff->polys[indx++] = k==0 ? indx2+i*(n-1) : indx2+2*fNz*n+2*(k-1)+i;
                        buff->polys[indx++] = indx2+2*(k+1)*n+i*(n-1);
                        buff->polys[indx++] = indx2+2*fNz*n+2*k+i;
                        buff->polys[indx++] = indx2+(2*k+3)*n+i*(n-1);
                    }
                }
                buff->polys[indx-8] = indx2+n;
                buff->polys[indx-2] = indx2+2*n-1;
            }
        }

        //*-* Paint in the pad
        PaintShape(buff,rangeView);

        if (strstr(option, "x3d")) {
            if(buff && buff->points && buff->segs)
                FillX3DBuffer(buff);
            else {
                gSize3D.numPoints -= buff->numPoints;
                gSize3D.numSegs   -= buff->numSegs;
                gSize3D.numPolys  -= buff->numPolys;
            }
        }

        delete [] points;
        if (buff->segs)     delete [] buff->segs;
        if (buff->polys)    delete [] buff->polys;
        if (buff)           delete    buff;
    }

    //______________________________________________________________________________
     void TPCON::PaintGLPoints(Float_t *vertex)
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*Paint BRIK via OpenGL *-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                            =====================

        gVirtualGL->PaintCone(vertex,-(GetNumberOfDivisions()+1),fNz);
    }
    //______________________________________________________________________________
     void TPCON::SetNumberOfDivisions (Int_t p)
    {
        if (GetNumberOfDivisions () == p) return;
        fNdiv=p;
        MakeTableOfCoSin();
    }
    //______________________________________________________________________________
     void TPCON::SetPoints(Float_t *buff)
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*Create PCON points*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                            ==================

        Int_t i, j;
        Int_t indx = 0;

        if (buff) {

            Int_t n            = GetNumberOfDivisions()+1;

    //*-* We've to checxk whether the table does exist and create it
    //*-* since fCoTab/fSiTab are not saved with any TShape::Streamer function
            if (!fCoTab)   MakeTableOfCoSin();

            for (i = 0; i < fNz; i++)
            {
                for (j = 0; j < n; j++)
                {
                    buff[indx++] = fRmin[i] * fCoTab[j];
                    buff[indx++] = fRmin[i] * fSiTab[j];
                    buff[indx++] = fDz[i];
                }
                for (j = 0; j < n; j++)
                {
                    buff[indx++] = fRmax[i] * fCoTab[j];
                    buff[indx++] = fRmax[i] * fSiTab[j];
                    buff[indx++] = fDz[i];
                }
            }
        }
    }

    //______________________________________________________________________________
     void TPCON::Sizeof3D() const
    {
    //*-*-*-*-*-*-*Return total X3D size of this shape with its attributes*-*-*-*-*-*
    //*-*          =======================================================

        Int_t n;

        n = GetNumberOfDivisions()+1;

        gSize3D.numPoints += fNz*2*n;
        gSize3D.numSegs   += 4*(fNz*n-1+(fDphi1 == 360));
        gSize3D.numPolys  += 2*(fNz*n-1+(fDphi1 == 360));
    }

    //_______________________________________________________________________
     void TPCON::Streamer(TBuffer &b)
    {
    //*-*-*-*-*-*-*-*-*Stream a class object*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*              =========================================
       if (b.IsReading()) {
          UInt_t R__s, R__c;
          Version_t R__v = b.ReadVersion(&R__s, &R__c);
          if (R__v > 1) {
             TPCON::Class()->ReadBuffer(b, this, R__v, R__s, R__c);
             return;
          }
          //====process old versions before automatic schema evolution
          TShape::Streamer(b);
          b >> fPhi1;
          b >> fDphi1;
          b >> fNz;
          fRmin  = new Float_t [fNz];
          fRmax  = new Float_t [fNz];
          fDz    = new Float_t [fNz];
          b.ReadArray(fRmin);
          b.ReadArray(fRmax);
          b.ReadArray(fDz);
          b >> fNdiv;
          b.CheckByteCount(R__s, R__c, TPCON::IsA());
          //====end of old versions

       } else {
          TPCON::Class()->WriteBuffer(b,this);
       }
    }