I am having trouble understanding how grobs are being placed into the plotting area. Here's an example of what I mean. Let's assume I have the following plot.
library(tidyverse)
p <- mpg %>%
ggplot(aes(year))
geom_bar()
p
I understand that I can get a list of grobs from that and plot it like so.
l <- ggplotGrob(p)
grid::grid.newpage()
grid::grid.draw(l)
From the structure of list l
I also understand that the rectangles are drawn as rectGrob
s. I found these in.
grobs <- l$grobs[[6]]
rect_grobs <- grobs$children[[3]]
But if I want to plot at least one rectangle using the data straight from rect_grobs
, nothing happens. This is independent of whether I create a new canvas with grid::grid.newpage()
or not.
# This draws nothing
grid::grid.draw(
grid::rectGrob(
x = rect_grobs$x[1],
y = rect_grobs$y[1],
height = rect_grobs$height[1],
width = rect_grobs$width[1],
just = rect_grobs$just[1],
)
)
Yet if I plug in the values from rect_grobs
by hand, then a rectangle is drawn in the wrong position. I suspect that this is related to viewports but I tried looking in l
, grobs
and rect_grobs
but I found no clue what viewport so set. Wherever I look, there is only vp = NULL
.
# This draws in wrong position
grid::grid.draw(
grid::rectGrob(
x = 0.0455,
y = 0.955,
width = 0.431,
height = 0.909,
just = 'left'
)
)
So, my questions are
- Why does the code not draw a rectangle if I take the values straight from the
rect_grobs
list? - How do I get the rectangle into the correct position if I manually add it?
CodePudding user response:
The reason why rect_grobs
doesn't draw is indeed that it has no viewport. If a grob has no viewport set, it will inherit from its parent, but since you have plucked it out of a gTable
, it has no parent. This is easy to rectify by giving it a default viewport:
rect_grobs$vp <- grid::viewport()
grid::grid.draw(rect_grobs)
As long as we specify the correct just
, we get the rectangle in the same place:
red_rect <- grid::rectGrob(
x = 0.0455,
y = 0.955,
width = 0.431,
height = 0.909,
just = c("left", "top"),
gp = grid::gpar(fill = "red")
)
grid::grid.draw(red_rect)
However, this is not the position the rectangle would be in a ggplot. That's because in ggplot
the panel has its own viewport. To draw the rectangle in this context, we need to navigate to the correct viewport before calling grid.draw
plot(p)
grid::seekViewport(grid::vpPath("layout", "panel.7-5-7-5"))
grid::grid.draw(red_rect)