// @(#)root/gl:$Id$ // Author: Matevz 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 "TGLAxisPainter.h" #include "TGLRnrCtx.h" #include "TGLCamera.h" #include "TGLIncludes.h" #include "TGLRnrCtx.h" #include "TGLFontManager.h" #include "TAttAxis.h" #include "TAxis.h" #include "TH1.h" #include "THLimitsFinder.h" #include "TMath.h" #include "TPRegexp.h" //============================================================================== // TGLAxisPainterBox //============================================================================== //______________________________________________________________________________ // // Utility class to paint axis in GL. ClassImp(TGLAxisPainter); //______________________________________________________________________________ TGLAxisPainter::TGLAxisPainter(): fExp(0), fMaxDigits(5), fDecimals(0), fAttAxis(0), fUseAxisColors(kTRUE), fFontMode(TGLFont::kTexture), fDir(1, 0, 0), fTMNDim(1), fLabelPixelFontSize(14), fLabel3DFontSize(1.0), fTitlePixelFontSize(14), fTitle3DFontSize(1.0), fLabelAlignH(TGLFont::kCenterH), fLabelAlignV(TGLFont::kCenterV), fAllZeroesRE(0) { // Constructor. fAllZeroesRE = new TPMERegexp("[-+]?0\\.0*$", "o"); } //______________________________________________________________________________ TGLAxisPainter::~TGLAxisPainter() { // Destructor. delete fAllZeroesRE; } //______________________________________________________________________________ void TGLAxisPainter::SetLabelAlign(TGLFont::ETextAlignH_e h, TGLFont::ETextAlignV_e v) { // Set label align. fLabelAlignH = h; fLabelAlignV = v; } //______________________________________________________________________________ void TGLAxisPainter::LabelsLimits(const char *label, Int_t &first, Int_t &last) const { // Find first and last character of a label. last = strlen(label) - 1; for (Int_t i = 0; i <= last; i++) { if (strchr("1234567890-+.", label[i])) { first = i; return; } } Error("LabelsLimits", "attempt to draw a blank label"); } //______________________________________________________________________________ void TGLAxisPainter::FormAxisValue(Double_t val, TString &s) const { // Returns formatted text suitable for display of value. s.Form(fFormat, val); s = s.Strip(TString::kLeading); if (s == "-." || s == "-0") { s = "0"; return; } Ssiz_t ld = s.Last('.') + 1; if (s.Length() - ld > fDecimals) s.Remove(ld + fDecimals); if (fDecimals == 0 && s.EndsWith(".")) s.Remove(s.Length() -1); fAllZeroesRE->Substitute(s, "0", kFALSE); } //______________________________________________________________________________ void TGLAxisPainter::SetTextFormat(Double_t min, Double_t max, Double_t bw1) { // Construct print format from given primary bin width. Double_t absMax = TMath::Max(TMath::Abs(min), TMath::Abs(max)); Double_t epsilon = 1e-5; Double_t absMaxLog = TMath::Log10(absMax) + epsilon; fExp = 0; Int_t if1, if2; Double_t xmicros = TMath::Power(10, -fMaxDigits); if (bw1 < xmicros && absMaxLog < 0) { // First case : bin width less than 0.001 fExp = (Int_t)absMaxLog; if (fExp % 3 == 1) fExp += TMath::Sign(2, fExp); if (fExp % 3 == 2) fExp += TMath::Sign(1, fExp); if1 = fMaxDigits; if2 = fMaxDigits - 2; } else { // Use x 10 n format. (only powers of 3 allowed) Float_t af = (absMax > 1) ? absMaxLog : TMath::Log10(absMax * 0.0001); af += epsilon; Int_t clog = Int_t(af) + 1; if (clog > fMaxDigits) { while (1) { fExp++; absMax /= 10; if (fExp % 3 == 0 && absMax <= TMath::Power(10, fMaxDigits - 1)) break; } } else if (clog < -fMaxDigits) { Double_t rne = 1 / TMath::Power(10, fMaxDigits - 2); while (1) { fExp--; absMax *= 10; if (fExp % 3 == 0 && absMax >= rne) break; } } Int_t na = 0; for (Int_t i = fMaxDigits - 1; i > 0; i--) { if (TMath::Abs(absMax) < TMath::Power(10, i)) na = fMaxDigits - i; } Double_t size = TMath::Abs(max - min); Int_t ndyn = (Int_t)(size / bw1); while (ndyn) { if (size / ndyn <= 0.999 && na < fMaxDigits - 2) { na++; ndyn /= 10; } else break; } if2 = na; if1 = TMath::Max(clog + na, fMaxDigits) + 1; } // compose text format if (TMath::Min(min, max) < 0)if1 = if1 + 1; if1 = TMath::Min(if1, 32); // In some cases, if1 and if2 are too small.... Double_t dwlabel = bw1 * TMath::Power(10, -fExp); while (dwlabel < TMath::Power(10, -if2)) { if1++; if2++; } if (if1 > 14) if1 = 14; if (if2 > 14) if2 = 14; if (if2) fFormat.Form("%%%d.%df", if1, if2); else fFormat.Form("%%%d.%df", if1 + 1, 1); // get decimal number TString chtemp; chtemp.Form("%g", dwlabel); fDecimals = 0; if (chtemp.First('.') != kNPOS) fDecimals = chtemp.Length() - chtemp.First('.') - 1; } /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ // // Utility functions. //______________________________________________________________________________ void TGLAxisPainter::RnrText(const TString &txt, const TGLVector3 &p, TGLFont::ETextAlignH_e aH, TGLFont::ETextAlignV_e aV, const TGLFont &font) const { // Render text at the given position. Offset depends of text aligment. if (fFontMode == TGLFont::kPixmap || fFontMode == TGLFont::kBitmap) { font.Render(txt, p.X(), p.Y(), p.Z(), aH, aV); } else { // In case of non pixmap font, size is adjusted to the projected view in order to // be visible on zoom out. In other words texture and polygon fonts imitate // pixmap font behaviour. glPushMatrix(); glTranslated(p.X(), p.Y(), p.Z()); Double_t sc = fLabel3DFontSize/fLabelPixelFontSize; glScaled(sc, sc, 1); font.Render(txt, 0, 0, 0, aH, aV); glPopMatrix(); } } //______________________________________________________________________________ void TGLAxisPainter::SetLabelFont(TGLRnrCtx &rnrCtx, const char* fontName, Int_t fontSize, Double_t size3d) { // Set label font derived from TAttAxis. rnrCtx.RegisterFontNoScale(fontSize, fontName, fFontMode, fLabelFont); fLabel3DFontSize = size3d; fLabelPixelFontSize = fLabelFont.GetSize(); } //______________________________________________________________________________ void TGLAxisPainter::RnrLabels() const { // Render label reading prepared list ov value-pos pairs. if (fUseAxisColors) TGLUtil::Color(fAttAxis->GetLabelColor()); glPushMatrix(); Float_t off = fAttAxis->GetLabelOffset() + fAttAxis->GetTickLength(); TGLVector3 offVec = fTMOff[0] * off; glTranslated(offVec.X(), offVec.Y(), offVec.Z()); fLabelFont.PreRender(); Double_t p = 0.; TString s; for (LabVec_t::const_iterator it = fLabVec.begin(); it != fLabVec.end(); ++it) { FormAxisValue((*it).second, s); p = (*it).first; RnrText(s, fDir*p, fLabelAlignH, fLabelAlignV, fLabelFont); } fLabelFont.PostRender(); glPopMatrix(); } //______________________________________________________________________________ void TGLAxisPainter::SetTitleFont(TGLRnrCtx &rnrCtx, const char* fontName, Int_t fontSize, Double_t size3d) { // Set title font derived from TAttAxis. rnrCtx.RegisterFontNoScale(fontSize, fontName, fFontMode, fTitleFont); fTitlePixelFontSize = fTitleFont.GetSize(); fTitle3DFontSize = size3d; } //______________________________________________________________________________ void TGLAxisPainter::RnrTitle(const TString &txt, TGLVector3 &pos , TGLFont::ETextAlignH_e aH, TGLFont::ETextAlignV_e aV) const { // Draw title at given position. if (fUseAxisColors) TGLUtil::Color(fAttAxis->GetTitleColor()); TString title = (fExp) ? Form("%s [10^%d]", txt.Data(), fExp) : txt; fTitleFont.PreRender(); RnrText(title, pos, aH, aV, fTitleFont); fTitleFont.PostRender(); } //______________________________________________________________________________ void TGLAxisPainter::RnrLines() const { // Render axis main line and tickmarks. if (fUseAxisColors) TGLUtil::Color(fAttAxis->GetAxisColor()); TGLUtil::LineWidth(1); glBegin(GL_LINES); // Main line. // Float_t min = fTMVec.front().first; Float_t max = fTMVec.back().first; TGLVector3 start = fDir * min; TGLVector3 end = fDir * max; glVertex3dv(start.Arr()); glVertex3dv(end.Arr()); // Tick-marks. // Support three possible directions and two orders. // Float_t tmsOrderFirst = fAttAxis->GetTickLength(); Float_t tmsOrderSecond = tmsOrderFirst * 0.5; TGLVector3 pos; TMVec_t::const_iterator it = fTMVec.begin(); Int_t nt = fTMVec.size()-1; it++; for (Int_t t = 1; t < nt; ++t, ++it) { pos = fDir * ((*it).first); for (Int_t dim = 0; dim < fTMNDim; dim++) { glVertex3dv(pos.Arr()); if ((*it).second) glVertex3dv((pos + fTMOff[dim]*tmsOrderSecond).Arr()); else glVertex3dv((pos + fTMOff[dim]*tmsOrderFirst).Arr()); } } glEnd(); } //______________________________________________________________________________ void TGLAxisPainter::PaintAxis(TGLRnrCtx &rnrCtx, TAxis* ax) { // GL render TAxis. fAttAxis = ax; Double_t min = ax->GetXmin(); Double_t max = ax->GetXmax(); if (min == max) { Error("TGLAxisPainter::PaintAxis", "axis without range"); return; } //______________________________________________________________________________ // Fill lablels value-pos and tick-marks position-length. Int_t n1a = TMath::FloorNint(fAttAxis->GetNdivisions() / 100); Int_t n2a = fAttAxis->GetNdivisions() - n1a * 100; Int_t bn1, bn2; Double_t bw1, bw2; // primary , secondary bin width Double_t bl1=0, bh1=0, bl2=0, bh2=0; // bin low, high values // Read limits from users range THLimitsFinder::Optimize(min, max, n1a, bl1, bh1, bn1, bw1); THLimitsFinder::Optimize(bl1, bl1 + bw1, n2a, bl2, bh2, bn2, bw2); //______________________________________________________________________________ // Get TM. First and last values are reserved for axis range // fTMVec.clear(); fLabVec.clear(); fTMVec.push_back(TM_t(min, -1)); Double_t v1 = bl1; Double_t v2 = 0; for (Int_t t1 = 0; t1 <= bn1; t1++) { fTMVec.push_back(TM_t(v1, 0)); fLabVec.push_back(Lab_t(v1, v1)); v2 = v1 + bw2; for (Int_t t2 = 1; t2 < bn2; t2++) { if (v2 > max) break; fTMVec.push_back(TM_t(v2, 1)); v2 += bw2; } v1 += bw1; } // complete low edges for 1.st order TM v2 = bl1 -bw2; while (v2 > min) { fTMVec.push_back(TM_t(v2, 1)); v2 -= bw2; } fTMVec.push_back(TM_t(max, -1)); //______________________________________________________________________________ // Get labels. In this case trivial one-one mapping. Double_t p = bl1; fLabVec.clear(); SetTextFormat(min, max, bw1); for (Int_t i = 0; i <= bn1; i++) { fLabVec.push_back(Lab_t(p, p)); p += bw1; } //______________________________________________________________________________ // Set font. // First projected axis length needed if use realtive font size. const char* labFontName = TGLFontManager::GetFontNameFromId(fAttAxis->GetLabelFont()); const char* titleFontName = TGLFontManager::GetFontNameFromId(fAttAxis->GetTitleFont()); // pixel font size is set externaly for pixmap and bitmap fonts // for texture and polygon fonts font size is set here, to get font resolution if (fFontMode == TGLFont::kPolygon || fFontMode == TGLFont::kTexture) { GLdouble mm[16], pm[16]; GLint vp[4]; glGetDoublev(GL_MODELVIEW_MATRIX, mm); glGetDoublev(GL_PROJECTION_MATRIX, pm); glGetIntegerv(GL_VIEWPORT, vp); GLdouble dn[3], up[3]; gluProject(fDir.X()*min, fDir.Y()*min, fDir.Z()*min, mm, pm, vp, &dn[0], &dn[1], &dn[2]); gluProject(fDir.X()*max, fDir.Y()*max, fDir.Z()*max, mm, 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])); fLabelPixelFontSize = TMath::Nint(len*fAttAxis->GetLabelSize()); fTitlePixelFontSize = TMath::Nint(len*fAttAxis->GetTitleSize()); } SetLabelFont(rnrCtx, labFontName, fLabelPixelFontSize, (max - min)*fAttAxis->GetLabelSize()); SetTitleFont(rnrCtx, titleFontName, fTitlePixelFontSize, (max - min)*fAttAxis->GetTitleSize()); //______________________________________________________________________________ // Draw. if (!fUseAxisColors) TGLUtil::Color(rnrCtx.ColorSet().Markup()); glDisable(GL_LIGHTING); RnrLines(); RnrLabels(); if (ax->GetTitle()) RnrTitle(ax->GetTitle(), fTitlePos, fLabelAlignH, fLabelAlignV); } //============================================================================== // TGLAxisPainterBox //============================================================================== //______________________________________________________________________________ // // Painter class for axes encompassing a 3D box. ClassImp(TGLAxisPainterBox); //______________________________________________________________________________ TGLAxisPainterBox::TGLAxisPainterBox() : TGLAxisPainter() { // Constructor. fAxis[0] = fAxis[1] = fAxis[2] = 0; } //______________________________________________________________________________ TGLAxisPainterBox::~TGLAxisPainterBox() { // Destructor. } //______________________________________________________________________________ void TGLAxisPainterBox::SetAxis3DTitlePos(TGLRnrCtx &rnrCtx) { // Get position of axes and titles from projected corners. Double_t x0 = fAxis[0]->GetXmin(); Double_t x1 = fAxis[0]->GetXmax(); Double_t y0 = fAxis[1]->GetXmin(); Double_t y1 = fAxis[1]->GetXmax(); Double_t z0 = fAxis[2]->GetXmin(); Double_t z1 = fAxis[2]->GetXmax(); // project corner points 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], z0, mm, pm, vp, &projX[0], &projY[0], &projZ[0]); gluProject(cornerX[1], cornerY[1], z0, mm, pm, vp, &projX[1], &projY[1], &projZ[1]); gluProject(cornerX[2], cornerY[2], z0, mm, pm, vp, &projX[2], &projY[2], &projZ[2]); gluProject(cornerX[3], cornerY[3], z0, 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; } } fAxisTitlePos[2].Set(cornerX[idxLeft], cornerY[idxLeft], z1); // 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: fAxisTitlePos[0].Set(x1, y0, z0); fAxisTitlePos[1].Set(x0, y1, z0); break; case 1: fAxisTitlePos[0].Set(x1, y0, z0); fAxisTitlePos[1].Set(x0, y1, z0); break; case 2: fAxisTitlePos[0].Set(x0, y1, z0); fAxisTitlePos[1].Set(x1, y0, z0); break; case 3: fAxisTitlePos[0].Set(x1, y1, z0); fAxisTitlePos[1].Set(x0, y0, z0); break; } } //______________________________________________________________________________ void TGLAxisPainterBox::DrawAxis3D(TGLRnrCtx &rnrCtx) { // Draw XYZ axis with bitmap font. // 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); // determine bitmap font size from length of projected vertical GLdouble dn[3]; GLdouble up[3]; gluProject(fAxisTitlePos[2].X(), fAxisTitlePos[2].Y(), fAxis[2]->GetXmin(), mm.Arr(), pm, vp, &dn[0], &dn[1], &dn[2]); gluProject(fAxisTitlePos[2].X(), fAxisTitlePos[2].Y(), fAxis[2]->GetXmax(), 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])); SetLabelPixelFontSize(TMath::CeilNint(len*fAxis[2]->GetLabelSize())); SetTitlePixelFontSize(TMath::CeilNint(len*fAxis[2]->GetTitleSize())); // Z axis // // tickmark vector = 10 pixels left fAxis[2]->SetTickLength(1.); // leave this relative factor neutral TGLVertex3 worldRef(fAxisTitlePos[2].X(), fAxisTitlePos[2].Y(), fAxisTitlePos[2].Z()); RefTMOff(0) = rnrCtx.RefCamera().ViewportDeltaToWorld(worldRef, -10, 0, &mm); SetTMNDim(1); RefDir().Set(0., 0., 1.); SetLabelAlign(TGLFont::kRight, TGLFont::kBottom); glPushMatrix(); glTranslatef(fAxisTitlePos[2].X(), fAxisTitlePos[2].Y(), 0); RefTitlePos().Set(RefTMOff(0).X(), RefTMOff(0).Y(),fAxisTitlePos[2].Z()); PaintAxis(rnrCtx, fAxis[2]); glPopMatrix(); // XY Axis // SetTMNDim(2); RefTMOff(1).Set(0, 0, fAxis[2]->GetXmin()- fAxis[2]->GetXmax()); SetLabelAlign(TGLFont::kCenterH, TGLFont::kBottom); // X glPushMatrix(); RefDir().Set(1, 0, 0); Float_t yOff = fAxis[0]->GetXmax() - fAxis[0]->GetXmin(); yOff *= 0.5f; if (fAxisTitlePos[0].Y() < fAxis[1]->GetXmax()) yOff = -yOff; RefTMOff(0).Set(0, yOff, 0); glTranslatef(0, fAxisTitlePos[0].Y(), fAxisTitlePos[0].Z()); RefTitlePos().Set(fAxisTitlePos[0].X(), yOff*1.5*fAxis[0]->GetTickLength(), 0); PaintAxis(rnrCtx, fAxis[0]); glPopMatrix(); // Y glPushMatrix(); RefDir().Set(0, 1, 0); Float_t xOff = fAxis[1]->GetXmax() - fAxis[1]->GetXmin(); if (fAxisTitlePos[1].X() < fAxis[0]->GetXmax()) xOff = -xOff; RefTMOff(0).Set(xOff, 0, 0); glTranslatef(fAxisTitlePos[1].X(), 0, fAxisTitlePos[1].Z()); RefTitlePos().Set(xOff*1.5*fAxis[1]->GetTickLength(), fAxisTitlePos[1].Y(), 0); PaintAxis(rnrCtx, fAxis[1]); glPopMatrix(); } //______________________________________________________________________________ void TGLAxisPainterBox::PlotStandard( TGLRnrCtx &rnrCtx, const TH1 *histo, const TGLBoundingBox &bbox) { // fAxis[0] = histo->GetXaxis(); fAxis[1] = histo->GetYaxis(); fAxis[2] = histo->GetZaxis(); // fAxis[2]->SetTitle("Z"); // fAxis[2]->SetLabelSize(0.04); // fAxis[2]->SetTitleSize(0.05); Double_t sx = (bbox.XMax() - bbox.XMin()) / (fAxis[0]->GetXmax() - fAxis[0]->GetXmin()); Double_t sy = (bbox.YMax() - bbox.YMin()) / (fAxis[1]->GetXmax() - fAxis[1]->GetXmin()); Double_t sz = (bbox.ZMax() - bbox.ZMin()) / (fAxis[2]->GetXmax() - fAxis[2]->GetXmin()); // draw glPushMatrix(); glScaled(sx, sy, sz); SetAxis3DTitlePos(rnrCtx); DrawAxis3D(rnrCtx); glPopMatrix(); }