如何在多面板图的面板上绘制箭头?

时间:2022-10-22 14:56:10

Suppose we have a multi-panel plot in R, created by using layout(). I would like to draw an arrow from a specified point in one panel to a specified point in another panel. Thus, the arrow goes across panels of the layout. The starting point of the arrow is specified in the coordinates of its panel, and the end point of the arrow is specified in the coordinates of the destination panel.

假设我们在R中有一个由layout()创建的多面板图。我想画一个箭头从一个面板中的一个指定点到另一个面板中的一个指定点。因此,箭头穿过布局的面板。箭头的起点在其面板的坐标中指定,箭头的终点在目标面板的坐标中指定。

As a minimal example, consider this:

作为一个最小的例子,考虑如下:

layout( matrix( 1:2 , nrow=2 ) )
plot( x=c(1,2) , y=c(1,2) , main="Plot 1" )
plot( x=c(10,20) , y=c(10,20) , main="Plot 2" )
# I want to make an arrow 
# from point c(x=1.2,y=1.2) in Plot 1 
# to point c(x=18,y=18) in Plot 2

I've searched for methods to accomplish this, but haven't found anything. Thank you for solutions or pointers.

我已经搜索了实现这个目标的方法,但是没有发现任何东西。谢谢你的解答和指点。

1 个解决方案

#1


2  

Update

