#include "Riostream.h"
#include <algorithm>
#include <string>

#ifdef WIN32
#include "Windows4root.h"
#endif

#include <GL/gl.h>
#include <GL/glu.h>

#include "THLimitsFinder.h"
#include "TVirtualGL.h"
#include "TVirtualX.h"
#include "TGaxis.h"
#include "TString.h"
#include "TPoint.h"
#include "TColor.h"
#include "TROOT.h"
#include "TAxis.h"
#include "TMath.h"
#include "TPad.h"
#include "TH1.h"

#include "TGLHistPainter.h"

ClassImp(TGLHistPainter)

//______________________________________________________________________________
TGLHistPainter::TGLHistPainter(TH1 *hist)
                   : fDefaultPainter(0),
                     fHist(hist),
                     fLastOption(kUnsupported),
                     fAxisX(hist->GetXaxis()),
                     fAxisY(hist->GetYaxis()),
                     fAxisZ(hist->GetZaxis()),
                     fMinX(0.), fMaxX(0.), fScaleX(1.), 
                     fMinXScaled(0.), fMaxXScaled(0.),
                     fMinY(0.), fMaxY(0.), fScaleY(1.),
                     fMinYScaled(0.), fMaxYScaled(0.),
                     fMinZ(0.), fMaxZ(0.), fScaleZ(1.),
                     fMinZScaled(0.), fMaxZScaled(0.),
                     fFactor(1.),
                     fRotation(100, 100),
                     fFrustum(), fCenter(), fShift(0.), fViewport(),
                     fFirstBinX(0), fLastBinX(0),
                     fFirstBinY(0), fLastBinY(0),
                     fLogX(kFALSE), fLogY(kFALSE), fLogZ(kFALSE),
                     fGLDevice(-1),
                     f2DPass(kFALSE),
                     fTexture()
{
   //Each TGLHistPainter has default painter as a member
   //to delegate unsupported calls

   InitDefaultPainter();
   InitTexture();
}

//______________________________________________________________________________
 TGLHistPainter::~TGLHistPainter()
{
   //

   delete fDefaultPainter;
}

