@@ -95,3 +95,166 @@ ggplot_build <- function(plot) {
95
95
layer_data <- function (plot , i = 1L ) {
96
96
ggplot_build(plot )$ data [[i ]]
97
97
}
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