// @(#)root/eve:$Id: TEveCaloLegoGL.cxx 37345 2010-12-06 16:53:39Z matevz $
// Author: Alja Mrak-Tadel 2007

/*************************************************************************
 * Copyright (C) 1995-2007, 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 "TAxis.h"
#include "TH2.h"
#include "THLimitsFinder.h"

#include "TGLViewer.h"
#include "TGLIncludes.h"
#include "TGLPhysicalShape.h"
#include "TGLRnrCtx.h"
#include "TGLSelectRecord.h"
#include "TGLScene.h"
#include "TGLCamera.h"
#include "TGLUtil.h"
#include "TColor.h"
#include "TROOT.h"


#include "TEveCaloLegoGL.h"
#include "TEveCalo.h"
#include "TEveManager.h"
#include "TEveRGBAPalette.h"

#include <algorithm>

//______________________________________________________________________________
// OpenGL renderer class for TEveCaloLego.
//

ClassImp(TEveCaloLegoGL);

//______________________________________________________________________________
TEveCaloLegoGL::TEveCaloLegoGL() :
   TGLObject(),

   fGridColor(-1),
   fFontColor(-1),

   fEtaAxis(0),
   fPhiAxis(0),
   fZAxis(0),
   fM(0),
   fDLCacheOK(kFALSE),
   fMaxVal(0),
   fValToPixel(0),
   fCurrentPixelsPerBin(0),
   fCells3D(kTRUE),
   fBinStep(-1)
{
   // Constructor.

   fDLCache = kFALSE;

   fEtaAxis = new TAxis();
   fPhiAxis = new TAxis();
   fZAxis   = new TAxis();

   fAxisPainter.SetFontMode(TGLFont::kPixmap);
}

//______________________________________________________________________________
TEveCaloLegoGL::~TEveCaloLegoGL()
{
   // Destructor.

   DLCachePurge();

   delete fEtaAxis;
   delete fPhiAxis;
   delete fZAxis;
}

//______________________________________________________________________________
Bool_t TEveCaloLegoGL::SetModel(TObject* obj, const Option_t* /*opt*/)
{
   // Set model object.

   fM = SetModelDynCast<TEveCaloLego>(obj);
   return kTRUE;
}

//______________________________________________________________________________
void TEveCaloLegoGL::SetBBox()
{
   // Set bounding box.

   SetAxisAlignedBBox(((TEveCaloLego*)fExternalObj)->AssertBBox());
}

//______________________________________________________________________________
void TEveCaloLegoGL::DLCacheDrop()
{
   // Drop all display-list definitions.

   fDLCacheOK = kFALSE;
   for (SliceDLMap_i i = fDLMap.begin(); i != fDLMap.end(); ++i)
      i->second = 0;

   TGLObject::DLCacheDrop();
}

//______________________________________________________________________________
void TEveCaloLegoGL::DLCachePurge()
{
   // Unregister all display-lists.

   // all lego cells
   fDLCacheOK = kFALSE;
   if (! fDLMap.empty()) {
      for (SliceDLMap_i i = fDLMap.begin(); i != fDLMap.end(); ++i) {
         if (i->second) {
            PurgeDLRange(i->second, 1);
            i->second = 0;
         }
      }
   }
   TGLObject::DLCachePurge();
}

//______________________________________________________________________________
void TEveCaloLegoGL::MakeQuad(Float_t x1, Float_t y1, Float_t z1,
      Float_t xw, Float_t yw, Float_t h) const
{
   // Draw an axis-aligned box using quads.

   //    z
   //    |
   //    |
   //    |________y
   //   /  6-------7
   //  /  /|      /|
   // x  5-------4 |
   //    | 2-----|-3
   //    |/      |/
   //    1-------0
   //

   Float_t x2 = x1 + xw;
   Float_t y2 = y1 + yw;
   Float_t z2 = z1 + h;

   if (x1 < fM->GetEtaMin()) x1 = fM->GetEtaMin();
   if (x2 > fM->GetEtaMax()) x2 = fM->GetEtaMax();

   if (y1 < fM->GetPhiMin()) y1 = fM->GetPhiMin();
   if (y2 > fM->GetPhiMax()) y2 = fM->GetPhiMax();

   glBegin(GL_QUADS);
   {
      // bottom 0123
      glNormal3f(0, 0, -1);
      glVertex3f(x2, y2, z1);
      glVertex3f(x2, y1, z1);
      glVertex3f(x1, y1, z1);
      glVertex3f(x1, y2, z1);
      // top 4765
      glNormal3f(0, 0, 1);
      glVertex3f(x2, y2, z2);
      glVertex3f(x1, y2, z2);
      glVertex3f(x1, y1, z2);
      glVertex3f(x2, y1, z2);

      // back 0451
      glNormal3f(1, 0, 0);
      glVertex3f(x2, y2, z1);
      glVertex3f(x2, y2, z2);
      glVertex3f(x2, y1, z2);
      glVertex3f(x2, y1, z1);
      // front 3267
      glNormal3f(-1, 0, 0);
      glVertex3f(x1, y2, z1);
      glVertex3f(x1, y1, z1);
      glVertex3f(x1, y1, z2);
      glVertex3f(x1, y2, z2);

      // left  0374
      glNormal3f(0, 1, 0);
      glVertex3f(x2, y2, z1);
      glVertex3f(x1, y2, z1);
      glVertex3f(x1, y2, z2);
      glVertex3f(x2, y2, z2);
      // right 1562
      glNormal3f(0, -1, 0);
      glVertex3f(x2, y1, z1);
      glVertex3f(x2, y1, z2);
      glVertex3f(x1, y1, z2);
      glVertex3f(x1, y1, z1);
   }
   glEnd();
}

