Skip to content

Commit 50d160a

Browse files
committed
[hist] Implement AutoZoom of TH1/TH2/TH3/TAxis to filled range
Fixes #14538
1 parent 031f6bd commit 50d160a

File tree

5 files changed

+269
-7
lines changed

5 files changed

+269
-7
lines changed

core/base/inc/TVirtualPad.h

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class TVirtualPad : public TObject, public TAttLine, public TAttFill,
9595
virtual void DrawClassObject(const TObject *obj, Option_t *option="") = 0;
9696
virtual TH1F *DrawFrame(Double_t xmin, Double_t ymin, Double_t xmax, Double_t ymax, const char *title="") = 0;
9797
virtual void ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) = 0;
98+
virtual void AutoZoomed() { Emit("AutoZoomed()"); } // *SIGNAL*
9899
virtual void UnZoomed() { Emit("UnZoomed()"); } // *SIGNAL*
99100
virtual Short_t GetBorderMode() const = 0;
100101
virtual Short_t GetBorderSize() const = 0;

hist/hist/inc/TAxis.h

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class TAxis : public TNamed, public TAttAxis {
173173
virtual void SetTimeDisplay(Int_t value) {fTimeDisplay = (value != 0);} // *TOGGLE*
174174
virtual void SetTimeFormat(const char *format=""); // *MENU*
175175
virtual void SetTimeOffset(Double_t toffset, Option_t *option="local");
176+
virtual void AutoZoom(); // *MENU*
176177
virtual void UnZoom(); // *MENU*
177178
virtual void ZoomOut(Double_t factor=0, Double_t offset=0); // *MENU*
178179

hist/hist/inc/TH1.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class TH1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
9797
friend class TH1Merger;
9898

9999
protected:
100-
Int_t fNcells; ///< Number of bins(1D), cells (2D) +U/Overflows
100+
Int_t fNcells; ///< Number of bins (1D) / total of cells (2D) / voxels (3D) +Under/Overflows
101101
TAxis fXaxis; ///< X axis descriptor
102102
TAxis fYaxis; ///< Y axis descriptor
103103
TAxis fZaxis; ///< Z axis descriptor
@@ -209,6 +209,8 @@ class TH1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
209209
virtual void AddBinContent(Int_t bin, Double_t w) = 0;
210210
static void AddDirectory(Bool_t add=kTRUE);
211211
static Bool_t AddDirectoryStatus();
212+
virtual void AutoZoom(); // *MENU*
213+
virtual void UnZoom(); // *MENU*
212214
void Browse(TBrowser *b) override;
213215
virtual Bool_t CanExtendAllAxes() const;
214216
virtual Double_t Chi2Test(const TH1* h2, Option_t *option = "UU", Double_t *res = nullptr) const;
@@ -323,10 +325,11 @@ class TH1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker {
323325

324326
virtual Int_t GetQuantiles(Int_t n, Double_t *xp, const Double_t *p = nullptr);
325327
virtual Double_t GetRandom(TRandom * rng = nullptr) const;
328+
void GetRangeOfFilledWeights(const Int_t dim, Int_t& first, Int_t& last, const bool includeUnderOverflow) const;
326329
virtual void GetStats(Double_t *stats) const;
327330
virtual Double_t GetStdDev(Int_t axis=1) const;
328331
virtual Double_t GetStdDevError(Int_t axis=1) const;
329-
Double_t GetSumOfAllWeights(const bool includeOverflow) const;
332+
Double_t GetSumOfAllWeights(const bool includeUnderOverflow) const;
330333
/// Return the sum of weights across all bins excluding under/overflows.
331334
/// \see TH1::GetSumOfAllWeights()
332335
virtual Double_t GetSumOfWeights() const { return GetSumOfAllWeights(false); }

hist/hist/src/TAxis.cxx

+85
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,91 @@ void TAxis::UnZoom()
13281328
gPad->UnZoomed();
13291329
}
13301330

1331+
////////////////////////////////////////////////////////////////////////////////
1332+
/// Automatically zoom the current axis to the range of the parent histogram
1333+
/// filled with non-zero contents (weights or errors)
1334+
/// \note For the bin content axis (yaxis) of a TH1 or (zaxis) of a TH2,
1335+
/// UnZoom is called instead. TH3, the PaletteAxis does not implement AutoZoom nor UnZoom
1336+
1337+
void TAxis::AutoZoom()
1338+
{
1339+
if (!gPad) {
1340+
Warning("TAxis::AutoZoom","Cannot AutoZoom if gPad does not exist. Did you mean to draw the TAxis first?");
1341+
return;
1342+
}
1343+
gPad->SetView();
1344+
if (!GetParent()) {
1345+
Warning("TAxis::AutoZoom","Cannot AutoZoom if parent does not exist. Did you mean to draw the TAxis first?");
1346+
return;
1347+
}
1348+
auto dim = strstr(GetName(), "xaxis") ? 0 : strstr(GetName(), "yaxis") ? 1 : strstr(GetName(), "zaxis") ? 2 : -1;
1349+
TH1 *hobj1 = static_cast<TH1 *>(GetParent());
1350+
Int_t first = -1, last = -1;
1351+
if (hobj1 && !strstr(hobj1->GetName(), "hframe")) {
1352+
auto ndims = hobj1->GetDimension();
1353+
// Sanity checks
1354+
if (dim == 0) {
1355+
if (ndims != 1 && ndims != 2 && ndims != 3) {
1356+
Warning("TAxis::AutoZoom","Cannot AutoZoom xaxis if TH has %d dimension(s)", ndims);
1357+
return;
1358+
}
1359+
} else if (dim == 1) {
1360+
if (ndims == 1) {
1361+
UnZoom(); // For a TH1, it acts as an AutoZoom of the yaxis (bin content axis)
1362+
return;
1363+
}
1364+
if (ndims != 2 && ndims != 3) {
1365+
Warning("TAxis::AutoZoom","Cannot AutoZoom yaxis if TH has %d dimension(s)", ndims);
1366+
return;
1367+
}
1368+
} else if (dim == 2) {
1369+
if (ndims == 2) {
1370+
UnZoom(); // For a TH2, it acts as an AutoZoom of the zaxis (bin content axis)
1371+
return;
1372+
}
1373+
if (ndims != 3) {
1374+
Warning("TAxis::AutoZoom","Cannot AutoZoom zaxis if TH has %d dimension(s)", ndims);
1375+
return;
1376+
}
1377+
}
1378+
1379+
hobj1->GetRangeOfFilledWeights(dim, first, last, false);
1380+
SetRange(first, last);
1381+
}
1382+
if (first == -1 && last == -1) {
1383+
// Must autozoom all histograms in the pad
1384+
Double_t globalMin = DBL_MAX;
1385+
Double_t globalMax = DBL_MIN;
1386+
TIter next(gPad->GetListOfPrimitives());
1387+
while (TObject *obj= next()) {
1388+
if (!obj || !obj->InheritsFrom(TH1::Class()))
1389+
continue;
1390+
TH1 *hobj = static_cast<TH1*>(obj);
1391+
if (!obj || !obj->InheritsFrom(TH1::Class()))
1392+
continue;
1393+
if (!strstr(hobj->GetName(), "hframe")) {
1394+
hobj->GetRangeOfFilledWeights(dim, first, last, false);
1395+
TAxis *ax = (dim == 0) ? hobj->GetXaxis() : (dim == 1) ? hobj->GetYaxis() : (dim == 2) ? hobj->GetZaxis() : nullptr;
1396+
if (ax) {
1397+
globalMin = std::min(globalMin, ax->GetBinLowEdge(first));
1398+
globalMax = std::max(globalMax, ax->GetBinUpEdge(last));
1399+
}
1400+
}
1401+
}
1402+
while (TObject *obj = next()) {
1403+
if (!obj || !obj->InheritsFrom(TH1::Class()))
1404+
continue;
1405+
TH1 *hobj = static_cast<TH1 *>(obj);
1406+
TAxis *ax = (dim == 0) ? hobj->GetXaxis() : (dim == 1) ? hobj->GetYaxis() : (dim == 2) ? hobj->GetZaxis() : nullptr;
1407+
if (ax) {
1408+
ax->SetRangeUser(globalMin, globalMax);
1409+
}
1410+
}
1411+
}
1412+
1413+
gPad->AutoZoomed();
1414+
}
1415+
13311416
////////////////////////////////////////////////////////////////////////////////
13321417
/// Zoom out by a factor of 'factor' (default =2)
13331418
/// uses previous zoom factor by default

hist/hist/src/TH1.cxx

+177-5
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,54 @@ Bool_t TH1::AddDirectoryStatus()
744744
return fgAddDirectory;
745745
}
746746

