Draw TLine Constrained by Axes

Hi all, I would like to draw lines on top of a TH2, but the lines are infinite and are defined by their slope and Y-intercept only.

Is there an easy way to make the line segment that is actually be drawn be constrained by the axes of the drawn TH2, rather than extending across the whole canvas beyond the axes?

Thanks,
Jean-François

I went ahead and coded a mostly-naive implementation of this. It’s mostly long because of my coding/indentation style, but it’s still the kind of thing I would like to see in a method. TLine doesn’t have too many methods on it yet, so maybe something like this could be added?

void RestrictLine(TLine & l, Double_t xmin, Double_t ymin, Double_t xmax, Double_t ymax)
{ // Basic line equations.
  Double_t m = (l.GetY2()-l.GetY1())/(l.GetX2()-l.GetX1());
  Double_t b = l.GetY1()-m*l.GetX1();
  
  // When does the line cross the limits requested?
  Double_t y_at_xmin = m*xmin+b;
  Double_t y_at_xmax = m*xmax+b;
  Double_t x_at_ymin = (ymin-b)/m;
  Double_t x_at_ymax = (ymax-b)/m;

  // This will hold the new start/end coordinates of the line.
  vector<pair<Double_t, Double_t> > xy;

  if( y_at_xmin <= ymax && y_at_xmin >= ymin )
    { // Crosses the left vertical edge inside the bounds.
      xy.push_back(pair<Double_t,Double_t>(xmin,y_at_xmin));
    }

  if( y_at_xmax <= ymax && y_at_xmax >= ymin )
    { // Crosses the right vertical edge inside the bounds.
      xy.push_back(pair<Double_t,Double_t>(xmax,y_at_xmax));
    }

  if( x_at_ymin <= xmax && x_at_ymin >= xmin )
    { // Crosses the bottom horizontal edge inside the bounds.
      xy.push_back(pair<Double_t,Double_t>(x_at_ymin,ymin));
    }

  if( x_at_ymax <= xmax && x_at_ymax >= xmin )
    { // Crosses the upper horizontal edge inside the bounds.
      xy.push_back(pair<Double_t,Double_t>(x_at_ymax,ymax));
    }

  if(xy.size() != 2)
    { // The line is only clipped if it enters and exits the box.
      // Note that this has a problem if the line lies along one
      // of the bounding edges, but I don't want to deal with that.
      cout << "Line crosses " << xy.size() << " edges of bounding box." << endl;
      return;
    }

  // Set the new coordinates of the line.
  l.SetX1(xy[0].first);
  l.SetY1(xy[0].second);
  l.SetX2(xy[1].first);
  l.SetY2(xy[1].second);
  return;
}

You can test it by giving these commands in CINT (after .L RestrictLine.C+, it needs to be compiled because of the vector<pair<…> >).

TH1F h("h","h",10,0,10);
h.Fill(5);
TLine L(-5,-.2,20,1.0);
L.Draw(); // See how the line flows out past the axes boundaries.
RestrictLine(L,0,0,10,1);
L.Draw(); // The bounds are properly applied without changing the slope/y-intercept.

Of course this still needs one to get the proper bounds, but that’s easy with whatever object holds the Axes with ->GetXmin() and ->GetXmax();.

Jean-François

See also: “vertical shaded area” thread

You can easily make your example usable as an interpreted code:

#include "TLine.h"

#include <utility>
#include <vector>
#include <iostream>

void RestrictLine(TLine & l, Double_t xmin, Double_t ymin, Double_t xmax, Double_t ymax)
{
#if defined(__CINT__)
  static int RunMeOnce = 0;
  if (!RunMeOnce) {
    RunMeOnce = 1;
    gSystem->Exec("rm -f AutoDict*pair*double*double*");
    // gInterpreter->GenerateDictionary("pair<double,double>;vector<pair<double,double> >", "utility;vector");
    gInterpreter->GenerateDictionary("vector<pair<double,double> >", "utility;vector");
  }
#endif /* defined(__CINT__) */
  
  // ...

Is there an update on this? Any way to draw a line and not have it cover the whole canvas?