//______________________________________________________________________________
void TEveCaloLegoGL::Make3DDisplayList(TEveCaloData::vCellId_t& cellList, SliceDLMap_t& dlMap, Bool_t selection) const
{
   // Create display-list that draws histogram bars for non-rebinned data.
   // It is used for filled and outline passes.

   TEveCaloData::CellData_t cellData;
   Int_t   prevTower = 0;
   Float_t offset = 0;

   // ids in eta phi rng
   Int_t nSlices = fM->fData->GetNSlices();
   for (Int_t s = 0; s < nSlices; ++s)
   {
      if (dlMap.empty() || dlMap[s] == 0)
         dlMap[s] = glGenLists(1);

      glNewList(dlMap[s], GL_COMPILE);

      for (UInt_t i = 0; i < cellList.size(); ++i)
      {
         if (cellList[i].fSlice > s) continue;
         if (cellList[i].fTower != prevTower) {
            offset = 0;
            prevTower = cellList[i].fTower;
         }

         fM->fData->GetCellData(cellList[i], cellData);
         if (s == cellList[i].fSlice)
         {
            if (selection) glLoadName(i);

            WrapTwoPi(cellData.fPhiMin, cellData.fPhiMax);
            MakeQuad(cellData.EtaMin(), cellData.PhiMin(), offset,
                     cellData.EtaDelta(), cellData.PhiDelta(), cellData.Value(fM->fPlotEt));
         }
         offset += cellData.Value(fM->fPlotEt);
      }
      glEndList();
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::Make3DDisplayListRebin(TEveCaloData::RebinData_t& rebinData, SliceDLMap_t& dlMap, Bool_t selection) const
{
   // Create display-list that draws histogram bars for rebinned data.
   // It is used for filled and outline passes.

   Int_t nSlices = fM->fData->GetNSlices();
   Float_t *vals;
   Float_t offset;
   Float_t y0, y1;

   for (Int_t s = 0; s < nSlices; ++s)
   {
      if (dlMap.empty() || dlMap[s] == 0)
         dlMap[s] = glGenLists(1);

      glNewList(dlMap[s], GL_COMPILE);

      for (Int_t i = 1; i <= fEtaAxis->GetNbins(); ++i)
      {
         for (Int_t j = 1; j <= fPhiAxis->GetNbins(); ++j)
         {
            const Int_t bin = (i)+(j)*(fEtaAxis->GetNbins()+2);

            if (rebinData.fBinData[bin] !=-1)
            {
               vals = rebinData.GetSliceVals(bin);
               offset =0;
               for (Int_t t = 0; t < s; ++t)
                  offset += vals[t];

               y0 = fPhiAxis->GetBinLowEdge(j);
               y1 = fPhiAxis->GetBinUpEdge(j);
               WrapTwoPi(y0, y1);

               if (selection) glLoadName(bin);

               MakeQuad(fEtaAxis->GetBinLowEdge(i), y0, offset,
                        fEtaAxis->GetBinWidth(i), y1-y0, vals[s]);
            }
         }
      }
      glEndList();
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::SetAxis3DTitlePos(TGLRnrCtx &rnrCtx, Float_t x0, Float_t x1, Float_t y0, Float_t y1) const
{
   const GLdouble *pm = rnrCtx.RefCamera().RefLastNoPickProjM().CArr();
   GLdouble mm[16];
   GLint    vp[4];
   glGetDoublev(GL_MODELVIEW_MATRIX,  mm);
   glGetIntegerv(GL_VIEWPORT, vp);
   GLdouble projX[4], projY[4], projZ[4];

   GLdouble cornerX[4];
   GLdouble cornerY[4];
   cornerX[0] = x0; cornerY[0] = y0;
   cornerX[1] = x1; cornerY[1] = y0;
   cornerX[2] = x1; cornerY[2] = y1;
   cornerX[3] = x0; cornerY[3] = y1;

   gluProject(cornerX[0], cornerY[0], 0, mm, pm, vp, &projX[0], &projY[0], &projZ[0]);
   gluProject(cornerX[1], cornerY[1], 0, mm, pm, vp, &projX[1], &projY[1], &projZ[1]);
   gluProject(cornerX[2], cornerY[2], 0, mm, pm, vp, &projX[2], &projY[2], &projZ[2]);
   gluProject(cornerX[3], cornerY[3], 0, mm, pm, vp, &projX[3], &projY[3], &projZ[3]);


   // Z axis location (left most corner)
   //
   Int_t idxLeft = 0;
   Float_t xt = projX[0];
   for (Int_t i = 1; i < 4; ++i) {
      if (projX[i] < xt) {
         xt  = projX[i];
         idxLeft = i;
      }
   }
   fZAxisTitlePos.Set(cornerX[idxLeft], cornerY[idxLeft], 1.05 * fMaxVal);


   // XY axis location (closest to eye) first
   //
   Float_t zt = 1.f;
   Float_t zMin = 0.f;
   Int_t idxFront = 0;
   for (Int_t i = 0; i < 4; ++i) {
      if (projZ[i] < zt) {
         zt  = projZ[i];
         idxFront = i;
      }
      if (projZ[i] > zMin) zMin = projZ[i];
   }


   Int_t xyIdx = idxFront;
   if (zMin - zt < 1e-2) xyIdx = 0; // avoid flipping in front view


   switch (xyIdx) {
      case 0:
         fXAxisTitlePos.fX = x1;
         fXAxisTitlePos.fY = y0;
         fYAxisTitlePos.fX = x0;
         fYAxisTitlePos.fY = y1;
         break;
      case 1:
         fXAxisTitlePos.fX = x0;
         fXAxisTitlePos.fY = y0;
         fYAxisTitlePos.fX = x1;
         fYAxisTitlePos.fY = y1;
         break;
      case 2:
         fXAxisTitlePos.fX = x0;
         fXAxisTitlePos.fY = y1;
         fYAxisTitlePos.fX = x1;
         fYAxisTitlePos.fY = y0;
         break;
      case 3:
         fXAxisTitlePos.fX = x1;
         fXAxisTitlePos.fY = y1;
         fYAxisTitlePos.fX = x0;
         fYAxisTitlePos.fY = y0;
         break;
   }

   // move title 5% over the axis length
   Float_t off = 0.05;
   Float_t tOffX = (x1-x0) * off; if (fYAxisTitlePos.fX > x0) tOffX = -tOffX;
   Float_t tOffY = (y1-y0) * off; if (fXAxisTitlePos.fY > y0) tOffY = -tOffY;
   fXAxisTitlePos.fX += tOffX;
   fYAxisTitlePos.fY += tOffY;


   // frame box
   //
   if (fM->fBoxMode)
   {
      // get corner closest to eye excluding left corner
      Double_t zm = 1.f;
      Int_t idxDepthT = 0;
      for (Int_t i = 0; i < 4; ++i)
      {
         if (projZ[i] < zm && projZ[i] >= zt && i != idxFront )
         {
            zm  = projZ[i];
            idxDepthT = i;
         }
      }
      if (idxFront == idxLeft)  idxFront =idxDepthT;

      switch (idxFront)
      {
         case 0:
            fBackPlaneXConst[0].Set(x1, y0, 0); fBackPlaneXConst[1].Set(x1, y1, 0);
            fBackPlaneYConst[0].Set(x0, y1, 0); fBackPlaneYConst[1].Set(x1, y1, 0);
            break;
         case 1:
            fBackPlaneXConst[0].Set(x0, y0, 0); fBackPlaneXConst[1].Set(x0, y1, 0);
            fBackPlaneYConst[0].Set(x0, y1, 0); fBackPlaneYConst[1].Set(x1, y1, 0);
            break;
         case 2:
            fBackPlaneXConst[0].Set(x0, y0, 0); fBackPlaneXConst[1].Set(x0, y1, 0);
            fBackPlaneYConst[0].Set(x0, y0, 0); fBackPlaneYConst[1].Set(x1, y0, 0);
            break;
         case 3:
            fBackPlaneXConst[0].Set(x1, y0, 0); fBackPlaneXConst[1].Set(x1, y1, 0);
            fBackPlaneYConst[0].Set(x0, y0, 0); fBackPlaneYConst[1].Set(x1, y0, 0);
            break;
      }
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::DrawAxis3D(TGLRnrCtx & rnrCtx) const
{
   // Draw z-axis and z-box at the appropriate grid corner-point including
   // tick-marks and labels.

   // set font size first depending on size of projected axis
 
   TGLMatrix mm;
   GLdouble pm[16];
   GLint    vp[4];
   glGetDoublev(GL_MODELVIEW_MATRIX, mm.Arr());
   glGetDoublev(GL_PROJECTION_MATRIX, pm);
   glGetIntegerv(GL_VIEWPORT, vp);

   GLdouble dn[3];
   GLdouble up[3];
   gluProject(fZAxisTitlePos.fX, fZAxisTitlePos.fY, 0                , mm.Arr(), pm, vp, &dn[0], &dn[1], &dn[2]);
   gluProject(fZAxisTitlePos.fX, fZAxisTitlePos.fY, fZAxisTitlePos.fZ, mm.Arr(), pm, vp, &up[0], &up[1], &up[2]);
   Double_t len = TMath::Sqrt((up[0] - dn[0]) * (up[0] - dn[0])
                              + (up[1] - dn[1]) * (up[1] - dn[1])
                              + (up[2] - dn[2]) * (up[2] - dn[2]));

   TGLVertex3 worldRef(fZAxisTitlePos.fX, fZAxisTitlePos.fY, fZAxisTitlePos.fZ);

   fAxisPainter.RefTMOff(0) = rnrCtx.RefCamera().ViewportDeltaToWorld(worldRef, -len, 0,  &mm);
   fAxisPainter.SetLabelPixelFontSize(TMath::Nint(len*fM->GetData()->GetEtaBins()->GetLabelSize()));
   fAxisPainter.SetTitlePixelFontSize(TMath::Nint(len*fM->GetData()->GetEtaBins()->GetTitleSize()));

   // Z axis
   //
   if (fM->fData->Empty() == kFALSE)
   {
      fZAxis->SetAxisColor(fGridColor);
      fZAxis->SetLabelColor(fFontColor);
      fZAxis->SetTitleColor(fFontColor);
      fZAxis->SetNdivisions(fM->fNZSteps*100 + 10);
      fZAxis->SetLimits(0, fMaxVal);
      fZAxis->SetTitle(fM->GetPlotEt() ? "Et[GeV]" : "E[GeV]");

      fAxisPainter.SetTMNDim(1);
      fAxisPainter.RefDir().Set(0., 0., 1.);
      fAxisPainter.SetLabelAlign(TGLFont::kRight, TGLFont::kCenterV);
      glPushMatrix();
      glTranslatef(fZAxisTitlePos.fX, fZAxisTitlePos.fY, 0);

      // tickmark vector = 10 pixels left
      fAxisPainter.RefTitlePos().Set(fAxisPainter.RefTMOff(0).X()*0.05,  fAxisPainter.RefTMOff(0).Y()*0.05, fZAxisTitlePos.fZ);
      fZAxis->SetLabelOffset(0.05);
      fZAxis->SetTickLength(0.05);
      fAxisPainter.PaintAxis(rnrCtx, fZAxis);
      glTranslated( fAxisPainter.RefTMOff(0).X(),  fAxisPainter.RefTMOff(0).Y(),  fAxisPainter.RefTMOff(0).Z());
      glPopMatrix();

      // draw box frame
      //
      if (fM->fBoxMode) {

         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);

         // box verticals
         TGLUtil::LineWidth(1);
         glBegin(GL_LINES);
         TGLUtil::Color(fGridColor);

         glVertex3f(fBackPlaneXConst[0].fX   ,fBackPlaneXConst[0].fY   ,0);
         glVertex3f(fBackPlaneXConst[0].fX   ,fBackPlaneXConst[0].fY   ,fMaxVal);
         glVertex3f(fBackPlaneXConst[1].fX   ,fBackPlaneXConst[1].fY   ,0);
         glVertex3f(fBackPlaneXConst[1].fX   ,fBackPlaneXConst[1].fY   ,fMaxVal);


         glVertex3f(fBackPlaneYConst[0].fX   ,fBackPlaneYConst[0].fY   ,0);
         glVertex3f(fBackPlaneYConst[0].fX   ,fBackPlaneYConst[0].fY   ,fMaxVal);
         glVertex3f(fBackPlaneYConst[1].fX   ,fBackPlaneYConst[1].fY   ,0);
         glVertex3f(fBackPlaneYConst[1].fX   ,fBackPlaneYConst[1].fY   ,fMaxVal);

         // box top
         glVertex3f(fBackPlaneXConst[0].fX   ,fBackPlaneXConst[0].fY   ,fMaxVal);
         glVertex3f(fBackPlaneXConst[1].fX   ,fBackPlaneXConst[1].fY   ,fMaxVal);
         glVertex3f(fBackPlaneYConst[0].fX   ,fBackPlaneYConst[0].fY   ,fMaxVal);
         glVertex3f(fBackPlaneYConst[1].fX   ,fBackPlaneYConst[1].fY   ,fMaxVal);

         glEnd();

         // box horizontals stippled
         glEnable(GL_LINE_STIPPLE);
         Int_t ondiv;
         Double_t omin, omax, bw1;
         THLimitsFinder::Optimize(0, fMaxVal, fM->fNZSteps, omin, omax, ondiv, bw1);

         glLineStipple(1, 0x5555);
         glBegin(GL_LINES);
         Float_t hz  = bw1;
         for (Int_t i = 1; i <= ondiv; ++i, hz += bw1) {
            glVertex3f(fBackPlaneXConst[0].fX   ,fBackPlaneXConst[0].fY   ,hz);
            glVertex3f(fBackPlaneXConst[1].fX   ,fBackPlaneXConst[1].fY   ,hz);
            glVertex3f(fBackPlaneYConst[0].fX   ,fBackPlaneYConst[0].fY   ,hz);
            glVertex3f(fBackPlaneYConst[1].fX   ,fBackPlaneYConst[1].fY   ,hz);
         }
         glEnd();

         glPopAttrib();
      }
   }

   // XY Axis
   //

   Float_t yOff  = fM->GetPhiRng();
   if (fXAxisTitlePos.fY < fM->GetPhiMax()) yOff = -yOff;

   Float_t xOff  = fM->GetEtaRng();
   if (fYAxisTitlePos.fX < fM->GetEtaMax()) xOff = -xOff;

   TAxis ax;
   ax.SetAxisColor(fGridColor);
   ax.SetLabelColor(fFontColor);
   ax.SetTitleColor(fFontColor);
   ax.SetTitleFont(fM->GetData()->GetEtaBins()->GetTitleFont());
   ax.SetLabelOffset(0.02);
   ax.SetTickLength(0.05);
   fAxisPainter.SetTMNDim(2);
   fAxisPainter.RefTMOff(1).Set(0, 0, -fMaxVal);
   fAxisPainter.SetLabelAlign(TGLFont::kCenterH, TGLFont::kBottom);

   // eta
   glPushMatrix();
   fAxisPainter.RefDir().Set(1, 0, 0);
   fAxisPainter.RefTMOff(0).Set(0, yOff, 0);
   glTranslatef(0, fXAxisTitlePos.fY, 0);
   ax.SetNdivisions(710);
   ax.SetLimits(fM->GetEtaMin(), fM->GetEtaMax());
   ax.SetTitle(fM->GetData()->GetEtaBins()->GetTitle());
   fAxisPainter.RefTitlePos().Set(fXAxisTitlePos.fX, yOff*1.5*ax.GetTickLength(), -fMaxVal*ax.GetTickLength());
   fAxisPainter.PaintAxis(rnrCtx, &ax);
   glPopMatrix();

   // phi
   fAxisPainter.RefDir().Set(0, 1, 0);
   fAxisPainter.RefTMOff(0).Set(xOff, 0, 0);
   ax.SetNdivisions(510);
   ax.SetLimits(fM->GetPhiMin(), fM->GetPhiMax());
   ax.SetTitle(fM->GetData()->GetPhiBins()->GetTitle());
   glPushMatrix();
   glTranslatef(fYAxisTitlePos.fX, 0, 0);
   fAxisPainter.RefTitlePos().Set( xOff*1.5*ax.GetTickLength(), fYAxisTitlePos.fY,  -fMaxVal*ax.GetTickLength());
   fAxisPainter.PaintAxis(rnrCtx, &ax);
   glPopMatrix();

} // DrawAxis3D

//______________________________________________________________________________
void  TEveCaloLegoGL::GetScaleForMatrix(Float_t& sx, Float_t& sy, Float_t& sz) const
{
   Double_t em, eM, pm, pM;
   fM->fData->GetEtaLimits(em, eM);
   fM->fData->GetPhiLimits(pm, pM);
   Double_t unit = ((eM - em) < (pM - pm)) ? (eM - em) : (pM - pm);
   sx = (eM - em) / (fM->GetEtaRng() * unit);
   sy = (pM - pm) / (fM->GetPhiRng() * unit);

   sz = 1;
   if (fM->fScaleAbs)
   {
      sz = fM->GetMaxTowerH() / fM->fMaxValAbs;
   }
   else if (!fM->fData->Empty())
   {
      sz = fM->GetMaxTowerH() / fMaxVal;
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::DrawAxis2D(TGLRnrCtx & rnrCtx) const
{
   // Draw XY axis.

   if (fM->GetData()->Empty())
      fAxisPainter.SetTMNDim(1);

   TGLCamera& cam  = rnrCtx.RefCamera();

   TAxis ax;
   ax.SetAxisColor(fGridColor);
   ax.SetLabelColor(fFontColor);
   ax.SetTitleColor(fFontColor);
   ax.SetTitleFont(fM->GetData()->GetEtaBins()->GetTitleFont());
   ax.SetLabelOffset(0.01);
   ax.SetTickLength(0.05);

   // set fonts
   fAxisPainter.SetAttAxis(&ax);

   // get projected length of diagonal to determine
   TGLMatrix mm;
   GLdouble pm[16];
   GLint    vp[4];
   glGetDoublev(GL_MODELVIEW_MATRIX, mm.Arr());
   glGetDoublev(GL_PROJECTION_MATRIX, pm);
   glGetIntegerv(GL_VIEWPORT, vp);

   GLdouble dn[3];
   GLdouble up[3];
   gluProject(fM->GetEtaMin(), fM->GetPhiMin(), 0, mm.Arr(), pm, vp, &dn[0], &dn[1], &dn[2]);
   gluProject(fM->GetEtaMax(), fM->GetPhiMax(), 0, mm.Arr(), pm, vp, &up[0], &up[1], &up[2]);
   Double_t len = TMath::Sqrt((up[0] - dn[0]) * (up[0] - dn[0])
                              + (up[1] - dn[1]) * (up[1] - dn[1])
                              + (up[2] - dn[2]) * (up[2] - dn[2]));

   // lock upper limit to of relative font size relative to viewport diagonal
   Double_t vpLimit = cam.RefViewport().Diagonal()*0.5/TMath::Sqrt2();
   len = TMath::Min(len, vpLimit);

   // eta
   fAxisPainter.SetLabelPixelFontSize(TMath::Nint(len*fM->GetData()->GetEtaBins()->GetLabelSize()));
   fAxisPainter.SetTitlePixelFontSize(TMath::Nint(len*fM->GetData()->GetEtaBins()->GetTitleSize()));
   ax.SetNdivisions(710);
   ax.SetLimits(fM->GetEtaMin(), fM->GetEtaMax());
   ax.SetTitle(fM->GetData()->GetEtaBins()->GetTitle());
   fAxisPainter.RefTitlePos().Set(fM->GetEtaMax(), -fM->GetPhiRng()*(ax.GetTickLength()+ ax.GetLabelOffset()), 0 );
   fAxisPainter.RefDir().Set(1, 0, 0);

   Float_t tmOffFrustX = cam.FrustumPlane(TGLCamera::kRight).D() + cam.FrustumPlane(TGLCamera::kLeft).D();
   fAxisPainter.RefTMOff(0).Set(0,  -TMath::Min(fM->GetPhiRng(), tmOffFrustX), 0);
   fAxisPainter.SetLabelAlign(TGLFont::kCenterH, TGLFont::kBottom);

   glPushMatrix();
   glTranslatef(0, fM->GetPhiMin(), 0);
   fAxisPainter.PaintAxis(rnrCtx, &ax);
   glPopMatrix();

   // phi
   ax.SetNdivisions(510);
   ax.SetLimits(fM->GetPhiMin(), fM->GetPhiMax());
   ax.SetTitle(fM->GetData()->GetPhiBins()->GetTitle());
   fAxisPainter.RefTitlePos().Set(-fM->GetEtaRng()*(ax.GetTickLength()+ ax.GetLabelOffset()), fM->GetPhiMax(), 0);
   fAxisPainter.RefDir().Set(0, 1, 0);
   Float_t tmOffFrustY = cam.FrustumPlane(TGLCamera::kTop).D() + cam.FrustumPlane(TGLCamera::kBottom).D();
   fAxisPainter.RefTMOff(0).Set(-TMath::Min(fM->GetEtaRng(), tmOffFrustY), 0, 0);
   fAxisPainter.SetLabelAlign(TGLFont::kRight, TGLFont::kCenterV);

   glPushMatrix();
   glTranslatef(fM->GetEtaMin(), 0, 0);
   fAxisPainter.PaintAxis(rnrCtx, &ax);
   glPopMatrix();

   fAxisPainter.SetTMNDim(2); 
}

//______________________________________________________________________________
Int_t TEveCaloLegoGL::GetGridStep(TGLRnrCtx &rnrCtx) const
{
   // Calculate view-dependent grid density.

   TGLCamera &camera = rnrCtx.RefCamera();
   Float_t l = -camera.FrustumPlane(TGLCamera::kLeft).D();
   Float_t r =  camera.FrustumPlane(TGLCamera::kRight).D();
   Float_t t =  camera.FrustumPlane(TGLCamera::kTop).D();
   Float_t b = -camera.FrustumPlane(TGLCamera::kBottom).D();
   Float_t frustD    = TMath::Hypot(r-l, t-b);

   GLint   vp[4]; glGetIntegerv(GL_VIEWPORT, vp);
   Float_t viewportD = TMath::Sqrt((vp[1] - vp[0]) * (vp[1] - vp[0]) + (vp[3] - vp[1]) * (vp[3] - vp[1]));
   Float_t deltaToViewport = viewportD/frustD;

   // average bin width
   GLdouble em, eM, pm, pM;
   fM->GetData()->GetEtaLimits(pm, pM);
   fM->GetData()->GetPhiLimits(em, eM);
   Int_t i0 = fM->fData->GetEtaBins()->FindBin(fM->GetEtaMin());
   Int_t i1 = fM->fData->GetEtaBins()->FindBin(fM->GetEtaMax());
   Int_t j0 = fM->fData->GetPhiBins()->FindBin(fM->GetPhiMin());
   Int_t j1 = fM->fData->GetPhiBins()->FindBin(fM->GetPhiMax());

   Float_t averageBinWidth = TMath::Hypot(eM - em, pM - pm)/TMath::Sqrt((i0 - i1) * (i0 - i1) + (j0 - j1) * (j0 - j1));
   Float_t ppb = deltaToViewport*averageBinWidth;

   Int_t ngroup = 1;
   if (fM->fAutoRebin && fM->fPixelsPerBin > ppb)
   {
      ngroup = TMath::Nint(fM->fPixelsPerBin*0.5/ppb); // symetrical rebin factor 2
      // limit rebin realtive to number of axis bins
      Int_t minN = TMath::Min(fM->fData->GetEtaBins()->GetNbins(), fM->fData->GetPhiBins()->GetNbins());
      if (ngroup * 4 > minN)
         ngroup = minN/4;
   }
   fCurrentPixelsPerBin = TMath::Nint(ppb);

   return ngroup;
}

//___________________________________________________________________________
void TEveCaloLegoGL::RebinAxis(TAxis *orig, TAxis *curr) const
{
   // Rebin eta, phi axis.

   Double_t center = 0.5 * (orig->GetXmin() + orig->GetXmax());
   Int_t    idx0   = orig->FindBin(center);
   Double_t bc     = orig->GetBinCenter(idx0);
   if (bc > center) --idx0;

   Int_t nbR = TMath::FloorNint(idx0/fBinStep) + TMath::FloorNint((orig->GetNbins() - idx0)/fBinStep);
   Int_t off = idx0 - TMath::FloorNint(idx0/fBinStep)*fBinStep;
   std::vector<Double_t> bins(nbR + 1);
   for (Int_t i = 0; i <= nbR; ++i)
   {
      bins[i] = orig->GetBinUpEdge(off + i*fBinStep);
   }
   curr->Set(nbR, &bins[0]);
}

//______________________________________________________________________________
void TEveCaloLegoGL::DrawHistBase(TGLRnrCtx &rnrCtx) const
{
   // Draw basic histogram components: x-y grid

   Float_t eta0 = fM->fEtaMin;
   Float_t eta1 = fM->fEtaMax;
   Float_t phi0 = fM->GetPhiMin();
   Float_t phi1 = fM->GetPhiMax();

   // XY grid
   //
   TGLUtil::Color(fGridColor);
   TGLUtil::LineWidth(1);
   glBegin(GL_LINES);
   glVertex2f(eta0, phi0);
   glVertex2f(eta0, phi1);
   glVertex2f(eta1, phi0);
   glVertex2f(eta1, phi1);

   glVertex2f(eta0, phi0);
   glVertex2f(eta1, phi0);
   glVertex2f(eta0, phi1);
   glVertex2f(eta1, phi1);

   // eta grid
   Float_t val;
   Int_t neb = fEtaAxis->GetNbins();
   for (Int_t i = 0; i<= neb; i++)
   {
      val = fEtaAxis->GetBinUpEdge(i);
      if (val > eta0 && val < eta1 )
      {
         glVertex2f(val, phi0);
         glVertex2f(val, phi1);
      }
   }

   // phi grid
   Int_t npb = fPhiAxis->GetNbins();
   for (Int_t i = 1; i <= npb; i++) {
       val = fPhiAxis->GetBinUpEdge(i);
      if (val > phi0 && val < phi1)
      {
         glVertex2f(eta0, val);
         glVertex2f(eta1, val);
      }
   }

   glEnd();

   // XYZ axes
   //
   glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
   TGLUtil::LineWidth(2);
   if (fCells3D)
   {
      SetAxis3DTitlePos(rnrCtx, eta0, eta1, phi0, phi1);
      DrawAxis3D(rnrCtx);
   }
   else
   {
      DrawAxis2D(rnrCtx);
   }
   glPopAttrib();
}

//______________________________________________________________________________
void TEveCaloLegoGL::DrawCells3D(TGLRnrCtx & rnrCtx) const
{
   // Render the calo lego-plot with OpenGL.

   // quads
   {
      for (SliceDLMap_i i = fDLMap.begin(); i != fDLMap.end(); ++i) {
         TGLUtil::ColorTransparency(fM->GetDataSliceColor(i->first), fM->GetData()->GetSliceTransparency(i->first));
         glLoadName(i->first);
         glPushName(0);
         glCallList(i->second);
         glPopName();
      }
   }
   // outlines
   {
      if (rnrCtx.SceneStyle() == TGLRnrCtx::kFill) {
         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
         glDisable(GL_POLYGON_OFFSET_FILL);
         TGLUtil::Color(1);
         for (SliceDLMap_i i = fDLMap.begin(); i != fDLMap.end(); ++i)
            glCallList(i->second);
      }
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::PrepareCell2DData(TEveCaloData::vCellId_t& cellList, vCell2D_t& cells2D) const
{
   // Prepare cells 2D data non-rebinned for drawing.

   Int_t   max_energy_slice, cellID=0;
   Float_t sum, max_energy;

   TEveCaloData::vCellId_t::iterator currentCell = cellList.begin();
   TEveCaloData::vCellId_t::iterator nextCell    = currentCell;
   ++nextCell;

   while (true)
   {
      TEveCaloData::CellData_t currentCellData;
      TEveCaloData::CellData_t nextCellData;

      fM->fData->GetCellData(*currentCell, currentCellData);
      sum = max_energy = currentCellData.Value(fM->fPlotEt);
      max_energy_slice = currentCell->fSlice;
      while (nextCell != cellList.end() && currentCell->fTower == nextCell->fTower)
      {
         fM->fData->GetCellData(*nextCell, nextCellData);
         Float_t energy = nextCellData.Value(fM->fPlotEt);
         sum += energy;
         if (energy > max_energy)
         {
            max_energy       = energy;
            max_energy_slice = nextCell->fSlice;
         }
         ++nextCell;
         ++cellID;
      }

      WrapTwoPi(currentCellData.fPhiMin, currentCellData.fPhiMax);
      cells2D.push_back(Cell2D_t(cellID, sum, max_energy_slice));
      cells2D.back().SetGeom(currentCellData.fEtaMin, currentCellData.fEtaMax,
                             currentCellData.fPhiMin, currentCellData.fPhiMax);

      if (nextCell == cellList.end())
         break;

      currentCell = nextCell;
      ++nextCell;
      ++cellID;
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::PrepareCell2DDataRebin(TEveCaloData::RebinData_t& rebinData, vCell2D_t& cells2D) const
{
   // Prepare cells 2D rebinned data for drawing.

   const Int_t nEta = fEtaAxis->GetNbins();
   const Int_t nPhi = fPhiAxis->GetNbins();
   std::vector<Float_t> vec;
   vec.assign((nEta + 2)*(nPhi + 2), 0.f);
   std::vector<Float_t> max_e;
   std::vector<Int_t>   max_e_slice;
   max_e.assign((nEta + 2) * (nPhi + 2), 0.f);
   max_e_slice.assign((nEta + 2) * (nPhi + 2), -1);

   for (UInt_t bin = 0; bin < rebinData.fBinData.size(); ++bin) {
      Float_t ssum = 0;
      if (rebinData.fBinData[bin] != -1) {
         Float_t *val = rebinData.GetSliceVals(bin);
         for (Int_t s = 0; s < rebinData.fNSlices; ++s) {
            ssum += val[s];
            if (val[s] > max_e[bin]) {
               max_e[bin]       = val[s];
               max_e_slice[bin] = s;
            }
         }
      }
      vec[bin] = ssum;
   }

   // smallest threshold
   Float_t threshold = fM->GetDataSliceThreshold(0);
   for (Int_t s = 1; s < fM->fData->GetNSlices(); ++s) {
      if (threshold > fM->GetDataSliceThreshold(s))
         threshold = fM->GetDataSliceThreshold(s);
   }

   // write cells
   for (Int_t i = 1; i <= fEtaAxis->GetNbins(); ++i) {
      for (Int_t j = 1; j <= fPhiAxis->GetNbins(); ++j) {
         const Int_t bin = j * (nEta + 2) + i;
         if (vec[bin] > threshold && rebinData.fBinData[bin] != -1) {
            cells2D.push_back(Cell2D_t(bin, vec[bin], max_e_slice[bin]));
            cells2D.back().SetGeom(fEtaAxis->GetBinLowEdge(i), fEtaAxis->GetBinUpEdge(i),
                                   fPhiAxis->GetBinLowEdge(j), fPhiAxis->GetBinUpEdge(j));
         }
      }
   }
}

//--------------------------------------------------------------------------------
void TEveCaloLegoGL::DrawCells2D(TGLRnrCtx &rnrCtx, vCell2D_t& cells2D) const
{
   // Draw cells in top view.

   Float_t bws    = -1; //smallest bin
   Float_t logMax = -1;

   Float_t baseOffset = fM->GetFixedHeightValIn2DMode()*fMaxVal;

   if (fM->f2DMode == TEveCaloLego::kValColor)
   {
      fM->AssertPalette();
      UChar_t col[4];

      for (vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i)
      {
         if (rnrCtx.SecSelection()) glLoadName(i->fId);
         glBegin(GL_POLYGON);
         fM->fPalette->ColorFromValue(TMath::FloorNint(i->fSumVal), col);
         col[3] = fM->GetData()->GetSliceTransparency(i->fMaxSlice);
         TGLUtil::Color4ubv(col);
         Float_t z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
         glVertex3f(i->fX0, i->fY0, z);
         glVertex3f(i->fX1, i->fY0, z);
         glVertex3f(i->fX1, i->fY1, z);
         glVertex3f(i->fX0, i->fY1, z);
         glEnd();
      }
   }
   else
   {
      Float_t x, y;
      if (!rnrCtx.HighlightOutline())
      {
         Float_t maxv = 0;
         bws = 1e5;
         for (vCell2D_i i = fCells2D.begin(); i != fCells2D.end(); ++i)
         {
            if (i->MinSize() < bws)   bws  = i->MinSize();
            if (i->fSumVal   > maxv)  maxv = i->fSumVal;
         }
         bws   *= 0.5f;
         logMax = TMath::Log10(maxv + 1);
         fValToPixel =  bws/logMax;
      }

      // special draw for name stack
      if (rnrCtx.SecSelection())
      {
         for (vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i)
         {
            glLoadName(i->fMaxSlice);
            glPushName(i->fId);

            glBegin(GL_QUADS);
            Float_t z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
            glVertex3f(i->fX0, i->fY0, z);
            glVertex3f(i->fX1, i->fY0, z);
            glVertex3f(i->fX1, i->fY1, z);
            glVertex3f(i->fX0, i->fY1, z);
            glEnd();

            glPopName();
         }
      }
      else
      {// optimised draw without name stack
         if (!rnrCtx.HighlightOutline())
         {
            glBegin(GL_POINTS);
            for (vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i)
            {
               TGLUtil::ColorTransparency(fM->fData->GetSliceColor(i->fMaxSlice), fM->fData->GetSliceTransparency(i->fMaxSlice));
               Float_t z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
               glVertex3f(i->X(), i->Y() , z);
            }
            glEnd();
         }

         glBegin(GL_QUADS);
         for (vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i)
         {
            TGLUtil::ColorTransparency(fM->fData->GetSliceColor(i->fMaxSlice), fM->fData->GetSliceTransparency(i->fMaxSlice));
            Float_t bw = fValToPixel*TMath::Log10(i->fSumVal+1);
            x = i->X();
            y = i->Y();
            Float_t z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
            glVertex3f(x - bw, y - bw, z);
            glVertex3f(x + bw, y - bw, z);
            glVertex3f(x + bw, y + bw, z);
            glVertex3f(x - bw, y + bw, z);
         }
         glEnd();

         if (fM->f2DMode == TEveCaloLego::kValSizeOutline)
         { 
            glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
            Float_t z    = 0;
            Float_t zOff = fMaxVal*0.001 ; // avoid polygon stipling
            glBegin(GL_QUADS);
            for ( vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i) {
               Char_t transp = TMath::Min(100, 80 + fM->fData->GetSliceTransparency(i->fMaxSlice) / 5);
               TGLUtil::ColorTransparency(fM->fData->GetSliceColor(i->fMaxSlice), transp);
               z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
               z -=  zOff;
               glVertex3f(i->fX0, i->fY0, z);
               glVertex3f(i->fX1, i->fY0, z);
               glVertex3f(i->fX1, i->fY1, z);
               glVertex3f(i->fX0, i->fY1, z);
            }
            glEnd();

            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glBegin(GL_QUADS);
            for ( vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i) {
               TGLUtil::ColorTransparency(fM->fData->GetSliceColor(i->fMaxSlice), 60);
               z = fM->GetHasFixedHeightIn2DMode() ? baseOffset : i->fSumVal;
               z +=  zOff;
               glVertex3f(i->fX0, i->fY0, z);
               glVertex3f(i->fX1, i->fY0, z);
               glVertex3f(i->fX1, i->fY1, z);
               glVertex3f(i->fX0, i->fY1, z);
            }
            glEnd();
            glPopAttrib();
         }
      }
   }

   // text
   if (fCurrentPixelsPerBin > fM->fDrawNumberCellPixels &&
       (rnrCtx.Selection() || rnrCtx.Highlight() || rnrCtx.HighlightOutline()) == kFALSE)
   {
      TGLUtil::Color(rnrCtx.ColorSet().Markup().GetColorIndex());
      TGLFont font;
      rnrCtx.RegisterFontNoScale(fM->fCellPixelFontSize, "arial", TGLFont::kPixmap, font);
      const char* txt;
      for (vCell2D_i i = cells2D.begin(); i != cells2D.end(); ++i) {

         Float_t val = i->fSumVal;
         if (val > 10)
            txt = Form("%d", TMath::Nint(val));
         else if (val > 1 )
            txt = Form("%.1f", val);
         else if (val > 0.01 )
            txt = Form("%.2f", 0.01*TMath::Nint(val*100));
         else
            txt = Form("~1e%d", TMath::Nint(TMath::Log10(val)));

         font.Render(txt, i->X(), i->Y(), val*1.2, TGLFont::kCenterH, TGLFont::kCenterV);
      }
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::DrawHighlight(TGLRnrCtx& rnrCtx, const TGLPhysicalShape* /*pshp*/, Int_t /*lvl*/) const
{
   // Draw highligted cells.

   if (fM->fData->GetCellsSelected().empty() && fM->fData->GetCellsHighlighted().empty())
   {
      return;
   }

   glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT );

   // modelview matrix
   glPushMatrix();
   Float_t sx, sy, sz;
   GetScaleForMatrix(sx, sy, sz);
   glScalef(sx, sy, sz);
   glTranslatef(-fM->GetEta(), -fM->fPhi, 0);

   glDisable(GL_LIGHTING);
   glDisable(GL_CULL_FACE);
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

   TGLUtil::LineWidth(2);
   TGLUtil::LockColor();
   if (!fM->fData->GetCellsHighlighted().empty()) 
   {
      glColor4ubv(rnrCtx.ColorSet().Selection(3).CArr());
      DrawSelectedCells(rnrCtx, fM->fData->GetCellsHighlighted());
   }
   if (!fM->fData->GetCellsSelected().empty())
   {
      Float_t dr[2];
      glGetFloatv(GL_DEPTH_RANGE,dr);
      glColor4ubv(rnrCtx.ColorSet().Selection(1).CArr());
      glDepthRange(dr[0], 0.8*dr[1]);
      DrawSelectedCells(rnrCtx, fM->fData->GetCellsSelected());
      glDepthRange(dr[0], dr[1]);
   }

   TGLUtil::UnlockColor();
   glPopMatrix();
   glPopAttrib();
}


//______________________________________________________________________________
void TEveCaloLegoGL::DrawSelectedCells(TGLRnrCtx & rnrCtx, TEveCaloData::vCellId_t cellsSelectedInput) const
{
   // Draw selected cells in highlight mode.

   // check eta&phi range of selected cells
   TEveCaloData::vCellId_t  cellsSelected;
   TEveCaloData::CellData_t cellData;
   for (TEveCaloData::vCellId_i i = cellsSelectedInput.begin(); i != cellsSelectedInput.end(); ++i)
   {
      fM->fData->GetCellData((*i), cellData);
      if (fM->CellInEtaPhiRng(cellData))
         cellsSelected.push_back(*i); 
   }

   // prepare rebin for 2D or 3D if necessary
   TEveCaloData::RebinData_t rebinDataSelected;
   if (fBinStep > 1)
   {
      fM->fData->Rebin(fEtaAxis, fPhiAxis, cellsSelected, fM->fPlotEt, rebinDataSelected);
      if (fM->fNormalizeRebin) {
         Float_t scale = 1.f / (fBinStep * fBinStep);
         for (std::vector<Float_t>::iterator it = rebinDataSelected.fSliceData.begin(); it != rebinDataSelected.fSliceData.end(); it++)
            (*it) *= scale;
      }
   }

   if (fCells3D)
   {
      Float_t offset =  0;
      if (fBinStep == 1)
      {
         for (TEveCaloData::vCellId_i j = cellsSelected.begin(); j != cellsSelected.end(); ++j)
         {
            offset = 0;
            {
               Int_t   orig_slice = j->fSlice;
               for (Int_t s = 0; s < orig_slice; ++s)
               {
                  j->fSlice = s;
                  fM->fData->GetCellData(*j, cellData);
                  offset += cellData.Value(fM->fPlotEt);
               }
               j->fSlice = orig_slice;
            }
            fM->fData->GetCellData(*j, cellData);
            WrapTwoPi(cellData.fPhiMin, cellData.fPhiMax);
            MakeQuad(cellData.EtaMin(), cellData.PhiMin(), offset,
                     cellData.EtaDelta(), cellData.PhiDelta(), cellData.Value(fM->fPlotEt));
         }
      }
      else
      {
         Float_t *vals;
         Float_t *valsRef;
         Float_t  y0, y1;
         Int_t    nSlices = fM->fData->GetNSlices();
         for (Int_t i = 1; i <= fEtaAxis->GetNbins(); ++i)
         {
            for (Int_t j = 1; j <= fPhiAxis->GetNbins(); ++j)
            {
               const Int_t bin = (i)+(j)*(fEtaAxis->GetNbins()+2);
               if (rebinDataSelected.fBinData[bin] !=-1)
               {
                  offset  = 0;
                  vals    = rebinDataSelected.GetSliceVals(bin);
                  valsRef = fRebinData.GetSliceVals(bin);
                  for (Int_t s = 0; s < nSlices; ++s)
                  {
                     if (vals[s] > 0)
                     {
                        y0 = fPhiAxis->GetBinLowEdge(j);
                        y1 = fPhiAxis->GetBinUpEdge(j);
                        WrapTwoPi(y0, y1);
                        MakeQuad(fEtaAxis->GetBinLowEdge(i), y0, offset,
                                 fEtaAxis->GetBinWidth(i), y1-y0, vals[s]);
                     }
                     offset += valsRef[s];
                  }
               }
            }
         }
      }
   }
   else
   {
      vCell2D_t cells2DSelected;
      if (fBinStep == 1)
      {
         // but is confusing since top view does not draw all slices at same time
         TEveCaloData::vCellId_i j    = cellsSelectedInput.begin();
         TEveCaloData::vCellId_i jEnd = cellsSelectedInput.end();
         std::set<Int_t> towers;
         while (j != jEnd)
         {
            towers.insert(j->fTower);
            ++j;
         }
         for (vCell2D_i i = fCells2D.begin(); i != fCells2D.end(); ++i)
         {
            TEveCaloData::CellId_t cell = fM->fCellList[i->fId];
            std::set<Int_t>::iterator ti = towers.find(cell.fTower);
            if (towers.find(cell.fTower) != towers.end())
            {
               cells2DSelected.push_back(*i);
            }
         }
      }
      else
      {
         PrepareCell2DDataRebin(rebinDataSelected, cells2DSelected);
      }
      DrawCells2D(rnrCtx, cells2DSelected);
   }
}

//______________________________________________________________________________
void TEveCaloLegoGL::DirectDraw(TGLRnrCtx & rnrCtx) const
{
   // Draw the object.

   if (! fM->fData || ! fM->fData->GetEtaBins() || ! fM->fData->GetPhiBins())
      return;

   // projection type
   if (fM->fProjection == TEveCaloLego::kAuto)
      fCells3D = (!(rnrCtx.RefCamera().IsOrthographic() && rnrCtx.RefCamera().GetCamBase().GetBaseVec(1).Z()));
   else if (fM->fProjection == TEveCaloLego::k2D)
      fCells3D = kFALSE;
   else if (fM->fProjection == TEveCaloLego::k3D)
      fCells3D = kTRUE;

   // rebin axsis , check limits, fix TwoPi cycling
   Int_t new_bin_step = GetGridStep(rnrCtx);

   // rebin data
   if (fM->AssertCellIdCache() || fBinStep != new_bin_step)
   {
      fBinStep = new_bin_step;
      fDLCacheOK   = kFALSE;
      fRebinData.Clear();

      RebinAxis(fM->fData->GetEtaBins(), fEtaAxis);
      RebinAxis(fM->fData->GetPhiBins(), fPhiAxis);

      if (fBinStep > 1)
      {
         fM->fData->Rebin(fEtaAxis, fPhiAxis, fM->fCellList, fM->fPlotEt, fRebinData);

         fMaxVal = 0;
         for (UInt_t i = 0; i < fRebinData.fSliceData.size(); i += fRebinData.fNSlices)
         {
            Double_t sum = 0;
            for (Int_t s = 0; s < fRebinData.fNSlices; s++)
            {
               sum += fRebinData.fSliceData[i+s];
            }
            if (sum > fMaxVal) fMaxVal = sum;
         }

         if (fM->fNormalizeRebin)
         {
            Float_t scale = 1.f / (fBinStep * fBinStep);
            for (std::vector<Float_t>::iterator it = fRebinData.fSliceData.begin(); it != fRebinData.fSliceData.end(); it++)
            {
               (*it) *= scale;
            }
            fMaxVal *= scale;
         }
      }
      else
      {
         fMaxVal = fM->GetMaxVal();
      }
   }

   // modelview matrix
   glPushMatrix();
   Float_t sx, sy, sz;
   GetScaleForMatrix(sx, sy, sz);
   glScalef(sx, sy, sz);
   glTranslatef(-fM->GetEta(), -fM->fPhi, 0);

   fFontColor = fM->fFontColor;
   fGridColor = fM->fGridColor;
   if (fGridColor < 0 || fFontColor < 0)
   {
      TColor* c1 = gROOT->GetColor(rnrCtx.ColorSet().Markup().GetColorIndex());
      TColor* c2 = gROOT->GetColor(rnrCtx.ColorSet().Background().GetColorIndex());
      Float_t f1, f2;
      if (fFontColor < 0) {
         f1 = 0.8; f2 = 0.2;
         fFontColor = TColor::GetColor(c1->GetRed()  *f1  + c2->GetRed()  *f2,
                                       c1->GetGreen()*f1  + c2->GetGreen()*f2,
                                       c1->GetBlue() *f1  + c2->GetBlue() *f2);
      }
      if (fGridColor < 0) {
         f1 = 0.3; f2 = 0.3;
         fGridColor = TColor::GetColor(c1->GetRed()  *f1  + c2->GetRed()  *f2,
                                       c1->GetGreen()*f1  + c2->GetGreen()*f2,
                                       c1->GetBlue() *f1  + c2->GetBlue() *f2);
      }
   }

   glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
   TGLUtil::LineWidth(1);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   if (!fM->fData->Empty())
   {
      glPushName(0);
      if (fCells3D)
      {
         if (fDLCacheOK == kFALSE)
         {
            if (fBinStep == 1)
               Make3DDisplayList(fM->fCellList, fDLMap, kTRUE);
            else
               Make3DDisplayListRebin(fRebinData, fDLMap, kTRUE);
            fDLCacheOK = kTRUE;
         }
         glEnable(GL_NORMALIZE);
         glEnable(GL_POLYGON_OFFSET_FILL);
         glPolygonOffset(0.8, 1);

         DrawCells3D(rnrCtx);
      }
      else
      {
         glDisable(GL_LIGHTING);

         fCells2D.clear();
         if (fBinStep == 1)
            PrepareCell2DData(fM->fCellList, fCells2D);
         else
            PrepareCell2DDataRebin(fRebinData, fCells2D);

         DrawCells2D(rnrCtx, fCells2D);
      }
      glPopName();
   }
   glPopAttrib();

   // draw histogram base
   if (rnrCtx.Selection() == kFALSE && rnrCtx.IsDrawPassFilled())
   {
      glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
      glDisable(GL_LIGHTING);
      DrawHistBase(rnrCtx);
      if (fM->fDrawHPlane) {
         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
         glDisable(GL_CULL_FACE);
         TGLUtil::ColorTransparency(fM->fPlaneColor, fM->fPlaneTransparency);
         Float_t zhp = fM->fHPlaneVal * fMaxVal;
         glBegin(GL_POLYGON);
         glVertex3f(fM->fEtaMin, fM->GetPhiMin(), zhp);
         glVertex3f(fM->fEtaMax, fM->GetPhiMin(), zhp);
         glVertex3f(fM->fEtaMax, fM->GetPhiMax(), zhp);
         glVertex3f(fM->fEtaMin, fM->GetPhiMax(), zhp);
         glEnd();
      }
      glPopAttrib();
   }

   glPopMatrix();
}

//______________________________________________________________________________
void TEveCaloLegoGL::ProcessSelection(TGLRnrCtx & /*rnrCtx*/, TGLSelectRecord & rec)
{
   // Processes tower selection from TGLViewer.

   TEveCaloData::vCellId_t sel;
   if (rec.GetN() > 2)
   {
      Int_t slice = rec.GetItem(1);
      Int_t cell  = rec.GetItem(2);

      if (fBinStep == 1)
      {
         Int_t tower = fM->fCellList[cell].fTower;
         while (cell > 0 && tower == fM->fCellList[cell].fTower)
         {
            sel.push_back(fM->fCellList[cell]);
            if (fCells3D) break;
            --cell;
         }
      }
      else
      {
         if (cell > 0)
         {
            Int_t nEta   = fEtaAxis->GetNbins();
            Int_t phiBin = Int_t(cell/(nEta+2));
            Int_t etaBin = cell - phiBin*(nEta+2);
            TEveCaloData::vCellId_t sl;
            fM->fData->GetCellList(fEtaAxis->GetBinCenter(etaBin), fEtaAxis->GetBinWidth(etaBin),
                                   fPhiAxis->GetBinCenter(phiBin), fPhiAxis->GetBinWidth(phiBin),
                                   sl);

            for (TEveCaloData::vCellId_i it = sl.begin(); it != sl.end(); ++it)
            {
               if (fCells3D) {
                  if ((*it).fSlice == slice ) sel.push_back(*it);
               } else {
                  if ((*it).fSlice <= slice ) sel.push_back(*it);
               }
            }
         }
      }
   }
   fM->fData->ProcessSelection(sel, rec);
}
