// @(#)root/histpainter:$Id: TGraph2DPainter.cxx,v 1.00
// Author: Olivier Couet

/*************************************************************************
 * 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 "TROOT.h"
#include "TGraph2DPainter.h"
#include "TMath.h"
#include "TList.h"
#include "TGraph.h"
#include "TGraph2D.h"
#include "TGraphDelaunay.h"
#include "TPolyLine.h"
#include "TPolyMarker.h"
#include "TVirtualPad.h"
#include "TView.h"
#include "THLimitsFinder.h"
#include "TStyle.h"
#include "Hoption.h"
#include "TH1.h"

R__EXTERN TH1  *gCurrentHist;
R__EXTERN Hoption_t Hoption;

ClassImp(TGraph2DPainter)


//______________________________________________________________________________
//
// TGraph2DPainter paints a TGraphDelaunay
//


//______________________________________________________________________________
TGraph2DPainter::TGraph2DPainter()
{
   // TGraph2DPainter default constructor

   fX        = 0;
   fY        = 0;
   fZ        = 0;
   fEX       = 0;
   fEY       = 0;
   fEZ       = 0;
   fXN       = 0;
   fYN       = 0;
   fPTried   = 0;
   fNTried   = 0;
   fMTried   = 0;
   fGraph2D  = 0;
   fDelaunay = 0;
   fXmin     = 0.;
   fXmax     = 0.;
   fYmin     = 0.;
   fYmax     = 0.;
   fZmin     = 0.;
   fZmax     = 0.;
   fXNmin    = 0;
   fXNmax    = 0;
   fYNmin    = 0;
   fYNmax    = 0;
   fNdt      = 0;
   fNpoints  = 0;
}


//______________________________________________________________________________
TGraph2DPainter::TGraph2DPainter(TGraphDelaunay *gd)
{
   // TGraph2DPainter constructor

   fDelaunay = gd;
   fGraph2D  = fDelaunay->GetGraph2D();
   fNpoints  = fGraph2D->GetN();
   fX        = fGraph2D->GetX();
   fY        = fGraph2D->GetY();
   fZ        = fGraph2D->GetZ();
   fEX       = fGraph2D->GetEX();
   fEY       = fGraph2D->GetEY();
   fEZ       = fGraph2D->GetEZ();
   fNdt      = 0;
   fXN       = 0;
   fYN       = 0;
   fXNmin    = 0;
   fXNmax    = 0;
   fYNmin    = 0;
   fYNmax    = 0;
   fPTried   = 0;
   fNTried   = 0;
   fMTried   = 0;
   fXmin     = 0.;
   fXmax     = 0.;
   fYmin     = 0.;
   fYmax     = 0.;
   fZmin     = 0.;
   fZmax     = 0.;
}


//______________________________________________________________________________
TGraph2DPainter::~TGraph2DPainter()
{
   // TGraph2DPainter destructor.
}


//______________________________________________________________________________
void TGraph2DPainter::FindTriangles()
{
   // Find triangles in fDelaunay and initialise the TGraph2DPainter values
   // needed to paint triangles or find contours.

   fDelaunay->FindAllTriangles();
   fNdt    = fDelaunay->GetNdt();
   fXN     = fDelaunay->GetXN();
   fYN     = fDelaunay->GetYN();
   fXNmin  = fDelaunay->GetXNmin();
   fXNmax  = fDelaunay->GetXNmax();
   fYNmin  = fDelaunay->GetYNmin();
   fYNmax  = fDelaunay->GetYNmax();
   fPTried = fDelaunay->GetPTried();
   fNTried = fDelaunay->GetNTried();
   fMTried = fDelaunay->GetMTried();
}


//______________________________________________________________________________
TList *TGraph2DPainter::GetContourList(Double_t contour)
{
   // Returns the X and Y graphs building a contour. A contour level may
   // consist in several parts not connected to each other. This function
   // finds them and returns them in a graphs' list.

   // Exit if the contour is outisde the Z range.
   Double_t zmin = gCurrentHist->GetMinimum();
   Double_t zmax = gCurrentHist->GetMaximum();
   if (Hoption.Logz) {
      if (zmin > 0) {
         zmin = TMath::Log10(zmin);
         zmax = TMath::Log10(zmax);
      } else {
         return 0;
      }
   }
   if(contour<zmin || contour>zmax) {
      Error("GetContourList", "Contour level (%g) outside the Z scope [%g,%g]",
      contour,zmin,zmax);
      return 0;
   }

   if (!fNdt) FindTriangles();

   TGraph *graph = 0;           // current graph
   Int_t npg     = 0;           // number of points in the current graph
   TList *list   = new TList(); // list holding all the graphs

   // Find all the segments making the contour

   Double_t r21, r20, r10;
   Int_t p0, p1, p2;
   Double_t x0, y0, z0;
   Double_t x1, y1, z1;
   Double_t x2, y2, z2;
   Int_t t[3],i,it,i0,i1,i2;

   // Allocate space to store the segments. They cannot be more than the
   // number of triangles.
   Double_t xs0c, ys0c, xs1c, ys1c;
   Double_t *xs0 = new Double_t[fNdt];
   Double_t *ys0 = new Double_t[fNdt];
   Double_t *xs1 = new Double_t[fNdt];
   Double_t *ys1 = new Double_t[fNdt];
   for (i=0;i<fNdt;i++) {
      xs0[i] = 0.;
      ys0[i] = 0.;
      xs1[i] = 0.;
      ys1[i] = 0.;
   }
   Int_t nbSeg   = 0;

   // Loop over all the triangles in order to find all the line segments
   // making the contour.
   for(it=0; it<fNdt; it++) {
      t[0] = fPTried[it];
      t[1] = fNTried[it];
      t[2] = fMTried[it];
      p0   = t[0]-1;
      p1   = t[1]-1;
      p2   = t[2]-1;
      x0   = fX[p0]; x2 = fX[p0];
      y0   = fY[p0]; y2 = fY[p0];
      z0   = fZ[p0]; z2 = fZ[p0];

      // Order along Z axis the points (xi,yi,zi) where "i" belongs to {0,1,2}
      // After this z0 < z1 < z2
      i0=0, i1=0, i2=0;
      if (fZ[p1]<=z0) {z0=fZ[p1]; x0=fX[p1]; y0=fY[p1]; i0=1;}
      if (fZ[p1]>z2)  {z2=fZ[p1]; x2=fX[p1]; y2=fY[p1]; i2=1;}
      if (fZ[p2]<=z0) {z0=fZ[p2]; x0=fX[p2]; y0=fY[p2]; i0=2;}
      if (fZ[p2]>z2)  {z2=fZ[p2]; x2=fX[p2]; y2=fY[p2]; i2=2;}
      if (i0==0 && i2==0) {
         Error("GetContourList", "wrong vertices ordering");
         delete [] xs0;
         delete [] ys0;
         delete [] xs1;
         delete [] ys1;
         return 0;
      } else {
         i1 = 3-i2-i0;
      }
      x1 = fX[t[i1]-1];
      y1 = fY[t[i1]-1];
      z1 = fZ[t[i1]-1];

      if (Hoption.Logz) {
         z0 = TMath::Log10(z0);
         z1 = TMath::Log10(z1);
         z2 = TMath::Log10(z2);
      }

      if(contour >= z0 && contour <=z2) {
         r20 = (contour-z0)/(z2-z0);
         xs0c = r20*(x2-x0)+x0;
         ys0c = r20*(y2-y0)+y0;
         if(contour >= z1 && contour <=z2) {
            r21 = (contour-z1)/(z2-z1);
            xs1c = r21*(x2-x1)+x1;
            ys1c = r21*(y2-y1)+y1;
         } else {
            r10 = (contour-z0)/(z1-z0);
            xs1c = r10*(x1-x0)+x0;
            ys1c = r10*(y1-y0)+y0;
         }
         // do not take the segments equal to a point
         if(xs0c != xs1c || ys0c != ys1c) {
            nbSeg++;
            xs0[nbSeg-1] = xs0c;
            ys0[nbSeg-1] = ys0c;
            xs1[nbSeg-1] = xs1c;
            ys1[nbSeg-1] = ys1c;
         }
      }
   }

   Bool_t *segUsed = new Bool_t[fNdt];
   for(i=0; i<fNdt; i++) segUsed[i]=kFALSE;

   // Find all the graphs making the contour. There is two kind of graphs,
   // either they are "opened" or they are "closed"

   // Find the opened graphs
   Double_t xc=0, yc=0, xnc=0, ync=0;
   Bool_t findNew;
   Bool_t s0, s1;
   Int_t is, js;
   for (is=0; is<nbSeg; is++) {
      if (segUsed[is]) continue;
      s0 = s1 = kFALSE;

      // Find to which segment is is connected. It can be connected
      // via 0, 1 or 2 vertices.
      for (js=0; js<nbSeg; js++) {
         if (is==js) continue;
         if (xs0[is]==xs0[js] && ys0[is]==ys0[js]) s0 = kTRUE;
         if (xs0[is]==xs1[js] && ys0[is]==ys1[js]) s0 = kTRUE;
         if (xs1[is]==xs0[js] && ys1[is]==ys0[js]) s1 = kTRUE;
         if (xs1[is]==xs1[js] && ys1[is]==ys1[js]) s1 = kTRUE;
      }

      // Segment is is alone, not connected. It is stored in the
      // list and the next segment is examined.
      if (!s0 && !s1) {
         graph = new TGraph();
         graph->SetPoint(npg,xs0[is],ys0[is]); npg++;
         graph->SetPoint(npg,xs1[is],ys1[is]); npg++;
         segUsed[is] = kTRUE;
         list->Add(graph); npg = 0;
         continue;
      }

      // Segment is is connected via 1 vertex only and can be considered
      // as the starting point of an opened contour.
      if (!s0 || !s1) {
         // Find all the segments connected to segment is
         graph = new TGraph();
         if (s0) {xc = xs0[is]; yc = ys0[is]; xnc = xs1[is]; ync = ys1[is];}
         if (s1) {xc = xs1[is]; yc = ys1[is]; xnc = xs0[is]; ync = ys0[is];}
         graph->SetPoint(npg,xnc,ync); npg++;
         segUsed[is] = kTRUE;
         js = 0;
L01:
         findNew = kFALSE;
         if (segUsed[js] && js<nbSeg) {
            js++;
            goto L01;
         } else if (xc==xs0[js] && yc==ys0[js]) {
            xc      = xs1[js];
            yc      = ys1[js];
            findNew = kTRUE;
         } else if (xc==xs1[js] && yc==ys1[js]) {
            xc      = xs0[js];
            yc      = ys0[js];
            findNew = kTRUE;
         }
         if (findNew) {
            segUsed[js] = kTRUE;
            graph->SetPoint(npg,xc,yc); npg++;
            js = 0;
            goto L01;
         }
         js++;
         if (js<nbSeg) goto L01;
         list->Add(graph); npg = 0;
      }
   }

   // Find the closed graphs. At this point all the remaining graphs
   // are closed. Any segment can be used to start the search.
   for (is=0; is<nbSeg; is++) {
      if (segUsed[is]) continue;

      // Find all the segments connected to segment is
      graph = new TGraph();
      segUsed[is] = kTRUE;
      xc = xs0[is];
      yc = ys0[is];
      js = 0;
      graph->SetPoint(npg,xc,yc); npg++;
L02:
      findNew = kFALSE;
      if (segUsed[js] && js<nbSeg) {
         js++;
         goto L02;
      } else if (xc==xs0[js] && yc==ys0[js]) {
         xc      = xs1[js];
         yc      = ys1[js];
         findNew = kTRUE;
      } else if (xc==xs1[js] && yc==ys1[js]) {
         xc      = xs0[js];
         yc      = ys0[js];
         findNew = kTRUE;
      }
      if (findNew) {
         segUsed[js] = kTRUE;
         graph->SetPoint(npg,xc,yc); npg++;
         js = 0;
         goto L02;
      }
      js++;
      if (js<nbSeg) goto L02;
      // Close the contour
      graph->SetPoint(npg,xs0[is],ys0[is]); npg++;
      list->Add(graph); npg = 0;
   }

   delete [] xs0;
   delete [] ys0;
   delete [] xs1;
   delete [] ys1;
   delete [] segUsed;
   return list;
}


//______________________________________________________________________________
void TGraph2DPainter::Paint(Option_t *option)
{
   // Paint a TGraphDelaunay according to the value of "option":
   //
   //   "TRI"  : The Delaunay triangles are drawn using filled area.
   //            An hidden surface drawing technique is used. The surface is
   //            painted with the current fill area color. The edges of each
   //            triangles are painted with the current line color.
   //   "TRIW" : The Delaunay triangles are drawn as wire frame
   //   "TRI1" : The Delaunay triangles are painted with color levels. The edges
   //            of each triangles are painted with the current line color.
   //   "TRI2" : the Delaunay triangles are painted with color levels.
   //   "P"    : Draw a marker at each vertex
   //   "P0"   : Draw a circle at each vertex. Each circle background is white.
   //   "PCOL" : Draw a marker at each vertex. The color of each marker is
   //            defined according to its Z position.
   //   "CONT" : Draw contours
   //   "LINE" : Draw a 3D polyline

   TString opt = option;
   opt.ToLower();
   Bool_t triangles = opt.Contains("tri")  ||
                      opt.Contains("tri1") ||
                      opt.Contains("tri2");
   if (opt.Contains("tri0")) triangles = kFALSE;

   Bool_t markers   = opt.Contains("p") && !triangles;
   Bool_t contour   = opt.Contains("cont");
   Bool_t line      = opt.Contains("line");
   Bool_t err       = opt.Contains("err");

   fGraph2D->TAttLine::Modify();
   fGraph2D->TAttFill::Modify();
   fGraph2D->TAttMarker::Modify();

   // Compute minimums and maximums
   TAxis *xaxis = gCurrentHist->GetXaxis();
   Int_t first = xaxis->GetFirst();
   fXmin = xaxis->GetBinLowEdge(first);
   if (Hoption.Logx && fXmin <= 0) fXmin = xaxis->GetBinUpEdge(xaxis->FindFixBin(0.01*xaxis->GetBinWidth(first)));
   fXmax = xaxis->GetBinUpEdge(xaxis->GetLast());
   TAxis *yaxis = gCurrentHist->GetYaxis();
   first = yaxis->GetFirst();
   fYmin = yaxis->GetBinLowEdge(first);
   if (Hoption.Logy && fYmin <= 0) fYmin = yaxis->GetBinUpEdge(yaxis->FindFixBin(0.01*yaxis->GetBinWidth(first)));
   fYmax = yaxis->GetBinUpEdge(yaxis->GetLast());
   fZmax = fGraph2D->GetZmax();
   fZmin = fGraph2D->GetZmin();
   if (Hoption.Logz && fZmin <= 0) fZmin = TMath::Min((Double_t)1, (Double_t)0.001*fGraph2D->GetZmax());

   if (triangles) PaintTriangles(option);
   if (contour)   PaintContour(option);
   if (line)      PaintPolyLine(option);
   if (err)       PaintErrors(option);
   if (markers)   PaintPolyMarker(option);
}


//______________________________________________________________________________
void TGraph2DPainter::PaintContour(Option_t * /*option*/)
{
   // Paints the 2D graph as a contour plot. Delaunay triangles are used
   // to compute the contours.

   // Initialize the levels on the Z axis
   Int_t ncolors  = gStyle->GetNumberOfColors();
   Int_t ndiv   = gCurrentHist->GetContour();
   if (ndiv == 0 ) {
      ndiv = gStyle->GetNumberContours();
      gCurrentHist->SetContour(ndiv);
   }
   Int_t ndivz  = TMath::Abs(ndiv);
   if (gCurrentHist->TestBit(TH1::kUserContour) == 0) gCurrentHist->SetContour(ndiv);

   Int_t theColor;
   TList *l;
   TGraph *g;
   TObject *obj;
   Double_t c;

   if (!fNdt) FindTriangles();

   for (Int_t k=0; k<ndiv; k++) {
      c = gCurrentHist->GetContourLevelPad(k);
      l = GetContourList(c);
      TIter next(l);
      while ((obj = next())) {
         if(obj->InheritsFrom(TGraph::Class()) ) {
            g=(TGraph*)obj;
            theColor = Int_t((k+0.99)*Float_t(ncolors)/Float_t(ndivz));
            g->SetLineColor(gStyle->GetColorPalette(theColor));
            g->Paint("l");
         }
      }
   }
}


