// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_hplot #define tools_hplot // Used in exlib::sg::axis. // // Code extracted from ROOT-4.03.02/root/graf/stc/TGaxis.cxx. // Itself built from code extracted from HPLOT. // // Take care, all the below is highly disgusting... // (You can even find.. gotos !) // // Except for the public methods we let the style "as it". #include "mnmx" #include "lina/vec3f" #include "mathd" //pi #include "snpf" #include "out_error" #include #include #include #include #include #include namespace tools { namespace hplot { class _text { public: _text(double aX,double aY, double aAngle,double aSize, const std::string& aString, short aAlign) :fX(aX),fY(aY) ,fAngle(aAngle),fSize(aSize) ,fString(aString),fAlign(aAlign) {} virtual ~_text(){} public: _text(const _text& aFrom) :fX(aFrom.fX),fY(aFrom.fY) ,fAngle(aFrom.fAngle),fSize(aFrom.fSize) ,fString(aFrom.fString) ,fAlign(aFrom.fAlign) {} _text& operator=(const _text& aFrom){ fX = aFrom.fX; fY = aFrom.fY; fAngle = aFrom.fAngle; fSize = aFrom.fSize; fString = aFrom.fString; fAlign = aFrom.fAlign; return *this; } public: double fX; double fY; double fAngle; //Degree double fSize; std::string fString; short fAlign; }; class axis { // Ok, you really want to read all that. You had been warned... enum { TAxis_kTickPlus = (1<<(9)), TAxis_kTickMinus = (1<<(10)), TAxis_kAxisRange = (1<<(11)), TAxis_kCenterTitle = (1<<(12)), TAxis_kCenterLabels = (1<<(14)), //bit 13 is used by TObject TAxis_kRotateTitle = (1<<(15)), TAxis_kPalette = (1<<(16)), TAxis_kNoExponent = (1<<(17)), TAxis_kLabelsHori = (1<<(18)), TAxis_kLabelsVert = (1<<(19)), TAxis_kLabelsDown = (1<<(20)), TAxis_kLabelsUp = (1<<(21)), TAxis_kIsInteger = (1<<(22)), TAxis_kMoreLogLabels = (1<<(23)), TAxis_kDecimals = (1<<(11)) }; //in fBits2 enum { kIsOnHeap = 0x01000000, // object is on heap kNotDeleted = 0x02000000, // object has not been deleted kZombie = 0x04000000, // object ctor failed kBitMask = 0x00ffffff }; static int GetTextFont() { return 132;} static double TMath_ATan2(double y, double x) { if (x != 0) return ::atan2(y, x); if (y == 0) return 0; if (y > 0) return tools::half_pi(); else return -tools::half_pi(); } //static short TMath_Abs(short d) { return (d >= 0) ? d : -d; } static int TMath_Abs(int d) { return (d >= 0) ? d : -d; } //static long TMath_Abs(long d) { return (d >= 0) ? d : -d; } //static float TMath_Abs(float d) { return (d >= 0) ? d : -d; } static double TMath_Abs(double d) { return (d >= 0) ? d : -d; } static void TGaxis_Rotate( double X, double Y, double CFI, double SFI ,double XT, double YT, double &U, double &V) { U = CFI*X-SFI*Y+XT; V = SFI*X+CFI*Y+YT; } public: axis(std::ostream& a_out) :m_out(a_out) //,fMaxDigits(5) ,fBits(kNotDeleted) ,fTickSize(0.03F) ,fLabelOffset(0.005F) ,fLabelSize(0.04F) ,fTitleOffset(1) ,fTitleSize(0.04F) ,fLabelFont(62) {} virtual ~axis(){} private: //to discourage inheriting that. axis(const axis& a_from):m_out(a_from.m_out){} axis& operator=(const axis&){return *this;} public: void set_title(const std::string& aTitle) { fTitle = aTitle; } private: bool testBit(unsigned int f) { return (bool) ((fBits & f) != 0); } static void TGaxis_LabelsLimits(std::ostream& a_out,const char *label, int &first,int &last) { last = int(::strlen(label))-1; for (int i=0; i<=last; i++) { if (::strchr("1234567890-+.", label[i]) ) { first = i; return; } } out_error(a_out,"LabelsLimits", "attempt to draw a blank label"); } static void SETOPT(const std::string& aCHOPT,char aChar,int& aOpt) { aOpt = aCHOPT.find(aChar)!=std::string::npos?1:0; } public: void paint(double xmin, double ymin, double xmax, double ymax, double& wmin,double& wmax, int& ndiv,const std::string& aCHOPT, double gridlength,bool drawGridOnly, std::vector& aLinesAxis, //n*(2+2) std::vector& aLinesGrid, //n*(2+2) std::vector<_text>& aTexts){ // Control function to draw an axis // ================================ // //============> Original authors (O.Couet C.E.Vandoni N.Cremel-Somon) // largely modified and converted to C++ class by Rene Brun // // _Input parameters: // // xmin : X origin coordinate in WC space. // xmax : X end axis coordinate in WC space. // ymin : Y origin coordinate in WC space. // ymax : Y end axis coordinate in WC space. // wmin : Lowest value for the tick mark // labels written on the axis. // wmax : Highest value for the tick mark labels // written on the axis. // ndiv : Number of divisions. // // ndiv=N1 + 100*N2 + 10000*N3 // N1=number of 1st divisions. // N2=number of 2nd divisions. // N3=number of 3rd divisions. // e.g.: // nndi=0 --> no tick marks. // nndi=2 --> 2 divisions, one tick mark in the middle // of the axis. // // chopt : Options (see below). // // chopt='G': loGarithmic scale, default is linear. // chopt='B': Blank axis. Useful to superpose axis. // // Orientation of tick marks on axis. // ---------------------------------- // // Tick marks are normally drawn on the positive side of the axis, // however, if X0=X1, then negative. // // chopt='+': tick marks are drawn on Positive side. (default) // chopt='-': tick mark are drawn on the negative side. // i.e: '+-' --> tick marks are drawn on both sides of the axis. // chopt='U': Unlabeled axis, default is labeled. // // Size of tick marks // ------------------ // By default, tick marks have a length equal to 3 per cent of the // axis length. // When the option "S" is specified, the length of the tick marks // is equal to fTickSize*axis_length, where fTickSize may be set // via TGaxis::SetTickSize. // // Position of labels on axis. // --------------------------- // // Labels are normally drawn on side opposite to tick marks. // However: // // chopt='=': on Equal side // // Orientation of labels on axis. // ------------------------------ // // Labels are normally drawn parallel to the axis. // However if X0=X1, then Orthogonal // if Y0=Y1, then Parallel // // Position of labels on tick marks. // --------------------------------- // // Labels are centered on tick marks. // However , if X0=X1, then they are right adjusted. // // chopt='R': labels are Right adjusted on tick mark. // (default is centered) // chopt='L': labels are Left adjusted on tick mark. // chopt='C': labels are Centered on tick mark. // chopt='M': In the Middle of the divisions. // // Format of labels. // ----------------- // // Blank characters are stripped, and then the // label is correctly aligned. the dot, if last // character of the string, is also stripped, // unless the option "." (a dot, or period) is specified. // if SetDecimals(true) has been called (bit TAxis_kDecimals set). // all labels have the same number of decimals after the "." // The same is true if gStyle->SetStripDecimals(false) has been called. // // In the following, we have some parameters, like // tick marks length and characters height (in percentage // of the length of the axis (WC)) // The default values are as follows: // // Primary tick marks: 3.0 % // Secondary tick marks: 1.5 % // Third order tick marks: .75 % // Characters height for labels: 4% // // Labels offset: 1.0 % // // Optional grid. // -------------- // // chopt='W': cross-Wire // In case of a log axis, the grid is only drawn for the primary // tick marks if the number of secondary and tertiary divisions is 0. // // Axis bining optimization. // ------------------------- // // By default the axis bining is optimized . // // chopt='N': No bining optimization // chopt='I': Integer labelling // // Maximum Number of Digits for the axis labels // -------------------------------------------- // See the static function TGaxis::SetMaxDigits // // Time representation. // -------------------- // // Axis labels may be considered as times, plotted in a defined // time format. // The format is set with SetTimeFormat(). // wmin and wmax are considered as two time values in seconds. // The time axis will be spread around the time offset value (set with // SetTimeOffset() ). Actually it will go from TimeOffset+wmin to // TimeOffset+wmax. // see examples in tutorials timeonaxis.C and timeonaxis2.C // // chopt='t': Plot times with a defined format instead of values // aLinesAxis.clear(); aLinesGrid.clear(); aTexts.clear(); double alfa, beta, ratio1, ratio2, grid_side; double axis_lengthN = 0; double axis_length0 = 0; double axis_length1 = 0; double charheight; double phil, phi, sinphi, cosphi, asinphi, acosphi; double BinLow, BinLow2, BinLow3; double BinHigh, BinHigh2, BinHigh3; double BinWidth, BinWidth2, BinWidth3; double xpl1, xpl2, ypl1, ypl2; double Xtick = 0; double Xtick0, Xtick1, DXtick=0; double Ytick, Ytick0, Ytick1; double Wlabel, DWlabel; double Xlabel, Ylabel; double DXlabel; double X0, X1, Y0, Y1, XX0, XX1, YY0, YY1; XX0 = XX1 = YY0 = YY1 = 0; double Xxmin, Xxmax, Yymin, Yymax; Xxmin = Xxmax = Yymin = Yymax = 0; double XLside,XMside; double WW, AF, RNE; double XX, YY; double Y; double Xtwo; int i, j, k, l, decade, ltick; int Mside, Lside; int IF1, IF2, NA, NF, NCH; int OptionLog,OptionBlank,OptionVert,OptionPlus,OptionMinus; int OptionUnlab,OptionPara; int OptionDown,OptionRight,OptionLeft,OptionCent,OptionEqual; int OptionDecimals=0,OptionDot; int OptionY,OptionText,OptionGrid,OptionSize,OptionNoopt; int OptionInt,OptionM,OptionUp,OptionX; int OptionTime; int first,last; int nbins; int N1Aold = 0; int NN1old = 0; int Xalign,Yalign; int ndyn; char LABEL[256]; char CHTEMP[256]; double rangeOffset = 0; double epsilon = 1e-5; const double kPI = tools::pi(); //GB double textSize = 0.05; //GB short textAlign = 11; //GB BinWidth = 0; //GB BinWidth2 = 0; //GB BinWidth3 = 0; //GB nbins = 0; //GB BinHigh = 0; //GB BinHigh2 = 0; //GB BinHigh3 = 0; //GB BinLow = 0; //GB BinLow2 = 0; //GB BinLow3 = 0; //GB first = 0; //GB last = 0; //GB double rwmi = wmin; double rwma = wmax; //out_error(m_out,"android_debug","start"); bool noExponent = testBit(TAxis_kNoExponent); // If MoreLogLabels = true more Log Intermediate Labels are drawn. bool MoreLogLabels = testBit(TAxis_kMoreLogLabels); // the following parameters correspond to the pad range in NDC // and the WC coordinates in the pad double padh = 1;//FIXME gPad->GetWh()*gPad->GetAbsHNDC(); double RWxmin = 0; double RWxmax = 1; double RWymin = 0; double RWymax = 1; SETOPT(aCHOPT,'G',OptionLog); SETOPT(aCHOPT,'B',OptionBlank); SETOPT(aCHOPT,'V',OptionVert); SETOPT(aCHOPT,'+',OptionPlus); SETOPT(aCHOPT,'-',OptionMinus); SETOPT(aCHOPT,'U',OptionUnlab); SETOPT(aCHOPT,'P',OptionPara); SETOPT(aCHOPT,'O',OptionDown); SETOPT(aCHOPT,'R',OptionRight); SETOPT(aCHOPT,'L',OptionLeft); SETOPT(aCHOPT,'C',OptionCent); SETOPT(aCHOPT,'=',OptionEqual); SETOPT(aCHOPT,'Y',OptionY); SETOPT(aCHOPT,'T',OptionText); SETOPT(aCHOPT,'W',OptionGrid); SETOPT(aCHOPT,'S',OptionSize); SETOPT(aCHOPT,'N',OptionNoopt); SETOPT(aCHOPT,'I',OptionInt); SETOPT(aCHOPT,'M',OptionM); SETOPT(aCHOPT,'0',OptionUp); SETOPT(aCHOPT,'X',OptionX); SETOPT(aCHOPT,'t',OptionTime); SETOPT(aCHOPT,'.',OptionDot); if (testBit(TAxis_kTickPlus)) OptionPlus = 2; if (testBit(TAxis_kTickMinus)) OptionMinus = 2; if (testBit(TAxis_kCenterLabels)) OptionM = 1; if (testBit(TAxis_kDecimals)) OptionDecimals = 1; /*FIXME if (fAxis) { if (fAxis->GetLabels()) { OptionM = 1; OptionText = 1; ndiv = fAxis->GetLast()-fAxis->GetFirst()+1; } }*/ // Set the grid length if (OptionGrid) { if (gridlength == 0) gridlength = 0.8; /*FIXME linegrid = new TLine(); linegrid->SetLineColor(gStyle->GetGridColor()); if (linegrid->GetLineColor() == 0) linegrid->SetLineColor(GetLineColor()); linegrid->SetLineStyle(gStyle->GetGridStyle()); linegrid->SetLineWidth(gStyle->GetGridWidth());*/ } if (OptionTime) { //printf("debug : SbAxisHPLOT::paint : fTimeFormat : \"%s\"\n", // fTimeFormat.c_str()); } //out_error(m_out,"android_debug","0000"); // Determine time format std::string timeformat; std::string::size_type IdF = fTimeFormat.find("%F"); if (IdF!=std::string::npos) { timeformat = fTimeFormat.substr(0,IdF); } else { timeformat = fTimeFormat; } //out_error(m_out,"android_debug","0001"); // determine the time offset and correct for time offset not being integer double timeoffset = 0; if (OptionTime) { if (IdF!=std::string::npos) { int LnF = int(fTimeFormat.size()); std::string stringtimeoffset = fTimeFormat.substr(IdF+2,LnF-(IdF+2)); int yy, mm, dd, hh, mi, ss; if (::sscanf(stringtimeoffset.c_str(), "%d-%d-%d %d:%d:%d", &yy, &mm, &dd, &hh, &mi, &ss) == 6) { struct tm tp; struct tm* tptest; time_t timeoffsettest; tp.tm_year = yy-1900; tp.tm_mon = mm-1; tp.tm_mday = dd; tp.tm_hour = hh; tp.tm_min = mi; tp.tm_sec = ss; tp.tm_isdst = 0; // daylight saving time is not in effect (see mktime man pages) timeoffset = double(mktime(&tp)); // have to correct this time to go back to UTC timeoffsettest = (time_t)((long)timeoffset); tptest = gmtime(&timeoffsettest); timeoffset += timeoffsettest - mktime(tptest); // Add the time offset's decimal part if it is there std::string::size_type Ids = stringtimeoffset.find("s"); if (Ids != std::string::npos) { float dp; size_t Lns = stringtimeoffset.size(); std::string sdp = stringtimeoffset.substr(Ids+1,Lns-(Ids+1)); ::sscanf(sdp.c_str(),"%g",&dp); timeoffset += dp; } // if OptionTime = 2 gmtime will be used instead of localtime if (stringtimeoffset.find("GMT")!=std::string::npos) OptionTime =2; } else { out_error(m_out,"PaintAxis", "Time offset has not the right format"); } } else { out_error(m_out,"PaintAxis", "%%F not found in fTimeFormat."); //FIXME timeoffset = gStyle->GetTimeOffset(); } wmin += timeoffset - (int)(timeoffset); wmax += timeoffset - (int)(timeoffset); // correct for time offset at a good limit (min, hour, // day, month, year) struct tm* tp0; time_t timetp = (time_t)((long)(timeoffset)); double range = wmax - wmin; long rangeBase = 60; if (range>60) rangeBase = 60*20; // minutes if (range>3600) rangeBase = 3600*20; // hours if (range>86400) rangeBase = 86400*20; // days if (range>2419200) rangeBase = 31556736; // months (average # days) rangeOffset = (double) ((long)(timeoffset)%rangeBase); if (range>31536000) { tp0 = gmtime(&timetp); tp0->tm_mon = 0; tp0->tm_mday = 1; tp0->tm_hour = 0; tp0->tm_min = 0; tp0->tm_sec = 0; tp0->tm_isdst = 0; // daylight saving time is not in effect (see mktime man pages) rangeBase = long(timetp-mktime(tp0)); // years rangeOffset = (double) (rangeBase); } wmax += rangeOffset; wmin += rangeOffset; } // Determine number of divisions 1, 2 and 3 int N1A = ndiv%100; int N2A = (ndiv%10000 - N1A)/100; int N3A = ndiv/10000; int NN3 = tools::mx(N3A,1); int NN2 = tools::mx(N2A,1)*NN3; int NN1 = tools::mx(N1A,1)*NN2+1; int Nticks= NN1; // Axis bining optimization is ignored if: // - the first and the last label are equal // - the number of divisions is 0 // - less than 1 primary division is requested // - logarithmic scale is requested if (wmin == wmax || ndiv == 0 || N1A <= 1 || OptionLog) { OptionNoopt = 1; OptionInt = 0; } // Axis bining optimization if ( (wmax-wmin) < 1 && OptionInt) { out_error(m_out,"PaintAxis", "option I not available"); OptionInt = 0; } //out_error(m_out,"android_debug","0002"); if (!OptionNoopt || OptionInt ) { // Primary divisions optimization // When integer labelling is required, Optimize is invoked first // and only if the result is not an integer labelling, AdjustBinSize // is invoked. optimizeLimits(wmin,wmax,N1A, BinLow,BinHigh,nbins,BinWidth, aCHOPT); if (OptionInt) { if (BinLow != double(int(BinLow)) || BinWidth != double(int(BinWidth))) { adjustBinSize(wmin,wmax,N1A,BinLow,BinHigh,nbins,BinWidth); } } if ((wmin-BinLow) > epsilon) { BinLow += BinWidth; nbins--; } if ((BinHigh-wmax) > epsilon) { BinHigh -= BinWidth; nbins--; } if (xmax == xmin) { double rtyw = (ymax-ymin)/(wmax-wmin); Xxmin = xmin; Xxmax = xmax; Yymin = rtyw*(BinLow-wmin) + ymin; Yymax = rtyw*(BinHigh-wmin) + ymin; } else { double rtxw = (xmax-xmin)/(wmax-wmin); Xxmin = rtxw*(BinLow-wmin) + xmin; Xxmax = rtxw*(BinHigh-wmin) + xmin; if (ymax == ymin) { Yymin = ymin; Yymax = ymax; } else { alfa = (ymax-ymin)/(xmax-xmin); beta = (ymin*xmax-ymax*xmin)/(xmax-xmin); Yymin = alfa*Xxmin + beta; Yymax = alfa*Xxmax + beta; } } /*GB if (fFunction) { Yymin = ymin; Yymax = ymax; Xxmin = xmin; Xxmax = xmax; } else*/ { wmin = BinLow; wmax = BinHigh; } // Secondary divisions optimization int NB2 = N2A; if (!OptionNoopt && N2A > 1 && BinWidth > 0) { optimizeLimits(wmin,wmin+BinWidth,N2A, BinLow2,BinHigh2,NB2,BinWidth2, aCHOPT); } // Tertiary divisions optimization int NB3 = N3A; if (!OptionNoopt && N3A > 1 && BinWidth2 > 0) { optimizeLimits(BinLow2,BinLow2+BinWidth2,N3A, BinLow3,BinHigh3,NB3,BinWidth3, aCHOPT); } N1Aold = N1A; NN1old = NN1; N1A = nbins; NN3 = tools::mx(NB3,1); NN2 = tools::mx(NB2,1)*NN3; NN1 = tools::mx(N1A,1)*NN2+1; Nticks = NN1; } //out_error(m_out,"android_debug","0003"); // Coordinates are normalized ratio1 = 1/(RWxmax-RWxmin); ratio2 = 1/(RWymax-RWymin); X0 = ratio1*(xmin-RWxmin); X1 = ratio1*(xmax-RWxmin); Y0 = ratio2*(ymin-RWymin); Y1 = ratio2*(ymax-RWymin); if (!OptionNoopt || OptionInt ) { XX0 = ratio1*(Xxmin-RWxmin); XX1 = ratio1*(Xxmax-RWxmin); YY0 = ratio2*(Yymin-RWymin); YY1 = ratio2*(Yymax-RWymin); } //out_error(m_out,"android_debug","0004"); if ((X0 == X1) && (Y0 == Y1)) { out_error(m_out,"PaintAxis", "length of axis is 0"); return; } // Return wmin, wmax and the number of primary divisions if (OptionX) { ndiv = N1A; return; } int maxDigits = 5; //FIXME if (fAxis) maxDigits = fMaxDigits; /*FIXME TLatex *textaxis = new TLatex(); lineaxis->SetLineColor(GetLineColor()); lineaxis->SetLineStyle(1); lineaxis->SetLineWidth(GetLineWidth()); textaxis->SetTextColor(GetTextColor()); textaxis->SetTextFont(GetTextFont()); if (!gPad->IsBatch()) { float chupxvsav, chupyvsav; gVirtualX->GetCharacterUp(chupxvsav, chupyvsav); gVirtualX->SetClipOFF(gPad->GetCanvasID()); } */ // Compute length of axis double axis_length = ::sqrt((X1-X0)*(X1-X0)+(Y1-Y0)*(Y1-Y0)); if (axis_length == 0) { out_error(m_out,"PaintAxis", "length of axis is 0"); return; //goto L210; } //out_error(m_out,"android_debug","0005"); if (!OptionNoopt || OptionInt) { axis_lengthN = ::sqrt((XX1-XX0)*(XX1-XX0)+(YY1-YY0)*(YY1-YY0)); axis_length0 = ::sqrt((XX0-X0)*(XX0-X0)+(YY0-Y0)*(YY0-Y0)); axis_length1 = ::sqrt((X1-XX1)*(X1-XX1)+(Y1-YY1)*(Y1-YY1)); if (axis_lengthN < epsilon) { OptionNoopt = 1; OptionInt = 0; wmin = rwmi; wmax = rwma; N1A = N1Aold; NN1 = NN1old; Nticks = NN1; if (OptionTime) { wmin += timeoffset - (int)(timeoffset) + rangeOffset; wmax += timeoffset - (int)(timeoffset) + rangeOffset; } } } //out_error(m_out,"android_debug","0006"); if (X0 == X1) { phi = 0.5*kPI; phil = phi; } else { phi = TMath_ATan2((Y1-Y0),(X1-X0)); int px0 = 0;//FIXME gPad->UtoPixel(X0); int py0 = 0;//FIXME gPad->VtoPixel(Y0); int px1 = 0;//FIXME gPad->UtoPixel(X1); int py1 = 0;//FIXME gPad->VtoPixel(Y1); if (X0 < X1) phil = TMath_ATan2(double(py0-py1), double(px1-px0)); else phil = TMath_ATan2(double(py1-py0), double(px0-px1)); } cosphi = ::cos(phi); sinphi = ::sin(phi); acosphi = TMath_Abs(cosphi); asinphi = TMath_Abs(sinphi); if (acosphi <= epsilon) { acosphi = 0; cosphi = 0; } if (asinphi <= epsilon) { asinphi = 0; sinphi = 0; } //out_error(m_out,"android_debug","0007"); // Mside positive, tick marks on positive side // Mside negative, tick marks on negative side // Mside zero, tick marks on both sides // Default is positive except for vertical axis Mside=1; if (X0 == X1 && Y1 > Y0) Mside = -1; if (OptionPlus) Mside = 1; if (OptionMinus) Mside = -1; if (OptionPlus && OptionMinus) Mside = 0; XMside = Mside; Lside = -Mside; if (OptionEqual) Lside = Mside; if (OptionPlus && OptionMinus) { Lside = -1; if (OptionEqual) Lside=1; } XLside = Lside; // Tick marks size double tick_side; if(XMside >= 0) tick_side = 1; else tick_side = -1; double atick[3]; if (OptionSize) atick[0] = tick_side*axis_length*fTickSize; else atick[0] = tick_side*axis_length*0.03; atick[1] = 0.5*atick[0]; atick[2] = 0.5*atick[1]; // Set the side of the grid if ((X0 == X1) && (Y1 > Y0)) grid_side =-1; else grid_side = 1; //out_error(m_out,"android_debug","0008"); // Compute Values if Function is given /*GB if(fFunction) { rwmi = fFunction->Eval(wmin); rwma = fFunction->Eval(wmax); if(rwmi > rwma) { double t = rwma; rwma = rwmi; rwmi = t; } }*/ // Draw the axis if needed... if (!OptionBlank) { xpl1 = X0; xpl2 = X1; ypl1 = Y0; ypl2 = Y1; aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } //out_error(m_out,"android_debug","0009"); // No bining if (ndiv == 0) return; //goto L210; if (wmin == wmax) { out_error(m_out,"PaintAxis", "wmin (%f) == wmax (%f)", wmin, wmax); return; //goto L210; } // Draw axis title if it exists if (!drawGridOnly && fTitle.size()) { textSize = fTitleSize; charheight = fTitleSize; if ((GetTextFont() % 10) > 2) { //FIXME charheight = charheight/gPad->GetWh(); } double toffset = fTitleOffset; if (toffset < 0.1) toffset = 1; if (X1 == X0) Ylabel = XLside*1.6*charheight*toffset; else Ylabel = XLside*1.3*charheight*toffset; if (Y1 == Y0) Ylabel = XLside*1.6*charheight*toffset; double axispos; if (testBit(TAxis_kCenterTitle)) axispos = 0.5*axis_length; else axispos = axis_length; if (testBit(TAxis_kRotateTitle)) { if (X1 >= X0) { if (testBit(TAxis_kCenterTitle)) textAlign = 22; else textAlign = 12; TGaxis_Rotate(axispos,Ylabel,cosphi,sinphi,X0,Y0,xpl1,ypl1); } else { if (testBit(TAxis_kCenterTitle)) textAlign = 22; else textAlign = 32; TGaxis_Rotate(axispos,Ylabel,cosphi,sinphi,X0,Y0,xpl1,ypl1); } out_error(m_out,"PaintAxis","debug : texts : dummy : 000\n"); aTexts.push_back(_text(xpl1,ypl1, phil=(kPI+phil)*180/kPI, fTitleSize, fTitle,textAlign)); } else { if (X1 >= X0) { if (testBit(TAxis_kCenterTitle)) textAlign = 22; else textAlign = 32; TGaxis_Rotate(axispos,Ylabel,cosphi,sinphi,X0,Y0,xpl1,ypl1); } else { if (testBit(TAxis_kCenterTitle)) textAlign = 22; else textAlign = 12; TGaxis_Rotate(axispos,Ylabel,cosphi,sinphi,X0,Y0,xpl1,ypl1); } aTexts.push_back(_text(xpl1,ypl1, phil*180/kPI,fTitleSize, fTitle,textAlign)); } } //out_error(m_out,"android_debug","0010"); // Labels preparation: // Get character height // Compute the labels orientation in case of overlaps // with alphanumeric labels for horizontal axis). charheight = fLabelSize; if (OptionText) charheight *= 0.66666; //FIXME textaxis->SetTextFont(fLabelFont); //FIXME textaxis->SetTextColor(GetLabelColor()); textSize = charheight; //FIXME textaxis->SetTextAngle(GetTextAngle()); if (fLabelFont%10 > 2) { charheight /= padh; } if (!OptionUp && !OptionDown && !OptionY) { if (!drawGridOnly && OptionText && ((ymin == ymax) || (xmin == xmax))) { textAlign = 32; OptionText = 2; //int nl = 0;//FIXME fAxis->GetLast()-fAxis->GetFirst()+1; //double angle = 0; out_error(m_out,"PaintAxis","debug : FIXME : 000\n"); /*FIXME for (i=fAxis->GetFirst(); i<=fAxis->GetLast(); i++) { textaxis->SetText(0,0,fAxis->GetBinLabel(i)); if (textaxis->GetXsize() < (xmax-xmin)/nl) continue; angle = -20; break; } for (i=fAxis->GetFirst(); i<=fAxis->GetLast(); i++) { if ((!::strcmp(fAxis->GetName(),"xaxis") && !gPad->testBit(kHori)) ||(!::strcmp(fAxis->GetName(),"yaxis") && gPad->testBit(kHori))) { if (nl > 50) angle = 90; if (fAxis->testBit(TAxis_kLabelsHori)) angle = 0; if (fAxis->testBit(TAxis_kLabelsVert)) angle = 90; if (fAxis->testBit(TAxis_kLabelsUp)) angle = 20; if (fAxis->testBit(TAxis_kLabelsDown)) angle =-20; if (angle== 0) textAlign = 23; if (angle== -20) textAlign = 12; out_error(m_out,"PaintAxis","debug : texts : dummy : 002\n"); textaxis->PaintLatex( fAxis->GetBinCenter(i), gPad->GetUymin() - 3*fAxis->GetLabelOffset()* (gPad->GetUymax()-gPad->GetUymin()), angle, charheight, fAxis->GetBinLabel(i)); } else if ((!::strcmp(fAxis->GetName(),"yaxis") && !gPad->testBit(kHori)) || (!::strcmp(fAxis->GetName(),"xaxis") && gPad->testBit(kHori))) { out_error(m_out,"PaintAxis","debug : texts : dummy : 003\n"); textaxis->PaintLatex( gPad->GetUxmin() - 3*fAxis->GetLabelOffset()* (gPad->GetUxmax()-gPad->GetUxmin()), fAxis->GetBinCenter(i), 0, charheight, fAxis->GetBinLabel(i)); } else { out_error(m_out,"PaintAxis","debug : texts : dummy : 004\n"); textaxis->PaintLatex( xmin - 3*fAxis->GetLabelOffset()* (gPad->GetUxmax()-gPad->GetUxmin()), ymin +(i-0.5)*(ymax-ymin)/nl, 0, charheight, fAxis->GetBinLabel(i)); } }*/ } } //out_error(m_out,"android_debug","0011"); // Now determine orientation of labels on axis /*FIXME if (!gPad->IsBatch()) { if (cosphi > 0) gVirtualX->SetCharacterUp(-sinphi,cosphi); else gVirtualX->SetCharacterUp(sinphi,-cosphi); if (X0 == X1) gVirtualX->SetCharacterUp(0,1); if (OptionVert) gVirtualX->SetCharacterUp(0,1); if (OptionPara) gVirtualX->SetCharacterUp(-sinphi,cosphi); if (OptionDown) gVirtualX->SetCharacterUp(cosphi,sinphi); }*/ // Now determine text alignment Xalign = 2; Yalign = 1; if (X0 == X1) Xalign = 3; if (Y0 != Y1) Yalign = 2; if (OptionCent) Xalign = 2; if (OptionRight) Xalign = 3; if (OptionLeft) Xalign = 1; if (TMath_Abs(cosphi) > 0.9) { Xalign = 2; } else { if (cosphi*sinphi > 0) Xalign = 1; if (cosphi*sinphi < 0) Xalign = 3; } textAlign = 10*Xalign+Yalign; //out_error(m_out,"android_debug","0012"); // Position of labels in Y if (X0 == X1) { if (OptionPlus && !OptionMinus) { if (OptionEqual) Ylabel = fLabelOffset/2 + atick[0]; else Ylabel = -fLabelOffset; } else { Ylabel = fLabelOffset; if (Lside < 0) Ylabel += atick[0]; } } else if (Y0 == Y1) { if (OptionMinus && !OptionPlus) { Ylabel = fLabelOffset+0.5*fLabelSize; Ylabel += TMath_Abs(atick[0]); } else { Ylabel = -fLabelOffset; if (Mside <= 0) Ylabel -= TMath_Abs(atick[0]); } if (OptionLog) Ylabel -= 0.5*charheight; } else { if (Mside+Lside >= 0) Ylabel = fLabelOffset; else Ylabel = -fLabelOffset; } if (OptionText) Ylabel /= 2; //out_error(m_out,"android_debug","0013"); // Draw the linear tick marks if needed... if (!OptionLog) { if (ndiv) { /*GB if (fFunction) { if (OptionNoopt && !OptionInt) { DXtick=(BinHigh-BinLow)/double(Nticks-1); } else { DXtick=(BinHigh-BinLow)/double(Nticks-1); } } else */ { if (OptionNoopt && !OptionInt) DXtick=axis_length/double(Nticks-1); else DXtick=axis_lengthN/double(Nticks-1); } for (k=0;kEval(xx)-rwmi; Xtick = zz* axis_length / TMath_Abs(rwma-rwmi); } else */ { Xtick = double(k)*DXtick; } Ytick = 0; if (!Mside) Ytick -= atick[ltick]; if ( OptionNoopt && !OptionInt) { TGaxis_Rotate(Xtick,Ytick,cosphi,sinphi,X0,Y0,xpl2,ypl2); TGaxis_Rotate(Xtick,atick[ltick],cosphi,sinphi,X0,Y0,xpl1,ypl1); } else { TGaxis_Rotate(Xtick,Ytick,cosphi,sinphi,XX0,YY0,xpl2,ypl2); TGaxis_Rotate(Xtick,atick[ltick],cosphi,sinphi,XX0,YY0,xpl1,ypl1); } if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { if (Mside) { xpl1 = xpl2; if (cosphi > 0) ypl1 = ypl2 + atick[ltick]; else ypl1 = ypl2 - atick[ltick]; } else { xpl1 = 0.5*(xpl1 + xpl2); xpl2 = xpl1; ypl1 = 0.5*(ypl1 + ypl2) + atick[ltick]; ypl2 = 0.5*(ypl1 + ypl2) - atick[ltick]; } } } if (!drawGridOnly) { aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } if (OptionGrid) { if (ltick == 0) { if (OptionNoopt && !OptionInt) { TGaxis_Rotate(Xtick,0,cosphi,sinphi,X0,Y0 ,xpl2,ypl2); TGaxis_Rotate (Xtick,grid_side*gridlength,cosphi,sinphi,X0,Y0, xpl1,ypl1); } else { TGaxis_Rotate(Xtick,0,cosphi ,sinphi,XX0,YY0,xpl2,ypl2); TGaxis_Rotate (Xtick,grid_side*gridlength ,cosphi,sinphi,XX0,YY0, xpl1,ypl1); } aLinesGrid.push_back((float)xpl1); aLinesGrid.push_back((float)ypl1); aLinesGrid.push_back((float)xpl2); aLinesGrid.push_back((float)ypl2); } } } Xtick0 = 0; Xtick1 = Xtick; if ((!OptionNoopt || OptionInt) && axis_length0) { int Nticks0; /*GB if (fFunction) Nticks0 = int((BinLow-wmin)/DXtick); else */ Nticks0 = int(axis_length0/DXtick); if (Nticks0 > 1000) Nticks0 = 1000; for (k=0; k<=Nticks0; k++) { ltick = 2; if (k%NN3 == 0) ltick = 1; if (k%NN2 == 0) ltick = 0; Ytick0 = 0; if (!Mside) Ytick0 -= atick[ltick]; /*GB if (fFunction) { Xtick0 = (fFunction->Eval(BinLow - double(k)*DXtick)-rwmi) * axis_length / TMath_Abs(rwma-rwmi); }*/ TGaxis_Rotate(Xtick0,Ytick0,cosphi,sinphi,XX0,YY0 ,xpl2,ypl2); TGaxis_Rotate(Xtick0,atick[ltick],cosphi,sinphi,XX0,YY0 ,xpl1,ypl1); if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { if (Mside) { xpl1 = xpl2; if (cosphi > 0) ypl1 = ypl2 + atick[ltick]; else ypl1 = ypl2 - atick[ltick]; } else { xpl1 = 0.5*(xpl1 + xpl2); xpl2 = xpl1; ypl1 = 0.5*(ypl1 + ypl2) + atick[ltick]; ypl2 = 0.5*(ypl1 + ypl2) - atick[ltick]; } } } if(!drawGridOnly) { aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } if (OptionGrid) { if (ltick == 0) { TGaxis_Rotate(Xtick0,0,cosphi,sinphi,XX0,YY0,xpl2,ypl2); TGaxis_Rotate (Xtick0,grid_side*gridlength,cosphi,sinphi,XX0,YY0, xpl1,ypl1); aLinesGrid.push_back((float)xpl1); aLinesGrid.push_back((float)ypl1); aLinesGrid.push_back((float)xpl2); aLinesGrid.push_back((float)ypl2); } } Xtick0 -= DXtick; } } if ((!OptionNoopt || OptionInt) && axis_length1) { int Nticks1; /*GB if (fFunction) Nticks1 = int((wmax-BinHigh)/DXtick); else */ Nticks1 = int(axis_length1/DXtick); if (Nticks1 > 1000) Nticks1 = 1000; for (k=0; k<=Nticks1; k++) { ltick = 2; if (k%NN3 == 0) ltick = 1; if (k%NN2 == 0) ltick = 0; Ytick1 = 0; if (!Mside) Ytick1 -= atick[ltick]; /*GB if (fFunction) { Xtick1 = (fFunction->Eval(BinHigh + double(k)*DXtick)-rwmi) * axis_length / TMath_Abs(rwma-rwmi); }*/ TGaxis_Rotate(Xtick1,Ytick1,cosphi,sinphi,XX0,YY0 ,xpl2,ypl2); TGaxis_Rotate(Xtick1,atick[ltick],cosphi,sinphi,XX0,YY0 ,xpl1,ypl1); if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { if (Mside) { xpl1 = xpl2; if (cosphi > 0) ypl1 = ypl2 + atick[ltick]; else ypl1 = ypl2 - atick[ltick]; } else { xpl1 = 0.5*(xpl1 + xpl2); xpl2 = xpl1; ypl1 = 0.5*(ypl1 + ypl2) + atick[ltick]; ypl2 = 0.5*(ypl1 + ypl2) - atick[ltick]; } } } if(!drawGridOnly) { aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } if (OptionGrid) { if (ltick == 0) { TGaxis_Rotate(Xtick1,0,cosphi,sinphi,XX0,YY0 ,xpl2,ypl2); TGaxis_Rotate (Xtick1,grid_side*gridlength,cosphi,sinphi,XX0,YY0, xpl1,ypl1); aLinesGrid.push_back((float)xpl1); aLinesGrid.push_back((float)ypl1); aLinesGrid.push_back((float)xpl2); aLinesGrid.push_back((float)ypl2); } } Xtick1 += DXtick; } } } } //out_error(m_out,"android_debug","0014"); // Draw the numeric labels if needed... if (!drawGridOnly && !OptionUnlab) { if (!OptionLog) { if (N1A) { // Spacing of labels if ((wmin == wmax) || (ndiv == 0)) { out_error(m_out,"PaintAxis", "wmin (%f) == wmax (%f), or ndiv == 0", wmin, wmax); return; //goto L210; } Wlabel = wmin; DWlabel = (wmax-wmin)/double(N1A); if (OptionNoopt && !OptionInt) DXlabel = axis_length/double(N1A); else DXlabel = axis_lengthN/double(N1A); char CHCODED[8]; int NEXE = 0; bool FLEXE = false; if (!OptionText && !OptionTime) { // We have to decide what format to generate // for numeric labels only) // Test the magnitude, decide format FLEXE = false; NEXE = 0; bool FLEXPO = false; bool FLEXNE = false; WW = tools::mx(TMath_Abs(wmin),TMath_Abs(wmax)); // First case : (wmax-wmin)/N1A less than 0.001 // 0.001 fMaxDigits of 5 (fMaxDigits) characters). // Then we use x 10 n // format. If AF >=0 x10 n cannot be used double xmicros = 0.00099; if (maxDigits) xmicros = ::pow(10.,-maxDigits); if (!noExponent && (TMath_Abs(wmax-wmin)/double(N1A)) < xmicros) { AF = ::log10(WW) + epsilon; if (AF < 0) { FLEXE = true; NEXE = int(AF); int IEXE = TMath_Abs(NEXE); if (IEXE%3 == 1) IEXE += 2; else if(IEXE%3 == 2) IEXE += 1; if (NEXE < 0) NEXE = -IEXE; else NEXE = IEXE; Wlabel = Wlabel*::pow(10.,IEXE); DWlabel = DWlabel*::pow(10.,IEXE); IF1 = maxDigits; IF2 = maxDigits-2; goto L110; } } if (WW >= 1) AF = ::log10(WW); else AF = ::log10(WW*0.0001); AF += epsilon; NF = int(AF)+1; if (!noExponent && NF > maxDigits) FLEXPO = true; if (!noExponent && NF < -maxDigits) FLEXNE = true; // Use x 10 n format. (only powers of 3 allowed) if (FLEXPO) { FLEXE = true; while (1) { NEXE++; WW /= 10; Wlabel /= 10; DWlabel /= 10; if (NEXE%3 == 0 && WW <= ::pow(10.,maxDigits-1)) break; } } if (FLEXNE) { FLEXE = true; RNE = 1/::pow(10.,maxDigits-2); while (1) { NEXE--; WW *= 10; Wlabel *= 10; DWlabel *= 10; if (NEXE%3 == 0 && WW >= RNE) break; } } NA = 0; for (i=maxDigits-1; i>0; i--) { if (TMath_Abs(WW) < ::pow(10.,i)) NA = maxDigits-i; } ndyn = N1A; while (ndyn) { double wdyn = TMath_Abs((wmax-wmin)/ndyn); if (wdyn <= 0.999 && NA < maxDigits-2) { NA++; ndyn /= 10; } else break; } IF2 = NA; IF1 = tools::mx(NF+NA,maxDigits)+1; L110: if (tools::mn(wmin,wmax) < 0)IF1 = IF1+1; IF1 = tools::mn(IF1,32); // In some cases, IF1 and IF2 are too small.... while (DWlabel < ::pow(10.,-IF2)) { IF1++; IF2++; } //char* CODED = &CHCODED[0]; //GB : comment out. if (IF1 > 14) IF1=14; if (IF2 > 14) IF2=14; if(IF2)snpf(CHCODED,sizeof(CHCODED),"%%%d.%df",IF1,IF2); else snpf(CHCODED,sizeof(CHCODED),"%%%d.%df",IF1+1,1); } // We draw labels snpf(CHTEMP,sizeof(CHTEMP),"%g",DWlabel); size_t ndecimals = 0; if (OptionDecimals) { char *dot = ::strchr(CHTEMP,'.'); if (dot) ndecimals = CHTEMP + ::strlen(CHTEMP) -dot; } int Nlabels; if (OptionM) Nlabels = N1A-1; else Nlabels = N1A; double wTimeIni = Wlabel; for ( k=0; k<=Nlabels; k++) { /*FIXME if (fFunction) { double xx = BinLow+double(k*NN2)*DXtick; double zz = fFunction->Eval(xx)-rwmi; Wlabel = xx; Xlabel = zz* axis_length / TMath_Abs(rwma-rwmi); } else */{ Xlabel = DXlabel*k; } if (OptionM) Xlabel += 0.5*DXlabel; if (!OptionText && !OptionTime) { snpf(LABEL,sizeof(LABEL),&CHCODED[0],Wlabel); LABEL[28] = 0; Wlabel += DWlabel; TGaxis_LabelsLimits(m_out,LABEL,first,last); //Eliminate blanks if (LABEL[first] == '.') { //check if '.' is preceeded by a digit ::strcpy(CHTEMP, "0"); ::strcat(CHTEMP, &LABEL[first]); ::strcpy(LABEL, CHTEMP); first = 1; last = int(::strlen(LABEL)); } if (LABEL[first] == '-' && LABEL[first+1] == '.') { ::strcpy(CHTEMP, "-0"); ::strcat(CHTEMP, &LABEL[first+1]); ::strcpy(LABEL, CHTEMP); first = 1; last = int(::strlen(LABEL)); } // We eliminate the non significant 0 after '.' if (ndecimals) { char *adot = ::strchr(LABEL,'.'); if (adot) adot[ndecimals] = 0; } else { while (LABEL[last] == '0') { LABEL[last] = 0; last--;} } // We eliminate the dot, unless dot is forced. if (LABEL[last] == '.') { if (!OptionDot) { LABEL[last] = 0; last--;} } } // Generate the time labels if (OptionTime) { double timed = Wlabel + (int)(timeoffset) - rangeOffset; time_t timelabel = (time_t)((long)(timed)); struct tm* utctis; if (OptionTime == 1) { utctis = localtime(&timelabel); } else { utctis = gmtime(&timelabel); } std::string timeformattmp; if (timeformat.size() < 220) timeformattmp = timeformat; else timeformattmp = "#splitline{Format}{too long}"; // Appends fractionnal part if seconds displayed if (DWlabel<0.9) { double tmpdb; size_t tmplast; snpf(LABEL,sizeof(LABEL),"%%S%7.5f",modf(timed,&tmpdb)); tmplast = ::strlen(LABEL)-1; // We eliminate the non significiant 0 after '.' while (LABEL[tmplast] == '0') { LABEL[tmplast] = 0; tmplast--; } //FIXME timeformattmp.ReplaceAll("%S",LABEL); // Replace the "0." at the begining by "s" //FIXME timeformattmp.ReplaceAll("%S0.","%Ss"); } ::strftime(LABEL,256,timeformattmp.c_str(),utctis); ::strcpy(CHTEMP,&LABEL[0]); first = 0; last=int(::strlen(LABEL))-1; Wlabel = wTimeIni + (k+1)*DWlabel; } // We generate labels (numeric or alphanumeric). if (OptionNoopt && !OptionInt) TGaxis_Rotate (Xlabel,Ylabel,cosphi,sinphi,X0,Y0,XX,YY); else TGaxis_Rotate (Xlabel,Ylabel,cosphi,sinphi,XX0,YY0,XX,YY); if (Y0 == Y1 && !OptionDown && !OptionUp) { YY -= 0.80*charheight; } if (OptionVert) { if (X0 != X1 && Y0 != Y1) { if (OptionNoopt && !OptionInt) TGaxis_Rotate (Xlabel,0,cosphi,sinphi,X0,Y0,XX,YY); else TGaxis_Rotate (Xlabel,0,cosphi,sinphi,XX0,YY0,XX,YY); if (cosphi > 0 ) YY += Ylabel; if (cosphi < 0 ) YY -= Ylabel; } } if (!OptionY || (X0 == X1)) { if (!OptionText) { if (first > last) ::strcpy(CHTEMP, " "); else ::strcpy(CHTEMP, &LABEL[first]); aTexts.push_back(_text(XX,YY, 0,textSize,CHTEMP, textAlign)); } else { if (OptionText == 1) { out_error(m_out,"PaintAxis","debug : texts : dummy : 006\n"); /*textaxis->PaintLatex (gPad->GetX1() + XX*(gPad->GetX2() - gPad->GetX1()), gPad->GetY1() + YY*(gPad->GetY2() - gPad->GetY1()), 0, textaxis->GetTextSize(), fAxis->GetBinLabel(k+fAxis->GetFirst()));*/ } } } else { // Text alignment is down int LNLEN = 0; if (!OptionText) LNLEN = last-first+1; else { int NHILAB = 0; if (k+1 > NHILAB) LNLEN = 0; } for ( l=1; l<=LNLEN; l++) { if (!OptionText) *CHTEMP = LABEL[first+l-2]; else { if (LNLEN == 0) ::strcpy(CHTEMP, " "); else ::strcpy(CHTEMP, "1"); } aTexts.push_back(_text(XX,YY, 0,textSize,CHTEMP, textAlign)); YY -= charheight*1.3; } } } // We use the format x 10 ** n if (FLEXE && !OptionText && NEXE) { //G.Barrand ::sprintf(LABEL,"#times10^{%d}", NEXE); snpf(LABEL,sizeof(LABEL), "x10^%d!", NEXE); //G.Barrand : PAW encoding. double Xfactor, Yfactor; if (X0 != X1) { Xfactor = X1-X0+0.1*charheight; Yfactor = 0; } else { Xfactor = Y1-Y0+0.1*charheight; Yfactor = 0; } TGaxis_Rotate (Xfactor,Yfactor,cosphi,sinphi,X0,Y0,XX,YY); textAlign = 11; aTexts.push_back(_text(XX,YY, 0,textSize,LABEL, textAlign)); } } } } // Log axis //out_error(m_out,"android_debug","0015"); if (OptionLog && ndiv) { unsigned int xi1=0,xi2 = 0,wi = 0,yi1=0,yi2,hi = 0; bool firstintlab = true, overlap = false; if ((wmin == wmax) || (ndiv == 0)) { out_error(m_out,"PaintAxis", "wmin (%f) == wmax (%f), or ndiv == 0", wmin, wmax); return; //goto L210; } if (wmin <= 0) { out_error(m_out,"PaintAxis", "negative logarithmic axis"); return; //goto L210; } if (wmax <= 0) { out_error(m_out,"PaintAxis", "negative logarithmic axis"); return; //goto L210; } double XMNLOG = ::log10(wmin); if (XMNLOG > 0) XMNLOG += 1.E-6; else XMNLOG -= 1.E-6; double X00 = 0; double X11 = axis_length; double H2 = ::log10(wmax); double H2SAV = H2; if (H2 > 0) H2 += 1.E-6; else H2 -= 1.E-6; int IH1 = int(XMNLOG); int IH2 = 1+int(H2); int NBININ = IH2-IH1+1; double AXMUL = (X11-X00)/(H2SAV-XMNLOG); // Plot decade and intermediate tick marks decade = IH1-2; int labelnumber = IH1; if ( XMNLOG > 0 && (XMNLOG-double(IH1) > 0) ) labelnumber++; for (j=1; j<=NBININ; j++) { // Plot decade firstintlab = true, overlap = false; decade++; if (X0 == X1 && j == 1) Ylabel += charheight*0.33; if (Y0 == Y1 && j == 1) Ylabel -= charheight*0.65; double Xone = X00+AXMUL*(double(decade)-XMNLOG); //the following statement is a trick to circumvent a gcc bug //GB if (j < 0) ::printf("j=%d\n",j); //G.Barrand : ??? if (X00 > Xone) goto L160; if (Xone > X11) break; Xtwo = Xone; Y = 0; if (!Mside) Y -= atick[0]; TGaxis_Rotate(Xone,Y,cosphi,sinphi,X0,Y0,xpl2,ypl2); TGaxis_Rotate(Xtwo,atick[0],cosphi,sinphi,X0,Y0,xpl1,ypl1); if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { if (Mside) { xpl1=xpl2; if (cosphi > 0) ypl1 = ypl2 + atick[0]; else ypl1 = ypl2 - atick[0]; } else { xpl1 = 0.5*(xpl1 + xpl2); xpl2 = xpl1; ypl1 = 0.5*(ypl1 + ypl2) + atick[0]; ypl2 = 0.5*(ypl1 + ypl2) - atick[0]; } } } if (!drawGridOnly) { aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } if (OptionGrid) { TGaxis_Rotate(Xone,0,cosphi,sinphi,X0,Y0,xpl2,ypl2); TGaxis_Rotate(Xone,grid_side*gridlength,cosphi,sinphi,X0,Y0, xpl1,ypl1); aLinesGrid.push_back((float)xpl1); aLinesGrid.push_back((float)ypl1); aLinesGrid.push_back((float)xpl2); aLinesGrid.push_back((float)ypl2); } if (!drawGridOnly && !OptionUnlab) { // We generate labels (numeric only). if (noExponent) { double rlab = ::pow(10.,labelnumber); snpf(LABEL,sizeof(LABEL), "%f", rlab); TGaxis_LabelsLimits(m_out,LABEL,first,last); while (last > first) { if (LABEL[last] != '0') break; LABEL[last] = 0; last--; } if (LABEL[last] == '.') {LABEL[last] = 0; last--;} } else { snpf(LABEL,sizeof(LABEL), "%d", labelnumber); TGaxis_LabelsLimits(m_out,LABEL,first,last); } TGaxis_Rotate (Xone,Ylabel,cosphi,sinphi,X0,Y0,XX,YY); if ((X0 == X1) && !OptionPara) { if (Lside < 0) { if (Mside < 0) { if (labelnumber == 0) NCH=1; else NCH=2; XX += NCH*charheight; } else { if (labelnumber >= 0) XX += 0.25*charheight; else XX += 0.50*charheight; } } XX += 0.25*charheight; } if ((Y0 == Y1) && !OptionDown && !OptionUp) { if (noExponent) YY += 0.33*charheight; } if (N1A == 0) return; //goto L210; int KMOD = NBININ/N1A; if (KMOD == 0) KMOD=1000000; if ((NBININ <= N1A) || (j == 1) || (j == NBININ) || ((NBININ > N1A) && (j%KMOD == 0))) { if (labelnumber == 0) { aTexts.push_back(_text(XX,YY, 0,textSize,"1", textAlign)); } else if (labelnumber == 1) { aTexts.push_back(_text(XX,YY, 0,textSize,"10", textAlign)); } else { if (noExponent) { out_error(m_out,"PaintAxis","debug : texts : FIXME : 003\n"); //FIXME textaxis->PaintTextNDC(XX,YY,&LABEL[first]); } else { //FIXME : support CERN-ROOT Latex encoding ? // ::sprintf(CHTEMP, "10^{%d}", labelnumber); snpf(CHTEMP,sizeof(CHTEMP), "10^%d?", labelnumber); //PAW encoding. aTexts.push_back(_text(XX,YY, 0,textSize,CHTEMP, textAlign)); } } } labelnumber++; } L160: for (k=2;k<10;k++) { // Plot intermediate tick marks //double Xone; //rm shadow warning. Xone = X00+AXMUL*(::log10(double(k))+double(decade)-XMNLOG); if (X00 > Xone) continue; if (Xone > X11) goto L200; Y = 0; if (!Mside) Y -= atick[1]; Xtwo = Xone; TGaxis_Rotate(Xone,Y,cosphi,sinphi,X0,Y0,xpl2,ypl2); TGaxis_Rotate(Xtwo,atick[1],cosphi,sinphi,X0,Y0,xpl1,ypl1); if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { if (Mside) { xpl1 = xpl2; if (cosphi > 0) ypl1 = ypl2 + atick[1]; else ypl1 = ypl2 - atick[1]; } else { xpl1 = 0.5*(xpl1+xpl2); xpl2 = xpl1; ypl1 = 0.5*(ypl1+ypl2) + atick[1]; ypl2 = 0.5*(ypl1+ypl2) - atick[1]; } } } int IDN = N1A*2; if ((NBININ <= IDN) || ((NBININ > IDN) && (k == 5))) { if(!drawGridOnly) { aLinesAxis.push_back((float)xpl1); aLinesAxis.push_back((float)ypl1); aLinesAxis.push_back((float)xpl2); aLinesAxis.push_back((float)ypl2); } // Draw the intermediate LOG labels if requested if (MoreLogLabels && !OptionUnlab && !drawGridOnly && !overlap) { if (noExponent) { double rlab = double(k)*::pow(10.,labelnumber-1); snpf(CHTEMP,sizeof(CHTEMP), "%g", rlab); } else { if (labelnumber-1 == 0) { snpf(CHTEMP,sizeof(CHTEMP), "%d", k); } else if (labelnumber-1 == 1) { snpf(CHTEMP,sizeof(CHTEMP), "%d", 10*k); } else { //G.Barrand : //::sprintf(CHTEMP, "%d#times10^{%d}", k, labelnumber-1); snpf(CHTEMP,sizeof(CHTEMP), "%dx10^%d!",k,labelnumber-1);//G.Barrand } } TGaxis_Rotate (Xone,Ylabel,cosphi,sinphi,X0,Y0,XX,YY); if ((Y0 == Y1) && !OptionDown && !OptionUp) { if (noExponent) YY += 0.33*charheight; } if (X0 == X1) XX += 0.25*charheight; if (OptionVert) { if ((X0 != X1) && (Y0 != Y1)) { TGaxis_Rotate(Xone,Ylabel,cosphi,sinphi,X0,Y0,XX,YY); if (cosphi > 0) YY += Ylabel; else YY -= Ylabel; } } //FIXME textaxis->SetTitle(CHTEMP); double u = XX; double v = YY; if (firstintlab) { //FIXME textaxis->GetBoundingBox(wi, hi); wi=(Uint)(wi*1.3); hi*=(Uint)(hi*1.3); xi1 = 0;//FIXME gPad->XtoAbsPixel(u); yi1 = 0;//FIXME gPad->YtoAbsPixel(v); firstintlab = false; out_error(m_out,"PaintAxis","debug : texts : dummy : 010\n"); aTexts.push_back(_text(u,v, 0,textSize,CHTEMP, textAlign)); } else { xi2 = 0;//FIXME gPad->XtoAbsPixel(u); yi2 = 0;//FIXME gPad->YtoAbsPixel(v); if ((X0 == X1 && yi1-hi <= yi2) || (Y0 == Y1 && xi1+wi >= xi2)){ overlap = true; } else { xi1 = xi2; yi1 = yi2; //FIXME textaxis->GetBoundingBox(wi, hi); wi=(Uint)(wi*1.3); hi*=(Uint)(hi*1.3); out_error(m_out,"PaintAxis","debug : texts : dummy : 011\n"); aTexts.push_back(_text(u,v, 0,textSize,CHTEMP, textAlign)); } } } // Draw the intermediate LOG grid if only three // decades are requested if (OptionGrid && NBININ <= 5 && ndiv > 100) { TGaxis_Rotate(Xone,0,cosphi,sinphi,X0,Y0,xpl2, ypl2); TGaxis_Rotate (Xone,grid_side*gridlength,cosphi,sinphi,X0,Y0,xpl1,ypl1); aLinesGrid.push_back((float)xpl1); aLinesGrid.push_back((float)ypl1); aLinesGrid.push_back((float)xpl2); aLinesGrid.push_back((float)ypl2); } } //endif ((NBININ <= IDN) || } //endfor (k=2;k<10;k++) } //endfor (j=1; j<=NBININ; j++) L200: int kuku=0; if (kuku) { } } //endif (OptionLog && ndiv) //out_error(m_out,"android_debug","end"); //L210: } /* void TGaxis_SetDecimals(bool dot) { // Set the Decimals flag // By default, blank characters are stripped, and then the // label is correctly aligned. The dot, if last character of the string, // is also stripped, unless this option is specified. // One can disable the option by calling axis.SetDecimals(true). // Note the bit is set in fBits (as opposed to fBits2 in TAxis!) if (dot) SetBit(TAxis_kDecimals); else ResetBit(TAxis_kDecimals); } void TGaxis_SetMaxDigits(int maxd) { // static function to set fMaxDigits for axis with the bin content // (y axis for 1-d histogram, z axis for 2-d histogram) //fMaxDigits is the maximum number of digits permitted for the axis //labels above which the notation with 10^N is used. //For example, to accept 6 digits number like 900000 on an axis //call TGaxis::SetMaxDigits(6). The default value is 5. //fMaxDigits must be greater than 0. fMaxDigits = maxd; if (maxd < 1) fMaxDigits = 1; } void TGaxis_SetMoreLogLabels(bool more) { // Set the kMoreLogLabels bit flag // When this option is selected more labels are drawn when in log scale // and there is a small number of decades (<3). // Note that this option is automatically inherited from TAxis if (more) SetBit(TAxis_kMoreLogLabels); else ResetBit(TAxis_kMoreLogLabels); } void TGaxis_SetNoExponent(bool noExponent) { // Set the NoExponent flag // By default, an exponent of the form 10^N is used when the label values // are either all very small or very large. // One can disable the exponent by calling axis.SetNoExponent(true). if (noExponent) SetBit(TAxis_kNoExponent); else ResetBit(TAxis_kNoExponent); } void TGaxis_SetOption(const std::string& option) { fCHOPT = option; } */ void set_time_format(const std::string& a_format) // Change the format used for time plotting // ======================================== // The format string for date and time use the same options as the one used // in the standard strftime C function, i.e. : // for date : // %a abbreviated weekday name // %b abbreviated month name // %d day of the month (01-31) // %m month (01-12) // %y year without century // // for time : // %H hour (24-hour clock) // %I hour (12-hour clock) // %p local equivalent of AM or PM // %M minute (00-59) // %S seconds (00-61) // %% % // { if (a_format.find("%F")!=std::string::npos || !a_format.size()) { fTimeFormat = a_format; //::printf("debug : SbAxisHPLOT::setTimeFormat : 000 : \"%s\"\n", // fTimeFormat.c_str()); return; } std::string::size_type IdF = fTimeFormat.find("%F"); if (IdF!=std::string::npos) { size_t LnF = fTimeFormat.size(); std::string stringtimeoffset = fTimeFormat.substr(IdF,LnF-IdF); fTimeFormat = a_format; fTimeFormat += stringtimeoffset; //::printf("debug : SbAxisHPLOT::setTimeFormat : 001 : \"%s\"\n", // fTimeFormat.c_str()); } else { fTimeFormat = a_format; // In CERN-ROOT : //SetTimeOffset(gStyle->GetTimeOffset()); //TAxis::fTimeOffset = 788918400; // UTC time at 01/01/95 //double UTC_time_1995_01_01__00_00_00 = 788918400; //CERN-ROOT //setTimeOffset(UTC_time_1995_01_01__00_00_00); //Be consistent with SoAxis::timeOffset being 0. double UTC_time_1970_01_01__00_00_00 = 0; //UNIX set_time_offset(UTC_time_1970_01_01__00_00_00); //::printf("debug : SbAxisHPLOT::setTimeFormat : 002 : \"%s\"\n", // fTimeFormat.c_str()); } } void set_time_offset(double toffset,bool a_is_gmt = false) { // Change the time offse t std::string::size_type IdF = fTimeFormat.find("%F"); if (IdF!=std::string::npos) { fTimeFormat = fTimeFormat.substr(0,IdF); } fTimeFormat += "%F"; time_t timeoff = (time_t)((long)(toffset)); struct tm* utctis = ::gmtime(&timeoff); char tmp[256]; ::strftime(tmp,256,"%Y-%m-%d %H:%M:%S",utctis); fTimeFormat += tmp; // append the decimal part of the time offset double ds = toffset-(int)toffset; if(ds!= 0) { snpf(tmp,sizeof(tmp),"s%g",ds); fTimeFormat += tmp; } // If the time is GMT, stamp fTimeFormat if (a_is_gmt) fTimeFormat += " GMT"; //::printf("debug : SbAxisHPLOT::setTimeOffset : \"%s\"\n", // fTimeFormat.c_str()); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// private: static void optimizeLimits( double A1,double A2,int nold ,double &BinLow, double &BinHigh ,int &nbins, double &BinWidth ,const std::string& aCHOPT ){ // static function to compute reasonable axis limits // // Input parameters: // // A1,A2 : Old WMIN,WMAX . // BinLow,BinHigh : New WMIN,WMAX . // nold : Old NDIV . // nbins : New NDIV . int lwid, kwid; int ntemp = 0; int jlog = 0; double siground = 0; double alb, awidth, sigfig; double timemulti = 1; int roundmode =0; int OptionTime; SETOPT(aCHOPT,'t',OptionTime); double AL = tools::mn(A1,A2); double AH = tools::mx(A1,A2); if (AL == AH) AH = AL+1; // if nold == -1 , program uses binwidth input from calling routine if (nold == -1 && BinWidth > 0 ) goto L90; ntemp = tools::mx(nold,2); if (ntemp < 1) ntemp = 1; L20: awidth = (AH-AL)/double(ntemp); timemulti = 1; if (awidth >= FLT_MAX) goto LOK; //in float.h if (awidth <= 0) goto LOK; // If time representation, bin width should be rounded to seconds // minutes, hours or days if (OptionTime && awidth>=60) { // if width in seconds, treat it as normal // width in minutes awidth /= 60; timemulti *=60; roundmode = 1; // round minutes (60) // width in hours ? if (awidth>=60) { awidth /= 60; timemulti *= 60; roundmode = 2; // round hours (24) // width in days ? if (awidth>=24) { awidth /= 24; timemulti *= 24; roundmode = 3; // round days (30) // width in months ? if (awidth>=30.43685) { // Mean month length in 1900. awidth /= 30.43685; timemulti *= 30.43685; roundmode = 2; // round months (12) // width in years ? if (awidth>=12) { awidth /= 12; timemulti *= 12; roundmode = 0; // round years (10) } } } } } // Get nominal bin width in exponential for m jlog = int(::log10(awidth)); if (jlog <-200 || jlog > 200) { BinLow = 0; BinHigh = 1; BinWidth = 0.01; nbins = 100; return; } if (awidth <= 1 && (!OptionTime || timemulti==1) ) jlog--; sigfig = awidth* ::pow(10.,-jlog) -1e-10; //in the above statement, it is important to substract 1e-10 //to avoid precision problems if the tests below // Round mantissa switch (roundmode) { // Round mantissa up to 1, 1.5, 2, 3, or 6 in case of minutes case 1: // case 60 if (sigfig <= 1) siground = 1; else if (sigfig <= 1.5 && jlog==1) siground = 1.5; else if (sigfig <= 2) siground = 2; else if (sigfig <= 3 && jlog ==1) siground = 3; else if (sigfig <= 5 && sigfig>3 && jlog ==0) siground = 5; //added (Damir in 3.10/02) else if (jlog==0) {siground = 1; jlog++;} else siground = 6; break; case 2: // case 12 and 24 // Round mantissa up to 1, 1.2, 2, 2.4, 3 or 6 in case of hours or months if (sigfig <= 1 && jlog==0) siground = 1; else if (sigfig <= 1.2 && jlog==1) siground = 1.2; else if (sigfig <= 2 && jlog==0) siground = 2; else if (sigfig <= 2.4 && jlog==1) siground = 2.4; else if (sigfig <= 3) siground = 3; else if (sigfig <= 6) siground = 6; else if (jlog==0) siground = 12; else siground = 2.4; break; //- Round mantissa up to 1, 1.4, 2, or 7 in case of days (weeks) case 3: // case 30 if (sigfig <= 1 && jlog==0) siground = 1; else if (sigfig <= 1.4 && jlog==1) siground = 1.4; else if (sigfig <= 3 && jlog ==1) siground = 3; else siground = 7; break; default : // Round mantissa up to 1, 2, 2.5, 5, or 10 in case of decimal number if (sigfig <= 1) siground = 1; else if (sigfig <= 2) siground = 2; else if (sigfig <= 5 && (!OptionTime || jlog<1)) siground = 5; else if (sigfig <= 6 && OptionTime && jlog==1) siground = 6; else {siground = 1; jlog++; } break; } BinWidth = siground* ::pow(10.,jlog); if (OptionTime) BinWidth *= timemulti; // Get new bounds from new width BinWidth L90: alb = AL/BinWidth; if (TMath_Abs(alb) > 1e9) { BinLow = AL; BinHigh = AH; if (nbins > 10*nold && nbins > 10000) nbins = nold; return; } lwid = int(alb); if (alb < 0) lwid--; BinLow = BinWidth*double(lwid); alb = AH/BinWidth + 1.00001; kwid = int(alb); if (alb < 0) kwid--; BinHigh = BinWidth*double(kwid); nbins = kwid - lwid; if (nold == -1) goto LOK; if (nold <= 5) { // Request for one bin is difficult case if (nold > 1 || nbins == 1)goto LOK; BinWidth = BinWidth*2; nbins = 1; goto LOK; } if (2*nbins == nold && !OptionTime) {ntemp++; goto L20; } LOK: double oldBinLow = BinLow; double oldBinHigh = BinHigh; int oldnbins = nbins; double atest = BinWidth*0.0001; //if (TMath_Abs(BinLow-A1) >= atest) { BinLow += BinWidth; nbins--; } //replaced by Damir in 3.10/02 //if (TMath_Abs(BinHigh-A2) >= atest) { BinHigh -= BinWidth; nbins--; } //by the next two lines if (AL-BinLow >= atest) { BinLow += BinWidth; nbins--; } if (BinHigh-AH >= atest) { BinHigh -= BinWidth; nbins--; } if (!OptionTime && BinLow >= BinHigh) { //this case may happen when nbins <=5 BinLow = oldBinLow; BinHigh = oldBinHigh; nbins = oldnbins; } else if (OptionTime && BinLow>=BinHigh) { nbins = 2*oldnbins; BinHigh = oldBinHigh; BinLow = oldBinLow; BinWidth = (oldBinHigh - oldBinLow)/nbins; atest = BinWidth*0.0001; if (AL-BinLow >= atest) { BinLow += BinWidth; nbins--; } if (BinHigh-AH >= atest) { BinHigh -= BinWidth; nbins--; } } } static void adjustBinSize( double A1,double A2,int nold ,double &BinLow, double &BinHigh, int &nbins, double &BinWidth ){ // Axis labels optimisation // ======================== // // This routine adjusts the bining of the axis // in order to have integer values for the labels // // _Input parameters: // // A1,A2 : Old WMIN,WMAX . // BinLow,BinHigh : New WMIN,WMAX . // nold : Old NDIV (primary divisions) // nbins : New NDIV . // BinWidth = TMath_Abs(A2-A1)/double(nold); if (BinWidth <= 1) { BinWidth = 1; BinLow = int(A1); } else { int width = int(BinWidth/5) + 1; BinWidth = 5*width; BinLow = int(A1/BinWidth)*BinWidth ; // We determine BinLow to have one tick mark at 0 // if there are negative labels. if (A1 < 0) { for (int ic=0; ic<1000; ic++) { double rbl = BinLow/BinWidth; int ibl = int(BinLow/BinWidth); if ( (rbl-ibl) == 0 || ic > width) { BinLow -= 5; break;} } } } BinHigh = int(A2); nbins = 0; double XB = BinLow; while (XB <= BinHigh) { XB += BinWidth; nbins++; } BinHigh = XB - BinWidth; } void setLabelOffset(float aValue) { fLabelOffset = aValue;} void setLabelSize(float aValue) { fLabelSize = aValue;} void setTitleOffset(float aValue) { fTitleOffset = aValue;} void setTitleSize(float aValue) { fTitleSize = aValue; } public: void set_tick_size(float aValue) { fTickSize = aValue;} private: std::ostream& m_out; //int fMaxDigits; //!Number of digits above which the 10>N notation is used private: //TObject : unsigned int fBits; //bit field status word float fTickSize; //Size of primary tick mark in NDC float fLabelOffset; //Offset of label wrt axis float fLabelSize; //Size of labels in NDC float fTitleOffset; //Offset of title wrt axis float fTitleSize; //Size of title in NDC int fLabelFont; //Font for labels std::string fTitle; //axis title std::string fTimeFormat; //Time format, ex: 09/12/99 12:34:00 }; }} #endif