boxCurve <- function(box1, box2,
                     from, to,
                     direction=c("right", "up"),
                     arrows=arrow(angle=15,
                                 length=unit(2, "mm"),
                                 type="closed"),
                     dotSize=unit(1, "mm"),
                     dotFill="white",
                     arrowFill="black") {
    if (!is.grob(box1))
        box1 <- grid.get(box1)
    if (!is.grob(box2))
        box2 <- grid.get(box2)
    fromLine <- length(box1$cnames) - match(from, box1$cnames)
    toLine <- match(to, box2$cnames)
    if (is.na(fromLine) || is.na(toLine))
        stop("'from' and 'to' must name existing columns")
    if (direction[1] == "right") {
        theta1 <- 0
        theta2 <- 180
        hadjust <- unit(-2, "mm")
        if (direction[2] == "up") {
            curvature <- 1
        } else {
            curvature <- -1
        }
    } else {
        theta1 <- 180
        theta2 <- 0
        hadjust <- unit(2, "mm")
        if (direction[2] == "up") {
            curvature <- -1
        } else {
            curvature <- 1
        }
    }
    grid.curve(grobX(box1, theta1) + hadjust,
               grobY(box1, 270) + unit(fromLine + 0.5, "lines"),
               grobX(box2, theta2),
               grobY(box2, 270) + unit(toLine + 0.5, "lines"),
               curvature=curvature,
               inflect=TRUE,
               arrow=arrows, gp=gpar(fill=arrowFill))
    grid.circle(grobX(box1, theta1) + hadjust,
                grobY(box1, 270) + unit(fromLine + 0.5, "lines"),
                r=0.5*dotSize, 
                gp=gpar(fill=dotFill))
}