//______________________________________________________________________________
void TGraph2DPainter::PaintErrors(Option_t * /* option */)
{
   // Paints the 2D graph as error bars

   Double_t temp1[3],temp2[3];

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintErrors", "No TView in current pad");
      return;
   }

   Int_t  it;

   Double_t *xm = new Double_t[2];
   Double_t *ym = new Double_t[2];

   fGraph2D->SetLineStyle(fGraph2D->GetLineStyle());
   fGraph2D->SetLineWidth(fGraph2D->GetLineWidth());
   fGraph2D->SetLineColor(fGraph2D->GetLineColor());
   fGraph2D->TAttLine::Modify();

   for (it=0; it<fNpoints; it++) {
      if(fX[it] < fXmin || fX[it] > fXmax) continue;
      if(fY[it] < fYmin || fY[it] > fYmax) continue;
      if (fEX) {
         temp1[0] = fX[it]-fEX[it];
         temp1[1] = fY[it];
         temp1[2] = fZ[it];
         temp1[0] = TMath::Max(temp1[0],fXmin);
         temp1[1] = TMath::Max(temp1[1],fYmin);
         temp1[2] = TMath::Max(temp1[2],fZmin);
         temp1[2] = TMath::Min(temp1[2],fZmax);
         if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
         if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
         if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[0] = temp2[0];
         ym[0] = temp2[1];

         temp1[0] = fX[it]+fEX[it];
         temp1[0] = TMath::Max(temp1[0],fXmin);
         if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[1] = temp2[0];
         ym[1] = temp2[1];
         gPad->PaintPolyLine(2,xm,ym);
      }
      if (fEY) {
         temp1[0] = fX[it];
         temp1[1] = fY[it]-fEY[it];
         temp1[2] = fZ[it];
         temp1[0] = TMath::Max(temp1[0],fXmin);
         temp1[1] = TMath::Max(temp1[1],fYmin);
         temp1[2] = TMath::Max(temp1[2],fZmin);
         temp1[2] = TMath::Min(temp1[2],fZmax);
         if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
         if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
         if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[0] = temp2[0];
         ym[0] = temp2[1];

         temp1[1] = fY[it]+fEY[it];
         temp1[1] = TMath::Max(temp1[1],fYmin);
         if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[1] = temp2[0];
         ym[1] = temp2[1];
         gPad->PaintPolyLine(2,xm,ym);
      }
      if (fEZ) {
         temp1[0] = fX[it];
         temp1[1] = fY[it];
         temp1[2] = fZ[it]-fEZ[it];
         temp1[0] = TMath::Max(temp1[0],fXmin);
         temp1[1] = TMath::Max(temp1[1],fYmin);
         temp1[2] = TMath::Max(temp1[2],fZmin);
         temp1[2] = TMath::Min(temp1[2],fZmax);
         if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
         if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
         if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[0] = temp2[0];
         ym[0] = temp2[1];

         temp1[2] = fZ[it]+fEZ[it];
         temp1[2] = TMath::Max(temp1[2],fZmin);
         temp1[2] = TMath::Min(temp1[2],fZmax);
         if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
         view->WCtoNDC(temp1, &temp2[0]);
         xm[1] = temp2[0];
         ym[1] = temp2[1];
         gPad->PaintPolyLine(2,xm,ym);
      }
   }
   delete [] xm;
   delete [] ym;
}