//______________________________________________________________________________
 Int_t TGLHistPainter::DistancetoPrimitive(Int_t px, Int_t py)
{
   //If fLastOption != kUnsupported, try to select hist or axis.
   //if not - gPad is selected (there are problems with TF2)

   if (fLastOption == kUnsupported)
      return fDefaultPainter->DistancetoPrimitive(px, py);
   else {
      if ((fGLDevice = gPad->GetGLDevice()) == -1) {
         Error("DistancetoPrimitive", "Current pad (gPad) does not have gl device\n");
         return 9999; //TF2 will try to select something itself
      }

      gGLManager->MakeCurrent(fGLDevice);

      if (!Select(px, py))
         gPad->SetSelected(gPad);// To avoid TF2 selection

      return 0;
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawPanel()
{
   //FIX
   
   if (fLastOption == kUnsupported)
      fDefaultPainter->DrawPanel();
}

//______________________________________________________________________________
 void TGLHistPainter::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   //If FLastOption == kUnsupported, delegate call.
   //If not, try to process itself

   if (fLastOption == kUnsupported)
      fDefaultPainter->ExecuteEvent(event, px, py);
   else {
      //
      py -= Int_t((1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh());
      px -= Int_t(gPad->GetXlowNDC() * gPad->GetWw());

      if ((fGLDevice = gPad->GetGLDevice()) == -1) {
         Error("ExecuteEvent", "current pad (gPad) does not have gl device\n");
         return;
      }

      gGLManager->MakeCurrent(fGLDevice);

      switch (event) {
      case kButton1Down :
         gGLManager->ExtractViewport(fGLDevice, fViewport);
         fRotation.SetBounds(fViewport[2], fViewport[3]);
         fRotation.Click(TPoint(px, py));
         gGLManager->MarkForDirectCopy(fGLDevice, kTRUE);
         break;
      case kButton1Motion :
         fRotation.Drag(TPoint(px, py));
         gGLManager->MarkForDirectCopy(fGLDevice, kTRUE);
         gGLManager->PaintSingleObject(this);
         gGLManager->Flush(fGLDevice);
         break;
      case kButton1Up:
         gGLManager->MarkForDirectCopy(fGLDevice, kFALSE);
         break;
      case kMouseMotion:
         gPad->SetCursor(kRotate);//Does not work for TF2 (?)
         break;
      }
   }
}

//______________________________________________________________________________
 void TGLHistPainter::FitPanel()
{
   //FIX
   
   if (fLastOption == kUnsupported)
      fDefaultPainter->FitPanel();
}

//______________________________________________________________________________
 TList *TGLHistPainter::GetContourList(Double_t contour)const
{
   //FIX

   return fDefaultPainter->GetContourList(contour);
}

//______________________________________________________________________________
 char *TGLHistPainter::GetObjectInfo(Int_t px, Int_t py)const
{
   //FIX

   return fDefaultPainter->GetObjectInfo(px, py);
}

//______________________________________________________________________________
 TList *TGLHistPainter::GetStack()const
{
   //FIX

   return fDefaultPainter->GetStack();
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::IsInside(Int_t x, Int_t y)
{
   //FIX

   if (fLastOption == kUnsupported)
      return fDefaultPainter->IsInside(x, y);
   
   return kFALSE;
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::IsInside(Double_t x, Double_t y)
{
   //FIX

   if (fLastOption == kUnsupported)
      return fDefaultPainter->IsInside(x, y);

   return kFALSE;
}

//______________________________________________________________________________
 void TGLHistPainter::PaintStat(Int_t dostat, TF1 *fit)
{
   //FIX

   fDefaultPainter->PaintStat(dostat, fit);
}

//______________________________________________________________________________
 void TGLHistPainter::ProcessMessage(const char *mess, const TObject *obj)
{
   //FIX

   if (fLastOption == kUnsupported)
      fDefaultPainter->ProcessMessage(mess, obj);
}

//______________________________________________________________________________
 void TGLHistPainter::SetHistogram(TH1 *hist)
{
   //FIX

   fHist = hist;
   fDefaultPainter->SetHistogram(hist);
}

//______________________________________________________________________________
 void TGLHistPainter::SetStack(TList *stack)
{
   //FIX
   
   if (fLastOption == kUnsupported)
      fDefaultPainter->SetStack(stack);
}

//______________________________________________________________________________
 Int_t TGLHistPainter::MakeCuts(char *cutsOpt)
{
   //FIX

   return fDefaultPainter->MakeCuts(cutsOpt);
}

//______________________________________________________________________________
 void TGLHistPainter::InitDefaultPainter()
{
   //FIX
   
   fDefaultPainter = TVirtualHistPainter::HistPainter(fHist);
}

//______________________________________________________________________________
 void TGLHistPainter::Paint(Option_t *o)
{
   //Final-overrider for TOvject's Paint, checks, if 
   //painter can draw itself or should pass to default painter

   if (f2DPass) return;

   TString option(o);
   option.ToLower();

   if ((fLastOption = GetPaintOption(option)) == kUnsupported) {
      gPad->SetCopyGLDevice(kFALSE);
      fDefaultPainter->Paint(o);
   } else {
      
      fGLDevice = gPad->GetGLDevice();

      if (fGLDevice != -1) {
         if (!InitPainter()) return;

         gGLManager->SelectGLPixmap(fGLDevice);
         gGLManager->MakeCurrent(fGLDevice);
         gGLManager->PaintSingleObject(this);
         //to deselect pixmap from Win32 DC
         //gGLManager->GetVirtualXInd(fGLDevice);
         gVirtualX->SelectWindow(gPad->GetPixmapID());
      }
      else
         fDefaultPainter->Paint(o);
   }
}

//______________________________________________________________________________
 void TGLHistPainter::Paint()
{
   //Paint method, which is indirectly called by gGLManager
   //at this moment, MakeCurrent must be done already for fGLDevice

   gGLManager->SelectGLPixmap(fGLDevice);
   gPad->SetCopyGLDevice(kTRUE);

   InitGL();
   SetGLParameters();
   ClearBuffer();

   switch (fLastOption) {
   case kLego :
      PaintLego();
      break;
   case kLego2 :
      PaintLego2();
      break;
   case kSurface:
      PaintSurface();
      break;
   case kSurface1:
   case kSurface2:
      PaintSurface1();
      break;
   case kSurface4:
      PaintSurface4();
      break;
   default:;//to shut up g++
   }
}

//______________________________________________________________________________
TGLHistPainter::EGLPaintOption TGLHistPainter::GetPaintOption(const TString &o)
{
   //Check, if Paint's option is supported

   std::string option(o.Data());
   std::string::size_type start = option.find("lego");
   std::string::size_type len = 0;

   if (start != std::string::npos)
      if ((len = option.length()) == 5) {// && option[4] == '1' || option.length() == 4)
         switch (option[4]) {
         case '1':
            return kLego; //will be kLego1 in future
         case '2':
            return kLego2;
         }
      } else if (len == 4)
         return kLego;
   
   start = option.find("surf");
   
   if (start != std::string::npos)
      if ((len = option.length()) == 5)
         switch (option[4]) {
         case '1':
            return kSurface1;
         case '2':
            return kSurface2;
         case '4':
            return kSurface4;
         }
      else if(len == 4)
         return kSurface;
         
   return kUnsupported;
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::InitPainter()
{
   //It's clear :)

   if (!SetSizes())
      return kFALSE;

   FillVertices();

   if (fLastOption == kSurface)
      SetNormals();
   else if (fLastOption == kSurface4 || fLastOption == kSurface1 || fLastOption == kSurface2)
      SetAverageNormals();

   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::SetSizes()
{
   //Having TH1 pointer, setup min/max sizes and scales

   fLogX = gPad->GetLogx();
   if (!SetAxisRange(fAxisX, fLogX, fFirstBinX, fLastBinX, fMinX, fMaxX)) {
      Error("SetSizes", "cannot set X axis to log scale\n");
      return kFALSE;
   }

   fLogY = gPad->GetLogy();
   if (!SetAxisRange(fAxisY, fLogY, fFirstBinY, fLastBinY, fMinY, fMaxY)) {
      Error("SetSizes", "cannot set Y axis to log scale\n");
      return kFALSE;
   }

   fMaxZ = fHist->GetCellContent(fFirstBinX, fFirstBinY);
   fMinZ = fMaxZ;
   Double_t summ = 0.;
   Double_t positiveMin = -1.;

   for (Int_t i = fFirstBinX; i <= fLastBinX; ++i)
      for (Int_t j = fFirstBinY; j <= fLastBinY; ++j) {
         Double_t val = fHist->GetCellContent(i, j);
         if (positiveMin < 0. && val > 0.) 
            positiveMin = val;
         else if (val > 0.)
            positiveMin = TMath::Min(positiveMin, val);
            
         fMaxZ = TMath::Max(val, fMaxZ);
         fMinZ = TMath::Min(val, fMinZ);
         summ += val;
      }
      
   Bool_t maximum = fHist->GetMaximumStored() != -1111;
   Bool_t minimum = fHist->GetMinimumStored() != -1111;

   if (maximum) fMaxZ = fHist->GetMaximumStored();
   if (minimum) fMinZ = fHist->GetMinimumStored();
   if (fMinZ >= fMaxZ) fMinZ = 0.001 * fMaxZ;//NEW

   fLogZ = gPad->GetLogz();

   if (fLogZ && fMaxZ <= 0) {
      Error("SetSizes", "log scale is requested for Z, but maximum less or equal 0 (%f)", fMaxZ);
      return kFALSE;
   }

   fFactor = fHist->GetNormFactor() > 0 ? fHist->GetNormFactor() : summ;
   if (summ) fFactor /= summ;
   if (!fFactor) fFactor = 1.;

   fMaxZ *= fFactor;
   fMinZ *= fFactor;

   if (fLogZ) {
      if (fMinZ <= 0.) {
         fMinZ = TMath::Min(1., 0.001 * fMaxZ);
         fHist->SetMinimum(fMinZ);
      }

      fMinZ = TMath::Log10(fMinZ);
      fMaxZ = TMath::Log10(fMaxZ);
      
      if (positiveMin > 0.)
         fMinZ = TMath::Min(TMath::Log10(positiveMin), fMinZ);
   }

   SetZLevels();
   AdjustScales();

   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::SetAxisRange(const TAxis *axis, Bool_t log, Int_t &first, Int_t &last,
                                          Double_t &min, Double_t &max)
{
   //Sets-up parameters for X or Y axis

   first = axis->GetFirst();
   last = axis->GetLast();
   min = axis->GetBinLowEdge(first);
   max = axis->GetBinLowEdge(last) + axis->GetBinWidth(last);

   if (log) {
      if (min <= 0.) 
         min = axis->GetBinUpEdge(axis->FindFixBin(0.01 * axis->GetBinWidth(first)));
      if (min <= 0. || max <= 0.)
         return kFALSE;
      Int_t bin = axis->FindFixBin(min);
      if (axis->GetBinLowEdge(bin) <= 0.) ++bin;//crashes under win32
      if (first < bin) first = bin;
      bin = axis->FindFixBin(max);
      if (last > bin) last = bin;
      min = TMath::Log10(min);
      max = TMath::Log10(max);
   }

   return kTRUE;
}

//______________________________________________________________________________
 void TGLHistPainter::AdjustScales()
{
   //Finds the maximum dimension and adjust scale coefficients

   Double_t xRange = fMaxX - fMinX;
   Double_t yRange = fMaxY - fMinY;
   Double_t zRange = fMinZ > 0. ? fMaxZ : fMaxZ - fMinZ;

   Double_t maxDim = TMath::Max(TMath::Max(xRange, yRange), zRange);
   
   fScaleX = maxDim / xRange;
   fScaleY = maxDim / yRange;
   fScaleZ = maxDim / zRange / 1.3; //1.5
   fMinXScaled = fMinX * fScaleX;
   fMaxXScaled = fMaxX * fScaleX;
   fMinYScaled = fMinY * fScaleY;
   fMaxYScaled = fMaxY * fScaleY;
   fMinZScaled = fMinZ * fScaleZ;
   fMaxZScaled = fMaxZ * fScaleZ;
}

//______________________________________________________________________________
 void TGLHistPainter::FillVertices()
{
   //Calculates table of X and Y for lego (Z is obtained during drawing) or
   //calculate mesh of triangles with vertices in the centres of bins

   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   if (fLastOption == kLego || fLastOption == kLego2) {
      fX.resize(nX + 1);
      fY.resize(nY + 1);

      for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
         if (fLogX) 
            fX[i] = TMath::Log10(fAxisX->GetBinLowEdge(ir)) * fScaleX;
         else 
            fX[i] = fAxisX->GetBinLowEdge(ir) * fScaleX;

      for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr)
         if (fLogY) 
            fY[j] = TMath::Log10(fAxisY->GetBinLowEdge(jr)) * fScaleY;
         else 
            fY[j] = fAxisY->GetBinLowEdge(jr) * fScaleY;

      Double_t maxX = fAxisX->GetBinLowEdge(fLastBinX) + fAxisX->GetBinWidth(fLastBinX);

      if (fLogX) fX[nX] = TMath::Log10(maxX) * fScaleX;
      else fX[nX] = maxX * fScaleX;

      Double_t maxY = fAxisY->GetBinLowEdge(fLastBinY) + fAxisY->GetBinWidth(fLastBinY);

      if (fLogY) fY[nY] = TMath::Log10(maxY) * fScaleY;
      else fY[nY] = maxY * fScaleY;
   } else {
      fMesh.resize(nX * nY);
      fMesh.SetRowLen(nY);

      for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
         for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr) {
            if (fLogX) fMesh[i][j].X() = TMath::Log10(fAxisX->GetBinCenter(ir)) * fScaleX;
            else fMesh[i][j].X() = fAxisX->GetBinCenter(ir) * fScaleX;
            if (fLogY) fMesh[i][j].Y() = TMath::Log10(fAxisY->GetBinCenter(jr)) * fScaleY;
            else fMesh[i][j].Y() = fAxisY->GetBinCenter(jr) * fScaleY;

            Double_t z = fHist->GetCellContent(ir, jr);
            
            if (fLogZ) {
            //bizarre
               if (z <= 0)
                  fMesh[i][j].Z() = fMinZ * fScaleZ;
               else
                  fMesh[i][j].Z() = TMath::Log10(z) * fScaleZ;
            } else
               fMesh[i][j].Z() = z * fScaleZ;
         }
   }
}

//______________________________________________________________________________
 void TGLHistPainter::SetNormals()
{
   //Calculates normals for triangles in surface.
   //"flat" normals == 1 normal per triangle
   //we have : four points (cell contents of four neighbouring hist bins)
   //but only three points are shurely in one plane, so build 2 triangles and their normals
   
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;
   
   fFaceNormals.resize((nX - 1) * (nY - 1));
   fFaceNormals.SetRowLen(nY - 1);

   for (Int_t i = 0; i < nX - 1; ++i)
      for (Int_t j = 0; j < nY - 1; ++j) {
         //first "bottom-left" triangle
         TMath::Normal2Plane(fMesh[i][j + 1].CArr(), fMesh[i][j].CArr(), fMesh[i + 1][j].CArr(),
                             fFaceNormals[i][j].first.Arr());
         //second "top-right" triangle
         TMath::Normal2Plane(fMesh[i + 1][j].CArr(), fMesh[i + 1][j + 1].CArr(), fMesh[i][j + 1].CArr(),
                             fFaceNormals[i][j].second.Arr());
      }
}

//______________________________________________________________________________
 void TGLHistPainter::SetAverageNormals()
{
   //Calculate averaged normals.
   //"averaged" normals == normal per vertex
   //This normal is average of
   //neighbouring triangles normals
   
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;
   
   fFaceNormals.resize((nX + 1) * (nY + 1));
   fFaceNormals.SetRowLen(nY + 1);
   
   //first, calculate normal for each triangle face
   for (Int_t i = 0; i < nX - 1; ++i)
      for (Int_t j = 0; j < nY - 1; ++j) {
         //second "top-right" triangle
         TMath::Normal2Plane(fMesh[i][j + 1].CArr(), fMesh[i][j].CArr(), fMesh[i + 1][j].CArr(),
                             fFaceNormals[i + 1][j + 1].first.Arr());
         //second "top-right" triangle
         TMath::Normal2Plane(fMesh[i + 1][j].CArr(), fMesh[i + 1][j + 1].CArr(), fMesh[i][j + 1].CArr(),
                             fFaceNormals[i + 1][j + 1].second.Arr());
      }
      
   fAverageNormals.resize(nX * nY);
   fAverageNormals.SetRowLen(nY);
      
   //second, lets calculate average normal for each vertex
   for (Int_t i = 0; i < nX; ++i)
      for (Int_t j = 0; j < nY; ++j) {
         TGLVector3 &norm = fAverageNormals[i][j];
         
         norm += fFaceNormals[i][j].second;
         norm += fFaceNormals[i][j + 1].first;
         norm += fFaceNormals[i][j + 1].second;
         norm += fFaceNormals[i + 1][j].first;
         norm += fFaceNormals[i + 1][j].second;
         norm += fFaceNormals[i + 1][j + 1].first;
         
         norm.Normalise();
      }
}

//______________________________________________________________________________
 void TGLHistPainter::InitGL()const
{
   //Simple gl initialization
   
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);

   if (fLastOption == kLego || fLastOption == kLego2)//with lego we should avoid back faces (or lego is really sloooow)
      glEnable(GL_CULL_FACE), glCullFace(GL_BACK);
   else //for surface we cannot cull faces, because we can look at the surface "from bottom"
      glDisable(GL_CULL_FACE);
}

//______________________________________________________________________________
 void TGLHistPainter::PaintLego()const
{
   //Draws lego and "profiles" on the back planes

   SetCamera();
   //main light
   const Float_t pos[] = {0.f, 0.f, 0.f, 1.f};
   glLightfv(GL_LIGHT0, GL_POSITION, pos);

   SetTransformation();
   
   Int_t fp = FrontPoint();
   DrawFrame(fp);

   Float_t difColor[] = {0.8f, 0.8f, 0.8f, 1.f};

   if (fHist->GetFillColor() != kWhite)
      if (TColor *c = gROOT->GetColor(fHist->GetFillColor()))
         c->GetRGB(difColor[0], difColor[1], difColor[2]);
   
   //save material properties in stack
   glPushAttrib(GL_LIGHTING_BIT);
   
   glMaterialfv(GL_FRONT, GL_DIFFUSE, difColor);
   const Float_t specColor[] = {0.5f, 0.f, 0.f, 1.f};
   glMaterialfv(GL_FRONT, GL_SPECULAR, specColor);
   glMaterialf(GL_FRONT, GL_SHININESS, 70.f);
   //
   //cycle through table
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1.f, 1.f);

   for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
      for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr) {
         Double_t zMax = fHist->GetCellContent(ir, jr);

         if (fLogZ)
            if (zMax <= 0.)
               continue;//I simply ignore this bizzare situation
            else 
               zMax = TMath::Log10(zMax) * fScaleZ;
         else zMax *= fScaleZ;

         DrawBoxFront(fX[i], fX[i + 1], fY[j], fY[j + 1], 0., zMax, fp);
      }

   glDisable(GL_POLYGON_OFFSET_FILL);

   //outlines cycle
   glDisable(GL_LIGHTING);
   glColor3d(0., 0., 0.);
   glPolygonMode(GL_FRONT, GL_LINE);

   for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
      for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr) {
         Double_t zMax = fHist->GetCellContent(ir, jr);

         if (fLogZ)
            if (zMax <= 0.)
               continue;//I simply ignore this bizzare situation
            else 
               zMax = TMath::Log10(zMax) * fScaleZ;
         else zMax *= fScaleZ;

         DrawBoxFront(fX[i], fX[i + 1], fY[j], fY[j + 1], 0., zMax, fp);
      }

   glPolygonMode(GL_FRONT, GL_FILL);
   glEnable(GL_LIGHTING);

   //restore material properties from stack
   glPopAttrib();

   DrawZeroPlane();

   glFlush();
   //now, gl drawing is finished, axes are drawn by TVirtualX
   DrawAxes(fp);
}

//______________________________________________________________________________
 void TGLHistPainter::PaintLego2()const
{
   //Draws lego2 and "profiles" on the back planes

   SetCamera();
   //main light
   const Float_t pos[] = {0.f, 0.f, 0.f, 1.f};
   glLightfv(GL_LIGHT0, GL_POSITION, pos);

   SetTransformation();
   
   Int_t fp = FrontPoint();
   DrawFrame(fp);

   glPushAttrib(GL_LIGHTING_BIT);
   const Float_t spec[] = {1.f, 1.f, 1.f, 1.f};
   glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
   glMaterialf(GL_FRONT, GL_SHININESS, 80.);
   Float_t diff[] = {0.9f, 0.9f, 0.9f, 1.f};
   glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);//without it I can get random mixture of texture and color

   glEnable(GL_TEXTURE_1D);
   glEnable(GL_DEPTH_TEST);
   glShadeModel(GL_SMOOTH);
   
   if (!glIsTexture(fTextureName)) {
      glGenTextures(1, &fTextureName);
   }

   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   glBindTexture(GL_TEXTURE_1D, fTextureName);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, kTexLength, 0,
                GL_RGBA, GL_UNSIGNED_BYTE, fTexture);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1.f, 1.f);

   //cycle through table
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
      for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr) {
         Double_t zMax = fHist->GetCellContent(ir, jr);

         if (fLogZ)
            if (zMax <= 0.)
               continue;//I simply ignore this bizzare situation
            else 
               zMax = TMath::Log10(zMax) * fScaleZ;
         else zMax *= fScaleZ;

         DrawBoxFrontTextured(fX[i], fX[i + 1], fY[j], fY[j + 1], 0., zMax, fp);
      }
      
   glDisable(GL_POLYGON_OFFSET_FILL);

   glDisable(GL_TEXTURE_1D);      
   glDeleteTextures(1, &fTextureName);
   
      //outlines cycle
   glDisable(GL_LIGHTING);
   glColor3d(0., 0., 0.);
   glPolygonMode(GL_FRONT, GL_LINE);

   for (Int_t i = 0, ir = fFirstBinX; i < nX; ++i, ++ir)
      for (Int_t j = 0, jr = fFirstBinY; j < nY; ++j, ++jr) {
         Double_t zMax = fHist->GetCellContent(ir, jr);

         if (fLogZ)
            if (zMax <= 0.)
               continue;//I simply ignore this bizzare situation
            else 
               zMax = TMath::Log10(zMax) * fScaleZ;
         else zMax *= fScaleZ;

         DrawBoxFront(fX[i], fX[i + 1], fY[j], fY[j + 1], 0., zMax, fp);
      }

   glPolygonMode(GL_FRONT, GL_FILL);
   glEnable(GL_LIGHTING);

   //restore material properties from stack
   glPopAttrib();

   DrawZeroPlane();

   glFlush();
   //now, gl drawing is finished, axes are drawn by TVirtualX
   DrawAxes(fp);
}

