@@ -95,3 +95,166 @@ ggplot_build <- function(plot) {
9595layer_data <- function (plot , i = 1L ) {
9696 ggplot_build(plot )$ data [[i ]]
9797}
98+
99+ # ' Build a plot with all the usual bits and pieces.
100+ # '
101+ # ' This function builds all grobs necessary for displaying the plot, and
102+ # ' stores them in a special data structure called a \code{\link{gtable}}.
103+ # ' This object is amenable to programmatic manipulation, should you want
104+ # ' to (e.g.) make the legend box 2 cm wide, or combine multiple plots into
105+ # ' a single display, preserving aspect ratios across the plots.
106+ # '
107+ # ' @seealso \code{\link{print.ggplot}} and \code{link{benchplot}} for
108+ # ' for functions that contain the complete set of steps for generating
109+ # ' a ggplot2 plot.
110+ # ' @return a \code{\link{gtable}} object
111+ # ' @keywords internal
112+ # ' @param plot plot object
113+ # ' @param data plot data generated by \code{\link{ggplot_build}}
114+ # ' @export
115+ ggplot_gtable <- function (data ) {
116+ plot <- data $ plot
117+ panel <- data $ panel
118+ data <- data $ data
119+ theme <- plot_theme(plot )
120+
121+ geom_grobs <- Map(function (l , d ) l $ draw_geom(d , panel , plot $ coordinates ),
122+ plot $ layers , data )
123+
124+ plot_table <- facet_render(plot $ facet , panel , plot $ coordinates ,
125+ theme , geom_grobs )
126+
127+ # Axis labels
128+ labels <- plot $ coordinates $ labels(list (
129+ x = xlabel(panel , plot $ labels ),
130+ y = ylabel(panel , plot $ labels )
131+ ))
132+ xlabel <- element_render(theme , " axis.title.x" , labels $ x , expand_y = TRUE )
133+ ylabel <- element_render(theme , " axis.title.y" , labels $ y , expand_x = TRUE )
134+
135+ # helper function return the position of panels in plot_table
136+ find_panel <- function (table ) {
137+ layout <- table $ layout
138+ panels <- layout [grepl(" ^panel" , layout $ name ), , drop = FALSE ]
139+
140+ data.frame (
141+ t = min(panels $ t ),
142+ r = max(panels $ r ),
143+ b = max(panels $ b ),
144+ l = min(panels $ l )
145+ )
146+ }
147+ panel_dim <- find_panel(plot_table )
148+
149+ xlab_height <- grobHeight(xlabel )
150+ plot_table <- gtable_add_rows(plot_table , xlab_height )
151+ plot_table <- gtable_add_grob(plot_table , xlabel , name = " xlab" ,
152+ l = panel_dim $ l , r = panel_dim $ r , t = - 1 , clip = " off" )
153+
154+ ylab_width <- grobWidth(ylabel )
155+ plot_table <- gtable_add_cols(plot_table , ylab_width , pos = 0 )
156+ plot_table <- gtable_add_grob(plot_table , ylabel , name = " ylab" ,
157+ l = 1 , b = panel_dim $ b , t = panel_dim $ t , clip = " off" )
158+
159+ # Legends
160+ position <- theme $ legend.position
161+ if (length(position ) == 2 ) {
162+ position <- " manual"
163+ }
164+
165+ legend_box <- if (position != " none" ) {
166+ build_guides(plot $ scales , plot $ layers , plot $ mapping , position , theme , plot $ guides , plot $ labels )
167+ } else {
168+ zeroGrob()
169+ }
170+
171+ if (is.zero(legend_box )) {
172+ position <- " none"
173+ } else {
174+ # these are a bad hack, since it modifies the contents of viewpoint directly...
175+ legend_width <- gtable_width(legend_box ) + theme $ legend.margin
176+ legend_height <- gtable_height(legend_box ) + theme $ legend.margin
177+
178+ # Set the justification of the legend box
179+ # First value is xjust, second value is yjust
180+ just <- valid.just(theme $ legend.justification )
181+ xjust <- just [1 ]
182+ yjust <- just [2 ]
183+
184+ if (position == " manual" ) {
185+ xpos <- theme $ legend.position [1 ]
186+ ypos <- theme $ legend.position [2 ]
187+
188+ # x and y are specified via theme$legend.position (i.e., coords)
189+ legend_box <- editGrob(legend_box ,
190+ vp = viewport(x = xpos , y = ypos , just = c(xjust , yjust ),
191+ height = legend_height , width = legend_width ))
192+ } else {
193+ # x and y are adjusted using justification of legend box (i.e., theme$legend.justification)
194+ legend_box <- editGrob(legend_box ,
195+ vp = viewport(x = xjust , y = yjust , just = c(xjust , yjust )))
196+ }
197+ }
198+
199+ panel_dim <- find_panel(plot_table )
200+ # for align-to-device, use this:
201+ # panel_dim <- summarise(plot_table$layout, t = min(t), r = max(r), b = max(b), l = min(l))
202+
203+ if (position == " left" ) {
204+ plot_table <- gtable_add_cols(plot_table , legend_width , pos = 0 )
205+ plot_table <- gtable_add_grob(plot_table , legend_box , clip = " off" ,
206+ t = panel_dim $ t , b = panel_dim $ b , l = 1 , r = 1 , name = " guide-box" )
207+ } else if (position == " right" ) {
208+ plot_table <- gtable_add_cols(plot_table , legend_width , pos = - 1 )
209+ plot_table <- gtable_add_grob(plot_table , legend_box , clip = " off" ,
210+ t = panel_dim $ t , b = panel_dim $ b , l = - 1 , r = - 1 , name = " guide-box" )
211+ } else if (position == " bottom" ) {
212+ plot_table <- gtable_add_rows(plot_table , legend_height , pos = - 1 )
213+ plot_table <- gtable_add_grob(plot_table , legend_box , clip = " off" ,
214+ t = - 1 , b = - 1 , l = panel_dim $ l , r = panel_dim $ r , name = " guide-box" )
215+ } else if (position == " top" ) {
216+ plot_table <- gtable_add_rows(plot_table , legend_height , pos = 0 )
217+ plot_table <- gtable_add_grob(plot_table , legend_box , clip = " off" ,
218+ t = 1 , b = 1 , l = panel_dim $ l , r = panel_dim $ r , name = " guide-box" )
219+ } else if (position == " manual" ) {
220+ # should guide box expand whole region or region without margin?
221+ plot_table <- gtable_add_grob(plot_table , legend_box ,
222+ t = panel_dim $ t , b = panel_dim $ b , l = panel_dim $ l , r = panel_dim $ r ,
223+ clip = " off" , name = " guide-box" )
224+ }
225+
226+ # Title
227+ title <- element_render(theme , " plot.title" , plot $ labels $ title , expand_y = TRUE )
228+ title_height <- grobHeight(title )
229+
230+ pans <- plot_table $ layout [grepl(" ^panel" , plot_table $ layout $ name ), ,
231+ drop = FALSE ]
232+
233+ plot_table <- gtable_add_rows(plot_table , title_height , pos = 0 )
234+ plot_table <- gtable_add_grob(plot_table , title , name = " title" ,
235+ t = 1 , b = 1 , l = min(pans $ l ), r = max(pans $ r ), clip = " off" )
236+
237+ # Margins
238+ plot_table <- gtable_add_rows(plot_table , theme $ plot.margin [1 ], pos = 0 )
239+ plot_table <- gtable_add_cols(plot_table , theme $ plot.margin [2 ])
240+ plot_table <- gtable_add_rows(plot_table , theme $ plot.margin [3 ])
241+ plot_table <- gtable_add_cols(plot_table , theme $ plot.margin [4 ], pos = 0 )
242+
243+ if (inherits(theme $ plot.background , " element" )) {
244+ plot_table <- gtable_add_grob(plot_table ,
245+ element_render(theme , " plot.background" ),
246+ t = 1 , l = 1 , b = - 1 , r = - 1 , name = " background" , z = - Inf )
247+ plot_table $ layout <- plot_table $ layout [c(nrow(plot_table $ layout ), 1 : (nrow(plot_table $ layout ) - 1 )),]
248+ plot_table $ grobs <- plot_table $ grobs [c(nrow(plot_table $ layout ), 1 : (nrow(plot_table $ layout ) - 1 ))]
249+ }
250+ plot_table
251+ }
252+
253+ # ' Generate a ggplot2 plot grob.
254+ # '
255+ # ' @param x ggplot2 object
256+ # ' @keywords internal
257+ # ' @export
258+ ggplotGrob <- function (x ) {
259+ ggplot_gtable(ggplot_build(x ))
260+ }
0 commit comments