//______________________________________________________________________________
void TGraph2DPainter::PaintLevels(Int_t *t,Double_t *x, Double_t *y,
                           Int_t nblev, Double_t *glev)
{
   // Paints one triangle.
   // nblev  = 0 : paint the color levels
   // nblev != 0 : paint the grid

   Int_t i, fillColor, ncolors, theColor0, theColor2;

   Int_t p0=t[0]-1;
   Int_t p1=t[1]-1;
   Int_t p2=t[2]-1;
   Double_t xl[2],yl[2];
   Double_t zl, r21, r20, r10;
   Double_t x0 = x[0]  , x2 = x[0];
   Double_t y0 = y[0]  , y2 = y[0];
   Double_t z0 = fZ[p0], z2 = fZ[p0];
   Double_t zmin = fZmin;
   Double_t zmax = fZmax;

   // Order along Z axis the points (xi,yi,zi) where "i" belongs to {0,1,2}
   // After this z0 < z1 < z2
   Int_t i0=0, i1=0, i2=0;
   if (fZ[p1]<=z0) {z0=fZ[p1]; x0=x[1]; y0=y[1]; i0=1;}
   if (fZ[p1]>z2)  {z2=fZ[p1]; x2=x[1]; y2=y[1]; i2=1;}
   if (fZ[p2]<=z0) {z0=fZ[p2]; x0=x[2]; y0=y[2]; i0=2;}
   if (fZ[p2]>z2)  {z2=fZ[p2]; x2=x[2]; y2=y[2]; i2=2;}
   i1 = 3-i2-i0;
   Double_t x1 = x[i1];
   Double_t y1 = y[i1];
   Double_t z1 = fZ[t[i1]-1];

   if (z0>zmax) z0 = zmax;
   if (z2>zmax) z2 = zmax;
   if (z0<zmin) z0 = zmin;
   if (z2<zmin) z2 = zmin;
   if (z1>zmax) z1 = zmax;
   if (z1<zmin) z1 = zmin;

   if (Hoption.Logz) {
      z0   = TMath::Log10(z0);
      z1   = TMath::Log10(z1);
      z2   = TMath::Log10(z2);
      zmin = TMath::Log10(zmin);
      zmax = TMath::Log10(zmax);
   }

   // zi  = Z values of the stripe number i
   // zip = Previous zi
   Double_t zi=0, zip=0;

   if (nblev <= 0) {
      // Paint the colors levels

      // Compute the color associated to z0 (theColor0) and z2 (theColor2)
      ncolors   = gStyle->GetNumberOfColors();
      theColor0 = (Int_t)( ((z0-zmin)/(zmax-zmin))*(ncolors-1) );
      theColor2 = (Int_t)( ((z2-zmin)/(zmax-zmin))*(ncolors-1) );

      // The stripes drawn to fill the triangles may have up to 5 points
      Double_t xp[5], yp[5];

      // rl = Ratio between z0 and z2 (long)
      // rs = Ratio between z0 and z1 or z1 and z2 (short)
      Double_t rl,rs;

      // ci = Color of the stripe number i
      // npf = number of point needed to draw the current stripe
      Int_t ci,npf;

      fillColor = fGraph2D->GetFillColor();

      // If the z0's color and z2's colors are the same, the whole triangle
      // can be painted in one go.
      if(theColor0 == theColor2) {
         fGraph2D->SetFillColor(gStyle->GetColorPalette(theColor0));
         fGraph2D->TAttFill::Modify();
         gPad->PaintFillArea(3,x,y);

      // The triangle must be painted with several colors
      } else {
         for(ci=theColor0; ci<=theColor2; ci++) {
            fGraph2D->SetFillColor(gStyle->GetColorPalette(ci));
            fGraph2D->TAttFill::Modify();
            if (ci==theColor0) {
               zi    = (((ci+1)*(zmax-zmin))/(ncolors-1))+zmin;
               xp[0] = x0;
               yp[0] = y0;
               rl    = (zi-z0)/(z2-z0);
               xp[1] = rl*(x2-x0)+x0;
               yp[1] = rl*(y2-y0)+y0;
               if (zi>=z1 || z0==z1) {
                  rs    = (zi-z1)/(z2-z1);
                  xp[2] = rs*(x2-x1)+x1;
                  yp[2] = rs*(y2-y1)+y1;
                  xp[3] = x1;
                  yp[3] = y1;
                  npf   = 4;
               } else {
                  rs    = (zi-z0)/(z1-z0);
                  xp[2] = rs*(x1-x0)+x0;
                  yp[2] = rs*(y1-y0)+y0;
                  npf   = 3;
               }
            } else if (ci==theColor2) {
               xp[0] = xp[1];
               yp[0] = yp[1];
               xp[1] = x2;
               yp[1] = y2;
               if (zi<z1 || z2==z1) {
                  xp[3] = xp[2];
                  yp[3] = yp[2];
                  xp[2] = x1;
                  yp[2] = y1;
                  npf   = 4;
               } else {
                  npf   = 3;
               }
            } else {
               zi    = (((ci+1)*(zmax-zmin))/(ncolors-1))+zmin;
               xp[0] = xp[1];
               yp[0] = yp[1];
               rl    = (zi-z0)/(z2-z0);
               xp[1] = rl*(x2-x0)+x0;
               yp[1] = rl*(y2-y0)+y0;
               if ( zi>=z1 && zip<=z1) {
                  xp[3] = x1;
                  yp[3] = y1;
                  xp[4] = xp[2];
                  yp[4] = yp[2];
                  npf   = 5;
               } else {
                  xp[3] = xp[2];
                  yp[3] = yp[2];
                  npf   = 4;
               }
               if (zi<z1) {
                  rs    = (zi-z0)/(z1-z0);
                  xp[2] = rs*(x1-x0)+x0;
                  yp[2] = rs*(y1-y0)+y0;
               } else {
                  rs    = (zi-z1)/(z2-z1);
                  xp[2] = rs*(x2-x1)+x1;
                  yp[2] = rs*(y2-y1)+y1;
               }
            }
            zip = zi;
            // Paint a stripe
            gPad->PaintFillArea(npf,xp,yp);
         }
      }
      fGraph2D->SetFillColor(fillColor);
      fGraph2D->TAttFill::Modify();

   } else {
      // Paint the grid levels
      fGraph2D->SetLineStyle(3);
      fGraph2D->TAttLine::Modify();
      for(i=0; i<nblev; i++){
         zl=glev[i];
         if(zl >= z0 && zl <=z2) {
            r21=(zl-z1)/(z2-z1);
            r20=(zl-z0)/(z2-z0);
            r10=(zl-z0)/(z1-z0);
            xl[0]=r20*(x2-x0)+x0;
            yl[0]=r20*(y2-y0)+y0;
            if(zl >= z1 && zl <=z2) {
               xl[1]=r21*(x2-x1)+x1;
               yl[1]=r21*(y2-y1)+y1;
            } else {
               xl[1]=r10*(x1-x0)+x0;
               yl[1]=r10*(y1-y0)+y0;
            }
            gPad->PaintPolyLine(2,xl,yl);
         }
      }
      fGraph2D->SetLineStyle(1);
      fGraph2D->TAttLine::Modify();
   }
}


//______________________________________________________________________________
void TGraph2DPainter::PaintPolyMarker(Option_t *option)
{
   // Paints the 2D graph as PaintPolyMarker

   Double_t temp1[3],temp2[3];

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintPolyMarker", "No TView in current pad");
      return;
   }

   TString opt = option;
   opt.ToLower();
   Bool_t markers0 = opt.Contains("p0");
   Bool_t colors   = opt.Contains("pcol");
   Int_t  ncolors  = gStyle->GetNumberOfColors();
   Int_t  it, theColor;

   Double_t *xm = new Double_t[fNpoints];
   Double_t *ym = new Double_t[fNpoints];
   Int_t    npd = 0;
   for (it=0; it<fNpoints; it++) {
      xm[it] = 0;
      ym[it] = 0;
      if(fX[it] < fXmin || fX[it] > fXmax) continue;
      if(fY[it] < fYmin || fY[it] > fYmax) continue;
      npd++;
      temp1[0] = fX[it];
      temp1[1] = fY[it];
      temp1[2] = fZ[it];
      temp1[0] = TMath::Max(temp1[0],fXmin);
      temp1[1] = TMath::Max(temp1[1],fYmin);
      temp1[2] = TMath::Max(temp1[2],fZmin);
      temp1[2] = TMath::Min(temp1[2],fZmax);
      if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
      if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
      if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
      view->WCtoNDC(temp1, &temp2[0]);
      xm[it] = temp2[0];
      ym[it] = temp2[1];
   }
   if (markers0) {
      PaintPolyMarker0(npd,xm,ym);
   } else if (colors) {
      for (it=0; it<fNpoints; it++) {
         theColor = (Int_t)( ((fZ[it]-fZmin)/(fZmax-fZmin))*(ncolors-1) );
         fGraph2D->SetMarkerColor(gStyle->GetColorPalette(theColor));
         fGraph2D->TAttMarker::Modify();
         gPad->PaintPolyMarker(1,&xm[it],&ym[it]);
      }
   } else {
      fGraph2D->SetMarkerStyle(fGraph2D->GetMarkerStyle());
      fGraph2D->SetMarkerSize(fGraph2D->GetMarkerSize());
      fGraph2D->SetMarkerColor(fGraph2D->GetMarkerColor());
      fGraph2D->TAttMarker::Modify();
      gPad->PaintPolyMarker(npd,xm,ym);
   }
   delete [] xm;
   delete [] ym;
}