//______________________________________________________________________________
 void TGLHistPainter::PaintSurface()const
{
   //Draws surface as a set of triangles, each triangle has one normal

   SetCamera();
   //main light
   const Float_t pos[] = {0.f, 0.f, 0.f, 1.f};
   glLightfv(GL_LIGHT0, GL_POSITION, pos);

   SetTransformation();

   Int_t fp = FrontPoint();
   DrawFrame(fp);

   Float_t color[] = {0.8f, 0.8f, 0.8f, 1.f};

   if (fHist->GetFillColor() != kWhite)
      if (TColor *c = gROOT->GetColor(fHist->GetFillColor()))
         c->GetRGB(color[0], color[1], color[2]);

   //save material properties in stack
   glPushAttrib(GL_LIGHTING_BIT);
   
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
   const Float_t spec[] = {1.f, 1.f, 1.f, 1.f};
   glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
   glMaterialf(GL_FRONT, GL_SHININESS, 80.);

   //cycle through table
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   for (Int_t i = 0; i < nX - 1; ++i)
      for (Int_t j = 0; j < nY - 1; ++j) {
         DrawFlatFace(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1], fFaceNormals[i][j].first);
         DrawFlatFace(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], fFaceNormals[i][j].second);
      }

   //restore material properties from stack
   glPopAttrib();
   
   DrawZeroPlane();

   glFlush();
   DrawAxes(fp);
}

//______________________________________________________________________________
 void TGLHistPainter::PaintSurface4()const
{
   //Draws surface with "averaged" normals
   //If you have nearly smooth surface, it will be smooth :)
   
   SetCamera();
   //main light
   const Float_t pos[] = {0.f, 0.f, 0.f, 1.f};
   glLightfv(GL_LIGHT0, GL_POSITION, pos);

   SetTransformation();

   Int_t fp = FrontPoint();
   DrawFrame(fp);

   Float_t color[] = {0.8f, 0.8f, 0.8f, 1.f};

   if (fHist->GetFillColor() != kWhite)
      if (TColor *c = gROOT->GetColor(fHist->GetFillColor()))
         c->GetRGB(color[0], color[1], color[2]);

   //save material properties in stack
   glPushAttrib(GL_LIGHTING_BIT);
   
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
   const Float_t spec[] = {1.f, 1.f, 1.f, 1.f};
   glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
   glMaterialf(GL_FRONT, GL_SHININESS, 80.);
   //cycle through table
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   for (Int_t i = 0; i < nX - 1; ++i)
      for (Int_t j = 0; j < nY - 1; ++j) {
         DrawFace(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                  fAverageNormals[i + 1][j], fAverageNormals[i][j], fAverageNormals[i][j + 1]);
         DrawFace(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                  fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], fAverageNormals[i + 1][j]);
      }

   //restore material properties from stack
   glPopAttrib();
   
   DrawZeroPlane();

   glFlush();
   DrawAxes(fp);
}

