From c07ede7f7e3a9db3a894bd75a00c66c2670ea4dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Lalik?= Date: Wed, 7 Jan 2026 18:08:01 +0100 Subject: [PATCH 1/2] [hist] Allow to customize pads layout for THStack and TMultiGraph When using PADS drawing option, now one can add number specyfiing number of columns for new pads layout. Rows will be calculated autmatically. --- hist/hist/inc/TMultiGraph.h | 2 +- hist/hist/src/THStack.cxx | 31 ++++++++++++++++++++------ hist/hist/src/TMultiGraph.cxx | 32 +++++++++++++++++++-------- hist/histpainter/src/THistPainter.cxx | 8 ++++--- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/hist/hist/inc/TMultiGraph.h b/hist/hist/inc/TMultiGraph.h index 3afb3cbf93d73..0d7df0a8ea1a1 100644 --- a/hist/hist/inc/TMultiGraph.h +++ b/hist/hist/inc/TMultiGraph.h @@ -73,7 +73,7 @@ class TMultiGraph : public TNamed { TAxis *GetXaxis(); TAxis *GetYaxis(); void Paint(Option_t *chopt = "") override; - void PaintPads(Option_t *chopt = ""); + void PaintPads(Option_t *chopt = "", Int_t fnx = 0); void PaintPolyLine3D(Option_t *chopt = ""); void PaintReverse(Option_t *chopt = ""); void Print(Option_t *chopt="") const override; diff --git a/hist/hist/src/THStack.cxx b/hist/hist/src/THStack.cxx index f56e7eeaeb155..48b48c0cfd231 100644 --- a/hist/hist/src/THStack.cxx +++ b/hist/hist/src/THStack.cxx @@ -759,10 +759,18 @@ void THStack::BuildAndPaint(Option_t *choptin, Bool_t paint, Bool_t rebuild_stac lclear = kFALSE; opt.ReplaceAll("noclear",""); } - if (opt.Contains("pads")) { + auto l = strstr(opt.Data(), "pads"); + if (l) { if (!paint) return; + Int_t fnx = 0; + if (sscanf(&l[4], "%d", &fnx) > 0) { + opt.ReplaceAll(TString::Format("pads%d", fnx), ""); + } else { + opt.ReplaceAll("pads", ""); + } + Int_t npads = fHists->GetSize(); TVirtualPad *padsav = gPad; //if pad is not already divided into subpads, divide it @@ -773,12 +781,21 @@ void THStack::BuildAndPaint(Option_t *choptin, Bool_t paint, Bool_t rebuild_stac nps++; } if (nps < npads) { - padsav->Clear(); - Int_t nx = (Int_t)TMath::Sqrt((Double_t)npads); - if (nx*nx < npads) nx++; - Int_t ny = nx; - if (((nx*ny)-nx) >= npads) ny--; - padsav->Divide(nx,ny); + if (fnx == 0) { + padsav->Clear(); + Int_t nx = (Int_t)TMath::Sqrt((Double_t)npads); + if (nx * nx < npads) + nx++; + Int_t ny = nx; + if (((nx * ny) - nx) >= npads) + ny--; + padsav->Divide(nx, ny); + } else { + Int_t ny = (Int_t)((Double_t)npads / fnx); + if (fnx * ny < npads) + ny++; + padsav->Divide(fnx, ny); + } } Int_t i = 1; diff --git a/hist/hist/src/TMultiGraph.cxx b/hist/hist/src/TMultiGraph.cxx index 16dcc1fb5fcbb..1a7dc6fe625cb 100644 --- a/hist/hist/src/TMultiGraph.cxx +++ b/hist/hist/src/TMultiGraph.cxx @@ -1171,8 +1171,13 @@ void TMultiGraph::Paint(Option_t *choptin) l = strstr(chopt.Data(),"PADS"); if (l) { - chopt.ReplaceAll("PADS",""); - PaintPads(chopt.Data()); + Int_t fnx = 0; + if (sscanf(&l[4], "%d", &fnx) > 0) { + chopt.ReplaceAll(TString::Format("PADS%d", fnx), ""); + } else { + chopt.ReplaceAll("PADS", ""); + } + PaintPads(chopt.Data(), fnx); return; } @@ -1372,11 +1377,11 @@ void TMultiGraph::Paint(Option_t *choptin) gfit->PaintStats(fit); } - //////////////////////////////////////////////////////////////////////////////// /// Divides the active pad and draws all Graphs in the Multigraph separately. +/// fnx parameter larger than 0 enforces number of columns for pad division -void TMultiGraph::PaintPads(Option_t *option) +void TMultiGraph::PaintPads(Option_t *option, Int_t fnx) { if (!gPad) return; @@ -1392,11 +1397,20 @@ void TMultiGraph::PaintPads(Option_t *option) } if (existingPads < neededPads) { curPad->Clear(); - Int_t nx = (Int_t)TMath::Sqrt((Double_t)neededPads); - if (nx*nx < neededPads) nx++; - Int_t ny = nx; - if (((nx*ny)-nx) >= neededPads) ny--; - curPad->Divide(nx,ny); + if (fnx == 0) { + Int_t nx = (Int_t)TMath::Sqrt((Double_t)neededPads); + if (nx * nx < neededPads) + nx++; + Int_t ny = nx; + if (((nx * ny) - nx) >= neededPads) + ny--; + curPad->Divide(nx, ny); + } else { + Int_t ny = (Int_t)((Double_t)neededPads / fnx); + if (fnx * ny < neededPads) + ny++; + curPad->Divide(fnx, ny); + } } Int_t i = 0; diff --git a/hist/histpainter/src/THistPainter.cxx b/hist/histpainter/src/THistPainter.cxx index 866f171e3a348..53c8bdb5a3eee 100644 --- a/hist/histpainter/src/THistPainter.cxx +++ b/hist/histpainter/src/THistPainter.cxx @@ -355,6 +355,7 @@ using `TH1::GetOption`: | "NOSTACK" | Histograms in the stack are all paint in the same pad as if the option `SAME` had been specified.| | "NOSTACKB" | Histograms are drawn next to each other as bar charts.| | "PADS" | The current pad/canvas is subdivided into a number of pads equal to the number of histograms in the stack and each histogram is paint into a separate pad.| +| "PADSn" | Like PADS but the current pad/canvas is subdivided into a `n` columns x `m` rows of pads where `n` is given and `m` is calculated.| | "PFC" | Palette Fill Color: stack's fill color is taken in the current palette. | | "PLC" | Palette Line Color: stack's line color is taken in the current palette. | | "PMC" | Palette Marker Color: stack's marker color is taken in the current palette. | @@ -2751,9 +2752,10 @@ the same pad as if the option `SAME` had been specified. This allows to compute X and Y scales common to all the histograms, like `TMultiGraph` does for graphs. -If the option `PADS` is specified, the current pad/canvas is -subdivided into a number of pads equal to the number of histograms and each -histogram is paint into a separate pad. +If the option `PADS` is specified, the current pad/canvas is subdivided into +a number of pads equal to the number of histograms and each histogram is paint +into a separate pad. With `PADSn`, the current pad/canvas is subdivided into +`n` columns x `m` rows of pads where `n` is given and `m` is calculated. The following example shows various types of stacks (hist023_THStack_simple.C). From 8c455c5e4678009bfcae3066821d5d4f1d601a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Lalik?= Date: Fri, 9 Jan 2026 10:55:06 +0100 Subject: [PATCH 2/2] [hist] PADSn for THStack and TMultiGraph update Implementation of review suggestions plus extra documentation. --- hist/hist/inc/TMultiGraph.h | 2 +- hist/hist/src/THStack.cxx | 2 +- hist/hist/src/TMultiGraph.cxx | 70 ++++++++++++++++++++++----- hist/histpainter/src/THistPainter.cxx | 4 +- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/hist/hist/inc/TMultiGraph.h b/hist/hist/inc/TMultiGraph.h index 0d7df0a8ea1a1..a85317db3c754 100644 --- a/hist/hist/inc/TMultiGraph.h +++ b/hist/hist/inc/TMultiGraph.h @@ -73,7 +73,7 @@ class TMultiGraph : public TNamed { TAxis *GetXaxis(); TAxis *GetYaxis(); void Paint(Option_t *chopt = "") override; - void PaintPads(Option_t *chopt = "", Int_t fnx = 0); + void PaintPads(Option_t *chopt = "", Int_t nColumn = 0); void PaintPolyLine3D(Option_t *chopt = ""); void PaintReverse(Option_t *chopt = ""); void Print(Option_t *chopt="") const override; diff --git a/hist/hist/src/THStack.cxx b/hist/hist/src/THStack.cxx index 48b48c0cfd231..0debab66221e9 100644 --- a/hist/hist/src/THStack.cxx +++ b/hist/hist/src/THStack.cxx @@ -781,7 +781,7 @@ void THStack::BuildAndPaint(Option_t *choptin, Bool_t paint, Bool_t rebuild_stac nps++; } if (nps < npads) { - if (fnx == 0) { + if (fnx <= 0) { padsav->Clear(); Int_t nx = (Int_t)TMath::Sqrt((Double_t)npads); if (nx * nx < npads) diff --git a/hist/hist/src/TMultiGraph.cxx b/hist/hist/src/TMultiGraph.cxx index 1a7dc6fe625cb..0d2b44125ec06 100644 --- a/hist/hist/src/TMultiGraph.cxx +++ b/hist/hist/src/TMultiGraph.cxx @@ -46,9 +46,10 @@ extern void H1LeastSquareSeqnd(Int_t n, Double_t *a, Int_t idim, Int_t &ifail, I - [Setting drawing options](\ref MG01a) - [Titles setting](\ref MG01b) - [The option \"3D\"](\ref MG01c) - - [Legend drawing](\ref MG01d) - - [Automatic coloring](\ref MG01e) - - [Reverse axis](\ref MG01f) + - [Options \"PADS\" and \"PADSn\"](\ref MG01d) + - [Legend drawing](\ref MG01e) + - [Automatic coloring](\ref MG01f) + - [Reverse axis](\ref MG01g) - [MultiGraphs' fitting](\ref MG02) - [Fit box position](\ref MG02a) - [Axis' limits setting](\ref MG03) @@ -155,6 +156,53 @@ Begin_Macro(source) End_Macro \anchor MG01d +#### Options "PADS" and "PADSn" + +Like for THStack, options `PADS` and `PADSn` to split drawing into individual pads for each graph are also available. + +| Option | Description | +|------------|-----------------------------------------------------------------| +| "PADS" | The current pad/canvas is subdivided into a number of pads equal to the number of graphs in the multigraph and each graph is paint into a separate pad.| +| "PADSn" | Like PADS but the current pad/canvas is subdivided into `n` columns, automatically calculating the number of rows.| + +Begin_Macro(source) +{ +auto c0 = new TCanvas("c1","multigraph L3",200,10,700,500); + +auto mg = new TMultiGraph(); + +auto gr1 = new TGraph(); gr1->SetLineColor(kBlue); +auto gr2 = new TGraph(); gr2->SetLineColor(kRed); +auto gr3 = new TGraph(); gr3->SetLineColor(kGreen); +auto gr4 = new TGraph(); gr4->SetLineColor(kOrange); + +Double_t dx = 6.28/1000; +Double_t x = -3.14; + +for (int i=0; i<=1000; i++) { + x = x+dx; + gr1->SetPoint(i,x,2.*TMath::Sin(x)); + gr2->SetPoint(i,x,TMath::Cos(x)); + gr3->SetPoint(i,x,TMath::Cos(x*x)); + gr4->SetPoint(i,x,TMath::Cos(x*x*x)); + } + + mg->Add(gr4); gr4->SetTitle("Cos(x*x*x)"); gr4->SetLineWidth(3); + mg->Add(gr3); gr3->SetTitle("Cos(x*x)") ; gr3->SetLineWidth(3); + mg->Add(gr2); gr2->SetTitle("Cos(x)") ; gr2->SetLineWidth(3); + mg->Add(gr1); gr1->SetTitle("2*Sin(x)") ; gr1->SetLineWidth(3); + + mg->SetTitle("Multi-graph Title; X-axis Title; Y-axis Title"); + + mg->Draw("a fb l pads3"); + + mg->GetHistogram()->GetXaxis()->SetRangeUser(0.,2.5); + gPad->Modified(); + gPad->Update(); +} +End_Macro + +\anchor MG01e #### Legend drawing The method TPad::BuildLegend is able to extract the graphs inside a @@ -216,7 +264,7 @@ Begin_Macro(source) } End_Macro -\anchor MG01e +\anchor MG01f #### Automatic coloring Automatic coloring according to the current palette is available as shown in the @@ -226,7 +274,7 @@ Begin_Macro(source) ../../../tutorials/visualisation/graphs/gr105_multigraphpalettecolor.C End_Macro -\anchor MG01f +\anchor MG01g #### Reverse axis \since **ROOT version 6.19/02** @@ -1379,9 +1427,9 @@ void TMultiGraph::Paint(Option_t *choptin) //////////////////////////////////////////////////////////////////////////////// /// Divides the active pad and draws all Graphs in the Multigraph separately. -/// fnx parameter larger than 0 enforces number of columns for pad division +/// nColumn parameter larger than 0 enforces number of columns for pad division -void TMultiGraph::PaintPads(Option_t *option, Int_t fnx) +void TMultiGraph::PaintPads(Option_t *option, Int_t nColumn) { if (!gPad) return; @@ -1397,7 +1445,7 @@ void TMultiGraph::PaintPads(Option_t *option, Int_t fnx) } if (existingPads < neededPads) { curPad->Clear(); - if (fnx == 0) { + if (nColumn <= 0) { Int_t nx = (Int_t)TMath::Sqrt((Double_t)neededPads); if (nx * nx < neededPads) nx++; @@ -1406,10 +1454,10 @@ void TMultiGraph::PaintPads(Option_t *option, Int_t fnx) ny--; curPad->Divide(nx, ny); } else { - Int_t ny = (Int_t)((Double_t)neededPads / fnx); - if (fnx * ny < neededPads) + Int_t ny = (Int_t)((Double_t)neededPads / nColumn); + if (nColumn * ny < neededPads) ny++; - curPad->Divide(fnx, ny); + curPad->Divide(nColumn, ny); } } Int_t i = 0; diff --git a/hist/histpainter/src/THistPainter.cxx b/hist/histpainter/src/THistPainter.cxx index 53c8bdb5a3eee..79a6dd6747d66 100644 --- a/hist/histpainter/src/THistPainter.cxx +++ b/hist/histpainter/src/THistPainter.cxx @@ -355,7 +355,7 @@ using `TH1::GetOption`: | "NOSTACK" | Histograms in the stack are all paint in the same pad as if the option `SAME` had been specified.| | "NOSTACKB" | Histograms are drawn next to each other as bar charts.| | "PADS" | The current pad/canvas is subdivided into a number of pads equal to the number of histograms in the stack and each histogram is paint into a separate pad.| -| "PADSn" | Like PADS but the current pad/canvas is subdivided into a `n` columns x `m` rows of pads where `n` is given and `m` is calculated.| +| "PADSn" | Like PADS but the current pad/canvas is subdivided into a `n` columns, automatically calculating the number of rows.| | "PFC" | Palette Fill Color: stack's fill color is taken in the current palette. | | "PLC" | Palette Line Color: stack's line color is taken in the current palette. | | "PMC" | Palette Marker Color: stack's marker color is taken in the current palette. | @@ -2755,7 +2755,7 @@ compute X and Y scales common to all the histograms, like If the option `PADS` is specified, the current pad/canvas is subdivided into a number of pads equal to the number of histograms and each histogram is paint into a separate pad. With `PADSn`, the current pad/canvas is subdivided into -`n` columns x `m` rows of pads where `n` is given and `m` is calculated. +`n` columns, automatically calculating the number of rows. The following example shows various types of stacks (hist023_THStack_simple.C).