I a have a TCanvas containing either a 2D histogram or TGraph / TGraph2D.
I am writing a function to resize the canvas, so that the unit length in the x and y axes occupies the same number of pixels, i.e. to get a real aspect ratio in the monitor compared to the spatial dimensions.
// Run with root -l -n test_ratio.cpp+
// Alternatively: root -l -n test_ratio.cpp+ -b -q
#include "TCanvas.h"
#include "TH2F.h"
#include "TF2.h"
#include "TROOT.h"
#include "TFrame.h"
/**
* \brief Function to resize a canvas so that a 2D histogram or TGraph / TGraph2D are shown in real aspect ratio
* \param c pointer to a TCanvas
* \return false if error is encountered, true otherwise
* \note It resizes the width of the canvas, not the height, as you normally have more space in your monitor horizontally than vertically
* \note Resize the canvas in Y before calling this function if you want a larger vertical height
* \note Call this function AFTER drawing AND zooming (SetUserRange) your TGraph or Histogram, otherwise it cannot infer your actual axes length
*
*/
bool SetRealAspectRatio(TCanvas* const c)
{
if(!c)
{
cout << "Error in SetRealAspectRatio: canvas is NULL";
return false;
}
{
//Get the current min-max values if SetUserRange has been called
c->Update();
const Double_t xmin = c->GetUxmin();
const Double_t xmax = c->GetUxmax();
const Double_t ymin = c->GetUymin();
const Double_t ymax = c->GetUymax();
//Get the length of zoomed x and y axes
const Double_t xlength = xmax - xmin;
const Double_t ylength = ymax - ymin;
const Double_t ratio = xlength/ylength;
//Get how many pixels are occupied by the canvas
const Int_t npx = c->GetWw();
const Int_t npy = c->GetWh();
//Get x-y coordinates at the edges of the canvas (extrapolating outside the axes, NOT at the edges of the histogram)
const Double_t x1 = c->GetX1();
const Double_t y1 = c->GetY1();
const Double_t x2 = c->GetX2();
const Double_t y2 = c->GetY2();
//Get now number of pixels including window borders
const Int_t bnpx = c->GetWindowWidth();
const Int_t bnpy = c->GetWindowHeight();
cout << "WindX\tWindY\tCanvX\tCanvY\tx1\ty1\tx2\ty2\tratiox/y\tCanvX/CanvY" << endl;
cout << bnpx << "\t" << bnpy << "\t" << npx << "\t" << npy << "\t" << x1 << "\t" << y1 << "\t" << x2 << "\t" << y2 << "\t" << ratio << "\t" << (double)npx/npy << "\tOriginal Canvas" << endl;
c->SetCanvasSize(npy*ratio, npy);
c->SetWindowSize((bnpx-npx)+npy*ratio, bnpy);
}
//Check now that resizing has worked
{
//Get the current min-max values if SetUserRange has been called
c->Update();
const Double_t xmin = c->GetUxmin();
const Double_t xmax = c->GetUxmax();
const Double_t ymin = c->GetUymin();
const Double_t ymax = c->GetUymax();
//Get the length of zoomed x and y axes
const Double_t xlength = xmax - xmin;
const Double_t ylength = ymax - ymin;
const Double_t ratio = xlength/ylength;
//Get how many pixels are occupied by the canvas
const Int_t npx = c->GetWw();
const Int_t npy = c->GetWh();
//Get x-y coordinates at the edges of the canvas (extrapolating outside the axes, NOT at the edges of the histogram)
const Double_t x1 = c->GetX1();
const Double_t y1 = c->GetY1();
const Double_t x2 = c->GetX2();
const Double_t y2 = c->GetY2();
//Get now number of pixels including window borders
const Int_t bnpx = c->GetWindowWidth();
const Int_t bnpy = c->GetWindowHeight();
cout << bnpx << "\t" << bnpy << "\t" << npx << "\t" << npy << "\t" << x1 << "\t" << y1 << "\t" << x2 << "\t" << y2 << "\t" << ratio << "\t" << (double)npx/npy << "\tModified Canvas" << endl;
//Check accuracy +/-1 pixel due to rounding
if(abs(npy*ratio - npx)<2)
{
cout << "Resizing finished successfully." << endl;
return true;
}
else
{
cout << "Resizing failed." << endl;
return false;
}
}
// References:
// https://root.cern.ch/root/roottalk/roottalk01/3676.html
// https://root-forum.cern.ch/t/making-the-both-axes-square-on-the-pad/4325/1
}
void test_ratio()
{
TF2 *xyg = new TF2("xyg","xygaus",0,10,0,10);
xyg->SetParameters(1,4.5,0.5,-4.5,0.5); //amplitude, meanx,sigmax,meany,sigmay
TCanvas* c = new TCanvas("c","c");
c->SetGridx();
c->SetGridy();
TH2* h2 = new TH2F("h2","h2", 50,0,10, 100,-8,-1);
h2->GetXaxis()->SetTitle("x / mm");
h2->GetYaxis()->SetTitle("y / mm");
h2->GetXaxis()->SetRangeUser(3,6);
h2->GetYaxis()->SetRangeUser(-7,-2);
h2->FillRandom("xyg",1000000);
h2->Draw("COLZ");
c->SaveAs("default_ratio.png");
SetRealAspectRatio(c);
c->SaveAs("real_aspect_ratio.png");
}
I have the following questions:
[ul]
[li] Is there an easier (default) way to do what I want? Something like c->SetRealAspectRatio()?[/li]
[li] Am I handling correctly the window border?[/li]
[li] Is the approach also valid for TGraph and TGraph2D?[/li]
[li] If I run it in batch mode, my original canvas is 2 pixels smaller in each dimension. 696x472 vs 698x474. This is also visible in the png output. Why does this happen?[/li][/ul]