//______________________________________________________________________________
 void TGLHistPainter::PaintSurface1()const
{
   //Paints surf1/surf2 options
   SetCamera();
   //main light
   const Float_t pos[] = {0.f, 0.f, 0.f, 1.f};
   glLightfv(GL_LIGHT0, GL_POSITION, pos);

   SetTransformation();

   Int_t fp = FrontPoint();
   DrawFrame(fp);

   glPushAttrib(GL_LIGHTING_BIT);
   const Float_t spec[] = {1.f, 1.f, 1.f, 1.f};
   glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
   glMaterialf(GL_FRONT, GL_SHININESS, 80.);
   Float_t diff[] = {0.9f, 0.9f, 0.9f, 1.f};
   glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);//without it I can get random mixture of texture and color

   glEnable(GL_TEXTURE_1D);
   glEnable(GL_DEPTH_TEST);
   glShadeModel(GL_SMOOTH);
   
   if (!glIsTexture(fTextureName)) {
      glGenTextures(1, &fTextureName);
   }

   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   glBindTexture(GL_TEXTURE_1D, fTextureName);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, kTexLength, 0,
                GL_RGBA, GL_UNSIGNED_BYTE, fTexture);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   //cycle through table
   Int_t nX = fLastBinX - fFirstBinX + 1;
   Int_t nY = fLastBinY - fFirstBinY + 1;

   //surface "surf2" is partially transparent
   if (fLastOption == kSurface2) {
      
      glDisable(GL_DEPTH_TEST);
      glDepthMask(GL_FALSE);
      glEnable(GL_BLEND);
      glDepthMask(GL_FALSE);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      //blend : to get "correct transparency", I have to draw 
      //faces starting from the farthest. 
      switch (fp) {
      case 2:
         for (Int_t i = 0; i < nX - 1; ++i)
            for (Int_t j = 0; j < nY - 1; ++j) {
               DrawFaceTextured(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                              fAverageNormals[i + 1][j], fAverageNormals[i][j], 
                              fAverageNormals[i][j + 1], fMinZScaled, fMaxZScaled);
               DrawFaceTextured(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                              fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], 
                              fAverageNormals[i + 1][j], fMinZScaled, fMaxZScaled);
            }
         break;
      case 1:
         for (Int_t i = 0; i < nX - 1; ++i)
            for (Int_t j = nY - 2; j >= 0; --j) {
               DrawFaceTextured(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                              fAverageNormals[i + 1][j], fAverageNormals[i][j], 
                              fAverageNormals[i][j + 1], fMinZScaled, fMaxZScaled);
               DrawFaceTextured(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                              fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], 
                              fAverageNormals[i + 1][j], fMinZScaled, fMaxZScaled);
            }
         break;
      case 0:
         for (Int_t i = nX - 2; i >= 0; --i)
            for (Int_t j = nY - 2; j >= 0; --j) {
               DrawFaceTextured(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                              fAverageNormals[i + 1][j], fAverageNormals[i][j], 
                              fAverageNormals[i][j + 1], fMinZScaled, fMaxZScaled);
               DrawFaceTextured(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                              fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], 
                              fAverageNormals[i + 1][j], fMinZScaled, fMaxZScaled);
            }
         break;
      case 3:
         for (Int_t i = nX - 2; i >= 0; --i)
            for (Int_t j = 0; j < nY - 1; ++j) {
               DrawFaceTextured(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                              fAverageNormals[i + 1][j], fAverageNormals[i][j], 
                              fAverageNormals[i][j + 1], fMinZScaled, fMaxZScaled);
               DrawFaceTextured(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                              fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], 
                              fAverageNormals[i + 1][j], fMinZScaled, fMaxZScaled);
            }
         break;
      }
      
      glEnable(GL_DEPTH_TEST);
      glDepthMask(GL_TRUE);
      glDisable(GL_TEXTURE_1D);
   } else {
      //surf1 - textured, non-transparent, with outlines
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1.f, 1.f);

      for (Int_t i = 0; i < nX - 1; ++i)
         for (Int_t j = 0; j < nY - 1; ++j) {
            DrawFaceTextured(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1],
                             fAverageNormals[i + 1][j], fAverageNormals[i][j], 
                             fAverageNormals[i][j + 1], fMinZScaled, fMaxZScaled);
            DrawFaceTextured(fMesh[i][j + 1], fMesh[i + 1][j + 1], fMesh[i + 1][j], 
                             fAverageNormals[i][j + 1], fAverageNormals[i + 1][j + 1], 
                             fAverageNormals[i + 1][j], fMinZScaled, fMaxZScaled);
         }

      glDisable(GL_POLYGON_OFFSET_FILL);
      glDisable(GL_TEXTURE_1D);      
      //Outlines:
      glDisable(GL_LIGHTING);
      glColor3d(0., 0., 0.);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

      for (Int_t i = 0; i < nX - 1; ++i)
         for (Int_t j = 0; j < nY - 1; ++j)
            DrawQuadOutline(fMesh[i + 1][j], fMesh[i][j], fMesh[i][j + 1], fMesh[i + 1][j + 1]);

      glEnable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   }
   
   //restore material properties from stack
   glPopAttrib();

   DrawZeroPlane();

   glFlush();
   DrawAxes(fp);
   
   glDeleteTextures(1, &fTextureName);
}

//______________________________________________________________________________
 void TGLHistPainter::SetGLParameters()
{
   //Sets viewport, bounds for arcball
   //Calculates arguments for glOrtho
   //Claculates center of scene and shift

   gGLManager->ExtractViewport(fGLDevice, fViewport);
   fRotation.SetBounds(fViewport[2], fViewport[3]);
   glViewport(fViewport[0], fViewport[1], fViewport[2], fViewport[3]);

   Double_t xRange = fMaxX - fMinX;
   Double_t yRange = fMaxY - fMinY;
   Double_t zRange = fMinZ > 0. ? fMaxZ : fMaxZ - fMinZ;

   Double_t maxDim = TMath::Max(TMath::Max(xRange, yRange), zRange);
   fCenter[0] = fMinX + xRange / 2;
   fCenter[1] = fMinY + yRange / 2;
   fCenter[2] = fMinZ > 0. ? zRange / 2 : fMinZ + zRange / 2;

   Double_t frx = 1., fry = 1.;

   if (fViewport[2] > fViewport[3])
      frx = fViewport[2] / double(fViewport[3]);
   else if (fViewport[2] < fViewport[3])
      fry = fViewport[3] / double(fViewport[2]);

   fFrustum[0] = maxDim;// / 1.15 * frx;
   fFrustum[1] = maxDim;// / 1.15 * fry;
   fFrustum[2] = maxDim * 0.707;
   fFrustum[3] = 3 * maxDim;
   fShift = maxDim * 1.5;//1.7;
}

