Logo ROOT  
Reference Guide
THLimitsFinder.cxx
Go to the documentation of this file.
1// @(#)root/hist:$Id$
2// Author: Rene Brun 14/01/2002
3/*************************************************************************
4 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "TH1.h"
12#include "TMath.h"
13#include "THLimitsFinder.h"
14
16
18
19/** \class THLimitsFinder
20 \ingroup Hist
21Class to compute nice axis limits.
22
23This class is called by default by the histogramming system
24and also by TTree::Draw, TTreePlayer::DrawSelect. TGaxis use it also to
25optimize axis labels.
26
27A different finder may be specified via THLimitsFinder::SetFinder.
28*/
29
30////////////////////////////////////////////////////////////////////////////////
31
33{
34}
35
36////////////////////////////////////////////////////////////////////////////////
37
39{
40}
41
42
43////////////////////////////////////////////////////////////////////////////////
44/// Compute the best axis limits for the X axis.
45///
46/// If the bit kIsInteger is set, the number of channels is also recomputed.
47/// The axis parameters are replaced by the optimized parameters.
48///
49/// Example:
50/// With the input parameters xmin=-1.467 and xmax=2.344, the function
51/// will compute better limits -1.8 and 2.7 and store them in the axis.
52
54{
55 Int_t newbins;
56 TAxis *xaxis = h->GetXaxis();
57
58 if (xmin >= xmax) {
59 if (xaxis->GetLabels()) {xmin = 0; xmax = xmin +xaxis->GetNbins();}
60 else {xmin -= 1; xmax += 1;}
61 }
62
64 newbins,xmin,xmax,
66
67 h->SetBins(newbins,xmin,xmax);
68
69 return 0;
70}
71
72////////////////////////////////////////////////////////////////////////////////
73/// Compute the best axis limits for the X and Y axis.
74///
75/// If the bit kIsInteger is set, the number of channels is also recomputed.
76/// The axis parameters are replaced by the optimized parameters
77
79{
80 Int_t newbinsx,newbinsy;
81 TAxis *xaxis = h->GetXaxis();
82 TAxis *yaxis = h->GetYaxis();
83
84 if (xmin >= xmax) {
85 if (xaxis->GetLabels()) {xmin = 0; xmax = xmin +xaxis->GetNbins();}
86 else {xmin -= 1; xmax += 1;}
87 }
88 if (ymin >= ymax) {
89 if (yaxis->GetLabels()) {ymin = 0; ymax = ymin +yaxis->GetNbins();}
90 else {ymin -= 1; ymax += 1;}
91 }
92
94 newbinsx,xmin,xmax,
96
98 newbinsy,ymin,ymax,
100
101 h->SetBins(newbinsx,xmin,xmax,newbinsy,ymin,ymax);
102 return 0;
103}
104
105////////////////////////////////////////////////////////////////////////////////
106/// Compute the best axis limits for the X, Y and Z axis.
107///
108/// If the bit kIsInteger is set, the number of channels is also recomputed.
109/// The axis parameters are replaced by the optimized parameters
110
112{
113 Int_t newbinsx,newbinsy,newbinsz;
114 TAxis *xaxis = h->GetXaxis();
115 TAxis *yaxis = h->GetYaxis();
116 TAxis *zaxis = h->GetZaxis();
117
118 if (xmin >= xmax) {
119 if (xaxis->GetLabels()) {xmin = 0; xmax = xmin +xaxis->GetNbins();}
120 else {xmin -= 1; xmax += 1;}
121 }
122 if (ymin >= ymax) {
123 if (yaxis->GetLabels()) {ymin = 0; ymax = ymin +yaxis->GetNbins();}
124 else {ymin -= 1; ymax += 1;}
125 }
126 if (zmin >= zmax) {
127 if (zaxis->GetLabels()) {zmin = 0; zmax = zmin +zaxis->GetNbins();}
128 else {zmin -= 1; zmax += 1;}
129 }
130
132 newbinsx,xmin,xmax,
133 xaxis->TestBit(TAxis::kIsInteger));
134
136 newbinsy,ymin,ymax,
137 yaxis->TestBit(TAxis::kIsInteger));
138
140 newbinsz,zmin,zmax,
141 zaxis->TestBit(TAxis::kIsInteger));
142
143 h->SetBins(newbinsx,xmin,xmax,newbinsy,ymin,ymax,newbinsz,zmin,zmax);
144 return 0;
145}
146
147////////////////////////////////////////////////////////////////////////////////
148/// Return pointer to the current finder.
149///
150/// Create one if none exists
151/// Use SetLimitsFinder to set a user defined finder.
152
154{
156 return fgLimitsFinder;
157}
158
159////////////////////////////////////////////////////////////////////////////////
160/// This static function can be used to specify a finder derived from THLimitsFinder.
161///
162/// The finder may redefine the functions FindGoodLimits.
163/// Note that the redefined functions may call THLimitsFinder::FindGoodLimits.
164
166{
167 fgLimitsFinder = finder;
168}
169
170////////////////////////////////////////////////////////////////////////////////
171/// Static function to compute reasonable axis limits
172///
173/// Input parameters:
174///
175/// \param[in] A1,A2 : Original axis limits
176/// \param[in] BinLow,BinHigh : Optimized axis limits. They should be initialized by the
177/// calling method for instance to 0.
178/// \param[out] nold : Original number of divisions.
179/// \param[out] nbins : Optimized number of divisions.
180/// \param[out] BinWidth : Optimized bin width. It should be initialized by the
181/// calling method for instance to 0.
182/// \param[in] option : "T" means Time axis.
183
185 Double_t &BinLow, Double_t &BinHigh,
186 Int_t &nbins, Double_t &BinWidth,
187 Option_t *option)
188{
189 Int_t lwid, kwid;
190 Int_t ntemp = 0;
191 Int_t jlog = 0;
192 Double_t siground = 0;
193 Double_t alb, awidth, sigfig;
194 Double_t timemulti = 1;
195 Int_t roundmode =0;
196
197 Int_t optionTime;
198 if(strchr(option,'t')) optionTime = 1; else optionTime = 0;
199
200 nbins = nold;
201
202 Double_t al = TMath::Min(A1,A2);
203 Double_t ah = TMath::Max(A1,A2);
204 if (al == ah) ah = al+1;
205 // if nold == -1 , program uses binwidth input from calling routine
206 if (nold == -1 && BinWidth > 0 ) goto L90;
207 ntemp = TMath::Max(nold,2);
208 if (ntemp < 1) ntemp = 1;
209
210L20:
211 awidth = (ah-al)/Double_t(ntemp);
212 timemulti = 1;
213 if (awidth >= FLT_MAX) goto LOK; //in float.h
214 if (awidth <= 0) goto LOK;
215
216// If time representation, bin width should be rounded to seconds
217// minutes, hours or days
218
219 if (optionTime && awidth>=60) { // if width in seconds, treat it as normal
220 // width in minutes
221 awidth /= 60; timemulti *=60;
222 roundmode = 1; // round minutes (60)
223 // width in hours ?
224 if (awidth>=60) {
225 awidth /= 60; timemulti *= 60;
226 roundmode = 2; // round hours (24)
227 // width in days ?
228 if (awidth>=24) {
229 awidth /= 24; timemulti *= 24;
230 roundmode = 3; // round days (30)
231 // width in months ?
232 if (awidth>=30.43685) { // Mean month length in 1900.
233 awidth /= 30.43685; timemulti *= 30.43685;
234 roundmode = 2; // round months (12)
235 // width in years ?
236 if (awidth>=12) {
237 awidth /= 12; timemulti *= 12;
238 roundmode = 0; // round years (10)
239 }
240 }
241 }
242 }
243 }
244// Get nominal bin width in exponential form
245
246 jlog = Int_t(TMath::Log10(awidth));
247 if (jlog <-200 || jlog > 200) {
248 BinLow = 0;
249 BinHigh = 1;
250 BinWidth = 0.01;
251 nbins = 100;
252 return;
253 }
254 if (awidth <= 1 && (!optionTime || timemulti==1) ) jlog--;
255 sigfig = awidth*TMath::Power(10,-jlog) -1e-10;
256 //in the above statement, it is important to subtract 1e-10
257 //to avoid precision problems if the tests below
258
259// Round mantissa
260
261 switch (roundmode) {
262
263// Round mantissa up to 1, 1.5, 2, 3, or 6 in case of minutes
264 case 1: // case 60
265 if (sigfig <= 1) siground = 1;
266 else if (sigfig <= 1.5 && jlog==1) siground = 1.5;
267 else if (sigfig <= 2) siground = 2;
268 else if (sigfig <= 3 && jlog ==1) siground = 3;
269 else if (sigfig <= 5 && sigfig>3 && jlog ==0) siground = 5; //added (Damir in 3.10/02)
270 else if (jlog==0) {siground = 1; jlog++;}
271 else siground = 6;
272 break;
273 case 2: // case 12 and 24
274
275// Round mantissa up to 1, 1.2, 2, 2.4, 3 or 6 in case of hours or months
276 if (sigfig <= 1 && jlog==0) siground = 1;
277 else if (sigfig <= 1.2 && jlog==1) siground = 1.2;
278 else if (sigfig <= 2 && jlog==0) siground = 2;
279 else if (sigfig <= 2.4 && jlog==1) siground = 2.4;
280 else if (sigfig <= 3) siground = 3;
281 else if (sigfig <= 6) siground = 6;
282 else if (jlog==0) siground = 12;
283 else siground = 2.4;
284 break;
285
286//- Round mantissa up to 1, 1.4, 2, or 7 in case of days (weeks)
287 case 3: // case 30
288 if (sigfig <= 1 && jlog==0) siground = 1;
289 else if (sigfig <= 1.4 && jlog==1) siground = 1.4;
290 else if (sigfig <= 3 && jlog ==1) siground = 3;
291 else siground = 7;
292 break;
293 default :
294
295// Round mantissa up to 1, 2, 2.5, 5, or 10 in case of decimal number
296 if (sigfig <= 1) siground = 1;
297 else if (sigfig <= 2) siground = 2;
298 else if (sigfig <= 5 && (!optionTime || jlog<1)) siground = 5;
299 else if (sigfig <= 6 && optionTime && jlog==1) siground = 6;
300 else {siground = 1; jlog++; }
301 break;
302 }
303
304 BinWidth = siground*TMath::Power(10,jlog);
305 if (optionTime) BinWidth *= timemulti;
306
307// Get new bounds from new width BinWidth
308
309L90:
310 alb = al/BinWidth;
311 if (TMath::Abs(alb) > 1e9) {
312 BinLow = al;
313 BinHigh = ah;
314 if (nbins > 10*nold && nbins > 10000) nbins = nold;
315 return;
316 }
317 lwid = Int_t(alb);
318 if (alb < 0) lwid--;
319 BinLow = BinWidth*Double_t(lwid);
320 alb = ah/BinWidth + 1.00001;
321 kwid = Int_t(alb);
322 if (alb < 0) kwid--;
323 BinHigh = BinWidth*Double_t(kwid);
324 nbins = kwid - lwid;
325 if (nold == -1) goto LOK;
326 if (nold <= 5) { // Request for one bin is difficult case
327 if (nold > 1 || nbins == 1)goto LOK;
328 BinWidth = BinWidth*2;
329 nbins = 1;
330 goto LOK;
331 }
332 if (2*nbins == nold && !optionTime) {ntemp++; goto L20; }
333
334LOK:
335 Double_t oldBinLow = BinLow;
336 Double_t oldBinHigh = BinHigh;
337 Int_t oldnbins = nbins;
338
339 Double_t atest = BinWidth*0.0001;
340 //if (TMath::Abs(BinLow-A1) >= atest) { BinLow += BinWidth; nbins--; } //replaced by Damir in 3.10/02
341 //if (TMath::Abs(BinHigh-A2) >= atest) { BinHigh -= BinWidth; nbins--; } //by the next two lines
342 if (al-BinLow >= atest) { BinLow += BinWidth; nbins--; }
343 if (BinHigh-ah >= atest) { BinHigh -= BinWidth; nbins--; }
344 if (!optionTime && BinLow >= BinHigh) {
345 //this case may happen when nbins <=5
346 BinLow = oldBinLow;
347 BinHigh = oldBinHigh;
348 nbins = oldnbins;
349 }
350 else if (optionTime && BinLow>=BinHigh) {
351 nbins = 2*oldnbins;
352 BinHigh = oldBinHigh;
353 BinLow = oldBinLow;
354 BinWidth = (oldBinHigh - oldBinLow)/nbins;
355 atest = BinWidth*0.0001;
356 if (al-BinLow >= atest) { BinLow += BinWidth; nbins--; }
357 if (BinHigh-ah >= atest) { BinHigh -= BinWidth; nbins--; }
358 }
359}
360
361////////////////////////////////////////////////////////////////////////////////
362/// Optimize axis limits.
363///
364/// When isInter=kTRUE, the function makes an integer binwidth
365/// and recompute the number of bins accordingly.
366
368{
369 Double_t binlow = 0,binhigh = 0,binwidth=0;
370 Int_t n=0;
371 Double_t dx = 0.1*(xmax-xmin);
372 if (isInteger) dx = 5*(xmax-xmin)/nbins;
373 Double_t umin = xmin - dx;
374 Double_t umax = xmax + dx;
375 if (umin < 0 && xmin >= 0) umin = 0;
376 if (umax > 0 && xmax <= 0) umax = 0;
377
378 THLimitsFinder::Optimize(umin,umax,nbins,binlow,binhigh,n,binwidth,"");
379
380 if (binwidth <= 0 || binwidth > 1.e+39) {
381 xmin = -1;
382 xmax = 1;
383 } else {
384 xmin = binlow;
385 xmax = binhigh;
386 }
387 if (isInteger) {
388 Int_t ixmin = Int_t(xmin);
389 Int_t ixmax = Int_t(xmax);
390 Double_t dxmin = Double_t(ixmin);
391 Double_t dxmax = Double_t(ixmax);
392 if (xmin < 0 && xmin != dxmin) xmin = dxmin - 1;
393 else xmin = dxmin;
394 if (xmax > 0 && xmax != dxmax) xmax = dxmax + 1;
395 else if (xmax ==0 && xmax == dxmax) xmax = 1;
396 else xmax = dxmax;
397 if (xmin >= xmax) xmax = xmin+1;
398 Int_t bw = Int_t((xmax-xmin)/nbins);
399 if (bw == 0) bw = 1;
400 nbins = Int_t((xmax-xmin)/bw);
401 if (xmin +nbins*bw < umax) {nbins++; xmax = xmin +nbins*bw;}
402 if (xmin > umin) {nbins++; xmin = xmax -nbins*bw;}
403 }
404 newbins = nbins;
405}
#define h(i)
Definition: RSha256.hxx:106
#define e(i)
Definition: RSha256.hxx:103
int Int_t
Definition: RtypesCore.h:43
double Double_t
Definition: RtypesCore.h:57
const char Option_t
Definition: RtypesCore.h:64
#define ClassImp(name)
Definition: Rtypes.h:361
float xmin
Definition: THbookFile.cxx:93
float ymin
Definition: THbookFile.cxx:93
float xmax
Definition: THbookFile.cxx:93
float ymax
Definition: THbookFile.cxx:93
Class to manage histogram axis.
Definition: TAxis.h:30
@ kIsInteger
Definition: TAxis.h:71
Int_t GetNbins() const
Definition: TAxis.h:121
THashList * GetLabels() const
Definition: TAxis.h:117
The TH1 histogram class.
Definition: TH1.h:56
Class to compute nice axis limits.
static void SetLimitsFinder(THLimitsFinder *finder)
This static function can be used to specify a finder derived from THLimitsFinder.
virtual ~THLimitsFinder()
static THLimitsFinder * GetLimitsFinder()
Return pointer to the current finder.
static void OptimizeLimits(Int_t nbins, Int_t &newbins, Double_t &xmin, Double_t &xmax, Bool_t isInteger)
Optimize axis limits.
static THLimitsFinder * fgLimitsFinder
virtual Int_t FindGoodLimits(TH1 *h, Double_t xmin, Double_t xmax)
Compute the best axis limits for the X axis.
THLimitsFinder()
Pointer to hist limits finder.
static void Optimize(Double_t A1, Double_t A2, Int_t nold, Double_t &BinLow, Double_t &BinHigh, Int_t &nbins, Double_t &BWID, Option_t *option="")
Static function to compute reasonable axis limits.
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:187
const Int_t n
Definition: legend1.C:16
Short_t Max(Short_t a, Short_t b)
Definition: TMathBase.h:212
LongDouble_t Power(LongDouble_t x, LongDouble_t y)
Definition: TMath.h:725
Short_t Min(Short_t a, Short_t b)
Definition: TMathBase.h:180
Double_t Log10(Double_t x)
Definition: TMath.h:754
Short_t Abs(Short_t d)
Definition: TMathBase.h:120