Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TAxis.cxx
Go to the documentation of this file.
1 // @(#)root/hist:$Id$
2 // Author: Rene Brun 12/12/94
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "Riostream.h"
13 #include "TAxis.h"
14 #include "TVirtualPad.h"
15 #include "TStyle.h"
16 #include "TError.h"
17 #include "THashList.h"
18 #include "TList.h"
19 #include "TAxisModLab.h"
20 #include "TH1.h"
21 #include "TObjString.h"
22 #include "TDatime.h"
23 #include "TTimeStamp.h"
24 #include "TROOT.h"
25 #include "TClass.h"
26 #include "TMath.h"
27 #include <time.h>
28 #include <cassert>
29 
30 ClassImp(TAxis);
31 
32 ////////////////////////////////////////////////////////////////////////////////
33 /** \class TAxis
34  \ingroup Hist
35  \brief Class to manage histogram axis
36 
37 This class manages histogram axis. It is referenced by TH1 and TGraph.
38 To make a graphical representation of an histogram axis, this class
39 references the TGaxis class. TAxis supports axis with fixed or variable bin sizes.
40 Labels may be associated to individual bins.
41 See examples of various axis representations drawn by class TGaxis.
42 *///////////////////////////////////////////////////////////////////////////////
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 /// Default constructor.
46 
47 TAxis::TAxis(): TNamed(), TAttAxis()
48 {
49  fNbins = 1;
50  fXmin = 0;
51  fXmax = 1;
52  fFirst = 0;
53  fLast = 0;
54  fParent = 0;
55  fLabels = 0;
56  fModLabs = 0;
57  fBits2 = 0;
58  fTimeDisplay = 0;
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 /// Axis constructor for axis with fix bin size
63 
64 TAxis::TAxis(Int_t nbins,Double_t xlow,Double_t xup): TNamed(), TAttAxis()
65 {
66  fParent = 0;
67  fLabels = 0;
68  fModLabs = 0;
69  Set(nbins,xlow,xup);
70 }
71 
72 ////////////////////////////////////////////////////////////////////////////////
73 /// Axis constructor for variable bin size
74 
75 TAxis::TAxis(Int_t nbins,const Double_t *xbins): TNamed(), TAttAxis()
76 {
77  fParent = 0;
78  fLabels = 0;
79  fModLabs = 0;
80  Set(nbins,xbins);
81 }
82 
83 ////////////////////////////////////////////////////////////////////////////////
84 /// Destructor.
85 
86 TAxis::~TAxis()
87 {
88  if (fLabels) {
89  fLabels->Delete();
90  delete fLabels;
91  fLabels = 0;
92  }
93  if (fModLabs) {
94  fModLabs->Delete();
95  delete fModLabs;
96  fModLabs = 0;
97  }
98 }
99 
100 ////////////////////////////////////////////////////////////////////////////////
101 /// Copy constructor.
102 
103 TAxis::TAxis(const TAxis &axis) : TNamed(axis), TAttAxis(axis), fLabels(0), fModLabs(0)
104 {
105  axis.Copy(*this);
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////
109 /// Assignment operator.
110 
111 TAxis& TAxis::operator=(const TAxis &orig)
112 {
113  orig.Copy( *this );
114  return *this;
115 }
116 
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 /// Choose a reasonable time format from the coordinates in the active pad
120 /// and the number of divisions in this axis
121 /// If orientation = "X", the horizontal axis of the pad will be used for ref.
122 /// If orientation = "Y", the vertical axis of the pad will be used for ref.
123 
124 const char *TAxis::ChooseTimeFormat(Double_t axislength)
125 {
126  const char *formatstr = nullptr;
127  Int_t reasformat = 0;
128  Int_t ndiv,nx1,nx2,n;
129  Double_t awidth;
130  Double_t length;
131 
132  if (!axislength) {
133  length = gPad->GetUxmax() - gPad->GetUxmin();
134  } else {
135  length = axislength;
136  }
137 
138  ndiv = GetNdivisions();
139  if (ndiv > 1000) {
140  nx2 = ndiv/100;
141  nx1 = TMath::Max(1, ndiv%100);
142  ndiv = 100*nx2 + Int_t(Double_t(nx1)*gPad->GetAbsWNDC());
143  }
144  ndiv = TMath::Abs(ndiv);
145  n = ndiv - (ndiv/100)*100;
146  awidth = length/n;
147 
148 // width in seconds ?
149  if (awidth>=.5) {
150  reasformat = 1;
151 // width in minutes ?
152  if (awidth>=30) {
153  awidth /= 60; reasformat = 2;
154 // width in hours ?
155  if (awidth>=30) {
156  awidth /=60; reasformat = 3;
157 // width in days ?
158  if (awidth>=12) {
159  awidth /= 24; reasformat = 4;
160 // width in months ?
161  if (awidth>=15.218425) {
162  awidth /= 30.43685; reasformat = 5;
163 // width in years ?
164  if (awidth>=6) {
165  awidth /= 12; reasformat = 6;
166  if (awidth>=2) {
167  awidth /= 12; reasformat = 7;
168  }
169  }
170  }
171  }
172  }
173  }
174  }
175 // set reasonable format
176  switch (reasformat) {
177  case 0:
178  formatstr = "%S";
179  break;
180  case 1:
181  formatstr = "%Mm%S";
182  break;
183  case 2:
184  formatstr = "%Hh%M";
185  break;
186  case 3:
187  formatstr = "%d-%Hh";
188  break;
189  case 4:
190  formatstr = "%d/%m";
191  break;
192  case 5:
193  formatstr = "%d/%m/%y";
194  break;
195  case 6:
196  formatstr = "%d/%m/%y";
197  break;
198  case 7:
199  formatstr = "%m/%y";
200  break;
201  }
202  return formatstr;
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 /// Copy axis structure to another axis
207 
208 void TAxis::Copy(TObject &obj) const
209 {
210  TNamed::Copy(obj);
211  TAttAxis::Copy(((TAxis&)obj));
212  TAxis &axis( ((TAxis&)obj) );
213  axis.fNbins = fNbins;
214  axis.fXmin = fXmin;
215  axis.fXmax = fXmax;
216  axis.fFirst = fFirst;
217  axis.fLast = fLast;
218  axis.fBits2 = fBits2;
219  fXbins.Copy(axis.fXbins);
220  axis.fTimeFormat = fTimeFormat;
221  axis.fTimeDisplay = fTimeDisplay;
222  axis.fParent = fParent;
223  if (axis.fLabels) {
224  axis.fLabels->Delete();
225  delete axis.fLabels;
226  axis.fLabels = 0;
227  }
228  if (fLabels) {
229  //Properly handle case where not all bins have labels
230  TIter next(fLabels);
231  TObjString *label;
232  if(! axis.fLabels) {
233  axis.fLabels = new THashList(axis.fNbins, 3);
234  }
235  while( (label=(TObjString*)next()) ) {
236  TObjString *copyLabel = new TObjString(*label);
237  axis.fLabels->Add(copyLabel);
238  copyLabel->SetUniqueID(label->GetUniqueID());
239  }
240  }
241  if (axis.fModLabs) {
242  axis.fModLabs->Delete();
243  delete axis.fModLabs;
244  axis.fModLabs = 0;
245  }
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 /// Compute distance from point px,py to an axis
250 
251 Int_t TAxis::DistancetoPrimitive(Int_t, Int_t)
252 {
253  return 9999;
254 }
255 
256 ////////////////////////////////////////////////////////////////////////////////
257 /// Execute action corresponding to one event
258 ///
259 /// This member function is called when an axis is clicked with the locator.
260 /// The axis range is set between the position where the mouse is pressed
261 /// and the position where it is released.
262 /// If the mouse position is outside the current axis range when it is released
263 /// the axis is unzoomed with the corresponding proportions.
264 /// Note that the mouse does not need to be in the pad or even canvas
265 /// when it is released.
266 
267 void TAxis::ExecuteEvent(Int_t event, Int_t px, Int_t py)
268 {
269  if (!gPad) return;
270  gPad->ExecuteEventAxis(event,px,py,this);
271 }
272 
273 ////////////////////////////////////////////////////////////////////////////////
274 /// Find bin number corresponding to abscissa x. NOTE: this method does not work with alphanumeric bins !!!
275 ///
276 /// If x is underflow or overflow, attempt to extend the axis if TAxis::kCanExtend is true.
277 /// Otherwise, return 0 or fNbins+1.
278 
279 Int_t TAxis::FindBin(Double_t x)
280 {
281  Int_t bin;
282  // NOTE: This should not be allowed for Alphanumeric histograms,
283  // but it is heavily used (legacy) in the TTreePlayer to fill alphanumeric histograms.
284  // but in case of alphanumeric do-not extend the axis. It makes no sense
285  if (IsAlphanumeric() && gDebug) Info("FindBin","Numeric query on alphanumeric axis - Sorting the bins or extending the axes / rebinning can alter the correspondence between the label and the bin interval.");
286  if (x < fXmin) { //*-* underflow
287  bin = 0;
288  if (fParent == 0) return bin;
289  if (!CanExtend() || IsAlphanumeric() ) return bin;
290  ((TH1*)fParent)->ExtendAxis(x,this);
291  return FindFixBin(x);
292  } else if ( !(x < fXmax)) { //*-* overflow (note the way to catch NaN)
293  bin = fNbins+1;
294  if (fParent == 0) return bin;
295  if (!CanExtend() || IsAlphanumeric() ) return bin;
296  ((TH1*)fParent)->ExtendAxis(x,this);
297  return FindFixBin(x);
298  } else {
299  if (!fXbins.fN) { //*-* fix bins
300  bin = 1 + int (fNbins*(x-fXmin)/(fXmax-fXmin) );
301  } else { //*-* variable bin sizes
302  //for (bin =1; x >= fXbins.fArray[bin]; bin++);
303  bin = 1 + TMath::BinarySearch(fXbins.fN,fXbins.fArray,x);
304  }
305  }
306  return bin;
307 }
308 
309 ////////////////////////////////////////////////////////////////////////////////
310 /// Find bin number with label.
311 /// If the List of labels does not exist create it and make the axis alphanumeric
312 /// If one wants just to add a single label- just call TAxis::SetBinLabel
313 /// If label is not in the list of labels do the following depending on the
314 /// bit TAxis::kCanExtend; of the axis.
315 /// - if the bit is set add the new label and if the number of labels exceeds
316 /// the number of bins, double the number of bins via TH1::LabelsInflate
317 /// - if the bit is not set and the histogram has labels in each bin
318 /// set the bit automatically and consider the histogram as alphanumeric
319 /// if histogram has only some bins with labels then the histogram is not
320 /// consider alphanumeric and return -1
321 ///
322 /// -1 is returned only when the Axis has no parent histogram
323 
324 Int_t TAxis::FindBin(const char *label)
325 {
326  //create list of labels if it does not exist yet
327  if (!fLabels) {
328  if (!fParent) return -1;
329  fLabels = new THashList(fNbins,3);
330  // we set the axis alphanumeric
331  // when list of labels does not exist
332  // do we want to do this also when histogram is not empty ?????
333  if (CanBeAlphanumeric() ) {
334  SetCanExtend(kTRUE);
335  SetAlphanumeric(kTRUE);
336  if (fXmax <= fXmin) {
337  //L.M. Dec 2010 in case of no min and max specified use 0 ->NBINS
338  fXmin = 0;
339  fXmax = fNbins;
340  }
341  }
342  }
343 
344  // search for label in the existing list and return it if it exists
345  TObjString *obj = (TObjString*)fLabels->FindObject(label);
346  if (obj) return (Int_t)obj->GetUniqueID();
347 
348  // if labels is not in the list and we have already labels
349  if (!IsAlphanumeric()) {
350  // if bins without labels exist or if the axis cannot be set to alphanumeric
351  if (HasBinWithoutLabel() || !CanBeAlphanumeric() ) {
352  Info("FindBin","Label %s is not in the list and the axis is not alphanumeric - ignore it",label);
353  return -1;
354  }
355  else {
356  Info("FindBin","Label %s not in the list. It will be added to the histogram",label);
357  SetCanExtend(kTRUE);
358  SetAlphanumeric(kTRUE);
359  }
360  }
361 
362  //Not yet in the list. Can we extend the axis ?
363  assert ( CanExtend() && IsAlphanumeric() );
364  // {
365  // if (gDebug>0)
366  // Info("FindBin","Label %s is not in the list and the axis cannot be extended - the entry will be added in the underflow bin",label);
367  // return 0;
368  // }
369 
370  Int_t n = fLabels->GetEntries();
371 
372  //may be we have to resize the histogram (doubling number of channels)
373  if (n >= fNbins) ((TH1*)fParent)->LabelsInflate(GetName());
374 
375  //add new label to the list: assign bin number
376  obj = new TObjString(label);
377  fLabels->Add(obj);
378  obj->SetUniqueID(n+1);
379  return n+1;
380 }
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 /// Find bin number with label.
384 /// If the List of labels does not exist or the label does not exist just return -1 .
385 /// Do not attempt to modify the axis. This is different than FindBin
386 
387 Int_t TAxis::FindFixBin(const char *label) const
388 {
389  //create list of labels if it does not exist yet
390  if (!fLabels) return -1;
391 
392  // search for label in the existing list and return it if it exists
393  TObjString *obj = (TObjString*)fLabels->FindObject(label);
394  if (obj) return (Int_t)obj->GetUniqueID();
395  return -1;
396 }
397 
398 
399 ////////////////////////////////////////////////////////////////////////////////
400 /// Find bin number corresponding to abscissa x
401 ///
402 /// Identical to TAxis::FindBin except that if x is an underflow/overflow
403 /// no attempt is made to extend the axis.
404 
405 Int_t TAxis::FindFixBin(Double_t x) const
406 {
407  Int_t bin;
408  if (x < fXmin) { //*-* underflow
409  bin = 0;
410  } else if ( !(x < fXmax)) { //*-* overflow (note the way to catch NaN
411  bin = fNbins+1;
412  } else {
413  if (!fXbins.fN) { //*-* fix bins
414  bin = 1 + int (fNbins*(x-fXmin)/(fXmax-fXmin) );
415  } else { //*-* variable bin sizes
416 // for (bin =1; x >= fXbins.fArray[bin]; bin++);
417  bin = 1 + TMath::BinarySearch(fXbins.fN,fXbins.fArray,x);
418  }
419  }
420  return bin;
421 }
422 
423 ////////////////////////////////////////////////////////////////////////////////
424 /// Return label for bin
425 
426 const char *TAxis::GetBinLabel(Int_t bin) const
427 {
428  if (!fLabels) return "";
429  if (bin <= 0 || bin > fNbins) return "";
430  TIter next(fLabels);
431  TObjString *obj;
432  while ((obj=(TObjString*)next())) {
433  Int_t binid = (Int_t)obj->GetUniqueID();
434  if (binid == bin) return obj->GetName();
435  }
436  return "";
437 }
438 
439 ////////////////////////////////////////////////////////////////////////////////
440 /// Return first bin on the axis
441 /// i.e. 1 if no range defined
442 /// NOTE: in some cases a zero is returned (see TAxis::SetRange)
443 
444 Int_t TAxis::GetFirst() const
445 {
446  if (!TestBit(kAxisRange)) return 1;
447  return fFirst;
448 }
449 
450 ////////////////////////////////////////////////////////////////////////////////
451 /// Return last bin on the axis
452 /// i.e. fNbins if no range defined
453 /// NOTE: in some cases a zero is returned (see TAxis::SetRange)
454 
455 Int_t TAxis::GetLast() const
456 {
457  if (!TestBit(kAxisRange)) return fNbins;
458  return fLast;
459 }
460 
461 ////////////////////////////////////////////////////////////////////////////////
462 /// Return center of bin
463 
464 Double_t TAxis::GetBinCenter(Int_t bin) const
465 {
466  Double_t binwidth;
467  if (!fXbins.fN || bin<1 || bin>fNbins) {
468  binwidth = (fXmax - fXmin) / Double_t(fNbins);
469  return fXmin + (bin-1) * binwidth + 0.5*binwidth;
470  } else {
471  binwidth = fXbins.fArray[bin] - fXbins.fArray[bin-1];
472  return fXbins.fArray[bin-1] + 0.5*binwidth;
473  }
474 }
475 
476 ////////////////////////////////////////////////////////////////////////////////
477 /// Return center of bin in log
478 /// With a log-equidistant binning for a bin with low and up edges, the mean is :
479 /// 0.5*(ln low + ln up) i.e. sqrt(low*up) in logx (e.g. sqrt(10^0*10^2) = 10).
480 /// Imagine a bin with low=1 and up=100 :
481 /// - the center in lin is (100-1)/2=50.5
482 /// - the center in log would be sqrt(1*100)=10 (!=log(50.5))
483 ///
484 /// NB: if the low edge of the bin is negative, the function returns the bin center
485 /// as computed by TAxis::GetBinCenter
486 
487 Double_t TAxis::GetBinCenterLog(Int_t bin) const
488 {
489  Double_t low,up;
490  if (!fXbins.fN || bin<1 || bin>fNbins) {
491  Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
492  low = fXmin + (bin-1) * binwidth;
493  up = low+binwidth;
494  } else {
495  low = fXbins.fArray[bin-1];
496  up = fXbins.fArray[bin];
497  }
498  if (low <=0 ) return GetBinCenter(bin);
499  return TMath::Sqrt(low*up);
500 }
501 ////////////////////////////////////////////////////////////////////////////////
502 /// Return low edge of bin
503 
504 Double_t TAxis::GetBinLowEdge(Int_t bin) const
505 {
506  if (fXbins.fN && bin > 0 && bin <=fNbins) return fXbins.fArray[bin-1];
507  Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
508  return fXmin + (bin-1) * binwidth;
509 }
510 
511 ////////////////////////////////////////////////////////////////////////////////
512 /// Return up edge of bin
513 
514 Double_t TAxis::GetBinUpEdge(Int_t bin) const
515 {
516  if (!fXbins.fN || bin < 1 || bin>fNbins) {
517  Double_t binwidth = (fXmax - fXmin) / Double_t(fNbins);
518  return fXmin + bin*binwidth;
519  }
520  return fXbins.fArray[bin];
521 }
522 
523 ////////////////////////////////////////////////////////////////////////////////
524 /// Return bin width
525 
526 Double_t TAxis::GetBinWidth(Int_t bin) const
527 {
528  if (fNbins <= 0) return 0;
529  if (fXbins.fN <= 0) return (fXmax - fXmin) / Double_t(fNbins);
530  if (bin >fNbins) bin = fNbins;
531  if (bin <1 ) bin = 1;
532  return fXbins.fArray[bin] - fXbins.fArray[bin-1];
533 }
534 
535 
536 ////////////////////////////////////////////////////////////////////////////////
537 /// Return an array with the center of all bins
538 
539 void TAxis::GetCenter(Double_t *center) const
540 {
541  Int_t bin;
542  for (bin=1; bin<=fNbins; bin++) *(center + bin-1) = GetBinCenter(bin);
543 }
544 
545 ////////////////////////////////////////////////////////////////////////////////
546 /// Return an array with the low edge of all bins
547 
548 void TAxis::GetLowEdge(Double_t *edge) const
549 {
550  Int_t bin;
551  for (bin=1; bin<=fNbins; bin++) *(edge + bin-1) = GetBinLowEdge(bin);
552 }
553 
554 ////////////////////////////////////////////////////////////////////////////////
555 /// Return *only* the time format from the string fTimeFormat
556 
557 const char *TAxis::GetTimeFormatOnly() const
558 {
559  static TString timeformat;
560  Int_t idF = fTimeFormat.Index("%F");
561  if (idF>=0) {
562  timeformat = fTimeFormat(0,idF);
563  } else {
564  timeformat = fTimeFormat;
565  }
566  return timeformat.Data();
567 }
568 
569 ////////////////////////////////////////////////////////////////////////////////
570 /// Return the ticks option (see SetTicks)
571 
572 const char *TAxis::GetTicks() const
573 {
574  if (TestBit(kTickPlus) && TestBit(kTickMinus)) return "+-";
575  if (TestBit(kTickMinus)) return "-";
576  return "+";
577 }
578 
579 ////////////////////////////////////////////////////////////////////////////////
580 /// this helper function checks if there is a bin without a label
581 /// if all bins have labels, the axis can / will become alphanumeric
582 
583 Bool_t TAxis::HasBinWithoutLabel() const
584 {
585  return fLabels->GetSize() != fNbins;
586 }
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// Set option(s) to draw axis with labels
590 /// option can be:
591 /// - "a" sort by alphabetic order
592 /// - ">" sort by decreasing values
593 /// - "<" sort by increasing values
594 /// - "h" draw labels horizontal
595 /// - "v" draw labels vertical
596 /// - "u" draw labels up (end of label right adjusted)
597 /// - "d" draw labels down (start of label left adjusted)
598 
599 void TAxis::LabelsOption(Option_t *option)
600 {
601  if (!fLabels) {
602  Warning("Sort","Cannot sort. No labels");
603  return;
604  }
605  TH1 *h = (TH1*)GetParent();
606  if (!h) {
607  Error("Sort","Axis has no parent");
608  return;
609  }
610 
611  h->LabelsOption(option,GetName());
612 }
613 
614 ////////////////////////////////////////////////////////////////////////////////
615 /// Copy axis attributes to this
616 
617 void TAxis::ImportAttributes(const TAxis *axis)
618 {
619  SetTitle(axis->GetTitle());
620  SetNdivisions(axis->GetNdivisions());
621  SetAxisColor(axis->GetAxisColor());
622  SetLabelColor(axis->GetLabelColor());
623  SetLabelFont(axis->GetLabelFont());
624  SetLabelOffset(axis->GetLabelOffset());
625  SetLabelSize(axis->GetLabelSize());
626  SetTickLength(axis->GetTickLength());
627  SetTitleOffset(axis->GetTitleOffset());
628  SetTitleSize(axis->GetTitleSize());
629  SetTitleColor(axis->GetTitleColor());
630  SetTitleFont(axis->GetTitleFont());
631  SetBit(TAxis::kCenterTitle, axis->TestBit(TAxis::kCenterTitle));
632  SetBit(TAxis::kCenterLabels, axis->TestBit(TAxis::kCenterLabels));
633  SetBit(TAxis::kRotateTitle, axis->TestBit(TAxis::kRotateTitle));
634  SetBit(TAxis::kNoExponent, axis->TestBit(TAxis::kNoExponent));
635  SetBit(TAxis::kTickPlus, axis->TestBit(TAxis::kTickPlus));
636  SetBit(TAxis::kTickMinus, axis->TestBit(TAxis::kTickMinus));
637  SetBit(TAxis::kMoreLogLabels, axis->TestBit(TAxis::kMoreLogLabels));
638  SetBit(TAxis::kDecimals, axis->TestBit(TAxis::kDecimals));
639  SetTimeFormat(axis->GetTimeFormat());
640 }
641 
642 
643 
644 ////////////////////////////////////////////////////////////////////////////////
645 /// Save axis attributes as C++ statement(s) on output stream out
646 
647 void TAxis::SaveAttributes(std::ostream &out, const char *name, const char *subname)
648 {
649  char quote = '"';
650  if (strlen(GetTitle())) {
651  TString t(GetTitle());
652  t.ReplaceAll("\\","\\\\");
653  out<<" "<<name<<subname<<"->SetTitle("<<quote<<t.Data()<<quote<<");"<<std::endl;
654  }
655  if (fTimeDisplay) {
656  out<<" "<<name<<subname<<"->SetTimeDisplay(1);"<<std::endl;
657  out<<" "<<name<<subname<<"->SetTimeFormat("<<quote<<GetTimeFormat()<<quote<<");"<<std::endl;
658  }
659  if (fLabels) {
660  TIter next(fLabels);
661  TObjString *obj;
662  while ((obj=(TObjString*)next())) {
663  out<<" "<<name<<subname<<"->SetBinLabel("<<obj->GetUniqueID()<<","<<quote<<obj->GetName()<<quote<<");"<<std::endl;
664  }
665  }
666 
667  if (fFirst || fLast) {
668  out<<" "<<name<<subname<<"->SetRange("<<fFirst<<","<<fLast<<");"<<std::endl;
669  }
670 
671  if (TestBit(kLabelsHori)) {
672  out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsHori);"<<std::endl;
673  }
674 
675  if (TestBit(kLabelsVert)) {
676  out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsVert);"<<std::endl;
677  }
678 
679  if (TestBit(kLabelsDown)) {
680  out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsDown);"<<std::endl;
681  }
682 
683  if (TestBit(kLabelsUp)) {
684  out<<" "<<name<<subname<<"->SetBit(TAxis::kLabelsUp);"<<std::endl;
685  }
686 
687  if (TestBit(kCenterLabels)) {
688  out<<" "<<name<<subname<<"->CenterLabels(true);"<<std::endl;
689  }
690 
691  if (TestBit(kCenterTitle)) {
692  out<<" "<<name<<subname<<"->CenterTitle(true);"<<std::endl;
693  }
694 
695  if (TestBit(kRotateTitle)) {
696  out<<" "<<name<<subname<<"->RotateTitle(true);"<<std::endl;
697  }
698 
699  if (TestBit(kDecimals)) {
700  out<<" "<<name<<subname<<"->SetDecimals();"<<std::endl;
701  }
702 
703  if (TestBit(kMoreLogLabels)) {
704  out<<" "<<name<<subname<<"->SetMoreLogLabels();"<<std::endl;
705  }
706 
707  if (TestBit(kNoExponent)) {
708  out<<" "<<name<<subname<<"->SetNoExponent();"<<std::endl;
709  }
710 
711  TAttAxis::SaveAttributes(out,name,subname);
712 }
713 
714 ////////////////////////////////////////////////////////////////////////////////
715 /// Initialize axis with fix bins
716 
717 void TAxis::Set(Int_t nbins, Double_t xlow, Double_t xup)
718 {
719  fNbins = nbins;
720  fXmin = xlow;
721  fXmax = xup;
722  if (!fParent) SetDefaults();
723  if (fXbins.fN > 0) fXbins.Set(0);
724 }
725 
726 ////////////////////////////////////////////////////////////////////////////////
727 /// Initialize axis with variable bins
728 
729 void TAxis::Set(Int_t nbins, const Float_t *xbins)
730 {
731  Int_t bin;
732  fNbins = nbins;
733  fXbins.Set(fNbins+1);
734  for (bin=0; bin<= fNbins; bin++)
735  fXbins.fArray[bin] = xbins[bin];
736  for (bin=1; bin<= fNbins; bin++)
737  if (fXbins.fArray[bin] < fXbins.fArray[bin-1])
738  Error("TAxis::Set", "bins must be in increasing order");
739  fXmin = fXbins.fArray[0];
740  fXmax = fXbins.fArray[fNbins];
741  if (!fParent) SetDefaults();
742 }
743 
744 ////////////////////////////////////////////////////////////////////////////////
745 /// Initialize axis with variable bins
746 
747 void TAxis::Set(Int_t nbins, const Double_t *xbins)
748 {
749  Int_t bin;
750  fNbins = nbins;
751  fXbins.Set(fNbins+1);
752  for (bin=0; bin<= fNbins; bin++)
753  fXbins.fArray[bin] = xbins[bin];
754  for (bin=1; bin<= fNbins; bin++)
755  if (fXbins.fArray[bin] < fXbins.fArray[bin-1])
756  Error("TAxis::Set", "bins must be in increasing order");
757  fXmin = fXbins.fArray[0];
758  fXmax = fXbins.fArray[fNbins];
759  if (!fParent) SetDefaults();
760 }
761 
762 ////////////////////////////////////////////////////////////////////////////////
763 /// Set axis alphanumeric
764 
765 void TAxis::SetAlphanumeric(Bool_t alphanumeric)
766 {
767  if (alphanumeric) fBits2 |= kAlphanumeric;
768  else fBits2 &= ~kAlphanumeric;
769 
770  // clear underflow and overflow (in an alphanumeric situation they do not make sense)
771  // NOTE: using AddBinContent instead of SetBinContent in order to not change
772  // the number of entries
773  //((TH1 *)fParent)->ClearUnderflowAndOverflow();
774  // L.M. 26.1.15 Keep underflow and overflows (see ROOT-7034)
775  if (gDebug && fParent) {
776  TH1 * h = dynamic_cast<TH1*>( fParent);
777  if (!h) return;
778  double s[TH1::kNstat];
779  h->GetStats(s);
780  if (s[0] != 0. && gDebug > 0)
781  Info("SetAlphanumeric","Histogram %s is set alphanumeric but has non-zero content",GetName());
782  }
783 }
784 
785 
786 ////////////////////////////////////////////////////////////////////////////////
787 /// Set axis default values (from TStyle)
788 
789 void TAxis::SetDefaults()
790 {
791  fFirst = 0;
792  fLast = 0;
793  fBits2 = 0;
794  char name[2];
795  strlcpy(name,GetName(),2);
796  name[1] = 0;
797  TAttAxis::ResetAttAxis(name);
798  fTimeDisplay = 0;
799  SetTimeFormat();
800 }
801 
802 ////////////////////////////////////////////////////////////////////////////////
803 /// Set label for bin.
804 /// If no label list exists, it is created. If all the bins have labels, the
805 /// axis becomes alphanumeric and extendable.
806 /// New labels will not be added with the Fill method but will end-up in the
807 /// underflow bin. See documentation of TAxis::FindBin(const char*)
808 
809 void TAxis::SetBinLabel(Int_t bin, const char *label)
810 {
811  if (!fLabels) fLabels = new THashList(fNbins,3);
812 
813  if (bin <= 0 || bin > fNbins) {
814  Error("SetBinLabel","Illegal bin number: %d",bin);
815  return;
816  }
817 
818  // Check whether this bin already has a label.
819  TIter next(fLabels);
820  TObjString *obj;
821  while ((obj=(TObjString*)next())) {
822  if ( obj->GetUniqueID()==(UInt_t)bin ) {
823  // It does. Overwrite it.
824  obj->SetString(label);
825  // LM need to rehash the labels list (see ROOT-5025)
826  fLabels->Rehash(fLabels->GetSize() );
827  return;
828  }
829  }
830  // It doesn't. Add this new label.
831  obj = new TObjString(label);
832  fLabels->Add(obj);
833  obj->SetUniqueID((UInt_t)bin);
834 
835  // check for Alphanumeric case (labels for each bin)
836  if (CanBeAlphanumeric() && fLabels->GetSize() == fNbins) {
837  SetAlphanumeric(kTRUE);
838  SetCanExtend(kTRUE);
839  }
840 }
841 
842 ////////////////////////////////////////////////////////////////////////////////
843 /// Define new text attributes for the label number "labNum". It allows to do a
844 /// fine tuning of the labels. All the attributes can be changed, even the
845 /// label text itself.
846 ///
847 /// \param[in] labNum Number of the label to be changed, negative numbers start from the end
848 /// \param[in] labAngle New angle value
849 /// \param[in] labSize New size (0 erase the label)
850 /// \param[in] labAlign New alignment value
851 /// \param[in] labColor New label color
852 /// \param[in] labFont New label font
853 /// \param[in] labText New label text
854 ///
855 /// If an attribute should not be changed just give the value "-1".
856 ///
857 /// If labnum=0 the list of modified labels is reset.
858 
859 void TAxis::ChangeLabel(Int_t labNum, Double_t labAngle, Double_t labSize,
860  Int_t labAlign, Int_t labColor, Int_t labFont,
861  TString labText)
862 {
863  if (!fModLabs) fModLabs = new TList();
864 
865  // Reset the list of modified labels.
866  if (labNum == 0) {
867  delete fModLabs;
868  fModLabs = 0;
869  return;
870  }
871 
872  TAxisModLab *ml = new TAxisModLab();
873  ml->SetLabNum(labNum);
874  ml->SetAngle(labAngle);
875  ml->SetSize(labSize);
876  ml->SetAlign(labAlign);
877  ml->SetColor(labColor);
878  ml->SetFont(labFont);
879  ml->SetText(labText);
880 
881  fModLabs->Add((TObject*)ml);
882 }
883 
884 
885 ////////////////////////////////////////////////////////////////////////////////
886 /// Set the viewing range for the axis from bin first to last.
887 /// To set a range using the axis coordinates, use TAxis::SetRangeUser.
888 ///
889 /// If first == last == 0 or if last < first or if the range specified does
890 /// not intersect at all with the maximum available range [0, fNbins + 1],
891 /// then the range is reset by removing the bit TAxis::kAxisRange. In this
892 /// case the functions TAxis::GetFirst() and TAxis::GetLast() will return 1
893 /// and fNbins.
894 ///
895 /// If the range specified partially intersects [0, fNbins + 1], then the
896 /// intersection range is set. For instance, if first == -2 and last == fNbins,
897 /// then the set range is [0, fNbins] (fFirst = 0 and fLast = fNbins).
898 ///
899 /// NOTE: for historical reasons, SetRange(0,0) resets the range even though Bin 0 is
900 /// technically reserved for the underflow; in order to set the range of the axis
901 /// so that it only includes the underflow, use SetRange(a,0), where a < 0
902 
903 void TAxis::SetRange(Int_t first, Int_t last)
904 {
905 
906  Int_t nCells = fNbins + 1; // bins + overflow
907 
908  // special reset range cases
909  if (last < first || (first < 0 && last < 0) ||
910  (first > nCells && last > nCells) || (first == 0 && last == 0)
911  ) {
912  fFirst = 1;
913  fLast = fNbins;
914  SetBit(kAxisRange, 0);
915  } else {
916  fFirst = std::max(first, 0);
917  fLast = std::min(last, nCells);
918  SetBit(kAxisRange, 1);
919  }
920 
921 }
922 
923 
924 ////////////////////////////////////////////////////////////////////////////////
925 /// Set the viewing range for the axis from ufirst to ulast (in user coordinates).
926 /// To set a range using the axis bin numbers, use TAxis::SetRange.
927 
928 void TAxis::SetRangeUser(Double_t ufirst, Double_t ulast)
929 {
930  if (!strstr(GetName(),"xaxis")) {
931  TH1 *hobj = (TH1*)GetParent();
932  if (hobj &&
933  ((hobj->GetDimension() == 2 && strstr(GetName(),"zaxis"))
934  || (hobj->GetDimension() == 1 && strstr(GetName(),"yaxis")))) {
935  hobj->SetMinimum(ufirst);
936  hobj->SetMaximum(ulast);
937  return;
938  }
939  }
940  Int_t ifirst = FindFixBin(ufirst);
941  Int_t ilast = FindFixBin(ulast);
942  // fixes for numerical error and for https://savannah.cern.ch/bugs/index.php?99777
943  if (GetBinUpEdge(ifirst) <= ufirst ) ifirst += 1;
944  if (GetBinLowEdge(ilast) >= ulast ) ilast -= 1;
945  SetRange(ifirst, ilast);
946 }
947 
948 ////////////////////////////////////////////////////////////////////////////////
949 /// Set ticks orientation.
950 /// option = "+" ticks drawn on the "positive side" (default)
951 /// option = "-" ticks drawn on the "negative side"
952 /// option = "+-" ticks drawn on both sides
953 
954 void TAxis::SetTicks(Option_t *option)
955 {
956  ResetBit(kTickPlus);
957  ResetBit(kTickMinus);
958  if (strchr(option,'+')) SetBit(kTickPlus);
959  if (strchr(option,'-')) SetBit(kTickMinus);
960 }
961 
962 ////////////////////////////////////////////////////////////////////////////////
963 /// Change the format used for time plotting
964 ///
965 /// The format string for date and time use the same options as the one used
966 /// in the standard strftime C function, i.e. :
967 /// for date :
968 ///
969 /// %a abbreviated weekday name
970 /// %b abbreviated month name
971 /// %d day of the month (01-31)
972 /// %m month (01-12)
973 /// %y year without century
974 ///
975 /// for time :
976 ///
977 /// %H hour (24-hour clock)
978 /// %I hour (12-hour clock)
979 /// %p local equivalent of AM or PM
980 /// %M minute (00-59)
981 /// %S seconds (00-61)
982 /// %% %
983 ///
984 /// This function allows also to define the time offset. It is done via %F
985 /// which should be appended at the end of the format string. The time
986 /// offset has the following format: 'yyyy-mm-dd hh:mm:ss'
987 /// Example:
988 ///
989 /// h = new TH1F("Test","h",3000,0.,200000.);
990 /// h->GetXaxis()->SetTimeDisplay(1);
991 /// h->GetXaxis()->SetTimeFormat("%d\/%m\/%y%F2000-02-28 13:00:01");
992 ///
993 /// This defines the time format being "dd/mm/yy" and the time offset as the
994 /// February 28th 2003 at 13:00:01
995 ///
996 /// If %F is not specified, the time offset used will be the one defined by:
997 /// gStyle->SetTimeOffset. For example like that:
998 ///
999 /// TDatime da(2003,02,28,12,00,00);
1000 /// gStyle->SetTimeOffset(da.Convert());
1001 
1002 void TAxis::SetTimeFormat(const char *tformat)
1003 {
1004  TString timeformat = tformat;
1005 
1006  if (timeformat.Index("%F")>=0 || timeformat.IsNull()) {
1007  fTimeFormat = timeformat;
1008  return;
1009  }
1010 
1011  Int_t idF = fTimeFormat.Index("%F");
1012  if (idF>=0) {
1013  Int_t lnF = fTimeFormat.Length();
1014  TString stringtimeoffset = fTimeFormat(idF,lnF);
1015  fTimeFormat = tformat;
1016  fTimeFormat.Append(stringtimeoffset);
1017  } else {
1018  fTimeFormat = tformat;
1019  SetTimeOffset(gStyle->GetTimeOffset());
1020  }
1021 }
1022 
1023 
1024 ////////////////////////////////////////////////////////////////////////////////
1025 /// Change the time offset
1026 /// If option = "gmt", set display mode to GMT.
1027 
1028 void TAxis::SetTimeOffset(Double_t toffset, Option_t *option)
1029 {
1030  TString opt = option;
1031  opt.ToLower();
1032 
1033  char tmp[20];
1034  time_t timeoff;
1035  struct tm* utctis;
1036  Int_t idF = fTimeFormat.Index("%F");
1037  if (idF>=0) fTimeFormat.Remove(idF);
1038  fTimeFormat.Append("%F");
1039 
1040  timeoff = (time_t)((Long_t)(toffset));
1041  // offset is always saved in GMT to allow file transport
1042  // to different time zones
1043  utctis = gmtime(&timeoff);
1044 
1045  strftime(tmp,20,"%Y-%m-%d %H:%M:%S",utctis);
1046  fTimeFormat.Append(tmp);
1047 
1048  // append the decimal part of the time offset
1049  Double_t ds = toffset-(Int_t)toffset;
1050  snprintf(tmp,20,"s%g",ds);
1051  fTimeFormat.Append(tmp);
1052 
1053  // add GMT/local option
1054  if (opt.Contains("gmt")) fTimeFormat.Append(" GMT");
1055 }
1056 
1057 
1058 ////////////////////////////////////////////////////////////////////////////////
1059 /// Stream an object of class TAxis.
1060 
1061 void TAxis::Streamer(TBuffer &R__b)
1062 {
1063  if (R__b.IsReading()) {
1064  UInt_t R__s, R__c;
1065  Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
1066  if (R__v > 5) {
1067  R__b.ReadClassBuffer(TAxis::Class(), this, R__v, R__s, R__c);
1068  return;
1069  }
1070  //====process old versions before automatic schema evolution
1071  TNamed::Streamer(R__b);
1072  TAttAxis::Streamer(R__b);
1073  R__b >> fNbins;
1074  if (R__v < 5) {
1075  Float_t xmin,xmax;
1076  R__b >> xmin; fXmin = xmin;
1077  R__b >> xmax; fXmax = xmax;
1078  Float_t *xbins = 0;
1079  Int_t n = R__b.ReadArray(xbins);
1080  fXbins.Set(n);
1081  for (Int_t i=0;i<n;i++) fXbins.fArray[i] = xbins[i];
1082  delete [] xbins;
1083  } else {
1084  R__b >> fXmin;
1085  R__b >> fXmax;
1086  fXbins.Streamer(R__b);
1087  }
1088  if (R__v > 2) {
1089  R__b >> fFirst;
1090  R__b >> fLast;
1091  // following lines required to repair for a bug in Root version 1.03
1092  if (fFirst < 0 || fFirst > fNbins) fFirst = 0;
1093  if (fLast < 0 || fLast > fNbins) fLast = 0;
1094  if (fLast < fFirst) { fFirst = 0; fLast = 0;}
1095  if (fFirst ==0 && fLast == 0) SetBit(kAxisRange,0);
1096  }
1097  if (R__v > 3) {
1098  R__b >> fTimeDisplay;
1099  fTimeFormat.Streamer(R__b);
1100  } else {
1101  SetTimeFormat();
1102  }
1103  R__b.CheckByteCount(R__s, R__c, TAxis::IsA());
1104  //====end of old versions
1105 
1106  } else {
1107  R__b.WriteClassBuffer(TAxis::Class(),this);
1108  }
1109 }
1110 
1111 ////////////////////////////////////////////////////////////////////////////////
1112 /// Reset first & last bin to the full range
1113 
1114 void TAxis::UnZoom()
1115 {
1116  if (!gPad) return;
1117  gPad->SetView();
1118 
1119  //unzoom object owning this axis
1120  SetRange(0,0);
1121  TH1 *hobj1 = (TH1*)GetParent();
1122  if (!strstr(GetName(),"xaxis")) {
1123  if (!hobj1) return;
1124  if (hobj1->GetDimension() == 2) {
1125  if (strstr(GetName(),"zaxis")) {
1126  hobj1->SetMinimum();
1127  hobj1->SetMaximum();
1128  hobj1->ResetBit(TH1::kIsZoomed);
1129  }
1130  return;
1131  }
1132  if (strcmp(hobj1->GetName(),"hframe") == 0 ) {
1133  hobj1->SetMinimum(fXmin);
1134  hobj1->SetMaximum(fXmax);
1135  } else {
1136  if (fXmin==hobj1->GetMinimum() && fXmax==hobj1->GetMaximum()) {
1137  hobj1->SetMinimum(fXmin);
1138  hobj1->SetMaximum(fXmax);
1139  } else {
1140  hobj1->SetMinimum();
1141  hobj1->SetMaximum();
1142  }
1143  hobj1->ResetBit(TH1::kIsZoomed);
1144  }
1145  }
1146  //must unzoom all histograms in the pad
1147  TIter next(gPad->GetListOfPrimitives());
1148  TObject *obj;
1149  while ((obj= next())) {
1150  if (!obj->InheritsFrom(TH1::Class())) continue;
1151  TH1 *hobj = (TH1*)obj;
1152  if (hobj == hobj1) continue;
1153  if (!strstr(GetName(),"xaxis")) {
1154  if (hobj->GetDimension() == 2) {
1155  if (strstr(GetName(),"zaxis")) {
1156  hobj->SetMinimum();
1157  hobj->SetMaximum();
1158  hobj->ResetBit(TH1::kIsZoomed);
1159  } else {
1160  hobj->GetYaxis()->SetRange(0,0);
1161  }
1162  return;
1163  }
1164  if (strcmp(hobj->GetName(),"hframe") == 0 ) {
1165  hobj->SetMinimum(fXmin);
1166  hobj->SetMaximum(fXmax);
1167  } else {
1168  hobj->SetMinimum();
1169  hobj->SetMaximum();
1170  hobj->ResetBit(TH1::kIsZoomed);
1171  }
1172  } else {
1173  hobj->GetXaxis()->SetRange(0,0);
1174  }
1175  }
1176 
1177  gPad->UnZoomed();
1178 }
1179 
1180 ////////////////////////////////////////////////////////////////////////////////
1181 /// Zoom out by a factor of 'factor' (default =2)
1182 /// uses previous zoom factor by default
1183 /// Keep center defined by 'offset' fixed
1184 /// ie. -1 at left of current range, 0 in center, +1 at right
1185 
1186 void TAxis::ZoomOut(Double_t factor, Double_t offset)
1187 {
1188 
1189  if (factor <= 0) factor = 2;
1190  Double_t center = (GetFirst()*(1-offset) + GetLast()*(1+offset))/2.;
1191  Int_t first = int(TMath::Floor(center+(GetFirst()-center)*factor + 0.4999999));
1192  Int_t last = int(TMath::Floor(center+(GetLast() -center)*factor + 0.5000001));
1193  if (first==GetFirst() && last==GetLast()) { first--; last++; }
1194  SetRange(first,last);
1195 }