(I'm keeping the previous answer below this, but this more-programmatic way is better given your comments.)

(我把之前的答案写在下面,但考虑到你的评论,这种更程序化的方式更好。)

The trick is knowing how to convert from "user" coordinates to the coordinates of the overarching device. This can be done with grconvertX and *Y. I've made some sloppy helper functions here, though they are barely necessary.

诀窍在于知道如何从“用户”坐标转换到总体设备的坐标。这可以用grconvertX和*Y来完成。我在这里做了一些帮助函数,虽然它们几乎是不必要的。

user2ndc <- function(x, y) {
  list(x = grconvertX(x, 'user', 'ndc'),
       y = grconvertY(y, 'user', 'ndc'))
}
ndc2user <- function(x, y) {
  list(x = grconvertX(x, 'ndc', 'user'),
       y = grconvertY(y, 'ndc', 'user'))
}

For the sake of keeping magic-constants out of the code, I'll predefine your points-of-interest:

为了将magic常数排除在代码之外,我将预先定义您的兴趣点:

pointfrom <- list(x = 1.2, y = 1.2)
pointto <- list(x = 18, y = 18)

It's important that the conversion from 'user' to 'ndc' happen while the plot is still current; once you switch from plot 1 to 2, the coordinates change.

重要的是,从“用户”到“ndc”的转换发生时,情节仍然是当前的;一旦你从图1切换到2,坐标就会改变。

layout( matrix( 1:2 , nrow=2 ) )

Plot 1.

图1所示。

plot( x=c(1,2) , y=c(1,2) , main="Plot 1" )
points(y~x, data=pointfrom, pch=16, col='red')
ndcfrom <- with(pointfrom, user2ndc(x, y))

Plot 2.

图2所示。

plot( x=c(10,20) , y=c(10,20) , main="Plot 2" )
points(y~x, data=pointto, pch=16, col='red')
ndcto <- with(pointto, user2ndc(x, y))

As I did before (far below here), I remap the region on which the next plotting commands will take place. Under the hood, layout is doing things like this. (Some neat tricks can be done with par(fig=..., new=T), including overlaying one plot in, around, or barely-overlapping another.)

正如我之前所做的(在这里下面很远的地方),我重新映射下一个绘制命令的区域。在引擎盖下,布局是这样做的。(一些简单的技巧可以用par来完成(fig=…),新=T),包括将一个地块覆盖在另一个地块上、周围或几乎重叠。

par(fig=c(0:1,0:1), new=TRUE)
plot.new()
newpoints <- ndc2user(c(ndcfrom$x, ndcto$x), c(ndcfrom$y, ndcto$y))
with(newpoints, arrows(x[1], y[1], x[2], y[2], col='green', lwd=2))

I might have been able to avoid the ndc2user conversino from ndc back to current user points, but that's playing with margins and axis-expansion and things like that, so I opted not to.

我本可以避免ndc2user conversino从ndc返回到当前的用户点,但那是在玩边距和axis扩展之类的东西,所以我选择了不这么做。

It is possible that the translated points may be outside of the user-points region of this last overlaid plot, in which case they may be masked. To fix this, add xpd=NA to arrows (or in a par(xpd=NA) before it).

翻译后的点可能不在这个最后叠加的图的用户点区域之外,在这种情况下,它们可能被掩盖。要解决这个问题,将xpd=NA添加到箭头(或者在前面的par(xpd=NA)中)。

如何在多面板图的面板上绘制箭头?


Generalized

Okay, so imagine you want to be able to determine the coordinates of any drawing after layout completion. There's a more complex implementation that currently supports what you're asking for. the only requirement is that you call NDC$add() after every (meaningful) plot. For example:

好,假设你想要在布局完成后确定任何一幅画的坐标。现在有一个更复杂的实现支持您的要求。唯一的要求是在每个(有意义的)图之后调用NDC$add()。例如:

NDC$reset()
layout(matrix(1:4, nrow=2))
plot(1)
NDC$add()
plot(11)
NDC$add()
plot(21)
NDC$add()
plot(31)
NDC$add()
with(NDC$convert(1:4, c(1,1,1,1), c(1,11,21,31)), {
  arrows(x[1], y[1], x[2], y[2], xpd=NA, col='red')
  arrows(x[2], y[2], x[3], y[3], xpd=NA, col='blue')
  arrows(x[3], y[3], x[4], y[4], xpd=NA, col='green')
})

Source can be found here: https://gist.github.com/r2evans/8a8ba8fff060bade13bf21e89f0616c5

来源可以在这里找到:https://gist.github.com/r2evans/8a8ba8fff060bade13bf21e89f0616c5

如何在多面板图的面板上绘制箭头?


Previous Answer

One way is to use par(fig=...,new=TRUE), but it does not preserve the coordinates you e

一种方法是使用par(fig=…,new=TRUE),但是它不保留坐标you e

layout(matrix(1:4,nr=2))
plot(1)
plot(1)
plot(1)
plot(1)
par(fig=c(0,1,0,1),new=TRUE)
plot.new()
lines(c(0.25,0.75),c(0.25,0.75),col='blue',lwd=2)

如何在多面板图的面板上绘制箭头?

Since you may be more likely to use this if you have better (non-arbitrary) control over the ends of the points, here's a trick to allow you more control over the points. If I use this, connectiong the top-left point with the bottom-right point:

因为如果你对点的末端有更好的(非任意的)控制,你可能更倾向于使用它,这里有一个技巧可以让你对点有更多的控制。如果我使用这个,连接左上点和右下点:

p <- locator(2)
str(p)
# List of 2
#  $ x: num [1:2] 0.181 0.819
#  $ y: num [1:2] 0.9738 0.0265

and then in place of lines above I use this:

然后我用这个代替上面的线

with(p, arrows(x[1], y[1], x[2], y[2], col='green', lwd=2))

I get

我得到

如何在多面板图的面板上绘制箭头?

(This picture and the values in p demonstrate how the coordinates are different. When using par(fig=...,new=T);plot.new();, the coordinates return to

这幅图和p中的值说明了坐标是如何不同的。使用par时(fig=…,new=T);plot.new()

par('usr')
# [1] -0.04  1.04 -0.04  1.04

There might be trickery to try to workaround this (such as if you need to automate this step), but it likely will be non-trivial (and not robust).

可能会有欺骗的方法来解决这个问题(例如,如果您需要自动化这个步骤),但是它很可能是非平凡的(并且不是健壮的)。

#1


2  

Update

(I'm keeping the previous answer below this, but this more-programmatic way is better given your comments.)

(我把之前的答案写在下面,但考虑到你的评论,这种更程序化的方式更好。)

The trick is knowing how to convert from "user" coordinates to the coordinates of the overarching device. This can be done with grconvertX and *Y. I've made some sloppy helper functions here, though they are barely necessary.

诀窍在于知道如何从“用户”坐标转换到总体设备的坐标。这可以用grconvertX和*Y来完成。我在这里做了一些帮助函数,虽然它们几乎是不必要的。

user2ndc <- function(x, y) {
  list(x = grconvertX(x, 'user', 'ndc'),
       y = grconvertY(y, 'user', 'ndc'))
}
ndc2user <- function(x, y) {
  list(x = grconvertX(x, 'ndc', 'user'),
       y = grconvertY(y, 'ndc', 'user'))
}

For the sake of keeping magic-constants out of the code, I'll predefine your points-of-interest:

为了将magic常数排除在代码之外,我将预先定义您的兴趣点:

pointfrom <- list(x = 1.2, y = 1.2)
pointto <- list(x = 18, y = 18)

It's important that the conversion from 'user' to 'ndc' happen while the plot is still current; once you switch from plot 1 to 2, the coordinates change.

重要的是,从“用户”到“ndc”的转换发生时,情节仍然是当前的;一旦你从图1切换到2,坐标就会改变。

layout( matrix( 1:2 , nrow=2 ) )

Plot 1.

图1所示。

plot( x=c(1,2) , y=c(1,2) , main="Plot 1" )
points(y~x, data=pointfrom, pch=16, col='red')
ndcfrom <- with(pointfrom, user2ndc(x, y))

Plot 2.

图2所示。

plot( x=c(10,20) , y=c(10,20) , main="Plot 2" )
points(y~x, data=pointto, pch=16, col='red')
ndcto <- with(pointto, user2ndc(x, y))

As I did before (far below here), I remap the region on which the next plotting commands will take place. Under the hood, layout is doing things like this. (Some neat tricks can be done with par(fig=..., new=T), including overlaying one plot in, around, or barely-overlapping another.)

正如我之前所做的(在这里下面很远的地方),我重新映射下一个绘制命令的区域。在引擎盖下,布局是这样做的。(一些简单的技巧可以用par来完成(fig=…),新=T),包括将一个地块覆盖在另一个地块上、周围或几乎重叠。

par(fig=c(0:1,0:1), new=TRUE)
plot.new()
newpoints <- ndc2user(c(ndcfrom$x, ndcto$x), c(ndcfrom$y, ndcto$y))
with(newpoints, arrows(x[1], y[1], x[2], y[2], col='green', lwd=2))

I might have been able to avoid the ndc2user conversino from ndc back to current user points, but that's playing with margins and axis-expansion and things like that, so I opted not to.

我本可以避免ndc2user conversino从ndc返回到当前的用户点,但那是在玩边距和axis扩展之类的东西,所以我选择了不这么做。

It is possible that the translated points may be outside of the user-points region of this last overlaid plot, in which case they may be masked. To fix this, add xpd=NA to arrows (or in a par(xpd=NA) before it).

翻译后的点可能不在这个最后叠加的图的用户点区域之外,在这种情况下,它们可能被掩盖。要解决这个问题,将xpd=NA添加到箭头(或者在前面的par(xpd=NA)中)。

如何在多面板图的面板上绘制箭头?


Generalized

Okay, so imagine you want to be able to determine the coordinates of any drawing after layout completion. There's a more complex implementation that currently supports what you're asking for. the only requirement is that you call NDC$add() after every (meaningful) plot. For example:

好,假设你想要在布局完成后确定任何一幅画的坐标。现在有一个更复杂的实现支持您的要求。唯一的要求是在每个(有意义的)图之后调用NDC$add()。例如:

NDC$reset()
layout(matrix(1:4, nrow=2))
plot(1)
NDC$add()
plot(11)
NDC$add()
plot(21)
NDC$add()
plot(31)
NDC$add()
with(NDC$convert(1:4, c(1,1,1,1), c(1,11,21,31)), {
  arrows(x[1], y[1], x[2], y[2], xpd=NA, col='red')
  arrows(x[2], y[2], x[3], y[3], xpd=NA, col='blue')
  arrows(x[3], y[3], x[4], y[4], xpd=NA, col='green')
})

Source can be found here: https://gist.github.com/r2evans/8a8ba8fff060bade13bf21e89f0616c5

来源可以在这里找到:https://gist.github.com/r2evans/8a8ba8fff060bade13bf21e89f0616c5

如何在多面板图的面板上绘制箭头?


Previous Answer

One way is to use par(fig=...,new=TRUE), but it does not preserve the coordinates you e

一种方法是使用par(fig=…,new=TRUE),但是它不保留坐标you e

layout(matrix(1:4,nr=2))
plot(1)
plot(1)
plot(1)
plot(1)
par(fig=c(0,1,0,1),new=TRUE)
plot.new()
lines(c(0.25,0.75),c(0.25,0.75),col='blue',lwd=2)

如何在多面板图的面板上绘制箭头?

Since you may be more likely to use this if you have better (non-arbitrary) control over the ends of the points, here's a trick to allow you more control over the points. If I use this, connectiong the top-left point with the bottom-right point:

因为如果你对点的末端有更好的(非任意的)控制,你可能更倾向于使用它,这里有一个技巧可以让你对点有更多的控制。如果我使用这个,连接左上点和右下点:

p <- locator(2)
str(p)
# List of 2
#  $ x: num [1:2] 0.181 0.819
#  $ y: num [1:2] 0.9738 0.0265

and then in place of lines above I use this:

然后我用这个代替上面的线

with(p, arrows(x[1], y[1], x[2], y[2], col='green', lwd=2))

I get

我得到

如何在多面板图的面板上绘制箭头?

(This picture and the values in p demonstrate how the coordinates are different. When using par(fig=...,new=T);plot.new();, the coordinates return to

这幅图和p中的值说明了坐标是如何不同的。使用par时(fig=…,new=T);plot.new()

par('usr')
# [1] -0.04  1.04 -0.04  1.04

There might be trickery to try to workaround this (such as if you need to automate this step), but it likely will be non-trivial (and not robust).

可能会有欺骗的方法来解决这个问题(例如,如果您需要自动化这个步骤),但是它很可能是非平凡的(并且不是健壮的)。