Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TRatioPlot.cxx
Go to the documentation of this file.
1 // @(#)root/gpad:$Id$
2 // Author: Paul Gessinger 25/08/2016
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2016, 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 "TRatioPlot.h"
13 #include "TROOT.h"
14 #include "TBrowser.h"
15 #include "TH1.h"
16 #include "TF1.h"
17 #include "TPad.h"
18 #include "TString.h"
19 #include "TMath.h"
20 #include "TGraphAsymmErrors.h"
21 #include "TGraphErrors.h"
22 #include "TGaxis.h"
23 #include "TLine.h"
24 #include "TVirtualFitter.h"
25 #include "TFitResult.h"
26 #include "THStack.h"
27 
28 #include <iostream>
29 
30 /** \class TRatioPlot
31  \ingroup gpad
32 Class for displaying ratios, differences and fit residuals.
33 
34 TRatioPlot has two constructors, one which accepts two histograms, and is responsible
35 for setting up the calculation of ratios and differences. This calculation is in part
36 delegated to `TEfficiency`. A single option can be given as a parameter, that is
37 used to determine which procedure is chosen. The remaining option string is then
38 passed through to the calculation, if applicable. The other constructor uses a
39 fitted histogram to calculate the fit residual and plot it with the histogram
40 and the fit function.
41 
42 ## Ratios and differences
43 The simplest case is passing two histograms without specifying any options. This defaults to using
44 `TGraphAsymmErrors::Divide`. The `option` variable is passed through, as are the parameters
45 `c1` and `c2`, that you can set via `TRatioPlot::SetC1` and `TRatioPlot::SetC1`. If you set the
46 `option` to `divsym` the method `TH1::Divide` will be used instead, also receiving all the parameters.
47 
48 Using the `option` `diff` or `diffsig`, both histograms will be subtracted, and in the case of diffsig,
49 the difference will be divided by the uncertainty. `c1` and `c2` will only be used to
50 scale the histograms using `TH1::Scale` prior to subtraction.
51 
52 Available options are for `option`:
53 | Option | Description |
54 | ---------- | ------------------------------------------------------------ |
55 | divsym | uses the histogram `TH1::Divide` method, yields symmetric errors |
56 | diff | subtracts the histograms |
57 | diffsig | subtracts the histograms and divides by the uncertainty |
58 
59 Begin_Macro(source)
60 ../../../tutorials/hist/ratioplot1.C
61 End_Macro
62 
63 ## Fit residuals
64 A second constructor only accepts a single histogram, but expects it to have a fitted
65 function. The function is used to calculate the residual between the fit and the
66 histogram. Here, it is expected that h1 has a fit function in it's list of functions. The class calculates the
67 difference between the histogram and the fit function at each point and divides it by the uncertainty. There
68 are a few option to steer which error is used (as is the case for `diffsig`). The default is to use
69 the statistical uncertainty from h1 using `TH1::GetBinError`. If the `option` string contains `errasym`, asymmetric
70 errors will be used. The type of error can be steered by `TH1::SetBinErrorOption`. The corresponding error will be used,
71 depending on if the function is below or above the bin content. The third option `errfunc` uses the square root of
72 the function value as the error.
73 
74 
75 Begin_Macro(source)
76 ../../../tutorials/hist/ratioplot2.C
77 End_Macro
78 
79 ## Error options for difference divided by uncertainty and fit residual
80 The uncertainty that is used in the calculation can be steered by providing
81 options to the `option` argument.
82 
83 | Option | Description |
84 | ---------- | ------------------------------------------------------------ |
85 | errasym | Uses calculated asymmetric errors from `TH1::GetBinErrorUp`/`TH1::GetBinErrorLow`. Note that you need to set `TH1::SetBinErrorOption` first |
86 | errfunc | Uses \f$ \sqrt{f(x)} \f$ as the error |
87 
88 The asymmetric error case uses the upper or lower error depending on the relative size
89 of the bin contents, or the bin content and the function value.
90 
91 ## Access to internal parts
92 You can access the internal objects that are used to construct the plot via a series of
93 methods. `TRatioPlot::GetUpperPad` and `TRatioPlot::GetLowerPad` can be used to draw additional
94 elements on top of the existing ones.
95 `TRatioPlot::GetLowerRefGraph` returns a reference to the lower pad's graph that
96 is responsible for the range, which enables you to modify the range.
97 
98 \image html gpad_ratioplot.png
99 */
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 /// TRatioPlot default constructor
103 
104 TRatioPlot::TRatioPlot()
105 {
106 }
107 
108 ////////////////////////////////////////////////////////////////////////////////
109 /// Destructor
110 
111 TRatioPlot::~TRatioPlot()
112 {
113 
114  gROOT->GetListOfCleanups()->Remove(this);
115 
116  if (fRatioGraph != 0) delete fRatioGraph;
117  if (fConfidenceInterval1 != 0) delete fConfidenceInterval1;
118  if (fConfidenceInterval2 != 0) delete fConfidenceInterval2;
119 
120  for (unsigned int i=0;i<fGridlines.size();++i) {
121  delete (fGridlines[i]);
122  }
123 
124  if (fSharedXAxis != 0) delete fSharedXAxis;
125  if (fUpperGXaxis != 0) delete fUpperGXaxis;
126  if (fLowerGXaxis != 0) delete fLowerGXaxis;
127  if (fUpperGYaxis != 0) delete fUpperGYaxis;
128  if (fLowerGYaxis != 0) delete fLowerGYaxis;
129  if (fUpperGXaxisMirror != 0) delete fUpperGXaxisMirror;
130  if (fLowerGXaxisMirror != 0) delete fLowerGXaxisMirror;
131  if (fUpperGYaxisMirror != 0) delete fUpperGYaxisMirror;
132  if (fLowerGYaxisMirror != 0) delete fLowerGYaxisMirror;
133 
134  if (fUpYaxis != 0) delete fUpYaxis;
135  if (fLowYaxis != 0) delete fLowYaxis;
136 
137 }
138 
139 ////////////////////////////////////////////////////////////////////////////////
140 /// Internal method that shares constructor logic
141 
142 void TRatioPlot::Init(TH1* h1, TH1* h2,Option_t *option)
143 {
144 
145  fH1 = h1;
146  fH2 = h2;
147 
148  SetupPads();
149 
150  TString optionString = TString(option);
151 
152  if (optionString.Contains("divsym")) {
153  optionString.ReplaceAll("divsym", "");
154  fMode = TRatioPlot::CalculationMode::kDivideHist;
155  } else if (optionString.Contains("diffsig")) {
156  optionString.ReplaceAll("diffsig", "");
157  fMode = TRatioPlot::CalculationMode::kDifferenceSign;
158 
159  // determine which error style
160  if (optionString.Contains("errasym")) {
161  fErrorMode = TRatioPlot::ErrorMode::kErrorAsymmetric;
162  optionString.ReplaceAll("errasym", "");
163  }
164 
165  if (optionString.Contains("errfunc")) {
166  fErrorMode = TRatioPlot::ErrorMode::kErrorFunc;
167  optionString.ReplaceAll("errfunc", "");
168  }
169  } else if (optionString.Contains("diff")) {
170  optionString.ReplaceAll("diff", "");
171  fMode = TRatioPlot::CalculationMode::kDifference;
172  } else {
173  fMode = TRatioPlot::CalculationMode::kDivideGraph; // <- default
174  }
175 
176  fOption = optionString;
177 
178 
179  fH1DrawOpt = "hist";
180  fH2DrawOpt = "E";
181  fGraphDrawOpt = "AP";
182 
183 
184  // build ratio, everything is ready
185  if (!BuildLowerPlot()) return;
186 
187  // taking x axis information from h1 by cloning it x axis
188  fSharedXAxis = (TAxis*)(fH1->GetXaxis()->Clone());
189  fUpYaxis = (TAxis*)(fH1->GetYaxis()->Clone());
190  fLowYaxis = (TAxis*)(fRatioGraph->GetYaxis()->Clone());
191 }
192 
193 ////////////////////////////////////////////////////////////////////////////////
194 /// Constructor for two histograms
195 ///
196 /// \param h1 First histogram
197 /// \param h2 Second histogram
198 /// \param option Steers the error calculation, as well as ratio / difference
199 
200 TRatioPlot::TRatioPlot(TH1* h1, TH1* h2, Option_t *option)
201  : fGridlines()
202 {
203  gROOT->GetListOfCleanups()->Add(this);
204 
205  if (!h1 || !h2) {
206  Warning("TRatioPlot", "Need two histograms.");
207  return;
208  }
209 
210  Bool_t h1IsTH1=h1->IsA()->InheritsFrom(TH1::Class());
211  Bool_t h2IsTH1=h2->IsA()->InheritsFrom(TH1::Class());
212 
213  if (!h1IsTH1 && !h2IsTH1) {
214  Warning("TRatioPlot", "Need two histograms deriving from TH2 or TH3.");
215  return;
216  }
217 
218  fHistDrawProxy = h1;
219 
220  Init(h1, h2, option);
221 
222 }
223 
224 ////////////////////////////////////////////////////////////////////////////////
225 /// Constructor which accepts a `THStack` and a histogram. Converts the
226 /// stack to a regular sum of its containing histograms for processing.
227 ///
228 /// \param st The THStack object
229 /// \param h2 The other histogram
230 /// \param option Steers the calculation of the lower plot
231 
232 TRatioPlot::TRatioPlot(THStack* st, TH1* h2, Option_t *option)
233 {
234  if (!st || !h2) {
235  Warning("TRatioPlot", "Need a histogram and a stack");
236  return;
237  }
238 
239  TList *stackHists = st->GetHists();
240 
241  if (stackHists->GetSize() == 0) {
242  Warning("TRatioPlot", "Stack does not have histograms");
243  return;
244  }
245 
246  TH1* tmpHist = (TH1*)stackHists->At(0)->Clone();
247  tmpHist->Reset();
248 
249  for (int i=0;i<stackHists->GetSize();++i) {
250  tmpHist->Add((TH1*)stackHists->At(i));
251  }
252 
253  fHistDrawProxy = st;
254 
255  Init(tmpHist, h2, option);
256 
257 }
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 /// Constructor for one histogram and a fit.
261 ///
262 /// \param h1 The histogram
263 /// \param option Steers the error calculation
264 /// \param fitres Explicit fit result to be used for calculation. Uses last fit if left empty
265 
266 TRatioPlot::TRatioPlot(TH1* h1, Option_t *option, TFitResult *fitres)
267  : fH1(h1),
268  fGridlines()
269 {
270  gROOT->GetListOfCleanups()->Add(this);
271 
272  if (!fH1) {
273  Warning("TRatioPlot", "Need a histogram.");
274  return;
275  }
276 
277  Bool_t h1IsTH1=fH1->IsA()->InheritsFrom(TH1::Class());
278 
279  if (!h1IsTH1) {
280  Warning("TRatioPlot", "Need a histogram deriving from TH2 or TH3.");
281  return;
282  }
283 
284  TList *h1Functions = fH1->GetListOfFunctions();
285 
286  if (h1Functions->GetSize() < 1) {
287  Warning("TRatioPlot", "Histogram given needs to have a (fit) function associated with it");
288  return;
289  }
290 
291 
292  fHistDrawProxy = h1;
293 
294  fFitResult = fitres;
295 
296  fMode = TRatioPlot::CalculationMode::kFitResidual;
297 
298  TString optionString = TString(option);
299 
300  // determine which error style
301  if (optionString.Contains("errasym")) {
302  fErrorMode = TRatioPlot::ErrorMode::kErrorAsymmetric;
303  optionString.ReplaceAll("errasym", "");
304  }
305 
306  if (optionString.Contains("errfunc")) {
307  fErrorMode = TRatioPlot::ErrorMode::kErrorFunc;
308  optionString.ReplaceAll("errfunc", "");
309  }
310 
311  fOption = optionString;
312 
313  if (!BuildLowerPlot()) return;
314 
315  // emulate option behaviour of TH1
316  if (fH1->GetSumw2N() > 0) {
317  fH1DrawOpt = "E";
318  } else {
319  fH1DrawOpt = "hist";
320  }
321  fGraphDrawOpt = "LX"; // <- default
322 
323  fSharedXAxis = (TAxis*)(fH1->GetXaxis()->Clone());
324  fUpYaxis = (TAxis*)(fH1->GetYaxis()->Clone());
325  fLowYaxis = (TAxis*)(fRatioGraph->GetYaxis()->Clone());
326 
327  //SyncAxesRanges();
328 
329  SetupPads();
330 
331 }
332 
333 ////////////////////////////////////////////////////////////////////////////////
334 /// Sets the drawing option for h1
335 
336 void TRatioPlot::SetH1DrawOpt(Option_t *opt)
337 {
338  fH1DrawOpt = opt;
339 }
340 
341 ////////////////////////////////////////////////////////////////////////////////
342 /// Sets the drawing option for h2
343 
344 void TRatioPlot::SetH2DrawOpt(Option_t *opt)
345 {
346  TString optString = TString(opt);
347  optString.ReplaceAll("same", "");
348  optString.ReplaceAll("SAME", "");
349 
350  fH2DrawOpt = optString;
351 }
352 
353 ////////////////////////////////////////////////////////////////////////////////
354 /// Sets the drawing option for the lower graph
355 
356 void TRatioPlot::SetGraphDrawOpt(Option_t *opt)
357 {
358  fGraphDrawOpt = opt;
359 }
360 
361 ////////////////////////////////////////////////////////////////////////////////
362 /// Sets the drawing option for the fit in the fit residual case
363 
364 void TRatioPlot::SetFitDrawOpt(Option_t *opt)
365 {
366  fFitDrawOpt = opt;
367 }
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 /// Setup the pads.
371 
372 void TRatioPlot::SetupPads() {
373 
374  // this method will delete all the pads before recreating them
375 
376  if (fUpperPad != 0) {
377  delete fUpperPad;
378  fUpperPad = 0;
379  }
380 
381  if (fLowerPad != 0) {
382  delete fLowerPad;
383  fLowerPad = 0;
384  }
385 
386  if (!gPad) {
387  Error("SetupPads", "need to create a canvas first");
388  return;
389  }
390 
391  double pm = fInsetWidth;
392  double width = gPad->GetWNDC();
393  double height = gPad->GetHNDC();
394  double f = height/width;
395 
396  fUpperPad = new TPad("upper_pad", "", pm*f, fSplitFraction, 1.-pm*f, 1.-pm);
397  fLowerPad = new TPad("lower_pad", "", pm*f, pm, 1.-pm*f, fSplitFraction);
398 
399  SetPadMargins();
400 
401  // connect to the pads signal
402  fUpperPad->Connect("RangeAxisChanged()", "TRatioPlot", this, "RangeAxisChanged()");
403  fLowerPad->Connect("RangeAxisChanged()", "TRatioPlot", this, "RangeAxisChanged()");
404 
405  fUpperPad->Connect("UnZoomed()", "TRatioPlot", this, "UnZoomed()");
406  fLowerPad->Connect("UnZoomed()", "TRatioPlot", this, "UnZoomed()");
407 
408  fUpperPad->Connect("Resized()", "TRatioPlot", this, "SubPadResized()");
409  fLowerPad->Connect("Resized()", "TRatioPlot", this, "SubPadResized()");
410 
411  if (fTopPad != 0) {
412  delete fTopPad;
413  fTopPad = 0;
414  }
415 
416  fTopPad = new TPad("top_pad", "", pm*f, pm, 1-pm*f, 1-pm);
417 
418  fTopPad->SetBit(kCannotPick);
419 
420 }
421 
422 ////////////////////////////////////////////////////////////////////////////////
423 /// Browse.
424 
425 void TRatioPlot::Browse(TBrowser *b)
426 {
427  Draw(b ? b->GetDrawOption() : "");
428  gPad->Update();
429 }
430 
431 ////////////////////////////////////////////////////////////////////////////////
432 /// Sets the top margin of the upper pad.
433 ///
434 /// \param margin The new margin
435 
436 void TRatioPlot::SetUpTopMargin(Float_t margin)
437 {
438  fUpTopMargin = margin;
439  SetPadMargins();
440 }
441 
442 ////////////////////////////////////////////////////////////////////////////////
443 /// Sets the bottom margin of the upper pad.
444 ///
445 /// \param margin The new margin
446 
447 void TRatioPlot::SetUpBottomMargin(Float_t margin)
448 {
449  fUpBottomMargin = margin;
450  SetPadMargins();
451 }
452 
453 ////////////////////////////////////////////////////////////////////////////////
454 /// Sets the top margin of the lower pad.
455 ///
456 /// \param margin The new margin
457 
458 void TRatioPlot::SetLowTopMargin(Float_t margin)
459 {
460  fLowTopMargin = margin;
461  SetPadMargins();
462 }
463 
464 ////////////////////////////////////////////////////////////////////////////////
465 /// Sets the bottom margin of the lower pad.
466 ///
467 /// \param margin The new margin
468 
469 void TRatioPlot::SetLowBottomMargin(Float_t margin)
470 {
471  fLowBottomMargin = margin;
472  SetPadMargins();
473 }
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 /// Sets the left margin of both pads.
477 /// \param margin The new margin
478 
479 void TRatioPlot::SetLeftMargin(Float_t margin)
480 {
481  fLeftMargin = margin;
482  SetPadMargins();
483 }
484 
485 ////////////////////////////////////////////////////////////////////////////////
486 /// Sets the right margin of both pads.
487 ///
488 /// \param margin The new margin
489 
490 void TRatioPlot::SetRightMargin(Float_t margin)
491 {
492  fRightMargin = margin;
493  SetPadMargins();
494 }
495 
496 ////////////////////////////////////////////////////////////////////////////////
497 /// Sets the margin that separates the two pads. The margin is split according
498 /// to the relative sizes of the pads
499 ///
500 /// \param margin The new margin
501 ///
502 /// Begin_Macro(source)
503 /// ../../../tutorials/hist/ratioplot6.C
504 /// End_Macro
505 
506 void TRatioPlot::SetSeparationMargin(Float_t margin)
507 {
508  Float_t sf = fSplitFraction;
509  fUpBottomMargin = margin/2./(1-sf);
510  fLowTopMargin = margin/2./sf;
511  SetPadMargins();
512 }
513 
514 ////////////////////////////////////////////////////////////////////////////////
515 /// Return the separation margin value.
516 
517 Float_t TRatioPlot::GetSeparationMargin() const
518 {
519  Float_t sf = fSplitFraction;
520  Float_t up = fUpBottomMargin * (1-sf);
521  Float_t down = fLowTopMargin * sf;
522  return up+down;
523 }
524 
525 ////////////////////////////////////////////////////////////////////////////////
526 /// Draws the ratio plot to the currently active pad. Therefore it requires that
527 /// a TCanvas has been created first.
528 ///
529 /// It takes the following options
530 ///
531 /// | Option | Description |
532 /// | ---------- | ------------------------------------------------------------ |
533 /// | grid / nogrid | enable (default) or disable drawing of dashed lines on lower plot |
534 /// | hideup | hides the first label of the upper axis if there is not enough space |
535 /// | fhideup | always hides the first label of the upper axis |
536 /// | hidelow (default) | hides the last label of the lower axis if there is not enough space |
537 /// | fhidelow | always hides the last label of the lower axis |
538 /// | nohide | does not hide a label if there is not enough space |
539 /// | noconfint | does not draw the confidence interval bands in the fit residual case |
540 /// | confint | draws the confidence interval bands in the fit residual case (default) |
541 
542 void TRatioPlot::Draw(Option_t *option)
543 {
544 
545  TString drawOpt = option;
546 
547  if (drawOpt.Contains("nogrid")) {
548  drawOpt.ReplaceAll("nogrid", "");
549  fShowGridlines = kFALSE;
550  } else if (drawOpt.Contains("grid")) {
551  drawOpt.ReplaceAll("grid", "");
552  fShowGridlines = kTRUE;
553  }
554 
555  if (drawOpt.Contains("noconfint")) {
556  drawOpt.ReplaceAll("noconfint", "");
557  fShowConfidenceIntervals = kFALSE;
558  } else if (drawOpt.Contains("confint")) {
559  drawOpt.ReplaceAll("confint", "");
560  fShowConfidenceIntervals = kTRUE; // <- default
561  }
562 
563  if (drawOpt.Contains("fhideup")) {
564  fHideLabelMode = TRatioPlot::HideLabelMode::kForceHideUp;
565  } else if (drawOpt.Contains("fhidelow")) {
566  fHideLabelMode = TRatioPlot::HideLabelMode::kForceHideLow;
567  } else if (drawOpt.Contains("hideup")) {
568  fHideLabelMode = TRatioPlot::HideLabelMode::kHideUp;
569  } else if (drawOpt.Contains("hidelow")) {
570  fHideLabelMode = TRatioPlot::HideLabelMode::kHideLow;
571  } else if (drawOpt.Contains("nohide")) {
572  fHideLabelMode = TRatioPlot::HideLabelMode::kNoHide;
573  } else {
574  fHideLabelMode = TRatioPlot::HideLabelMode::kHideLow; // <- default
575  }
576 
577  if (!gPad) {
578  Error("Draw", "need to create a canvas first");
579  return;
580  }
581 
582  TVirtualPad *padsav = gPad;
583  fParentPad = gPad;
584 
585  fUpperPad->SetLogy(fParentPad->GetLogy());
586  fUpperPad->SetLogx(fParentPad->GetLogx());
587  fLowerPad->SetLogx(fParentPad->GetLogx());
588 
589  fUpperPad->SetGridx(fParentPad->GetGridx());
590  fUpperPad->SetGridy(fParentPad->GetGridy());
591  fLowerPad->SetGridx(fParentPad->GetGridx());
592  fLowerPad->SetGridy(fParentPad->GetGridy());
593 
594  // we are a TPad
595 
596  fUpperPad->Draw();
597  fLowerPad->Draw();
598 
599  fTopPad->SetFillStyle(0);
600  fTopPad->Draw();
601 
602  fUpperPad->cd();
603 
604  fConfidenceInterval2->SetFillColor(fCi1Color);
605  fConfidenceInterval1->SetFillColor(fCi2Color);
606 
607  if (fMode == TRatioPlot::CalculationMode::kFitResidual) {
608  TF1 *func = dynamic_cast<TF1*>(fH1->GetListOfFunctions()->At(0));
609 
610  if (func == 0) {
611  // this is checked in constructor and should thus not occur
612  Error("BuildLowerPlot", "h1 does not have a fit function");
613  return;
614  }
615 
616  fH1->Draw("A"+fH1DrawOpt);
617  func->Draw(fFitDrawOpt+"same");
618 
619  fLowerPad->cd();
620 
621  if (fShowConfidenceIntervals) {
622  fConfidenceInterval2->Draw("IA3");
623  fConfidenceInterval1->Draw("3");
624  fRatioGraph->Draw(fGraphDrawOpt+"SAME");
625  } else {
626  fRatioGraph->Draw("IA"+fGraphDrawOpt+"SAME");
627  }
628  } else {
629 
630  if (fHistDrawProxy) {
631  if (fHistDrawProxy->InheritsFrom(TH1::Class())) {
632  ((TH1*)fHistDrawProxy)->Draw("A"+fH1DrawOpt);
633  } else if (fHistDrawProxy->InheritsFrom(THStack::Class())) {
634  ((THStack*)fHistDrawProxy)->Draw("A"+fH1DrawOpt);
635  } else {
636  Warning("Draw", "Draw proxy not of type TH1 or THStack, not drawing it");
637  }
638  }
639 
640  fH2->Draw("A"+fH2DrawOpt+"same");
641 
642  fLowerPad->cd();
643 
644  TString opt = fGraphDrawOpt;
645  fRatioGraph->Draw("IA"+fGraphDrawOpt);
646 
647  }
648 
649  // assign same axis ranges to lower pad as in upper pad
650  // the visual axes will be created on paint
651  SyncAxesRanges();
652 
653  CreateGridline();
654 
655  padsav->cd();
656  AppendPad();
657 }
658 
659 ////////////////////////////////////////////////////////////////////////////////
660 /// Returns the reference graph for the lower pad, which means the graph that
661 /// is responsible for setting the coordinate system. It is the first graph
662 /// added to the primitive list of the lower pad.
663 /// This reference can be used to set the minimum and maximum of the lower pad.
664 /// Note that `TRatioPlot::Draw` needs to have been called first, since the
665 /// graphs are only created then.
666 ///
667 /// Begin_Macro(source)
668 /// ../../../tutorials/hist/ratioplot3.C
669 /// End_Macro
670 
671 TGraph* TRatioPlot::GetLowerRefGraph() const
672 {
673  if (fLowerPad == 0) {
674  Error("GetLowerRefGraph", "Lower pad has not been defined");
675  return 0;
676  }
677 
678  TList *primlist = fLowerPad->GetListOfPrimitives();
679  if (primlist->GetSize() == 0) {
680  Error("GetLowerRefGraph", "Lower pad does not have primitives");
681  return 0;
682  }
683 
684  TObjLink *lnk = primlist->FirstLink();
685 
686  while (lnk) {
687  TObject *obj = lnk->GetObject();
688 
689  if (obj->InheritsFrom(TGraph::Class())) {
690  return (TGraph*)obj;
691  }
692 
693  lnk = lnk->Next();
694  }
695 
696  Error("GetLowerRefGraph", "Did not find graph in list");
697  return 0;
698 }
699 
700 ////////////////////////////////////////////////////////////////////////////////
701 /// Return the reference object. Its the first TH1 or THStack type object
702 /// in the upper pads list of primitives.
703 /// Note that it returns a `TObject`, so you need to test and cast it to use it.
704 
705 TObject* TRatioPlot::GetUpperRefObject() const
706 {
707  TList *primlist = fUpperPad->GetListOfPrimitives();
708  TObject *refobj = 0;
709  for (Int_t i=0;i<primlist->GetSize();++i) {
710  refobj = primlist->At(i);
711  if (refobj->InheritsFrom(TH1::Class()) || refobj->InheritsFrom(THStack::Class())) {
712  return refobj;
713  }
714  }
715 
716  Error("GetUpperRefObject", "No upper ref object of TH1 or THStack type found");
717  return 0;
718 }
719 
720 ////////////////////////////////////////////////////////////////////////////////
721 /// Gets the x axis of the object returned by `TRatioPlot::GetUpperRefObject`.
722 
723 TAxis* TRatioPlot::GetUpperRefXaxis() const
724 {
725  TObject *refobj = GetUpperRefObject();
726 
727  if (!refobj) return 0;
728 
729  if (refobj->InheritsFrom(TH1::Class())) {
730  return ((TH1*)refobj)->GetXaxis();
731  } else if (refobj->InheritsFrom(THStack::Class())) {
732  return ((THStack*)refobj)->GetXaxis();
733  }
734 
735  return 0;
736 }
737 
738 ////////////////////////////////////////////////////////////////////////////////
739 /// Gets the y axis of the object returned by `TRatioPlot::GetUpperRefObject`.
740 
741 TAxis* TRatioPlot::GetUpperRefYaxis() const
742 {
743  TObject *refobj = GetUpperRefObject();
744 
745  if (!refobj) return 0;
746 
747  if (refobj->InheritsFrom(TH1::Class())) {
748  return ((TH1*)refobj)->GetYaxis();
749  } else if (refobj->InheritsFrom(THStack::Class())) {
750  return ((THStack*)refobj)->GetYaxis();
751  }
752 
753  return 0;
754 }
755 
756 ////////////////////////////////////////////////////////////////////////////////
757 /// Create a grid line
758 
759 void TRatioPlot::CreateGridline()
760 {
761 
762  if (!fShowGridlines) {
763  return; // don't draw them
764  }
765 
766  TVirtualPad *padsav = gPad;
767 
768  fLowerPad->cd();
769 
770  unsigned int dest = fGridlinePositions.size();
771 
772  Double_t lowYFirst = fLowerPad->GetUymin();
773  Double_t lowYLast = fLowerPad->GetUymax();
774 
775  double y;
776  int outofrange = 0;
777  for (unsigned int i=0;i<fGridlinePositions.size();++i) {
778  y = fGridlinePositions.at(i);
779 
780  if (y < lowYFirst || lowYLast < y) {
781  ++outofrange;
782  }
783 
784  }
785 
786  dest = dest - outofrange;
787 
788  // clear all
789  for (unsigned int i=0;i<fGridlines.size();++i) {
790  delete fGridlines.at(i);
791  }
792 
793  fGridlines.erase(fGridlines.begin(), fGridlines.end());
794 
795  for (unsigned int i=0;i<dest;++i) {
796  TLine *newline = new TLine(0, 0, 0, 0);
797  newline->SetLineStyle(2);
798  newline->Draw();
799  fGridlines.push_back(newline);
800  }
801 
802  Double_t first = fSharedXAxis->GetBinLowEdge(fSharedXAxis->GetFirst());
803  Double_t last = fSharedXAxis->GetBinUpEdge(fSharedXAxis->GetLast());
804 
805  TLine *line;
806  unsigned int skipped = 0;
807  for (unsigned int i=0;i<fGridlinePositions.size();++i) {
808  y = fGridlinePositions[i];
809 
810  if (y < lowYFirst || lowYLast < y) {
811  // this is one of the ones that was out of range
812  ++skipped;
813  continue;
814  }
815 
816  line = fGridlines.at(i-skipped);
817 
818  line->SetX1(first);
819  line->SetX2(last);
820  line->SetY1(y);
821  line->SetY2(y);
822  }
823 
824  padsav->cd();
825 }
826 
827 ////////////////////////////////////////////////////////////////////////////////
828 /// Creates the visual axes when painting.
829 
830 void TRatioPlot::Paint(Option_t * /*opt*/)
831 {
832  // create the visual axes
833  CreateVisualAxes();
834  CreateGridline();
835 
836  if (fIsUpdating) fIsUpdating = kFALSE;
837 }
838 
839 ////////////////////////////////////////////////////////////////////////////////
840 /// Syncs the axes ranges from the shared ones to the actual ones.
841 
842 void TRatioPlot::SyncAxesRanges()
843 {
844  // get ranges from the shared axis clone
845  Double_t first = fSharedXAxis->GetBinLowEdge(fSharedXAxis->GetFirst());
846  Double_t last = fSharedXAxis->GetBinUpEdge(fSharedXAxis->GetLast());
847 
848  // set range on computed graph, have to set it twice because
849  // TGraph's axis looks strange otherwise
850  TAxis *ref = GetLowerRefXaxis();
851  ref->SetLimits(first, last);
852  ref->SetRangeUser(first, last);
853 
854  GetUpperRefXaxis()->SetRangeUser(first, last);
855 
856 }
857 
858 ////////////////////////////////////////////////////////////////////////////////
859 /// Build the lower plot according to which constructor was called, and
860 /// which options were passed.
861 
862 Int_t TRatioPlot::BuildLowerPlot()
863 {
864  // Clear and delete the graph if not exists
865  if (fRatioGraph != 0) {
866  fRatioGraph->IsA()->Destructor(fRatioGraph);
867  fRatioGraph = 0;
868  }
869 
870  if (fConfidenceInterval1 == 0) {
871  fConfidenceInterval1 = new TGraphErrors();
872  }
873 
874  if (fConfidenceInterval2 == 0) {
875  fConfidenceInterval2 = new TGraphErrors();
876  }
877 
878  static Double_t divideGridlines[] = {0.7, 1.0, 1.3};
879  static Double_t diffGridlines[] = {0.0};
880  static Double_t signGridlines[] = {1.0, 0.0, -1.0};
881 
882  // Determine the divide mode and create the lower graph accordingly
883  // Pass divide options given in constructor
884  if (fMode == TRatioPlot::CalculationMode::kDivideGraph) {
885  // use TGraphAsymmErrors Divide method to create
886 
887  SetGridlines(divideGridlines, 3);
888 
889  TH1 *tmpH1 = (TH1*)fH1->Clone();
890  TH1 *tmpH2 = (TH1*)fH2->Clone();
891 
892  tmpH1->Scale(fC1);
893  tmpH2->Scale(fC2);
894 
895  TGraphAsymmErrors *ratioGraph = new TGraphAsymmErrors();
896  ratioGraph->Divide(tmpH1, tmpH2, fOption.Data());
897  fRatioGraph = ratioGraph;
898 
899  delete tmpH1;
900  delete tmpH2;
901 
902  } else if (fMode == TRatioPlot::CalculationMode::kDifference) {
903  SetGridlines(diffGridlines, 3);
904 
905  TH1 *tmpHist = (TH1*)fH1->Clone();
906 
907  tmpHist->Reset();
908 
909  tmpHist->Add(fH1, fH2, fC1, -1*fC2);
910  fRatioGraph = new TGraphErrors(tmpHist);
911 
912  delete tmpHist;
913  } else if (fMode == TRatioPlot::CalculationMode::kDifferenceSign) {
914 
915  SetGridlines(signGridlines, 3);
916 
917  fRatioGraph = new TGraphAsymmErrors();
918  Int_t ipoint = 0;
919  Double_t res;
920  Double_t error;
921 
922  Double_t val;
923  Double_t val2;
924 
925  for (Int_t i=0; i<=fH1->GetNbinsX();++i) {
926  val = fH1->GetBinContent(i);
927  val2 = fH2->GetBinContent(i);
928 
929  if (fErrorMode == TRatioPlot::ErrorMode::kErrorAsymmetric) {
930 
931  Double_t errUp = fH1->GetBinErrorUp(i);
932  Double_t errLow = fH1->GetBinErrorLow(i);
933 
934  if (val - val2 > 0) {
935  // h1 > h2
936  error = errLow;
937  } else {
938  // h1 < h2
939  error = errUp;
940  }
941 
942  } else if (fErrorMode == TRatioPlot::ErrorMode::kErrorSymmetric) {
943  error = fH1->GetBinError(i);
944  } else {
945  Warning("BuildLowerPlot", "error mode is invalid");
946  error = 0;
947  }
948 
949  if (error != 0) {
950 
951  res = (val - val2) / error;
952 
953  ((TGraphAsymmErrors*)fRatioGraph)->SetPoint(ipoint, fH1->GetBinCenter(i), res);
954  ((TGraphAsymmErrors*)fRatioGraph)->SetPointError(ipoint, fH1->GetBinWidth(i)/2., fH1->GetBinWidth(i)/2., 0.5, 0.5);
955 
956  ++ipoint;
957 
958  }
959  }
960 
961  } else if (fMode == TRatioPlot::CalculationMode::kFitResidual) {
962 
963  SetGridlines(signGridlines, 3);
964 
965  TF1 *func = dynamic_cast<TF1*>(fH1->GetListOfFunctions()->At(0));
966 
967  if (func == 0) {
968  // this is checked in constructor and should thus not occur
969  Error("BuildLowerPlot", "h1 does not have a fit function");
970  return 0;
971  }
972 
973  fRatioGraph = new TGraphAsymmErrors();
974  Int_t ipoint = 0;
975 
976  Double_t res;
977  Double_t error;
978 
979  std::vector<double> ci1;
980  std::vector<double> ci2;
981 
982  Double_t *x_arr = new Double_t[fH1->GetNbinsX()];
983  std::fill_n(x_arr, fH1->GetNbinsX(), 0);
984  Double_t *ci_arr1 = new Double_t[fH1->GetNbinsX()];
985  std::fill_n(ci_arr1, fH1->GetNbinsX(), 0);
986  Double_t *ci_arr2 = new Double_t[fH1->GetNbinsX()];
987  std::fill_n(ci_arr2, fH1->GetNbinsX(), 0);
988  for (Int_t i=0; i<fH1->GetNbinsX();++i) {
989  x_arr[i] = fH1->GetBinCenter(i+1);
990  }
991 
992  Double_t cl1 = fCl1;
993  Double_t cl2 = fCl2;
994 
995  if (fFitResult != 0) {
996  // use this to get conf int
997 
998  fFitResult->GetConfidenceIntervals(fH1->GetNbinsX(), 1, 1, x_arr, ci_arr1, cl1);
999  for (Int_t i=1; i<=fH1->GetNbinsX();++i) {
1000  ci1.push_back(ci_arr1[i-1]);
1001  }
1002 
1003  fFitResult->GetConfidenceIntervals(fH1->GetNbinsX(), 1, 1, x_arr, ci_arr2, cl2);
1004  for (Int_t i=1; i<=fH1->GetNbinsX();++i) {
1005  ci2.push_back(ci_arr2[i-1]);
1006  }
1007  } else {
1008  (TVirtualFitter::GetFitter())->GetConfidenceIntervals(fH1->GetNbinsX(), 1, x_arr, ci_arr1, cl1);
1009  for (Int_t i=1; i<=fH1->GetNbinsX();++i) {
1010  ci1.push_back(ci_arr1[i-1]);
1011  }
1012  (TVirtualFitter::GetFitter())->GetConfidenceIntervals(fH1->GetNbinsX(), 1, x_arr, ci_arr2, cl2);
1013  for (Int_t i=1; i<=fH1->GetNbinsX();++i) {
1014  ci2.push_back(ci_arr2[i-1]);
1015  }
1016 
1017  }
1018 
1019  Double_t x;
1020  Double_t val;
1021 
1022  for (Int_t i=0; i<=fH1->GetNbinsX();++i) {
1023  val = fH1->GetBinContent(i);
1024  x = fH1->GetBinCenter(i+1);
1025 
1026  if (fErrorMode == TRatioPlot::ErrorMode::kErrorAsymmetric) {
1027 
1028  Double_t errUp = fH1->GetBinErrorUp(i);
1029  Double_t errLow = fH1->GetBinErrorLow(i);
1030 
1031  if (val - func->Eval(fH1->GetBinCenter(i)) > 0) {
1032  // h1 > fit
1033  error = errLow;
1034  } else {
1035  // h1 < fit
1036  error = errUp;
1037  }
1038 
1039  } else if (fErrorMode == TRatioPlot::ErrorMode::kErrorSymmetric) {
1040  error = fH1->GetBinError(i);
1041  } else if (fErrorMode == TRatioPlot::ErrorMode::kErrorFunc) {
1042 
1043  error = sqrt(func->Eval(x));
1044 
1045  } else {
1046  Warning("BuildLowerPlot", "error mode is invalid");
1047  error = 0;
1048  }
1049 
1050  if (error != 0) {
1051 
1052  res = (fH1->GetBinContent(i)- func->Eval(fH1->GetBinCenter(i) ) ) / error;
1053  //__("x="<< x << " y=" << res << " err=" << error);
1054 
1055  ((TGraphAsymmErrors*)fRatioGraph)->SetPoint(ipoint, fH1->GetBinCenter(i), res);
1056  ((TGraphAsymmErrors*)fRatioGraph)->SetPointError(ipoint, fH1->GetBinWidth(i)/2., fH1->GetBinWidth(i)/2., 0.5, 0.5);
1057 
1058  fConfidenceInterval1->SetPoint(ipoint, x, 0);
1059  fConfidenceInterval1->SetPointError(ipoint, x, i < (Int_t)ci1.size() ? ci1[i] / error : 0);
1060  fConfidenceInterval2->SetPoint(ipoint, x, 0);
1061  fConfidenceInterval2->SetPointError(ipoint, x, i < (Int_t)ci2.size() ? ci2[i] / error : 0);
1062 
1063  ++ipoint;
1064 
1065  }
1066 
1067  }
1068  delete [] x_arr;
1069  delete [] ci_arr1;
1070  delete [] ci_arr2;
1071  } else if (fMode == TRatioPlot::CalculationMode::kDivideHist){
1072  SetGridlines(divideGridlines, 3);
1073 
1074  // Use TH1's Divide method
1075  TH1 *tmpHist = (TH1*)fH1->Clone();
1076  tmpHist->Reset();
1077 
1078  tmpHist->Divide(fH1, fH2, fC1, fC2, fOption.Data());
1079  fRatioGraph = new TGraphErrors(tmpHist);
1080 
1081  delete tmpHist;
1082  } else {
1083  // this should not occur
1084  Error("BuildLowerPlot", "Invalid fMode value");
1085  return 0;
1086  }
1087 
1088  // need to set back to "" since recreation. we don't ever want
1089  // title on lower graph
1090 
1091  if (fRatioGraph == 0) {
1092  Error("BuildLowerPlot", "Error creating lower graph");
1093  return 0;
1094  }
1095 
1096  fRatioGraph->SetTitle("");
1097  fConfidenceInterval1->SetTitle("");
1098  fConfidenceInterval2->SetTitle("");
1099 
1100  return 1;
1101 }
1102 
1103 ////////////////////////////////////////////////////////////////////////////////
1104 /// (Re-)Creates the TGAxis objects that are used for consistent display of the
1105 /// axes.
1106 
1107 void TRatioPlot::CreateVisualAxes()
1108 {
1109  TVirtualPad *padsav = gPad;
1110  fTopPad->cd();
1111 
1112  // this is for errors
1113  TString thisfunc = "CreateVisualAxes";
1114 
1115  // figure out where the axis has to go.
1116  // Implicit assumption is, that the top pad spans the full other pads
1117  Double_t upTM = fUpperPad->GetTopMargin();
1118  Double_t upBM = fUpperPad->GetBottomMargin();
1119  Double_t upLM = fUpperPad->GetLeftMargin();
1120  Double_t upRM = fUpperPad->GetRightMargin();
1121 
1122  Double_t lowTM = fLowerPad->GetTopMargin();
1123  Double_t lowBM = fLowerPad->GetBottomMargin();
1124  Double_t lowLM = fLowerPad->GetLeftMargin();
1125  Double_t lowRM = fLowerPad->GetRightMargin();
1126 
1127  Double_t first = fSharedXAxis->GetBinLowEdge(fSharedXAxis->GetFirst());
1128  Double_t last = fSharedXAxis->GetBinUpEdge(fSharedXAxis->GetLast());
1129 
1130  Double_t upYFirst = fUpperPad->GetUymin();
1131  Double_t upYLast = fUpperPad->GetUymax();
1132  Double_t lowYFirst = fLowerPad->GetUymin();
1133  Double_t lowYLast = fLowerPad->GetUymax();
1134 
1135  Float_t sf = fSplitFraction;
1136 
1137  // check if gPad has the all sides axis set
1138  Bool_t mirroredAxes = fParentPad->GetFrameFillStyle() == 0;
1139  Bool_t axistop = fParentPad->GetTickx() == 1 || mirroredAxes;
1140  Bool_t axisright = fParentPad->GetTicky() == 1 || mirroredAxes;
1141 
1142  Bool_t logx = fUpperPad->GetLogx() || fLowerPad->GetLogx();
1143  Bool_t uplogy = fUpperPad->GetLogy();
1144  Bool_t lowlogy = fLowerPad->GetLogy();
1145 
1146  if (uplogy) {
1147 
1148  upYFirst = TMath::Power(10, upYFirst);
1149  upYLast = TMath::Power(10, upYLast);
1150 
1151  if (upYFirst <= 0 || upYLast <= 0) {
1152  Error(thisfunc, "Cannot set upper Y axis to log scale");
1153  }
1154  }
1155 
1156  if (lowlogy) {
1157  lowYFirst = TMath::Power(10, lowYFirst);
1158  lowYLast = TMath::Power(10, lowYLast);
1159 
1160  if (lowYFirst <= 0 || lowYLast <= 0) {
1161  Error(thisfunc, "Cannot set lower Y axis to log scale");
1162  }
1163 
1164  }
1165 
1166  // this is different than in y, y already has pad coords converted, x not...
1167  if (logx) {
1168  if (first <= 0 || last <= 0) {
1169  Error(thisfunc, "Cannot set X axis to log scale");
1170  }
1171  }
1172 
1173  // determine axes options to create log axes if needed
1174  TString xopt = "";
1175  if (logx) xopt.Append("G");
1176  TString upyopt = "";
1177  if (uplogy) upyopt.Append("G");
1178  TString lowyopt = "";
1179  if (lowlogy) lowyopt.Append("G");
1180 
1181  // only actually create them once, reuse otherwise b/c memory
1182  if (fUpperGXaxis == 0) {
1183  fUpperGXaxis = new TGaxis(0, 0, 1, 1, 0, 1, 510, "+U"+xopt);
1184  fUpperGXaxis->Draw();
1185  }
1186 
1187  if (fUpperGYaxis == 0) {
1188  fUpperGYaxis = new TGaxis(0, 0, 1, 1, upYFirst, upYLast, 510, "S"+upyopt);
1189  fUpperGYaxis->Draw();
1190  }
1191 
1192  if (fLowerGXaxis == 0) {
1193  fLowerGXaxis = new TGaxis(0, 0, 1, 1, first, last, 510, "+S"+xopt);
1194  fLowerGXaxis->Draw();
1195  }
1196 
1197  if (fLowerGYaxis == 0) {
1198  fLowerGYaxis = new TGaxis(0, 0, 1, 1, lowYFirst, lowYLast, 510, "-S"+lowyopt);
1199  fLowerGYaxis->Draw();
1200  }
1201 
1202  // import infos from TAxis
1203  ImportAxisAttributes(fUpperGXaxis, GetUpperRefXaxis());
1204  ImportAxisAttributes(fUpperGYaxis, GetUpperRefYaxis());
1205  ImportAxisAttributes(fLowerGXaxis, GetLowerRefXaxis());
1206  ImportAxisAttributes(fLowerGYaxis, GetLowerRefYaxis());
1207 
1208  // lower x axis needs to get title from upper x
1209  fLowerGXaxis->SetTitle(fUpperGXaxis->GetTitle());
1210 
1211  // (re)set all the axes properties to what we want them
1212  fUpperGXaxis->SetTitle("");
1213 
1214  fUpperGXaxis->SetX1(upLM);
1215  fUpperGXaxis->SetX2(1-upRM);
1216  fUpperGXaxis->SetY1(upBM*(1-sf)+sf);
1217  fUpperGXaxis->SetY2(upBM*(1-sf)+sf);
1218  fUpperGXaxis->SetWmin(first);
1219  fUpperGXaxis->SetWmax(last);
1220 
1221  fUpperGYaxis->SetX1(upLM);
1222  fUpperGYaxis->SetX2(upLM);
1223  fUpperGYaxis->SetY1(upBM*(1-sf)+sf);
1224  fUpperGYaxis->SetY2( (1-upTM)*(1-sf)+sf );
1225  fUpperGYaxis->SetWmin(upYFirst);
1226  fUpperGYaxis->SetWmax(upYLast);
1227 
1228  fLowerGXaxis->SetX1(lowLM);
1229  fLowerGXaxis->SetX2(1-lowRM);
1230  fLowerGXaxis->SetY1(lowBM*sf);
1231  fLowerGXaxis->SetY2(lowBM*sf);
1232  fLowerGXaxis->SetWmin(first);
1233  fLowerGXaxis->SetWmax(last);
1234 
1235  fLowerGYaxis->SetX1(lowLM);
1236  fLowerGYaxis->SetX2(lowLM);
1237  fLowerGYaxis->SetY1(lowBM*sf);
1238  fLowerGYaxis->SetY2((1-lowTM)*sf);
1239  fLowerGYaxis->SetWmin(lowYFirst);
1240  fLowerGYaxis->SetWmax(lowYLast);
1241 
1242  fUpperGXaxis->SetNdivisions(fSharedXAxis->GetNdivisions());
1243  fUpperGYaxis->SetNdivisions(fUpYaxis->GetNdivisions());
1244  fLowerGXaxis->SetNdivisions(fSharedXAxis->GetNdivisions());
1245  fLowerGYaxis->SetNdivisions(fLowYaxis->GetNdivisions());
1246 
1247  fUpperGXaxis->SetOption("+U"+xopt);
1248  fUpperGYaxis->SetOption("S"+upyopt);
1249  fLowerGXaxis->SetOption("+S"+xopt);
1250  fLowerGYaxis->SetOption("-S"+lowyopt);
1251 
1252  // normalize the tick sizes. y axis ticks should be consistent
1253  // even if their length is different
1254  Double_t ratio = ( (upBM-(1-upTM))*(1-sf) ) / ( (lowBM-(1-lowTM))*sf ) ;
1255  fUpperGXaxis->SetLabelSize(0.);
1256  Double_t ticksize = fUpperGYaxis->GetTickSize()*ratio;
1257  fLowerGYaxis->SetTickSize(ticksize);
1258 
1259  if (fHideLabelMode == TRatioPlot::HideLabelMode::kForceHideUp) {
1260 
1261  fUpperGYaxis->ChangeLabel(1, -1, 0);
1262 
1263  } else if (fHideLabelMode == TRatioPlot::HideLabelMode::kForceHideLow) {
1264 
1265  fLowerGYaxis->ChangeLabel(-1, -1, 0);
1266 
1267  } else {
1268  if (GetSeparationMargin() < 0.025) {
1269 
1270  if (fHideLabelMode != TRatioPlot::HideLabelMode::kNoHide) {
1271  if (fHideLabelMode == TRatioPlot::HideLabelMode::kHideUp) {
1272  fUpperGYaxis->ChangeLabel(1, -1, 0);
1273  } else if (fHideLabelMode == TRatioPlot::HideLabelMode::kHideLow) {
1274  fLowerGYaxis->ChangeLabel(-1, -1, 0);
1275  }
1276  }
1277 
1278  } else {
1279  // reset
1280  if (fHideLabelMode == TRatioPlot::HideLabelMode::kHideUp) {
1281  fUpperGYaxis->ChangeLabel(0);
1282  } else if (fHideLabelMode == TRatioPlot::HideLabelMode::kHideLow) {
1283  fLowerGYaxis->ChangeLabel(0);
1284  }
1285 
1286  }
1287  }
1288 
1289  // Create the axes on the other sides of the graphs
1290  // This is steered by an option on the containing pad or self
1291  if (axistop || axisright) {
1292 
1293  // only actually create them once, reuse otherwise b/c memory
1294  if (fUpperGXaxisMirror == 0) {
1295  fUpperGXaxisMirror = (TGaxis*)fUpperGXaxis->Clone();
1296  if (axistop) fUpperGXaxisMirror->Draw();
1297  }
1298 
1299  if (fLowerGXaxisMirror == 0) {
1300  fLowerGXaxisMirror = (TGaxis*)fLowerGXaxis->Clone();
1301  if (axistop) fLowerGXaxisMirror->Draw();
1302  }
1303 
1304  if (fUpperGYaxisMirror == 0) {
1305  fUpperGYaxisMirror = (TGaxis*)fUpperGYaxis->Clone();
1306  if (axisright) fUpperGYaxisMirror->Draw();
1307  }
1308 
1309  if (fLowerGYaxisMirror == 0) {
1310  fLowerGYaxisMirror = (TGaxis*)fLowerGYaxis->Clone();
1311  if (axisright) fLowerGYaxisMirror->Draw();
1312  }
1313 
1314  // import attributes from shared axes
1315  ImportAxisAttributes(fUpperGXaxisMirror, GetUpperRefXaxis());
1316  ImportAxisAttributes(fUpperGYaxisMirror, GetUpperRefYaxis());
1317  ImportAxisAttributes(fLowerGXaxisMirror, GetLowerRefXaxis());
1318  ImportAxisAttributes(fLowerGYaxisMirror, GetLowerRefYaxis());
1319 
1320  // remove titles
1321  fUpperGXaxisMirror->SetTitle("");
1322  fUpperGYaxisMirror->SetTitle("");
1323  fLowerGXaxisMirror->SetTitle("");
1324  fLowerGYaxisMirror->SetTitle("");
1325 
1326  // move them about and set required positions
1327  fUpperGXaxisMirror->SetX1(upLM);
1328  fUpperGXaxisMirror->SetX2(1-upRM);
1329  fUpperGXaxisMirror->SetY1((1-upTM)*(1-sf)+sf);
1330  fUpperGXaxisMirror->SetY2((1-upTM)*(1-sf)+sf);
1331  fUpperGXaxisMirror->SetWmin(first);
1332  fUpperGXaxisMirror->SetWmax(last);
1333 
1334  fUpperGYaxisMirror->SetX1(1-upRM);
1335  fUpperGYaxisMirror->SetX2(1-upRM);
1336  fUpperGYaxisMirror->SetY1(upBM*(1-sf)+sf);
1337  fUpperGYaxisMirror->SetY2( (1-upTM)*(1-sf)+sf );
1338  fUpperGYaxisMirror->SetWmin(upYFirst);
1339  fUpperGYaxisMirror->SetWmax(upYLast);
1340 
1341  fLowerGXaxisMirror->SetX1(lowLM);
1342  fLowerGXaxisMirror->SetX2(1-lowRM);
1343  fLowerGXaxisMirror->SetY1((1-lowTM)*sf);
1344  fLowerGXaxisMirror->SetY2((1-lowTM)*sf);
1345  fLowerGXaxisMirror->SetWmin(first);
1346  fLowerGXaxisMirror->SetWmax(last);
1347 
1348  fLowerGYaxisMirror->SetX1(1-lowRM);
1349  fLowerGYaxisMirror->SetX2(1-lowRM);
1350  fLowerGYaxisMirror->SetY1(lowBM*sf);
1351  fLowerGYaxisMirror->SetY2((1-lowTM)*sf);
1352  fLowerGYaxisMirror->SetWmin(lowYFirst);
1353  fLowerGYaxisMirror->SetWmax(lowYLast);
1354 
1355  // also needs normalized tick size
1356  fLowerGYaxisMirror->SetTickSize(ticksize);
1357 
1358  fUpperGXaxisMirror->SetOption("-S"+xopt);
1359  fUpperGYaxisMirror->SetOption("+S"+upyopt);
1360  fLowerGXaxisMirror->SetOption("-S"+xopt);
1361  fLowerGYaxisMirror->SetOption("+S"+lowyopt);
1362 
1363  fUpperGXaxisMirror->SetNdivisions(fSharedXAxis->GetNdivisions());
1364  fUpperGYaxisMirror->SetNdivisions(fUpYaxis->GetNdivisions());
1365  fLowerGXaxisMirror->SetNdivisions(fSharedXAxis->GetNdivisions());
1366  fLowerGYaxisMirror->SetNdivisions(fLowYaxis->GetNdivisions());
1367 
1368  fUpperGXaxisMirror->SetLabelSize(0.);
1369  fLowerGXaxisMirror->SetLabelSize(0.);
1370  fUpperGYaxisMirror->SetLabelSize(0.);
1371  fLowerGYaxisMirror->SetLabelSize(0.);
1372  }
1373 
1374  padsav->cd();
1375 
1376 }
1377 
1378 ////////////////////////////////////////////////////////////////////////////////
1379 /// Sets the margins of all the pads to the value specified in class members.
1380 /// This one is called whenever those are changed, e.g. in setters
1381 
1382 void TRatioPlot::SetPadMargins()
1383 {
1384  fUpperPad->SetTopMargin(fUpTopMargin);
1385  fUpperPad->SetBottomMargin(fUpBottomMargin);
1386  fUpperPad->SetLeftMargin(fLeftMargin);
1387  fUpperPad->SetRightMargin(fRightMargin);
1388  fLowerPad->SetTopMargin(fLowTopMargin);
1389  fLowerPad->SetBottomMargin(fLowBottomMargin);
1390  fLowerPad->SetLeftMargin(fLeftMargin);
1391  fLowerPad->SetRightMargin(fRightMargin);
1392 }
1393 
1394 ////////////////////////////////////////////////////////////////////////////////
1395 /// Figures out which pad margin has deviated from the stored ones,
1396 /// to figure out what the new nominal is and set the other pad to it
1397 /// subsequently.
1398 
1399 Bool_t TRatioPlot::SyncPadMargins()
1400 {
1401 
1402  Bool_t changed = kFALSE;
1403 
1404  if (fUpperPad->GetLeftMargin() != fLeftMargin) {
1405  fLeftMargin = fUpperPad->GetLeftMargin();
1406  changed = kTRUE;
1407  }
1408  else if (fLowerPad->GetLeftMargin() != fLeftMargin) {
1409  fLeftMargin = fLowerPad->GetLeftMargin();
1410  changed = kTRUE;
1411  }
1412 
1413  if (fUpperPad->GetRightMargin() != fRightMargin) {
1414  fRightMargin = fUpperPad->GetRightMargin();
1415  changed = kTRUE;
1416  }
1417  else if (fLowerPad->GetRightMargin() != fRightMargin) {
1418  fRightMargin = fLowerPad->GetRightMargin();
1419  changed = kTRUE;
1420  }
1421 
1422  // only reset margins, if any of the margins changed
1423  if (changed) {
1424  SetPadMargins();
1425  }
1426 
1427  Bool_t verticalChanged = kFALSE;
1428 
1429  if (fUpperPad->GetBottomMargin() != fUpBottomMargin) {
1430 
1431  verticalChanged = kTRUE;
1432  fUpBottomMargin = fUpperPad->GetBottomMargin();
1433 
1434  }
1435 
1436  if (fLowerPad->GetTopMargin() != fLowTopMargin) {
1437 
1438  verticalChanged = kTRUE;
1439  fLowTopMargin = fLowerPad->GetTopMargin();
1440 
1441  }
1442 
1443  if (fLowerPad->GetBottomMargin() != fLowBottomMargin) {
1444 
1445  fLowBottomMargin = fLowerPad->GetBottomMargin();
1446 
1447  }
1448 
1449  if (fUpperPad->GetTopMargin() != fUpTopMargin) {
1450 
1451  fUpTopMargin = fUpperPad->GetTopMargin();
1452 
1453  }
1454 
1455  // only reset margins, if any of the margins changed
1456  if (verticalChanged) {
1457  SetPadMargins();
1458  }
1459 
1460  return changed || verticalChanged;
1461 
1462 }
1463 
1464 ////////////////////////////////////////////////////////////////////////////////
1465 /// Slot that receives the RangeAxisChanged signal from any of the pads and
1466 /// reacts correspondingly.
1467 
1468 void TRatioPlot::RangeAxisChanged()
1469 {
1470  // check if the ratio plot is already drawn.
1471  if (!IsDrawn()) {
1472  // not drawn yet
1473  return;
1474  }
1475 
1476  // Only run this concurrently once, in case it's called async
1477  if (fIsUpdating) {
1478  return;
1479  }
1480 
1481  fIsUpdating = kTRUE;
1482 
1483  // find out if logx has changed
1484  if (fParentPad->GetLogx()) {
1485  if (!fUpperPad->GetLogx() || !fLowerPad->GetLogx()) {
1486  fParentPad->SetLogx(kFALSE);
1487  }
1488  } else {
1489  if (fUpperPad->GetLogx() || fLowerPad->GetLogx()) {
1490  fParentPad->SetLogx(kTRUE);
1491  }
1492  }
1493 
1494  // set log to pad
1495  fUpperPad->SetLogx(fParentPad->GetLogx());
1496  fLowerPad->SetLogx(fParentPad->GetLogx());
1497 
1498  // get axis ranges for upper and lower
1499  TAxis *uprefx = GetUpperRefXaxis();
1500  Double_t upFirst = uprefx->GetBinLowEdge(uprefx->GetFirst());
1501  Double_t upLast = uprefx->GetBinUpEdge(uprefx->GetLast());
1502 
1503  TAxis *lowrefx = GetLowerRefXaxis();
1504  Double_t lowFirst = lowrefx->GetBinLowEdge(lowrefx->GetFirst());
1505  Double_t lowLast = lowrefx->GetBinUpEdge(lowrefx->GetLast());
1506 
1507  Double_t globFirst = fSharedXAxis->GetBinLowEdge(fSharedXAxis->GetFirst());
1508  Double_t globLast = fSharedXAxis->GetBinUpEdge(fSharedXAxis->GetLast());
1509 
1510  Bool_t upChanged = kFALSE;
1511  Bool_t lowChanged = kFALSE;
1512 
1513  // determine which one has changed
1514  if (upFirst != globFirst || upLast != globLast) {
1515  fSharedXAxis->SetRangeUser(upFirst, upLast);
1516  upChanged = kTRUE;
1517  }
1518  else if (lowFirst != globFirst || lowLast != globLast) {
1519  fSharedXAxis->SetRangeUser(lowFirst, lowLast);
1520  lowChanged = kTRUE;
1521  }
1522 
1523  if (upChanged || lowChanged) {
1524  SyncAxesRanges();
1525  CreateVisualAxes();
1526  CreateGridline();
1527 
1528  // @TODO: Updating is not working when zooming on the lower plot. Axes update, but upper hist only on resize
1529  fUpperPad->Modified();
1530  fLowerPad->Modified();
1531  fTopPad->Modified();
1532  fParentPad->Modified();
1533  }
1534 
1535  // sync the margins in case the user has dragged one of them
1536  Bool_t marginsChanged = SyncPadMargins();
1537 
1538  if (marginsChanged) {
1539  fUpperPad->Modified();
1540  fLowerPad->Modified();
1541  fTopPad->Modified();
1542  fParentPad->Modified();
1543  }
1544 
1545  CreateVisualAxes();
1546  CreateGridline();
1547  fIsUpdating = kFALSE;
1548 }
1549 
1550 ////////////////////////////////////////////////////////////////////////////////
1551 /// Slot for the UnZoomed signal that was introduced to TAxis.
1552 /// Unzoom both pads
1553 
1554 void TRatioPlot::UnZoomed()
1555 {
1556  // this is what resets the range
1557  fSharedXAxis->SetRange(0, 0);
1558  SyncAxesRanges();
1559 
1560  // Flushing
1561  fUpperPad->Modified();
1562  fLowerPad->Modified();
1563  fTopPad->Modified();
1564  fParentPad->Modified();
1565 }
1566 
1567 ////////////////////////////////////////////////////////////////////////////////
1568 /// Slot that handles common resizing of upper and lower pad.
1569 
1570 void TRatioPlot::SubPadResized()
1571 {
1572 
1573  if (fIsPadUpdating) {
1574  return;
1575  }
1576 
1577  fIsPadUpdating = kTRUE;
1578 
1579  Float_t upylow = fUpperPad->GetYlowNDC();
1580  Float_t lowylow = fLowerPad->GetYlowNDC();
1581  Float_t lowh = fLowerPad->GetHNDC();
1582  Float_t lowyup = lowylow + lowh;
1583 
1584  Bool_t changed = kFALSE;
1585 
1586  if (upylow != fSplitFraction) {
1587  // up changed
1588  SetSplitFraction(upylow);
1589  changed = kTRUE;
1590  }
1591  else if (lowyup != fSplitFraction) {
1592  // low changed
1593  SetSplitFraction(lowyup);
1594  changed = kTRUE;
1595  }
1596 
1597  if (changed) {
1598  CreateVisualAxes();
1599  }
1600 
1601  fIsPadUpdating = kFALSE;
1602 
1603 }
1604 
1605 ////////////////////////////////////////////////////////////////////////////////
1606 /// Check if ... is drawn.
1607 
1608 Bool_t TRatioPlot::IsDrawn()
1609 {
1610  TList *siblings = fParentPad->GetListOfPrimitives();
1611  return siblings->FindObject(this) != 0;
1612 }
1613 
1614 ////////////////////////////////////////////////////////////////////////////////
1615 /// Set the fraction of the parent pad, at which the to sub pads should meet
1616 
1617 void TRatioPlot::SetSplitFraction(Float_t sf)
1618 {
1619  if (fParentPad == 0) {
1620  Warning("SetSplitFraction", "Can only be used after TRatioPlot has been drawn.");
1621  return;
1622  }
1623 
1624  fSplitFraction = sf;
1625  double pm = fInsetWidth;
1626  double width = fParentPad->GetWNDC();
1627  double height = fParentPad->GetHNDC();
1628  double f = height/width;
1629 
1630  fUpperPad->SetPad(pm*f, fSplitFraction, 1.-pm*f, 1.-pm);
1631  fLowerPad->SetPad(pm*f, pm, 1.-pm*f, fSplitFraction);
1632 }
1633 
1634 ////////////////////////////////////////////////////////////////////////////////
1635 /// Set the inset on the outer sides of all the pads. It's used to make the outer
1636 /// pad draggable.
1637 
1638 void TRatioPlot::SetInsetWidth(Double_t width)
1639 {
1640  if (fParentPad == 0) {
1641  Warning("SetInsetWidth", "Can only be used after TRatioPlot has been drawn.");
1642  return;
1643  }
1644 
1645  fInsetWidth = width;
1646  SetSplitFraction(fSplitFraction);
1647 
1648  double pm = fInsetWidth;
1649  double w = fParentPad->GetWNDC();
1650  double h = fParentPad->GetHNDC();
1651  double f = h/w;
1652  fTopPad->SetPad(pm*f, pm, 1-pm*f, 1-pm);
1653 }
1654 
1655 ////////////////////////////////////////////////////////////////////////////////
1656 /// Sets the confidence levels used to calculate the bands in the fit residual
1657 /// case. Defaults to 1 and 2 sigma.
1658 
1659 void TRatioPlot::SetConfidenceLevels(Double_t c1, Double_t c2)
1660 {
1661  fCl1 = c1;
1662  fCl2 = c2;
1663  if (!BuildLowerPlot()) return;
1664 }
1665 
1666 ////////////////////////////////////////////////////////////////////////////////
1667 /// Set where horizontal, dashed lines are drawn on the lower pad.
1668 /// Can be used to override existing default lines (or disable them).
1669 ///
1670 /// \param gridlines Vector of y positions for the dashes lines
1671 ///
1672 /// Begin_Macro(source)
1673 /// ../../../tutorials/hist/ratioplot4.C
1674 /// End_Macro
1675 
1676 void TRatioPlot::SetGridlines(std::vector<double> gridlines)
1677 {
1678  fGridlinePositions = gridlines;
1679 }
1680 
1681 ////////////////////////////////////////////////////////////////////////////////
1682 /// Set where horizontal, dashed lines are drawn on the lower pad.
1683 /// Can be used to override existing default lines (or disable them).
1684 ///
1685 /// \param gridlines Double_t array of y positions for the dashed lines
1686 /// \param numGridlines Length of gridlines
1687 
1688 void TRatioPlot::SetGridlines(Double_t *gridlines, Int_t numGridlines)
1689 {
1690  fGridlinePositions.clear();
1691 
1692  for (Int_t i=0;i<numGridlines;++i) {
1693  fGridlinePositions.push_back(gridlines[i]);
1694  }
1695 }
1696 
1697 ////////////////////////////////////////////////////////////////////////////////
1698 /// Set the confidence interval colors.
1699 ///
1700 /// \param ci1 Color of the 1 sigma band
1701 /// \param ci2 Color of the 2 sigma band
1702 /// Sets the color of the 1 and 2 sigma bands in the fit residual case.
1703 /// Begin_Macro(source)
1704 /// ../../../tutorials/hist/ratioplot5.C
1705 /// End_Macro
1706 
1707 void TRatioPlot::SetConfidenceIntervalColors(Color_t ci1, Color_t ci2)
1708 {
1709  fCi1Color = ci1;
1710  fCi2Color = ci2;
1711 }
1712 
1713 ////////////////////////////////////////////////////////////////////////////////
1714 /// Internal method to import TAxis attributes to a TGaxis. Copied from
1715 /// `TGaxis::ImportAxisAttributes`
1716 
1717 void TRatioPlot::ImportAxisAttributes(TGaxis *gaxis, TAxis *axis)
1718 {
1719  gaxis->SetLineColor(axis->GetAxisColor());
1720  gaxis->SetTextColor(axis->GetTitleColor());
1721  gaxis->SetTextFont(axis->GetTitleFont());
1722  gaxis->SetLabelColor(axis->GetLabelColor());
1723  gaxis->SetLabelFont(axis->GetLabelFont());
1724  gaxis->SetLabelSize(axis->GetLabelSize());
1725  gaxis->SetLabelOffset(axis->GetLabelOffset());
1726  gaxis->SetTickSize(axis->GetTickLength());
1727  gaxis->SetTitle(axis->GetTitle());
1728  gaxis->SetTitleOffset(axis->GetTitleOffset());
1729  gaxis->SetTitleSize(axis->GetTitleSize());
1730  gaxis->SetBit(TAxis::kCenterTitle, axis->TestBit(TAxis::kCenterTitle));
1731  gaxis->SetBit(TAxis::kCenterLabels, axis->TestBit(TAxis::kCenterLabels));
1732  gaxis->SetBit(TAxis::kRotateTitle, axis->TestBit(TAxis::kRotateTitle));
1733  gaxis->SetBit(TAxis::kNoExponent, axis->TestBit(TAxis::kNoExponent));
1734  gaxis->SetBit(TAxis::kTickPlus, axis->TestBit(TAxis::kTickPlus));
1735  gaxis->SetBit(TAxis::kTickMinus, axis->TestBit(TAxis::kTickMinus));
1736  gaxis->SetBit(TAxis::kMoreLogLabels, axis->TestBit(TAxis::kMoreLogLabels));
1737  if (axis->GetDecimals()) gaxis->SetBit(TAxis::kDecimals); //the bit is in TAxis::fAxis2
1738  gaxis->SetTimeFormat(axis->GetTimeFormat());
1739 }