In a previous question I asked how to get margin line locations in user coordinates. I received an excellent answer in the form of the line2user
function. However, I cannot figure out how to modify the function to work when either the x or y axis is on a log scale.
I have made a couple modifications to accommodate a log-scaled axis:
line2user <- function(line, side, log = "") {
lh <- par('cin')[2] * par('cex') * par('lheight')
x_off <- diff(grconvertX(0:1, 'inches', 'user'))
y_off <- diff(grconvertY(0:1, 'inches', 'user'))
usr <- par('usr') ## Added by me
if (grepl("x", log)) usr[1:2] <- 10^usr[1:2] ## Added by me
if (grepl("y", log)) usr[3:4] <- 10^usr[3:4] ## Added by me
switch(side,
`1` = usr[3] - line * y_off * lh,
`2` = usr[1] - line * x_off * lh,
`3` = usr[4] + line * y_off * lh,
`4` = usr[2] + line * x_off * lh,
stop("Side must be 1, 2, 3, or 4", call.=FALSE))
}
However, I cannot figure out how to properly adjust the xoff
and yoff
variables to draw the proper lines. Illustration:
setup_plot <- function(log = "") {
par(mar = c(2, 10, 2, 2), oma = rep(2, 4))
plot.new()
plot.window(xlim = c(1, 10), ylim = c(1, 10), log = log)
box(which = "plot", lwd = 2, col = "gray40")
box(which = "figure", lwd = 2, col = "darkred")
box(which = "outer", lwd = 2, col = "darkgreen")
text(x = 0.5, y = 0.5,
labels = "Plot Region",
col = "gray40", font = 2)
mtext(side = 3, text = "Figure region", line = 0.5, col = "darkred", font = 2)
mtext(side = 3, text = "Device region", line = 2.5, col = "darkgreen", font = 2)
for (i in 0:9) {
mtext(side = 2, col = "darkred", text = paste0("Line", i), line = i)
}
}
setup_plot(log = "x")
abline(v=line2user(line=0:9, side=2, log = "x"), xpd=TRUE, lty=2)
The compressed lines make sense after considering the following example:
plot(10)
diff(grconvertX(0:1, 'inches', 'user'))
## [1] 0.08121573 (on my device)
plot(10, log = "x")
diff(grconvertX(0:1, 'inches', 'user'))
## [1] 0.0297354 (on my device)
How can I get the correct x_off
and y_off
values when working with a log-scaled axis?
Here's a version that works with log-scale and linear scale axes. The trick is to express line locations in npc
coordinates rather than user
coordinates, since the latter are of course not linear when axes are on log scales.
line2user <- function(line, side) {
lh <- par('cin')[2] * par('cex') * par('lheight')
x_off <- diff(grconvertX(c(0, lh), 'inches', 'npc'))
y_off <- diff(grconvertY(c(0, lh), 'inches', 'npc'))
switch(side,
`1` = grconvertY(-line * y_off, 'npc', 'user'),
`2` = grconvertX(-line * x_off, 'npc', 'user'),
`3` = grconvertY(1 + line * y_off, 'npc', 'user'),
`4` = grconvertX(1 + line * x_off, 'npc', 'user'),
stop("Side must be 1, 2, 3, or 4", call.=FALSE))
}
And here are a couple of examples, applied to your setup_plot
with mar=c(5, 5, 5, 5)
:
setup_plot()
axis(1, line=5)
axis(2, line=5)
abline(h=line2user(0:4, 1), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 2), lty=3, xpd=TRUE)
abline(h=line2user(0:4, 3), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 4), lty=3, xpd=TRUE)
setup_plot(log='x')
axis(1, line=5)
axis(2, line=5)
abline(h=line2user(0:4, 1), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 2), lty=3, xpd=TRUE)
abline(h=line2user(0:4, 3), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 4), lty=3, xpd=TRUE)
setup_plot(log='y')
axis(1, line=5)
axis(2, line=5)
abline(h=line2user(0:4, 1), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 2), lty=3, xpd=TRUE)
abline(h=line2user(0:4, 3), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 4), lty=3, xpd=TRUE)
setup_plot(log='xy')
axis(1, line=5)
axis(2, line=5)
abline(h=line2user(0:4, 1), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 2), lty=3, xpd=TRUE)
abline(h=line2user(0:4, 3), lty=3, xpd=TRUE)
abline(v=line2user(0:4, 4), lty=3, xpd=TRUE)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With