747+
////////////////////////////////////////////////////////////////////////////////
748+
/// Autozoom histogram in all their axes
749+
/// \see TAxis::AutoZoom
750+
751+
void TH1::AutoZoom()
752+
{
753+
const auto ndims = GetDimension();
754+
if (ndims < 1 || ndims > 3)
755+
return;
756+
// First apply autozoom in pure coordinate axis
757+
if (ndims >= 1)
758+
GetXaxis()->AutoZoom();
759+
if (ndims >= 2)
760+
GetYaxis()->AutoZoom();
761+
if (ndims >= 3)
762+
GetZaxis()->AutoZoom();
763+
// Now apply autozoom in the bin content axis if it's a TH1 or TH2
764+
if (ndims == 1)
765+
GetYaxis()->AutoZoom();
766+
else if (ndims == 2)
767+
GetZaxis()->AutoZoom();
768+
// For 3D, there is no UnZoom or AutoZoom implemented for TPaletteAxis
769+
}
770+
771+
////////////////////////////////////////////////////////////////////////////////
772+
/// Unzoom histogram in all their axes
773+
/// \see TAxis::UnZoom
774+
775+
void TH1::UnZoom()
776+
{
777+
const auto ndims = GetDimension();
778+
if (ndims < 1 || ndims > 3)
779+
return;
780+
// First apply Unzoom in pure coordinate axis
781+
if (ndims >= 1)
782+
GetXaxis()->UnZoom();
783+
if (ndims >= 2)
784+
GetYaxis()->UnZoom();
785+
if (ndims >= 3)
786+
GetZaxis()->UnZoom();
787+
// Now apply unzoom in the bin content axis if it's a TH1 or TH2
788+
if (ndims == 1)
789+
GetYaxis()->UnZoom();
790+
else if (ndims == 2)
791+
GetZaxis()->UnZoom();
792+
// For 3D, there is no UnZoom or AutoZoom implemented for TPaletteAxis
793+
}
794+
747795
////////////////////////////////////////////////////////////////////////////////
748796
/// Browse the Histogram object.
749797