//______________________________________________________________________________
void TGraph2DPainter::PaintPolyLine(Option_t * /* option */)
{
   // Paints the 2D graph as PaintPolyLine

   Double_t temp1[3],temp2[3];

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintPolyLine", "No TView in current pad");
      return;
   }

   Int_t  it;

   Double_t *xm = new Double_t[fNpoints];
   Double_t *ym = new Double_t[fNpoints];
   Int_t    npd = 0;
   for (it=0; it<fNpoints; it++) {
      if(fX[it] < fXmin || fX[it] > fXmax) continue;
      if(fY[it] < fYmin || fY[it] > fYmax) continue;
      npd++;
      temp1[0] = fX[it];
      temp1[1] = fY[it];
      temp1[2] = fZ[it];
      temp1[0] = TMath::Max(temp1[0],fXmin);
      temp1[1] = TMath::Max(temp1[1],fYmin);
      temp1[2] = TMath::Max(temp1[2],fZmin);
      temp1[2] = TMath::Min(temp1[2],fZmax);
      if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
      if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
      if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
      view->WCtoNDC(temp1, &temp2[0]);
      xm[it] = temp2[0];
      ym[it] = temp2[1];
   }
   fGraph2D->SetLineStyle(fGraph2D->GetLineStyle());
   fGraph2D->SetLineWidth(fGraph2D->GetLineWidth());
   fGraph2D->SetLineColor(fGraph2D->GetLineColor());
   fGraph2D->TAttLine::Modify();
   gPad->PaintPolyLine(npd,xm,ym);
   delete [] xm;
   delete [] ym;
}


