Skip to content

srt --- slope text with function?

5 messages · ivo welch, Duncan Murdoch

#
[resent, plus small addition; I do not understand why gmail sent a
weird charset.]

Dear R wizards:

I would love to write a general function that matches the slope of a plotted
line in an xy-plot at a particular x,y location.  something like

   x<- (1:10)^2; y<- 40:50;
   plot( x,y, type="l", xlim=c(0,90) )
   srt.at5 = text.at.current.plot.with.slope( x, y,  5);
   text( x[5],y[5], pos=3, srt=srt.at.5);

to do this, I first need to compute the function slope around x[5], which is
an easy task.  alas, the harder task is that I need to scale this by the
plot aspect ratio and the axes.  How can a function read this from the
current plot?

(Has someone written such a function, perhaps more embellished, to save me
the debugging effort?)

Or, is there an alternative to srt, which slopes the text relative to the
existing scale?

  *** come to think of it, what I would really like is the ability of
text to 'snake' itself along the line itself.  I doubt that this is
easily possible, but I just wanted to ask.

help appreciated.

sincerely,

/ivo welch
#
On 2/4/2006 3:50 PM, ivo welch wrote:
I haven't done this, but you can presumably work it out from the 
conversions implied by the "fig", "fin", "plt", and/or "usr" values.
Using strsplit and strwidth you should be able to do it, but it will 
probably look quite ugly.

Duncan Murdoch
#
Thank you, Duncan.  This led me to the info I needed.  Here is a
simple utility function that does what I needed---maybe it will come
in helpful for others.

################################################################
#### native.slope computes a suitable srt from a function around
#### a point on a function.  This is useful until text() gets
#### an srt parameter that is relative to the coordinate system.
####   (Ideally, R would be able to slope along a function.)
################################################################

native.slope <- function( x, y, where.i,
			 xlim = par()$xaxp, ylim= par()$yaxp,
			 asp.ratio = (par()$fin)[1]/(par()$fin)[2] ) {
  if (where.i<=1) { return(0); }
  if (where.i>=length(y)) { return(0); }
  if (length(x)!=length(y)) {
    stop("native.slope: Sorry, but x and y must have equal dimensions,
not ", length(x), " and ", length(y), "\n"); }

  # native slope in a 1:1 coordinate system
  d= ( (y[where.i-1]-y[where.i+1])/(x[where.i-1]-x[where.i+1]) );
  if (is.na(d)) return(0); # we do not know how to handle an undefined
spot at a function!

  d.m= (ylim[2]-ylim[1])/(xlim[2]-xlim[1]); # now adjust by the axis scale
  if (is.na(d)) stop("native.slope: internal error, I do not have
sensible axis dimensions (", xlim, ylim, ")\n");

  if (is.na(asp.ratio)) stop("native.slope: internal error, I do not
have a reasonable drawing aspect ratio");

  net.slope= d/asp.ratio/d.m;
  return(slope = atan(net.slope)/pi*180.0 )
}


# some test code
x<- seq(-10,20,by=0.1)
y<- x*x;

plot( x, y, type="l" );

display= ((1:length(y))%%40 == 0)

for (i in 1:(length(y))) {
  if (display[i]) {
    points(x[i],y[i], pch=19);
    srt= native.slope( x, y, i );
    text( x[i], y[i], paste(i,"=",x[i],"=",srt), srt=srt, cex=0.9 );
  }
}
On 2/4/06, Duncan Murdoch <murdoch at stats.uwo.ca> wrote:
#
Some more experimentation reveals that I need to also adjust for the
plot dimensions.  (And there are other issues I probably do not know
yet and totally misprogrammed...just trying to be helpful.)

native.slope <- function( x, y, where.i,
			 xlim = par()$xaxp, ylim= par()$yaxp,
			 asp.ratio = (par()$fin)[1]/(par()$fin)[2],
			 debug =0) {
  if (where.i<=1) { return(0); }
  if (where.i>=length(y)) { return(0); }
  if (length(x)!=length(y)) {
    stop("native.slope: Sorry, but x and y must have equal dimensions,
not ", length(x), " and ", length(y), "\n"); }

  # native slope in a 1:1 coordinate system
  d= ( (y[where.i-1]-y[where.i+1])/(x[where.i-1]-x[where.i+1]) );
  if (is.na(d)) return(0); # we do not know how to handle an undefined
spot at a function!

  d.m= (ylim[2]-ylim[1])/(xlim[2]-xlim[1]); # now adjust by the axis scale
  if (is.na(d)) stop("native.slope: internal error, I do not have
sensible axis dimensions (", xlim, ylim, ")\n");

  if (is.na(asp.ratio)) stop("native.slope: internal error, I do not
have a reasonable drawing aspect ratio");

  ## alas, we also need to take into account the plot region:
  pq= par()$plt; plt.distort= (pq[2]-pq[1])/(pq[4]-pq[3]);

  net.slope= d/asp.ratio/d.m / plt.distort;

  slope = atan(net.slope)/pi*180.0;


  if (debug) {
    cat("xlim=", par()$xaxp, "\n");
    cat("ylim=", par()$yaxp, "\n\n");

    cat("native.slope: d=", d, " (",y[where.i-1],y[where.i+1],
x[where.i-1], x[where.i+1],")",
	"d.m=",d.m, " (", ylim[2],ylim[1],xlim[2],xlim[1], ")",
	"asp.ratio=", (par()$fin)[1], ":", (par()$fin)[2], "==>", net.slope,
"=", slope, "deg\n");
  }

  return( slope = slope );
}
On 2/4/06, ivo welch <ivowel at gmail.com> wrote:
#
I don't think it's got the slope exactly right - you'll see this if you 
go to a really extreme aspect ratio by changing the shape of a window to 
be long and thin before you call your code.  To fix this:

  - use "usr" rather than "xaxp" and "yaxp" to get the limits of the 
plot region in user coordinates; those two refer to the ticks, not the 
whole plot region as "usr" does.

- "fin" is the whole figure region, not just the plot region; you need 
to use "plt" to modify it to find the plot region within it.  So I think 
the aspect ratio should really be done as

   pars <- par("fin", "plt")

   asp.ratio <- (diff(pars$plt)[1]*pars$fin[1]) /
                (diff(pars$plt)[3]*pars$fin[2])

Some other suggestions:

  - split the function into two:  one that determines a slope from the 
data, and one that converts a slope to an angle suitable for "srt".  (I 
think the latter would have pretty wide use; the former is pretty 
specialized for data the way you're using it).

  - use the fact that defaults in a function call can be local variables 
in the function, so that you only need one call to par() instead of 4. 
(The 4 calls probably take a negigible amount of time, but it just looks 
wasteful to make them.)

Duncan Murdoch
On 2/4/2006 5:19 PM, ivo welch wrote: