Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added documentation/doxygen/images/canvas_divide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 46 additions & 35 deletions graf2d/gpad/src/TPad.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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).
Expand Down Expand Up @@ -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<ny;iy++) {
y2 = 1 - iy*dy - ymargin;
y1 = y2 - dy + 2*ymargin;
if (y1 < 0) y1 = 0;
if (y1 > y2) continue;
for (ix=0;ix<nx;ix++) {
x1 = ix*dx + xmargin;
x2 = x1 +dx -2*xmargin;
if (x1 > 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;
Expand All @@ -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;i<nx;i++) {
x1 = i*dx+xl;
x2 = x1 + dx;
auto x1 = i * dx + xl;
auto x2 = x1 + dx;
if (i == 0) x1 = 0;
if (i == nx-1) x2 = 1-xr;
for (Int_t j=0;j<ny;j++) {
number = j*nx + i +1;
y2 = 1 -j*dy -yt;
y1 = y2 - dy;
auto y2 = 1 - j * dy - yt;
auto y1 = y2 - dy;
if (j == 0) y2 = 1-yt;
if (j == ny-1) y1 = 0;
name.Form("%s_%d", GetName(), number);
title.Form("%s_%d", GetTitle(), number);
pad = new TPad(name.Data(), title.Data(), x1, y1, x2, y2, color);
auto pad = new TPad(name.Data(), title.Data(), x1, y1, x2, y2, color);
pad->SetNumber(number);
pad->SetBorderMode(0);
if (i == 0) pad->SetLeftMargin(xl*nx);
Expand Down
118 changes: 118 additions & 0 deletions tutorials/visualisation/graphics/canvas_divide_example.C
Original file line number Diff line number Diff line change
@@ -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);");
}