//______________________________________________________________________________
 void TGLHistPainter::DrawBoxFront(Double_t xmin, Double_t xmax, Double_t ymin, 
                             Double_t ymax, Double_t zmin, Double_t zmax, Int_t fp)
{
   //Draws lego's bar as 3d box
   
   if (zmax < zmin) 
      std::swap(zmax, zmin);

   //top and bottom are always drawn (though I can skip one them)
   glBegin(GL_POLYGON);
   glNormal3d(0., 0., 1.);
   glVertex3d(xmax, ymin, zmax);
   glVertex3d(xmax, ymax, zmax);
   glVertex3d(xmin, ymax, zmax);
   glVertex3d(xmin, ymin, zmax);
   glEnd();

   glBegin(GL_POLYGON);
   glNormal3d(0., 0., -1.);
   glVertex3d(xmax, ymin, zmin);
   glVertex3d(xmin, ymin, zmin);
   glVertex3d(xmin, ymax, zmin);
   glVertex3d(xmax, ymax, zmin);
   glEnd();

   //two back faces cannot be seen, I know, which point is front, so I can
   //escape passing to polygons to gl.
   //For example : plane 0, I draw it only if front point is 3 or 0
   /*
               | z
               |
               |       |
               |   0   |
               |      3|
            |1 0----|--3 ------>y
            | / 2   | /
            |/      |/
            1-------2
           / 
          /
           x
   */

   switch (fp) {
   case 0://0 is the front point, draw planes 0 and 1
      glBegin(GL_POLYGON);
      glNormal3d(-1., 0., 0.);//plane 0
      glVertex3d(xmin, ymin, zmax);
      glVertex3d(xmin, ymax, zmax);
      glVertex3d(xmin, ymax, zmin);
      glVertex3d(xmin, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., -1., 0.);//plane 1
      glVertex3d(xmax, ymin, zmax);
      glVertex3d(xmin, ymin, zmax);
      glVertex3d(xmin, ymin, zmin);
      glVertex3d(xmax, ymin, zmin);
      glEnd();
      break;
   case 1://1 is the front point, draw planes 1 and 2
      glBegin(GL_POLYGON);
      glNormal3d(0., -1., 0.);//plane 1
      glVertex3d(xmax, ymin, zmax);
      glVertex3d(xmin, ymin, zmax);
      glVertex3d(xmin, ymin, zmin);
      glVertex3d(xmax, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(1., 0., 0.);//plane 2
      glVertex3d(xmax, ymin, zmax);
      glVertex3d(xmax, ymin, zmin);
      glVertex3d(xmax, ymax, zmin);
      glVertex3d(xmax, ymax, zmax);
      glEnd();
      break;
   case 2://2 is the front point, draw planes 2 and 3
      glBegin(GL_POLYGON);
      glNormal3d(1., 0., 0.);//plane 2
      glVertex3d(xmax, ymin, zmax);
      glVertex3d(xmax, ymin, zmin);
      glVertex3d(xmax, ymax, zmin);
      glVertex3d(xmax, ymax, zmax);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., 1., 0.);//plane 3
      glVertex3d(xmax, ymax, zmax);
      glVertex3d(xmax, ymax, zmin);
      glVertex3d(xmin, ymax, zmin);
      glVertex3d(xmin, ymax, zmax);
      glEnd();
      break;
   case 3://3 is the front point, draw planes 3 and 0
      glBegin(GL_POLYGON);
      glNormal3d(-1., 0., 0.);//plane 0
      glVertex3d(xmin, ymin, zmax);
      glVertex3d(xmin, ymax, zmax);
      glVertex3d(xmin, ymax, zmin);
      glVertex3d(xmin, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., 1., 0.);//plane 3
      glVertex3d(xmax, ymax, zmax);
      glVertex3d(xmax, ymax, zmin);
      glVertex3d(xmin, ymax, zmin);
      glVertex3d(xmin, ymax, zmax);
      glEnd();
      break;
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawBoxFrontTextured(Double_t xmin, Double_t xmax, Double_t ymin, 
                                          Double_t ymax, Double_t zmin, Double_t zmax,
                                          Int_t fp)const
{
   //Draws lego's bar as 3d box
   
   if (zmax < zmin) 
      std::swap(zmax, zmin);

   Double_t zRange = fMaxZScaled - fMinZScaled;

   //top and bottom are always drawn (though I can skip one them)
   glBegin(GL_POLYGON);
   glNormal3d(0., 0., 1.);
   glTexCoord1d((zmax - fMinZScaled) / zRange);   
   glVertex3d(xmax, ymin, zmax);
   glTexCoord1d((zmax - fMinZScaled) / zRange);   
   glVertex3d(xmax, ymax, zmax);
   glTexCoord1d((zmax - fMinZScaled) / zRange);   
   glVertex3d(xmin, ymax, zmax);
   glTexCoord1d((zmax - fMinZScaled) / zRange);
   glVertex3d(xmin, ymin, zmax);
   glEnd();

   glBegin(GL_POLYGON);
   glNormal3d(0., 0., -1.);
   glTexCoord1d((zmin - fMinZScaled) / zRange);
   glVertex3d(xmax, ymin, zmin);
   glTexCoord1d((zmin - fMinZScaled) / zRange);
   glVertex3d(xmin, ymin, zmin);
   glTexCoord1d((zmin - fMinZScaled) / zRange);
   glVertex3d(xmin, ymax, zmin);
   glTexCoord1d((zmin - fMinZScaled) / zRange);
   glVertex3d(xmax, ymax, zmin);
   glEnd();

   //two back faces cannot be seen, I know, which point is front, so I can
   //escape passing to polygons to gl.
   //For example : plane 0, I draw it only if front point is 3 or 0
   /*
               | z
               |
               |       |
               |   0   |
               |      3|
            |1 0----|--3 ------>y
            | / 2   | /
            |/      |/
            1-------2
           / 
          /
           x
   */

   switch (fp) {
   case 0://0 is the front point, draw planes 0 and 1
      glBegin(GL_POLYGON);
      glNormal3d(-1., 0., 0.);//plane 0
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmax);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., -1., 0.);//plane 1
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmax);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmin);
      glEnd();
      break;
   case 1://1 is the front point, draw planes 1 and 2
      glBegin(GL_POLYGON);
      glNormal3d(0., -1., 0.);//plane 1
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmax);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(1., 0., 0.);//plane 2
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmin);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmax);
      glEnd();
      break;
   case 2://2 is the front point, draw planes 2 and 3
      glBegin(GL_POLYGON);
      glNormal3d(1., 0., 0.);//plane 2
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymin, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmin);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmax);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., 1., 0.);//plane 3
      glTexCoord1d((zmax - fMinZScaled) / zRange); 
      glVertex3d(xmax, ymax, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmin);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmax);
      glEnd();
      break;
   case 3://3 is the front point, draw planes 3 and 0
      glBegin(GL_POLYGON);
      glNormal3d(-1., 0., 0.);//plane 0
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmax);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymin, zmin);
      glEnd();
      glBegin(GL_POLYGON);
      glNormal3d(0., 1., 0.);//plane 3
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmax);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmax, ymax, zmin);
      glTexCoord1d((zmin - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmin);
      glTexCoord1d((zmax - fMinZScaled) / zRange);
      glVertex3d(xmin, ymax, zmax);
      glEnd();
      break;
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawFlatFace(const TGLVertex3 &v1, const TGLVertex3 &v2, const TGLVertex3 &v3,
                                  const TGLVector3 &norm)
{
   //Draws triangle flat face, one normal per face
   
   glBegin(GL_POLYGON);
   glNormal3dv(norm.CArr());
   glVertex3dv(v1.CArr());
   glVertex3dv(v2.CArr());
   glVertex3dv(v3.CArr());
   glEnd();
}

//______________________________________________________________________________
 void TGLHistPainter::DrawFace(const TGLVertex3 &v1, const TGLVertex3 &v2, const TGLVertex3 &v3,
                              const TGLVector3 &norm1, const TGLVector3 &norm2, const TGLVector3 &norm3)
{
   //Draws triangle face, each vertex has its own averaged normal
   
   glBegin(GL_POLYGON);
   glNormal3dv(norm1.CArr());
   glVertex3dv(v1.CArr());
   glNormal3dv(norm2.CArr());
   glVertex3dv(v2.CArr());
   glNormal3dv(norm3.CArr());
   glVertex3dv(v3.CArr());
   glEnd();
}

//______________________________________________________________________________
 void TGLHistPainter::DrawFaceTextured(const TGLVertex3 &v1, const TGLVertex3 &v2, 
                                      const TGLVertex3 &v3, const TGLVector3 &norm1,
                                      const TGLVector3 &norm2, const TGLVector3 &norm3,
                                      Double_t zMin, Double_t zMax)
{
   Double_t zRange = zMax - zMin;

   glBegin(GL_POLYGON);
   glNormal3dv(norm1.CArr());
   glTexCoord1d((v1.Z() - zMin) / zRange);   
   glVertex3dv(v1.CArr());
   glNormal3dv(norm2.CArr());
   glTexCoord1d((v2.Z() - zMin) / zRange);
   glVertex3dv(v2.CArr());
   glNormal3dv(norm3.CArr());
   glTexCoord1d((v3.Z() - zMin) / zRange);
   glVertex3dv(v3.CArr());
   glEnd();   
}

//______________________________________________________________________________
 void TGLHistPainter::DrawQuadOutline(const TGLVertex3 &v1, const TGLVertex3 &v2, 
                                     const TGLVertex3 &v3, const TGLVertex3 &v4)
{
   glBegin(GL_POLYGON);
   glVertex3dv(v1.CArr());
   glVertex3dv(v2.CArr());
   glVertex3dv(v3.CArr());
   glVertex3dv(v4.CArr());
   glEnd();
}

//______________________________________________________________________________
 void TGLHistPainter::DrawFrame(Int_t frontPoint)const
{
   //Draws frame box around histogramm, surface or surface4,
   //draws grids and "profiles" for lego

   //save material properties in stack
   glPushAttrib(GL_LIGHTING_BIT);
   
   //back planes and bottom are always drawn first, in fact, I do not need depth test.
   //Planes are 85% opaque to make their color "softer"
   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   Float_t backColor[] = {0.9f, 0.9f, 0.9f, 0.85f};

   if (gPad->GetFrameFillColor() != kWhite)
      if (TColor *c = gROOT->GetColor(gPad->GetFrameFillColor()))
         c->GetRGB(backColor[0], backColor[1], backColor[2]);

   glMaterialfv(GL_FRONT, GL_DIFFUSE, backColor);

   //First, bottom plane at the minimum;
   //draw it with offset to remove "artefacts" when have "near-zero" overlapping polygons 
   //(such polygons can be in lego, surface or it can be zero-plane itself)
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(2.f, 2.f);//offset is 2., because lego uses offset 1

   Double_t zMin = fMinZ > 0. ? 0. : fMinZScaled;

   glBegin(GL_POLYGON);
   glNormal3d(0., 0., 1.);
   glVertex3d(fMinXScaled, fMinYScaled, zMin);
   glVertex3d(fMaxXScaled, fMinYScaled, zMin);
   glVertex3d(fMaxXScaled, fMaxYScaled, zMin);
   glVertex3d(fMinXScaled, fMaxYScaled, zMin);
   glEnd();

   glDisable(GL_POLYGON_OFFSET_FILL);
   
/*
      Each front point has two opposite back planes 
      
              |       |
              |   0   |
              |      3|
           |1 0----|--3
           | / 2   | /
           |/      |/
           1-------2
      For example in backPlanes 2d array first subarray holds numbers of opposite planes:
      
      Point 0 has 3 and 2, 1 - 0 and 3 etc.
*/
   Int_t backPlanes[][2] = {{3, 2}, {0, 3}, {1, 0}, {2, 1}};
   //DrawBackPlane should draw frame's part and corresponding profile for lego
   DrawBackPlane(backPlanes[frontPoint][0]);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, backColor);
   DrawBackPlane(backPlanes[frontPoint][1]);

   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);
   
   //restore material properties
   glPopAttrib();
}

//______________________________________________________________________________
 void TGLHistPainter::SetCamera()const
{
   //Clears gl buffer, sets projection
/*   
   Color_t ci = gPad->GetFillColor();
   TColor *color = gROOT->GetColor(ci);
   Float_t sc[3] = {1.f, 1.f, 1.f};
   
   if (color)
      color->GetRGB(sc[0], sc[1], sc[2]);
   
   glClearColor(sc[0], sc[1], sc[2], 1.);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
*/
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-fFrustum[0], fFrustum[0], -fFrustum[1], fFrustum[1], fFrustum[2], fFrustum[3]);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

