diff --git a/documentation/doxygen/images/canvas_divide.png b/documentation/doxygen/images/canvas_divide.png new file mode 100644 index 0000000000000..41a31592823a2 Binary files /dev/null and b/documentation/doxygen/images/canvas_divide.png differ diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 2f046520d815f..be5b96c6c3883 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -1225,13 +1225,12 @@ Int_t TPad::DistancetoPrimitive(Int_t px, Int_t py) /// Automatic pad generation by division. /// /// - The current canvas is divided in nx by ny equal divisions (pads). -/// - xmargin defines the horizontal spacing around each pad as a percentage of the canvas -/// width. Therefore, the distance between two adjacent pads along the x-axis is equal -/// to twice the xmargin value. -/// - ymargin defines the vertical spacing around each pad as a percentage of the canvas -/// height. Therefore, the distance between two adjacent pads along the y-axis is equal -/// to twice the ymargin value. +/// - xmargin defines the horizontal spacing between each pad as a percentage of the canvas +/// width. +/// - ymargin defines the vertical spacing between each pad as a percentage of the canvas +/// height. /// - color is the color of the new pads. If 0, color is the canvas color. +/// - All pads are contained within the inner area defined by the canvas margins. /// /// Pads are automatically named `canvasname_n` where `n` is the division number /// starting from top left pad. @@ -1240,6 +1239,15 @@ Int_t TPad::DistancetoPrimitive(Int_t px, Int_t py) /// /// \image html gpad_pad3.png /// +/// Example if: +/// /// ~~~ {.cpp} +/// c->SetMargin(0.30, 0.05, 0.10, 0.10); +/// c->Divide(nx, ny, 0.03, 0.05, 46); +/// ~~~ +/// \image html canvas_divide.png +/// +/// More examples are in `tutorials/visualisation/graphics/canvas_divide_example.C` +/// /// Once a pad is divided into sub-pads, one can set the current pad /// to a subpad with a given division number as illustrated above /// with TPad::cd(subpad_number). @@ -1298,40 +1306,43 @@ void TPad::Divide(Int_t nx, Int_t ny, Float_t xmargin, Float_t ymargin, Int_t co TContext ctxt(kTRUE); cd(); - if (nx <= 0) nx = 1; - if (ny <= 0) ny = 1; - Int_t ix, iy; - Double_t x1, y1, x2, y2, dx, dy; - TPad *pad; + if (nx == 0) + nx = 1; + if (ny == 0) + ny = 1; + + Double_t xl = GetLeftMargin(); + Double_t xr = GetRightMargin(); + Double_t yb = GetBottomMargin(); + Double_t yt = GetTopMargin(); + TString name, title; - Int_t n = 0; if (color == 0) color = GetFillColor(); if (xmargin >= 0 && ymargin >= 0) { //general case - dy = 1/Double_t(ny); - dx = 1/Double_t(nx); - for (iy=0;iy y2) continue; - for (ix=0;ix x2) continue; + auto dx = (1 - xl - xr - xmargin * (nx - 1)) / nx; // width of a subpad + auto dy = (1 - yt - yb - ymargin * (ny - 1)) / ny; // height of a subpad + + Int_t n = 0; + for (auto iy = 0; iy < ny; iy++) { + auto y2 = 1 - yt - iy * (dy + ymargin); + auto y1 = y2 - dy; + if (y1 < yb) + y1 = yb; + for (auto ix = 0; ix < nx; ix++) { + auto x1 = xl + ix * (dx + xmargin); + auto x2 = x1 + dx; + if (x2 > (1 - xr)) + xr = 1 - xr; n++; name.Form("%s_%d", GetName(), n); - pad = new TPad(name.Data(), name.Data(), x1, y1, x2, y2, color); + auto pad = new TPad(name.Data(), name.Data(), x1, y1, x2, y2, color); pad->SetNumber(n); pad->Draw(); } } } else { // special case when xmargin < 0 or ymargin < 0 - Double_t xl = GetLeftMargin(); - Double_t xr = GetRightMargin(); - Double_t yb = GetBottomMargin(); - Double_t yt = GetTopMargin(); xl /= (1-xl+xr)*nx; xr /= (1-xl+xr)*nx; yb /= (1-yb+yt)*ny; @@ -1340,23 +1351,23 @@ void TPad::Divide(Int_t nx, Int_t ny, Float_t xmargin, Float_t ymargin, Int_t co SetRightMargin(xr); SetBottomMargin(yb); SetTopMargin(yt); - dx = (1-xl-xr)/nx; - dy = (1-yb-yt)/ny; + auto dx = (1 - xl - xr) / nx; + auto dy = (1 - yb - yt) / ny; Int_t number = 0; for (Int_t i=0;iSetNumber(number); pad->SetBorderMode(0); if (i == 0) pad->SetLeftMargin(xl*nx); diff --git a/tutorials/visualisation/graphics/canvas_divide_example.C b/tutorials/visualisation/graphics/canvas_divide_example.C new file mode 100644 index 0000000000000..adceab3eb1bda --- /dev/null +++ b/tutorials/visualisation/graphics/canvas_divide_example.C @@ -0,0 +1,118 @@ +// variants: 0 - custom values +// 1 - default values +// 2+ - old default values +void canvas_divide_example(int use_variant = 0) +{ + auto wx = 600; // width and heigh + auto wy = 400; + + auto nx = 3; // top-level pad division + auto ny = 2; + + auto ml = 0.30; // top-level pad margins + auto mb = 0.10; + auto mr = 0.05; + auto mt = 0.10; + + auto c = new TCanvas("canvas_divide", "canvas_divide", wx, wy); + c->SetFillColor(19); + + if (use_variant == 0) { + c->SetMargin(ml, mr, mb, mt); + c->Divide(nx, ny, 0.03, 0.05, 46); + } else if (use_variant == 1) { + c->Divide(nx, ny, 0.01, 0.01, 46); + } else { + c->SetMargin(0.01, 0.01, 0.01, 0.01); + c->Divide(nx, ny, 0.02, 0.02, 46); + } + + ml = c->GetLeftMargin(); + mb = c->GetBottomMargin(); + mr = c->GetRightMargin(); + mt = c->GetTopMargin(); + + auto h = new TH1F("", "", 100, -3.3, 3.3); + h->GetXaxis()->SetLabelFont(43); + h->GetXaxis()->SetLabelSize(12); + h->GetYaxis()->SetLabelFont(43); + h->GetYaxis()->SetLabelSize(12); + h->GetYaxis()->SetNdivisions(505); + h->SetMaximum(30 * nx * ny); + h->SetFillColor(41); + + Int_t number = 0; + for (Int_t i = 0; i < nx * ny; i++) { + number++; + c->cd(number); + h->FillRandom("gaus", 1000); + h->DrawCopy(); + } + + c->cd(); + + TArrow arr; + + arr.DrawArrow(0, 0.5, ml, 0.5, 0.01, "<|>"); + arr.DrawArrow(0.5, 0, 0.5, mb, 0.01, "<|>"); + arr.DrawArrow(1 - mr, 0.5, 1, 0.5, 0.01, "<|>"); + arr.DrawArrow(0.5, 1 - mt, 0.5, 1, 0.01, "<|>"); + + TLatex tex_x; + tex_x.SetNDC(1); + tex_x.SetTextSize(0.03); + tex_x.SetTextAlign(12); + tex_x.SetTextAngle(90); + + TLatex tex_y; + tex_y.SetNDC(1); + tex_y.SetTextSize(0.03); + tex_y.SetTextAlign(12); + + tex_x.DrawLatex(ml / 2, 0.5, TString::Format(" ml = %.2f", ml)); + tex_x.DrawLatex(1 - mr / 2, 0.5, TString::Format(" mr = %.2f", mr)); + + tex_y.DrawLatex(0.5, mb / 2, TString::Format(" mb = %.2f", mb)); + tex_y.DrawLatex(0.5, 1 - mt / 2, TString::Format(" mt = %.2f", mt)); + + for (int i = 0; i < nx; ++i) { + for (int j = 0; j < ny; ++j) { + + float x1, x2, xc, y1, y2, yc; + + auto spad = c->GetPad(1 + j * nx + i); // current pad + x1 = spad->GetXlowNDC() + spad->GetWNDC(); + xc = spad->GetXlowNDC() + spad->GetWNDC() / 2; + if (i < (nx - 1)) { + auto spad_nx = c->GetPad(1 + j * nx + (i + 1)); // next pad in x + x2 = spad_nx->GetXlowNDC(); + } + auto xm = x2 - x1; + + if (j < (ny - 1)) { + auto spad_ny = c->GetPad(1 + (j + 1) * nx + i); // next pad in y + y1 = spad_ny->GetYlowNDC() + spad->GetHNDC(); + } + y2 = spad->GetYlowNDC(); + yc = spad->GetYlowNDC() + spad->GetHNDC() / 2; + auto ym = y2 - y1; + + if (i < (nx - 1)) { + arr.DrawArrow(x1, yc, x2, yc, 0.01, "<|>"); + tex_x.DrawLatex((x1 + x2) / 2, yc, TString::Format(" xm = %.2f", xm)); + } + if (j < (ny - 1)) { + arr.DrawArrow(xc, y1, xc, y2, 0.01, "<|>"); + tex_y.DrawLatex(xc, (y1 + y2) / 2, TString::Format(" ym = %.2f", ym)); + } + } + } + + TText text; + text.SetTextSize(0.03); + text.SetTextFont(102); + text.SetNDC(1); + + text.DrawText(0.01, 0.97, "c->SetMargin(ml, mr, mb, mt);"); + text.DrawText(0.01, 0.94, "c->Divide(nx, ny, xm, ym);"); +}