Search code examples
rgraphicsr-grid

Use grid.curve to connect grobs in different viewpoints


R graphics has the grid package which in theory allows one to use grid.curve to create curved arrows between shapes (see grid.curve description here https://www.stat.auckland.ac.nz/~paul/grid/curve/curve.pdf). However, it seems grid.curve cannot connect grobs across viewpoints.

I would like to change the following code to connect the two circles with a curved arrow as in the grid.curve function examples. Does anyone know how to do this?

Example code below

library(grid)
library(gridExtra)

# Layout Branches
pdf("test.pdf")
grid.newpage()
layout=grid.layout(nrow=2,ncol=2)
pushViewport(viewport(layout=layout,name="base"))

#Now add circles for states
seekViewport("base")
for (ii in 1:2) {
  for(jj in 1:2) {
   name=paste(ii,jj,sep="a")    
   name2=paste(ii,jj,sep="_")  
   pushViewport(viewport(layout.pos.col=jj, layout.pos.row=ii, name = name2)) 
   grid.circle(r=.4, name = name)       
   upViewport()
}}

seekViewport("1_1")
grid.move.to(grobX("1a1",0),grobY("1a1",0)) 
seekViewport("2_2")
grid.line.to(grobX("2a2",180),grobY("2a2",180))
dev.off()

enter image description here


Solution

  • This should do the trick:

    ## Your code
    grid.newpage()
    layout=grid.layout(nrow=2,ncol=2)
    pushViewport(viewport(layout=layout,name="base"))
    seekViewport("base")
    for (ii in 1:2) {
      for(jj in 1:2) {
       name=paste(ii,jj,sep="a")
       name2=paste(ii,jj,sep="_")
       pushViewport(viewport(layout.pos.col=jj, layout.pos.row=ii, name = name2))
       grid.circle(r=.4, name = name)
       upViewport()
    }}
    
    ## Define a function that returns the location of the specified
    ## grob-edge point, in terms of the npc coordinate system of the
    ## entire device
    getDeviceNPC <- function(vpName, grobName, theta) {
        seekViewport(vpName)
        A <- grid.move.to(grobX(grobName, theta), grobY(grobName, theta))
        x <- convertWidth(A$x, unitTo="inches")
        y <- convertHeight(A$y, unitTo="inches")
        xy <- unit((c(x,y,1) %*% current.transform())[1:2], "inches")
        seekViewport("base")
        convertUnit(xy, unitTo="npc")
    }
    
    ## Use it to extract locations of a couple of points
    A <- getDeviceNPC(vpName="1_1", grobName="1a1", theta=0)
    B <- getDeviceNPC(vpName="2_2", grobName="2a2", theta=180)
    
    ## Draw a curve between the points
    grid.curve(A[1], A[2], B[1], B[2], gp=gpar(col="red"), arrow=arrow(),
               curvature=-1, inflect=TRUE)
    

    enter image description here