//______________________________________________________________________________
 void TGLHistPainter::SetTransformation()const
{
   //Applies rotations and translations before drawing

   glTranslated(0., 0., -fShift);
   glMultMatrixd(fRotation.GetRotMatrix());
   glRotated(45., 1., 0., 0.);
   glRotated(-45., 0., 1., 0.);
   glRotated(-90., 0., 1., 0.);
   glRotated(-90., 1., 0., 0.);
   glTranslated(-fCenter[0] * fScaleX, -fCenter[1] * fScaleY, -fCenter[2] * fScaleZ);
}

namespace {
   bool Compare(const TGLVertex3 &v1, const TGLVertex3 &v2)
   {
      return v1.Z() < v2.Z();
   }
}

/*
In minZ plane I have 4 points 0 {minX, minY} 1 {maxX, minY} 2 {maxX, maxY} 3 {minX, maxY}
                 0-----3
                /     /
               1-----2

*/

//______________________________________________________________________________
 Int_t TGLHistPainter::FrontPoint()const
{
   //Converts 3d points into window coordinate system
   //and find the nearest
   
   Double_t mvMatrix[16] = {0.};
   glGetDoublev(GL_MODELVIEW_MATRIX, mvMatrix);
   Double_t prMatrix[16] = {0.};
   glGetDoublev(GL_PROJECTION_MATRIX, prMatrix);
   
   Double_t zMin = fMinZScaled > 0. ? 0. : fMinZScaled;
   Double_t xy[][2] = {{fMinXScaled, fMinYScaled}, {fMaxXScaled, fMinYScaled},
                       {fMaxXScaled, fMaxYScaled}, {fMinXScaled, fMaxYScaled}};

   for (Int_t i = 0; i < 4; ++i) {
      gluProject(xy[i][0], xy[i][1], zMin, mvMatrix, prMatrix, fViewport,
                 &f2DAxes[i].X(), &f2DAxes[i].Y(), &f2DAxes[i].Z());
      gluProject(xy[i][0], xy[i][1], fMaxZScaled, mvMatrix, prMatrix, fViewport,
                 &f2DAxes[i + 4].X(), &f2DAxes[i + 4].Y(), &f2DAxes[i + 4].Z());
   }

   return std::min_element(f2DAxes, f2DAxes + 4, ::Compare) - f2DAxes;
}

//______________________________________________________________________________
 void TGLHistPainter::DrawBackPlane(Int_t plane)const
{
   //Draw back plane with number 'plane'

   TGLVertex3 v0, v1, v2, v3;
   TGLVector3 normal;
   
   Double_t zMin = fMinZScaled > 0. ? 0. : fMinZScaled;

   switch (plane) {
   case 0 :
      v0.Set(fMinXScaled, fMinYScaled, zMin);
      v1.Set(fMinXScaled, fMaxYScaled, zMin);
      v2.Set(fMinXScaled, fMaxYScaled, fMaxZScaled);
      v3.Set(fMinXScaled, fMinYScaled, fMaxZScaled);
      normal.Set(1., 0., 0.);
      break;
   case 1 :
      v0.Set(fMinXScaled, fMinYScaled, zMin);
      v1.Set(fMinXScaled, fMinYScaled, fMaxZScaled);
      v2.Set(fMaxXScaled, fMinYScaled, fMaxZScaled);
      v3.Set(fMaxXScaled, fMinYScaled, zMin);
      normal.Set(0., 1., 0.);
      break;
   case 2 :
      v0.Set(fMaxXScaled, fMinYScaled, zMin);
      v1.Set(fMaxXScaled, fMinYScaled, fMaxZScaled);
      v2.Set(fMaxXScaled, fMaxYScaled, fMaxZScaled);
      v3.Set(fMaxXScaled, fMaxYScaled, zMin);
      normal.Set(-1., 0., 0.);
      break;
   case 3 :
      v0.Set(fMaxXScaled, fMaxYScaled, zMin);
      v1.Set(fMaxXScaled, fMaxYScaled, fMaxZScaled);
      v2.Set(fMinXScaled, fMaxYScaled, fMaxZScaled);
      v3.Set(fMinXScaled, fMaxYScaled, zMin);
      normal.Set(0., -1., 0.);
      break;
   }

   glBegin(GL_POLYGON);
   glNormal3dv(normal.CArr());
   glVertex3dv(v0.CArr());
   glVertex3dv(v1.CArr());
   glVertex3dv(v2.CArr());
   glVertex3dv(v3.CArr());
   glEnd();

   //try to antialias back plane outline
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);


   glDisable(GL_DEPTH_TEST);
   glDisable(GL_LIGHTING);
   glColor3d(0., 0., 0.);
   glBegin(GL_LINE_LOOP);
   glVertex3dv(v0.CArr());
   glVertex3dv(v1.CArr());
   glVertex3dv(v2.CArr());
   glVertex3dv(v3.CArr());
   glEnd();
   glEnable(GL_LIGHTING);
   glEnable(GL_DEPTH_TEST);

   glDisable(GL_BLEND);
   glDisable(GL_LINE_SMOOTH);
   
//   if (fLastOption == kLego)
   DrawProfile(plane);
      
   DrawGrid(plane);
}

namespace {

   /*
   minZ plane, 4 points 0 {minX, minY} 1 {maxX, minY} 2 {maxX, maxY} 3 {minX, maxY}
                    0-----3
                   /     /
                  1-----2
   Each point has left and right neighbour:
        0     1     2     3
       / \   / \   / \   / \
      3   1 0   2 1   3 2   0
   */
   
   void Draw2DAxis(TAxis *axis, Double_t xMin, Double_t yMin, Double_t xMax, Double_t yMax,
                   Double_t min, Double_t max, Bool_t log, Bool_t z = kFALSE)
   {
      //Axes are drawn with help of TGaxis class

      std::string option;
      option.reserve(20);
      
      if (xMin > xMax || z) option += "SDH=+";
      else option += "SDH=-";
      
      if (log) option += 'G';
      
      Int_t nDiv = axis->GetNdivisions();
      
      if (nDiv < 0) {
         option += 'N';
         nDiv = -nDiv;
      }
      
      TGaxis axisPainter;
      axisPainter.SetLineWidth(1);
      
      static const Double_t zero = 0.001;
      
      if (TMath::Abs(xMax - xMin) >= zero || TMath::Abs(yMax - yMin) >= zero) {
         axisPainter.ImportAxisAttributes(axis);
         axisPainter.SetLabelOffset(axis->GetLabelOffset() + axis->GetTickLength());

         if (log) {
            min = TMath::Power(10, min);
            max = TMath::Power(10, max);
         }
         //Option time display is required ?
         if (axis->GetTimeDisplay()) {
            option += 't';

            if (!strlen(axis->GetTimeFormatOnly()))
               axisPainter.SetTimeFormat(axis->ChooseTimeFormat(max - min));
            else
               axisPainter.SetTimeFormat(axis->GetTimeFormat());
         }

         axisPainter.SetOption(option.c_str());
         axisPainter.PaintAxis(xMin, yMin, xMax, yMax, min, max, nDiv, option.c_str());
      }
   }
   
   const Int_t gFramePoints[][2] = {{3, 1}, {0, 2}, {1, 3}, {2, 0}};
   
   //each point has two "neighbouring axes" (left and right). Axes types are 1 (ordinata) and 0 (abscissa)
   const Int_t gAxisType[][2] = {{1, 0}, {0, 1}, {1, 0}, {0, 1}};
}

//______________________________________________________________________________
 void TGLHistPainter::DrawAxes(Int_t frontPoint)const
{
   //Using front point, find, where to draw axes and which labels to use for them

   Int_t pixmap = gGLManager->GetVirtualXInd(fGLDevice);
   gVirtualX->SelectWindow(pixmap);
   gVirtualX->SetDrawMode(TVirtualX::kCopy);//TCanvas by default sets in kInverse

   Int_t left = gFramePoints[frontPoint][0];
   Int_t right = gFramePoints[frontPoint][1];
   
   //Now, only 2D coors to draw axes

   Double_t xLeft = gPad->AbsPixeltoX(Int_t(gPad->GetXlowNDC() * gPad->GetWw() + f2DAxes[left].X()));
   Double_t yLeft = gPad->AbsPixeltoY(Int_t(fViewport[3] - f2DAxes[left].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]));
   
   Double_t xMid = gPad->AbsPixeltoX(Int_t(gPad->GetXlowNDC() * gPad->GetWw() + f2DAxes[frontPoint].X()));
   Double_t yMid = gPad->AbsPixeltoY(Int_t(fViewport[3] - f2DAxes[frontPoint].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]));

   Double_t xRight = gPad->AbsPixeltoX(Int_t(gPad->GetXlowNDC() * gPad->GetWw() + f2DAxes[right].X()));
   Double_t yRight = gPad->AbsPixeltoY(Int_t(fViewport[3] - f2DAxes[right].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]));
   
   const Double_t points[][2] = {{fMinX, fMinY}, {fMaxX, fMinY}, {fMaxX, fMaxY}, {fMinX, fMaxY}};
   Int_t leftType = gAxisType[frontPoint][0];
   Int_t rightType = gAxisType[frontPoint][1];
   Double_t leftLabel = points[left][leftType];
   Double_t leftMidLabel = points[frontPoint][leftType];
   Double_t rightMidLabel = points[frontPoint][rightType];
   Double_t rightLabel = points[right][rightType];

   if (xLeft - xMid || yLeft - yMid) {//To supress error messages from TGaxis
      TAxis *axis = leftType ? fAxisY : fAxisX;
      if (leftLabel < leftMidLabel)
         Draw2DAxis(axis, xLeft, yLeft, xMid, yMid, leftLabel, leftMidLabel, leftType ? fLogY : fLogX);
      else
         Draw2DAxis(axis, xMid, yMid, xLeft, yLeft, leftMidLabel, leftLabel, leftType ? fLogY : fLogX);
   }

   if (xRight - xMid || yRight - yMid) {//To supress error messages from TGaxis
      TAxis *axis = rightType ? fAxisY : fAxisX;
      if (rightMidLabel < rightLabel)
         Draw2DAxis(axis, xMid, yMid, xRight, yRight, rightMidLabel, rightLabel, rightType ? fLogY : fLogX);
      else
         Draw2DAxis(axis, xRight, yRight, xMid, yMid, rightLabel, rightMidLabel, rightType ? fLogY : fLogX);
   }
    
   Double_t xUp = gPad->AbsPixeltoX(Int_t(gPad->GetXlowNDC() * gPad->GetWw() + f2DAxes[left + 4].X()));
   Double_t yUp = gPad->AbsPixeltoY(Int_t(fViewport[3] - f2DAxes[left + 4].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]));

   Draw2DAxis(fAxisZ, xLeft, yLeft, xUp, yUp, fMinZ, fMaxZ, fLogZ, kTRUE);

   f2DPass = kTRUE;