@@ -7904,19 +7952,143 @@ void TH1::ResetStats()
79047952
if (fSumw2.fN > 0 && fTsumw > 0 && stats[1] > 0 ) fEntries = stats[0]*stats[0]/ stats[1];
79057953
}
79067954

7955+
void TH1::GetRangeOfFilledWeights(const Int_t dim, Int_t& first, Int_t& last, const bool includeUnderOverflow) const
7956+
{
7957+
if (fBuffer) const_cast<TH1*>(this)->BufferEmpty();
7958+
7959+
const Int_t start = (includeUnderOverflow ? 0 : 1);
7960+
const Int_t lastX = fXaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
7961+
const Int_t lastY = fYaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
7962+
const Int_t lastZ = fZaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
7963+
7964+
const auto ndims = GetDimension();
7965+
R__ASSERT(dim == 0 || dim == 1 || dim == 2);
7966+
if (ndims == 1) {
7967+
R__ASSERT(dim == 0);
7968+
} else if (ndims == 2) {
7969+
R__ASSERT(dim == 0 || dim == 1);
7970+
} else if (ndims == 3) {
7971+
R__ASSERT(dim == 0 || dim == 1 || dim == 2);
7972+
}
7973+
7974+
if (dim == 0) {
7975+
first = start;
7976+
for(Int_t binx = start; binx <= lastX; binx++) {
7977+
for(auto biny = start; biny <= lastY; biny++) {
7978+
for(auto binz = start; binz <= lastZ; binz++) {
7979+
auto bin = GetBin(binx, biny, binz);
7980+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
7981+
{
7982+
first = binx;
7983+
// Break:
7984+
binx = lastX;
7985+
biny = lastY;
7986+
binz = lastZ;
7987+
}
7988+
}
7989+
}
7990+
}
7991+
last = lastX;
7992+
for(Int_t binx = lastX; binx >= start; binx--) {
7993+
for(auto biny = start; biny <= lastY; biny++) {
7994+
for(auto binz = start; binz <= lastZ; binz++) {
7995+
auto bin = GetBin(binx, biny, binz);
7996+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
7997+
{
7998+
last = binx;
7999+
// Break:
8000+
binx = start;
8001+
biny = lastY;
8002+
binz = lastZ;
8003+
}
8004+
}
8005+
}
8006+
}
8007+
return;
8008+
} else if (dim == 1) {
8009+
first = start;
8010+
for(auto biny = start; biny <= lastY; biny++) {
8011+
for(Int_t binx = start; binx <= lastX; binx++) {
8012+
for(auto binz = start; binz <= lastZ; binz++) {
8013+
auto bin = GetBin(binx, biny, binz);
8014+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
8015+
{
8016+
first = biny;
8017+
// Break:
8018+
binx = lastX;
8019+
biny = lastY;
8020+
binz = lastZ;
8021+
}
8022+
}
8023+
}
8024+
}
8025+
last = lastY;
8026+
for(Int_t biny = lastY; biny >= start; biny--) {
8027+
for(auto binx = start; binx <= lastX; binx++) {
8028+
for(auto binz = start; binz <= lastZ; binz++) {
8029+
auto bin = GetBin(binx, biny, binz);
8030+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
8031+
{
8032+
last = biny;
8033+
// Break:
8034+
binx = lastX;
8035+
biny = start;
8036+
binz = lastZ;
8037+
}
8038+
}
8039+
}
8040+
}
8041+
return;
8042+
} else if (dim == 2) {
8043+
first = start;
8044+
for(auto binz = start; binz <= lastZ; binz++) {
8045+
for(Int_t binx = start; binx <= lastX; binx++) {
8046+
for(auto biny = start; biny <= lastY; biny++) {
8047+
auto bin = GetBin(binx, biny, binz);
8048+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
8049+
{
8050+
first = biny;
8051+
// Break:
8052+
binx = lastX;
8053+
biny = lastY;
8054+
binz = lastZ;
8055+
}
8056+
}
8057+
}
8058+
}
8059+
last = lastZ;
8060+
for(Int_t binz = lastZ; binz >= start; binz--) {
8061+
for(auto binx = start; binx <= lastX; binx++) {
8062+
for(auto biny = start; biny <= lastY; biny++) {
8063+
auto bin = GetBin(binx, biny, binz);
8064+
if (RetrieveBinContent(bin) != 0 || GetBinError(bin) != 0)
8065+
{
8066+
last = binz;
8067+
// Break:
8068+
binx = lastX;
8069+
biny = lastY;
8070+
binz = start;
8071+
}
8072+
}
8073+
}
8074+
}
8075+
return;
8076+
}
8077+
}
8078+
79078079
////////////////////////////////////////////////////////////////////////////////
79088080
/// Return the sum of all weights
79098081
/// \param includeOverflow true to include under/overflows bins, false to exclude those.
79108082
/// \note Different from TH1::GetSumOfWeights, that always excludes those
79118083

7912-
Double_t TH1::GetSumOfAllWeights(const bool includeOverflow) const
8084+
Double_t TH1::GetSumOfAllWeights(const bool includeUnderOverflow) const
79138085
{
79148086
if (fBuffer) const_cast<TH1*>(this)->BufferEmpty();
79158087

7916-
const Int_t start = (includeOverflow ? 0 : 1);
7917-
const Int_t lastX = fXaxis.GetNbins() + (includeOverflow ? 1 : 0);
7918-
const Int_t lastY = fYaxis.GetNbins() + (includeOverflow ? 1 : 0);
7919-
const Int_t lastZ = fZaxis.GetNbins() + (includeOverflow ? 1 : 0);
8088+
const Int_t start = (includeUnderOverflow ? 0 : 1);
8089+
const Int_t lastX = fXaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
8090+
const Int_t lastY = fYaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
8091+
const Int_t lastZ = fZaxis.GetNbins() + (includeUnderOverflow ? 1 : 0);
79208092
Double_t sum =0;
79218093
for(auto binz = start; binz <= lastZ; binz++) {
79228094
for(auto biny = start; biny <= lastY; biny++) {

0 commit comments

Comments
 (0)