//______________________________________________________________________________
void TGraph2DPainter::PaintPolyMarker0(Int_t n, Double_t *x, Double_t *y)
{
   // Paints a circle at each vertex. Each circle background is white.

   fGraph2D->SetMarkerSize(fGraph2D->GetMarkerSize());
   Int_t mc = fGraph2D->GetMarkerColor();
   for (Int_t i=0; i<n; i++) {
      fGraph2D->SetMarkerStyle(20);
      fGraph2D->SetMarkerColor(0);
      fGraph2D->TAttMarker::Modify();
      gPad->PaintPolyMarker(1,&x[i],&y[i]);
      fGraph2D->SetMarkerStyle(24);
      fGraph2D->SetMarkerColor(mc);
      fGraph2D->TAttMarker::Modify();
      gPad->PaintPolyMarker(1,&x[i],&y[i]);
   }
}


//______________________________________________________________________________
void TGraph2DPainter::PaintTriangles(Option_t *option)
{
   // Paints the 2D graph as triangles

   Double_t x[4], y[4], temp1[3],temp2[3];
   Int_t it,t[3];
   Int_t *order = 0;
   Double_t *dist = 0;

   TView *view = gPad->GetView();
   if (!view) {
      Error("PaintTriangles", "No TView in current pad");
      return;
   }

   TString opt = option;
   opt.ToLower();
   Bool_t tri1      = opt.Contains("tri1");
   Bool_t tri2      = opt.Contains("tri2");
   Bool_t markers   = opt.Contains("p");
   Bool_t markers0  = opt.Contains("p0");
   Bool_t wire      = opt.Contains("w");

   // Define the grid levels drawn on the triangles.
   // The grid levels are aligned on the Z axis' main tick marks.
   Int_t nblev=0;
   Double_t *glev=0;
   if (!tri1 && !tri2 && !wire) {
      Int_t ndivz = gCurrentHist->GetZaxis()->GetNdivisions()%100;
      Int_t nbins;
      Double_t binLow, binHigh, binWidth;

      // Find the main tick marks positions.
      Double_t *r0 = view->GetRmin();
      Double_t *r1 = view->GetRmax();

      if (ndivz > 0) {
         THLimitsFinder::Optimize(r0[2], r1[2], ndivz,
                                  binLow, binHigh, nbins, binWidth, " ");
      } else {
         nbins = TMath::Abs(ndivz);
         binLow = r0[2];
         binHigh = r1[2];
         binWidth = (binHigh-binLow)/nbins;
      }
      // Define the grid levels
      nblev = nbins+1;
      glev = new Double_t[nblev];
      for (Int_t i = 0; i < nblev; ++i) glev[i] = binLow+i*binWidth;
   }

   // Initialize the levels on the Z axis
   if (tri1 || tri2) {
      Int_t ndiv   = gCurrentHist->GetContour();
      if (ndiv == 0 ) {
         ndiv = gStyle->GetNumberContours();
         gCurrentHist->SetContour(ndiv);
      }
      if (gCurrentHist->TestBit(TH1::kUserContour) == 0) gCurrentHist->SetContour(ndiv);
   }

   // For each triangle, compute the distance between the triangle centre
   // and the back planes. Then these distances are sorted in order to draw
   // the triangles from back to front.
   if (!fNdt) FindTriangles();
   Double_t cp = TMath::Cos(view->GetLongitude()*TMath::Pi()/180.);
   Double_t sp = TMath::Sin(view->GetLongitude()*TMath::Pi()/180.);
   order = new Int_t[fNdt];
   dist  = new Double_t[fNdt];
   Double_t xd,yd;
   Int_t p, n, m;
   Bool_t o = kFALSE;
   for (it=0; it<fNdt; it++) {
      p = fPTried[it];
      n = fNTried[it];
      m = fMTried[it];
      xd = (fXN[p]+fXN[n]+fXN[m])/3;
      yd = (fYN[p]+fYN[n]+fYN[m])/3;
      if ((cp >= 0) && (sp >= 0.)) {
         dist[it] = -(fXNmax-xd+fYNmax-yd);
      } else if ((cp <= 0) && (sp >= 0.)) {
         dist[it] = -(fXNmax-xd+yd-fYNmin);
         o = kTRUE;
      } else if ((cp <= 0) && (sp <= 0.)) {
         dist[it] = -(xd-fXNmin+yd-fYNmin);
      } else {
         dist[it] = -(xd-fXNmin+fYNmax-yd);
         o = kTRUE;
      }
   }
   TMath::Sort(fNdt, dist, order, o);

   // Draw the triangles and markers if requested
   fGraph2D->SetFillColor(fGraph2D->GetFillColor());
   Int_t fs = fGraph2D->GetFillStyle();
   fGraph2D->SetFillStyle(1001);
   fGraph2D->TAttFill::Modify();
   fGraph2D->SetLineColor(fGraph2D->GetLineColor());
   fGraph2D->TAttLine::Modify();
   int lst = fGraph2D->GetLineStyle();
   for (it=0; it<fNdt; it++) {
      t[0] = fPTried[order[it]];
      t[1] = fNTried[order[it]];
      t[2] = fMTried[order[it]];
      for (Int_t k=0; k<3; k++) {
         if(fX[t[k]-1] < fXmin || fX[t[k]-1] > fXmax) goto endloop;
         if(fY[t[k]-1] < fYmin || fY[t[k]-1] > fYmax) goto endloop;
         temp1[0] = fX[t[k]-1];
         temp1[1] = fY[t[k]-1];
         temp1[2] = fZ[t[k]-1];
         temp1[0] = TMath::Max(temp1[0],fXmin);
         temp1[1] = TMath::Max(temp1[1],fYmin);
         temp1[2] = TMath::Max(temp1[2],fZmin);
         temp1[2] = TMath::Min(temp1[2],fZmax);
         if (Hoption.Logx) temp1[0] = TMath::Log10(temp1[0]);
         if (Hoption.Logy) temp1[1] = TMath::Log10(temp1[1]);
         if (Hoption.Logz) temp1[2] = TMath::Log10(temp1[2]);
         view->WCtoNDC(temp1, &temp2[0]);
         x[k] = temp2[0];
         y[k] = temp2[1];
      }
      x[3] = x[0];
      y[3] = y[0];
      if (tri1 || tri2) PaintLevels(t,x,y);
      if (!tri1 && !tri2 && !wire) {
         gPad->PaintFillArea(3,x,y);
         PaintLevels(t,x,y,nblev,glev);
      }
      if (!tri2) gPad->PaintPolyLine(4,x,y);
      if (markers) {
         if (markers0) {
            PaintPolyMarker0(3,x,y);
         } else {
            fGraph2D->SetMarkerStyle(fGraph2D->GetMarkerStyle());
            fGraph2D->SetMarkerSize(fGraph2D->GetMarkerSize());
            fGraph2D->SetMarkerColor(fGraph2D->GetMarkerColor());
            fGraph2D->TAttMarker::Modify();
            gPad->PaintPolyMarker(3,x,y);
         }
      }
endloop:
      continue;
   }
   fGraph2D->SetFillStyle(fs);
   fGraph2D->SetLineStyle(lst);
   fGraph2D->TAttLine::Modify();
   fGraph2D->TAttFill::Modify();
   delete [] order;
   delete [] dist;
   if (glev) delete [] glev;
}