/*
   TObjOptLink *lnk = static_cast<TObjOptLink *>(gPad->GetListOfPrimitives()->FirstLink());
   
   while (lnk) {
      TObject *obj = lnk->GetObject();

      obj->Paint(lnk->GetOption());
      lnk = static_cast<TObjOptLink *>(lnk->Next());
   }
*/

   f2DPass = kFALSE;

   gGLManager->SelectGLPixmap(fGLDevice);
}

//______________________________________________________________________________
 Bool_t TGLHistPainter::Select(Int_t x, Int_t y)const
{
   //find hist "square" on screen
   SetCamera();
   SetTransformation();
   Int_t frontPoint = FrontPoint();
   
   Double_t xMin = f2DAxes[0].X();
   Double_t yMin = fViewport[3] - f2DAxes[0].Y();
   Double_t xMax = xMin;
   Double_t yMax = yMin;

   for (Int_t i = 1; i < 8; ++i) {
      xMin = TMath::Min(xMin, f2DAxes[i].X() + gPad->GetXlowNDC() * gPad->GetWw());
      xMax = TMath::Max(xMax, f2DAxes[i].X() + gPad->GetXlowNDC() * gPad->GetWw());
      yMin = TMath::Min(yMin, fViewport[3] - f2DAxes[i].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]);
      yMax = TMath::Max(yMax, fViewport[3] - f2DAxes[i].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1]);
   }

   if (x >= xMin && x <= xMax && y >= yMin && y <= yMax) {
      //select axes
      SelectAxes(frontPoint, x, y);
      return kTRUE;
   }
   
   return kFALSE;
}

namespace {
   Bool_t TestLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x, Double_t y)
   {
      Double_t xMin = TMath::Min(x1, x2);
      Double_t xMax = TMath::Max(x1, x2);
      Double_t yMin = TMath::Min(y1, y2);
      Double_t yMax = TMath::Max(y1, y2);

      if (x < xMin - 2 || x > xMax + 2 || y < yMin - 2 || y > yMax + 2)
         return kFALSE;

      Double_t a = y2 - y1;
      Double_t b = x1 - x2;
      Double_t c = -a * x1 -b * y1;
      Double_t mu = 1 / TMath::Sqrt(a * a + b * b);

      if (c < 0.) mu *= -1.;
      
      Double_t r = TMath::Abs(a * mu * x + b * mu * y + c * mu);

      return r < 3.;
   }
}

//______________________________________________________________________________
 void TGLHistPainter::SelectAxes(Int_t front, Int_t x, Int_t y)const
{
   //Checks, if axis can be selected
   
   Int_t left = gFramePoints[front][0];
   Double_t xLeft = f2DAxes[left].X() + gPad->GetXlowNDC() * gPad->GetWw();
   Double_t yLeft = fViewport[3] - f2DAxes[left].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1];
   Double_t xMid = f2DAxes[front].X() + gPad->GetXlowNDC() * gPad->GetWw();
   Double_t yMid = fViewport[3] - f2DAxes[front].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1];

   if (TMath::Abs(xMid - xLeft) > 0.001 || TMath::Abs(yMid - yLeft) > 0.001) {
      if (TestLine(xLeft, yLeft, xMid, yMid, x, y)) {
      //left was selected
         if (gAxisType[front][0]) gPad->SetSelected(fAxisY);
         else gPad->SetSelected(fAxisX);
         return;
      }
   }

   Int_t right = gFramePoints[front][1];
   Double_t xRight = f2DAxes[right].X() + gPad->GetXlowNDC() * gPad->GetWw();
   Double_t yRight = fViewport[3] - f2DAxes[right].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1];

   if (TMath::Abs(xMid - xRight) > 0.001 || TMath::Abs(yMid - yRight) > 0.001) {
      if (TestLine(xMid, yMid, xRight, yRight, x, y)) {
      //right was selected
         if (gAxisType[front][1]) gPad->SetSelected(fAxisY);
         else gPad->SetSelected(fAxisX);
         return;
      }
   }

   Double_t xUp = f2DAxes[left + 4].X() + gPad->GetXlowNDC() * gPad->GetWw();
   Double_t yUp = fViewport[3] - f2DAxes[left + 4].Y() + (1 - gPad->GetHNDC() - gPad->GetYlowNDC()) * gPad->GetWh() + fViewport[1];

   if (TMath::Abs(xLeft - xUp) > 0.001 || TMath::Abs(yLeft - yUp) > 0.001) {
      if (TestLine(xLeft, yLeft, xUp, yUp, x ,y))
      //left was selected
         gPad->SetSelected(fAxisZ);
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawZeroPlane()const
{
   //Blue, semi-transparent plane at zero-level
   
   glPushAttrib(GL_LIGHTING_BIT);

   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   const Float_t color[] = {0.f, 0.3f, 0.8f, 0.15f};
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color);

   //if abs(fMinZ) less than 1, I can get partially overlapping polys
   if (TMath::Abs(fMinZ) > 1.) {
      glBegin(GL_POLYGON);
      glNormal3d(0., 0., 1.);
      glVertex3d(fMinXScaled, fMinYScaled, 0.);
      glVertex3d(fMaxXScaled, fMinYScaled, 0.);
      glVertex3d(fMaxXScaled, fMaxYScaled, 0.);
      glVertex3d(fMinXScaled, fMaxYScaled, 0.);
      glEnd();
   }
      
   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);
   
   glPopAttrib();
}

//______________________________________________________________________________
 void TGLHistPainter::DrawProfile(Int_t plane)const
{
   //Draws profiles on back planes

   //profile's color is a mixture of back plane color and gray
   const Float_t color[] = {0.4, 0.4, 0.4, 0.25f};
   glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   
   //to avoid different visual artefacts,
   //profile is drawn without depth test and
   //without z-buffer
   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
   glDisable(GL_DEPTH_TEST);
   if (fLastOption == kLego || fLastOption == kLego2)
      glDisable(GL_CULL_FACE);//to avoid point order checks during bin drawing

   if (!plane || plane == 2)
      fLastOption == kLego || fLastOption == kLego2 ? DrawLegoProfileY(plane) : DrawSurfaceProfileY(plane);
   else
      fLastOption == kLego || fLastOption == kLego2 ? DrawLegoProfileX(plane) : DrawSurfaceProfileX(plane);

   if (fLastOption == kLego || fLastOption == kLego2)
      glEnable(GL_CULL_FACE);
      
   glEnable(GL_DEPTH_TEST);
   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);
}

