ROOT TLine and TLatex at the same angle?

I’ve attached a file here. Now, ignoring all other elements, the code is below. The problem is that I set the slope of a line I draw, on a TH2 with equal-length axes, to 1… I also SetTextAngle(45) for the text that I draw on. The line and the text seem to have different angles! Even more concerning, drawing the TH2 with the “TEXT45” option seems to have the text aligned with the line!

How can I get the “Kinematically Forbidden” text to line up? Is it a problem with aspect ratio? How do I fix this? I must be missing something stupid. Note, this is indeed pyroot, but reproducible in ROOT/C++ as well.

def nbins(start, stop, step):
    return abs(int( (stop - start) / step ))

def axis_labels(x_label="m_{#tilde{g}} [GeV]", y_label="m_{#tilde{#chi}^{0}_{1}} [GeV]", z_label="", title=""):
    return ';'.join([title, x_label, y_label, z_label])

c = ROOT.TCanvas("c", "", 0, 0, 800, 600)
c.SetTopMargin(0.07)
c.SetRightMargin(0.16)
hist = ROOT.TH2F(name,
                axis_labels(z_label=label),
                nbins(200, 2500, 100),
                200,
                2500,
                nbins(0, 2300, 100),
                0,
                2300)

# fill hist here
hist.Draw("TEXT45 COLZ")

# draw line
l=ROOT.TLine(1000,1000,2000,2000)
l.SetLineStyle(2)
l.DrawLine(350, 0, 2500, 2150)

# Draw Kinematically Forbidden as well
txt = ROOT.TLatex()
txt.SetTextFont(12)
txt.SetTextAngle(45)
txt.SetTextSize(0.02)
txt.DrawText(1425, 1200, "Kinematically Forbidden")

c.SaveAs(savefilename + ".pdf")


It is because your canvas does not have an orthonormal setup. A circle drawn on top of this histo will be an ellipse.1 unit along X is longer than 1 unit along Y. The text in bins is aligned because the bins have the same aspect ratio as the pad.
The following example works as you expect …

void text_angle3()
{
   TCanvas *ta = new TCanvas("Ta","Ta",0,0,300,326);

   TH2F *h = new TH2F("h","h",20,0.,2500.,20,0.,2300);
   h->Fill(1000.,1000.,123.);
      h->Fill(1500.,1000.,1230.);

   h->Draw("col");
   h->Draw("text45 same");
   TLine *l = new TLine(0.,0.,2300.,2300.);
   l->Draw();
   TText *t = new TText(1000.,1100.,"text angle 45");
   t->SetTextAngle(45.);
   t->Draw();
}

What in your code is fixing that aspect ratio? Or how can I calculate it to make it happen correctly? Is it the TCanvas initialization, or the TLine initialization or? (Side note: what are the x1,x2,y1,y2 for the TLine constructor? Is it the size of the frame it’s contained in, because it’s clearly not about the bottom left/top right of the rectangle if the line was a diagonal drawn for it).

the TCanvas initialization

first and last point of the line …

Why do you initialize the canvas with

   TCanvas *ta = new TCanvas("Ta","Ta",0,0,300,326);

instead of

   TCanvas *ta = new TCanvas("Ta","Ta",0,0,300,300);

if you wanted it to be 1:1? How can this be correctly implemented if there are other objects on the canvas, such as the color bar and axes labels? EG: if other things take up room and squish the plot in more, there’s no way to make sure it’s 1:1 aspect ratio even if the canvas is?

because of the last point explained here:
root.cern.ch/doc/master/classTCanvas.html

At creation time, no matter if in interactive or batch mode, the canvas size defines the size of the canvas window (including the size of the window manager’s decoration). To define precisely the graphics area size of a canvas in the interactive mode, the following four lines of code should be used:

{
   Double_t w = 600;
   Double_t h = 600;
   TCanvas * c1 = new TCanvas("c", "c", w, h);
   c->SetWindowSize(w + (w - c->GetWw()), h + (h - c->GetWh()));
}

and in the batch mode simply do:

c->SetCanvasSize(w,h);

What’s the difference between the size of the canvas window and the graphics area size of a canvas? Was that answering my first question or my second question? I still don’t know where 326 comes from.

Ok here is the little trigonometry exercise you are looking for:

void text_parallel_to_a_line() {
   TCanvas *C = new TCanvas("C","C", 800,400);
   C->DrawFrame(0.,0.,1.,1.);
   TLine *l = new TLine(0.,0.,1.,1.);
   l->Draw();
   TText *t1 = new TText(0.1,0.1,"This text has an angle of 45 degrees");
   t1->SetTextAngle(45);
   t1->Draw();

   Double_t W = (Double_t) C->GetWw();
   Double_t H = (Double_t) C->GetWh();
   Double_t A = (180*TMath::ATan(H/W))/(TMath::Pi());
   TText *t2 = new TText(0.3,0.3,Form("This text is parallel to the line the angle is %g degrees", A));
   t2->SetTextAngle(A);
   t2->Draw();
}

Hi, I’m still not understanding because it’s not clear how things are being defined. You’ve introduced a frame now, but I’m not sure why that’s needed. When I ran your above code example, I got the following image where the text doesn’t appear to be parallel to the line. Which is why I was asking if this line

   TCanvas *ta = new TCanvas("Ta","Ta",0,0,300,326);

has a typo or not. How can I guarantee that regardless of whatever size of canvas’ aspect ratio is being chosen, that the TH2F respects a 1:1 aspect ratio? This doesn’t seem possible to do at all. When I run your second block of code, the text that states it should be parallel to the line isn’t. What’s very confusing I think is that setting the angle of the line to be drawn and setting the angle of the text to be drawn do completely different things.




The Drawframe is just to have a plot …
Ok I will modify my initial macro to make it work.

void text_angle3()
{
   TCanvas *C = new TCanvas("C","C", 800,400);
   TH2F *h = new TH2F("h","h",20,0.,2500.,20,0.,2500);
   h->Fill(1000.,1000.,123.);
      h->Fill(1500.,1000.,1230.);

   h->Draw("col");
   h->Draw("text45 same");
   TLine *l = new TLine(0.,0.,2300.,2300.);
   l->Draw();
   gPad->Update();

   Double_t W = (Double_t) C->GetWw();
   Double_t H = (Double_t) C->GetWh();
   Double_t A = (180*TMath::ATan(H/W))/(TMath::Pi());
   TText *t2 = new TText(0.3,0.3,Form("This text is parallel to the line the angle is %g degrees", A));
   t2->SetTextAngle(A);
   t2->Draw();
}


This only works if the line drawn is 45 degrees correct? How do you do this for lines that aren’t drawn at 45 degrees? It’s not clear how the canvas affects this.

This post is from January … I had to refresh my mind about it… looking at the last piece of code I sent you it seems there is no assumption on the text angle as it is calculated from the canvas width and heigh. Do you have a none working example showing what you are talking about… ???

This is an example where the canvas width/height are not 1:1 – but the slope of the line is 45 degrees, and the text isn’t aligned at all.

In this particular case, I see the following values:

Line y_max, y_min, x_max, x_min: 1653.32 453.32 2000 800
Canvas Ww, Wh: 1598.0 1006.0
Slope of Line and Angle: 1.0 45.0
Slope of Canvas and Angle: 0.629536921151 32.1919299393

It seems like the line isn’t drawn at 45 degrees or ~32 degrees, but somewhere in between… but I don’t know why or how to fix this since I need to know the angle of the line absolutely with respect to the page. Is this not possible?

Can you provide a small macro reproducing this issue ? It will be easier to fix your code than trying to mimic it …

import ROOT

C = ROOT.TCanvas("C","C", 1600, 1200)
C.SetRightMargin(0.16)
h = ROOT.TH2F("h","h",20,0.,2500.,20,0.,2500)
h.Fill(1000.,1000.,123.)
h.Fill(1500.,1000.,1230.);
h.Draw("col");
h.Draw("text45 same");
l = ROOT.TLine(0.,0.,2300.,2300.)
l.Draw();
ROOT.gPad.Update()
W = float(C.GetWw())
H = float(C.GetWh())
A = (180.*ROOT.TMath.ATan(H/W))/(ROOT.TMath.Pi())
t2 = ROOT.TText(0.3,0.3,"This text is parallel to the line the angle is {0:0.2f} degrees".format(A));
t2.SetTextAngle(A)
t2.Draw()
ROOT.gPad.Update()

raw_input()

It appears that setting margins on the canvas screw up the calculations, but we need these margins to properly control spacing for the labels.

I would prefer a C++ one … never mind I will translate …

The following macro does what you want. I have improved it a bit in order to have the automatic update of the text angle when you resize the Canvas interactively.

textangle.C (910 Bytes)

Thanks! This looks like it works. How did you get it to automatically update when you resize the canvas? Also, this seems like a somewhat convoluted/complicated thing to do since it appears TLines and TTexts are treated differently when trying to figure out the angles.