//______________________________________________________________________________
 void TGLHistPainter::DrawLegoProfileX(Int_t plane)const
{
   //Draws X lego's profile on 'plane'
   //for each 'row' find min and max
   //and draw them as rectangle

   const Int_t nBins = fLastBinX - fFirstBinX + 1;
   const Double_t y = plane == 1 ? fMinYScaled : fMaxYScaled;
   const Double_t normal[] = {0., plane == 1 ? 1. : -1., 0.};

   for (Int_t i = 0, ir = fFirstBinX; i < nBins; ++i, ++ir) {
      Double_t xMin = fX[i];
      Double_t xMax = fX[i + 1];

      PD_t z = GetMaxRowContent(ir);
      //MAXROW      
      if (fLogZ) {
         if (z.second <= 0.) continue;
         z.second = TMath::Log10(z.second);
         if (z.first > 0.)
            z.first = TMath::Log10(z.first);
         else z.first = 0.;
      }
      
      z.first *= fScaleZ;
      z.second *= fScaleZ;

      if (z.first > 0. && z.second > 0.) z.first = 0.;
      
      glBegin(GL_POLYGON);
      glNormal3dv(normal);
      glVertex3d(xMin, y, z.first);
      glVertex3d(xMin, y, z.second);
      glVertex3d(xMax, y, z.second);
      glVertex3d(xMax, y, z.first);
      glEnd();
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawLegoProfileY(Int_t plane)const
{
   //Draws Y lego's profile on 'plane'
   //for each 'column' find min and max
   //and draw them as rectangle

   const Int_t nBins = fLastBinY - fFirstBinY + 1;
   const Double_t x = plane ? fMaxXScaled : fMinXScaled;
   const Double_t normal[] = {plane ? -1. : 1., 0., 0.};
   
   for (Int_t i = 0, ir = fFirstBinY; i < nBins; ++i, ++ir) {
      Double_t yMin = fY[i];
      Double_t yMax = fY[i + 1];

      PD_t z = GetMaxColumnContent(ir);
      //MAXCOL
      if (fLogZ) {
         if (z.second <= 0.) continue;
         z.second = TMath::Log10(z.second);
         if (z.first > 0.)
            z.first = TMath::Log10(z.first);
         else z.first = 0.;
      }
      
      z.first *= fScaleZ;
      z.second *= fScaleZ;

      if (z.second > 0. && z.first > 0) z.first = 0.;
   
      glBegin(GL_POLYGON);
      glNormal3dv(normal);
      glVertex3d(x, yMin, z.first);
      glVertex3d(x, yMax, z.first);
      glVertex3d(x, yMax, z.second);
      glVertex3d(x, yMin, z.second);
      glEnd();
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawSurfaceProfileX(Int_t plane)const
{
   //Draws X surface's profile on 'plane'
   //for each 'row' find min and max
   //and draw them as rectangle

   const Int_t nBins = fLastBinX - fFirstBinX + 1;
   const Double_t y = plane == 1 ? fMinYScaled : fMaxYScaled;
   const Double_t normal[] = {0., plane == 1 ? 1. : -1., 0.};

   for (Int_t i = 0; i < nBins - 1; ++i) {
      Double_t xMin = fMesh[i][0].X();
      Double_t xMax = fMesh[i + 1][0].X();

      Double_t zMax1 = fMesh[i][0].Z();
      Double_t zMin1 = zMax1;
      Double_t zMax2 = fMesh[i + 1][0].Z();
      Double_t zMin2 = zMax2;
      
      for (Int_t j = 1; j < nBins; ++j) {
         zMax1 = TMath::Max(fMesh[i][j].Z(), zMax1);
         zMin1 = TMath::Min(fMesh[i][j].Z(), zMin1);
         zMax2 = TMath::Max(fMesh[i + 1][j].Z(), zMax2);
         zMin2 = TMath::Min(fMesh[i + 1][j].Z(), zMin2);
      }
     
      glBegin(GL_POLYGON);
      glNormal3dv(normal);
      glVertex3d(xMin, y, zMin1);
      glVertex3d(xMin, y, zMax1);
      glVertex3d(xMax, y, zMax2);
      glVertex3d(xMax, y, zMin2);
      glEnd();
   }
}

//______________________________________________________________________________
 void TGLHistPainter::DrawSurfaceProfileY(Int_t plane)const
{
   //Draws Y surface's profile on 'plane'
   //for each 'row' find min and max
   //and draw them as rectangle

   const Int_t nBins = fLastBinY - fFirstBinY + 1;
   const Double_t x = plane ? fMaxXScaled : fMinXScaled;
   const Double_t normal[] = {plane ? -1. : 1., 0., 0.};

   for (Int_t i = 0; i < nBins - 1; ++i) {
      Double_t yMin = fMesh[0][i].Y();
      Double_t yMax = fMesh[0][i + 1].Y();

      Double_t zMax1 = fMesh[0][i].Z();
      Double_t zMin1 = zMax1;
      Double_t zMax2 = fMesh[0][i + 1].Z();
      Double_t zMin2 = zMax2;
      
      for (Int_t j = 1; j < nBins; ++j) {
         zMax1 = TMath::Max(fMesh[j][i].Z(), zMax1);
         zMin1 = TMath::Min(fMesh[j][i].Z(), zMin1);
         zMax2 = TMath::Max(fMesh[j][i + 1].Z(), zMax2);
         zMin2 = TMath::Min(fMesh[j][i + 1].Z(), zMin2);
      }
     
      glBegin(GL_POLYGON);
      glNormal3dv(normal);
      glVertex3d(x, yMin, zMin1);
      glVertex3d(x, yMin, zMax1);
      glVertex3d(x, yMax, zMax2);
      glVertex3d(x, yMax, zMin2);
      glEnd();
   }
}

//______________________________________________________________________________
TGLHistPainter::PD_t TGLHistPainter::GetMaxRowContent(Int_t row)const
{
   //Having one row in 2d hist, find minimum and maximum

   Double_t zMax = fHist->GetBinContent(row, fFirstBinY);
   Double_t zMin = zMax;      

   for (Int_t next = fFirstBinY + 1; next <= fLastBinY; ++next) {
      zMax = TMath::Max(zMax, fHist->GetBinContent(row, next));
      zMin = TMath::Min(zMin, fHist->GetBinContent(row, next));
   }
      
   return std::make_pair(zMin, zMax);
}

//______________________________________________________________________________
TGLHistPainter::PD_t TGLHistPainter::GetMaxColumnContent(Int_t col)const
{
   //Having one column in 2d hist, find minimum and maximum

   Double_t zMax = fHist->GetBinContent(fFirstBinX, col);
   Double_t zMin = zMax;
   
   for (Int_t next = fFirstBinX + 1; next <= fLastBinX; ++next) {
      zMax = TMath::Max(zMax, fHist->GetBinContent(next, col));
      zMin = TMath::Min(zMin, fHist->GetBinContent(next, col));
   }
      
   return std::make_pair(zMin, zMax);
}

//______________________________________________________________________________
 void TGLHistPainter::SetZLevels()
{
   //Levels for grid

   Double_t zMin = fMinZ > 0. ? 0. : fMinZ;
   Double_t zMax = fMaxZ;//maxZ passes as non-const reference into Optimize, thus can be changed
   Int_t nDiv = fAxisZ->GetNdivisions() % 100;
   Int_t nBins = 0;
   Double_t binLow = 0., binHigh = 0., binWidth = 0.;
   
   THLimitsFinder::Optimize(zMin, zMax, nDiv, binLow, binHigh, nBins, binWidth, " ");
   fZLevels.resize(nBins + 1);
   
   for (Int_t i = 0; i < nBins + 1; ++i)
      fZLevels[i] = binLow + i * binWidth;
}

//______________________________________________________________________________
 void TGLHistPainter::DrawGrid(Int_t plane)const
{
   //Grid at XOZ or YOZ back plane

   //First, save current line stipple state
   //to avoid problems somewhere
   glPushAttrib(GL_LINE_BIT);

   glEnable(GL_LINE_STIPPLE);
   const UShort_t stipple = 0x5555;
   glLineStipple(1, stipple);

   glDisable(GL_DEPTH_TEST);
   glDisable(GL_LIGHTING);
   glColor3d(0., 0., 0.);
   
   //try to anti-alias these lines
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
      
   Double_t lineCaps[][4] = {
                              {fMinXScaled, fMinXScaled, fMinYScaled, fMaxYScaled},
                              {fMinXScaled, fMaxXScaled, fMinYScaled, fMinYScaled}, 
                              {fMaxXScaled, fMaxXScaled, fMinYScaled, fMaxYScaled},
                              {fMinXScaled, fMaxXScaled, fMaxYScaled, fMaxYScaled}
                              };

   for (UInt_t i = 0; i < fZLevels.size(); ++i) {
      glBegin(GL_LINES);
      glVertex3d(lineCaps[plane][0], lineCaps[plane][2], fZLevels[i] * fScaleZ);
      glVertex3d(lineCaps[plane][1], lineCaps[plane][3], fZLevels[i] * fScaleZ);
      glEnd();
   }

   //disable AA
   glDisable(GL_BLEND);
   glDisable(GL_LINE_SMOOTH);   

   glEnable(GL_LIGHTING);
   glEnable(GL_DEPTH_TEST);
   
   glPopAttrib();
}

 void TGLHistPainter::ClearBuffer()const
{
   //Clears gl buffers

   Color_t ci = gPad->GetFillColor();
   TColor *color = gROOT->GetColor(ci);
   Float_t sc[3] = {1.f, 1.f, 1.f};
   
   if (color)
      color->GetRGB(sc[0], sc[1], sc[2]);
   
   glClearColor(sc[0], sc[1], sc[2], 1.);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

namespace {
   const GLubyte gDefTexture1[4 * TGLHistPainter::kTexLength] = 
           {
          //R    G    B    A
            128, 0,   255, 200,
            169, 4,   240, 200,
            199, 73,  255, 200,
            222, 149, 253, 200,
            255, 147, 201, 200,
            255, 47,  151, 200,
            232, 0,   116, 200,
            253, 0,   0,   200,
            255, 62,  62,  200,
            217, 111, 15,  200,
            242, 151, 28,  200,
            245, 172, 73,  200,
            251, 205, 68,  200,
            255, 255, 21,  200,
            255, 255, 128, 200,
            255, 255, 185, 200
           };

   const GLubyte gDefTexture2[4 * TGLHistPainter::kTexLength] = 
           {
          //R    G    B    A
            230, 0,   115, 255,
            255, 62,  158, 255,
            255, 113, 113, 255,
            255, 98,  21,  255,
            255, 143, 89,  255,
            249, 158, 23,  255,
            252, 197, 114, 255,
            252, 228, 148, 255,
            66,  189, 121, 255,
            121, 208, 160, 255,
            89,  245, 37,  255,
            183, 251, 159, 255,
            0,   113, 225, 255,
            64,  159, 255, 255,
            145, 200, 255, 255,
            202, 228, 255, 255
           };
}

//______________________________________________________________________________
 void TGLHistPainter::InitTexture()
{
   for (Int_t i = 0, e = kTexLength * 4; i < e; ++i)
      fTexture[i] = gDefTexture1[i];
}


ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.