#' Build a Graphic
#'
#' Builds and renders a [Graphic] using a [Renderer].
#'
#' @export
Builder <- R6Class(
"Builder", inherit = Graphic,
public = list(
renderer = NULL,
initialize = function(graphic, renderer = IdentityRenderer$new()) {
self$layers <- graphic$layers
self$scales <- graphic$scales
self$set_coord(graphic$coord)
self$set_facet(graphic$facet)
self$set_theme(graphic$theme)
self$set_renderer(renderer)
},
set_renderer = function(renderer) {
assert_r6(renderer, "Renderer")
self$renderer <- renderer
invisible(self)
},
# high-level functions ---------------------------
build_init = function() {
with_theme(self$theme, {
self$train_facet()
self$create_panels()
self$plot_data <- self$create_plot_data()
})
invisible(self)
},
build = function() {
if (is.null(self$plot_data) || is.null(self$panels)) {
self$build_init()
}
with_theme(self$theme, {
plot_data <- self$prepare_data_and_scales(self$plot_data)
plot_data <- self$compute_statistics(plot_data)
plot_data <- self$compute_positions(plot_data)
plot_data <- self$map_non_position_scales(plot_data)
plot_data <- self$finish_data(plot_data)
self$plot_data <- plot_data
})
invisible(self)
},
render = function() {
if (is.null(self$plot_data) ||
is.null(self$panels) ||
all(purrr::map_lgl(self$plot_data, is.null))) {
self$build()
}
with_theme(self$theme, {
self$train_guides()
self$renderer$render_panels(
self,
!!!purrr::imap(self$panels, function(panel, panel_index) {
self$renderer$render_panel(
panel,
!!!purrr::imap(self$layers$lst, function(layer, layer_index) {
layer$geom$render_panel(
self$plot_data[panel_index, layer_index][[1]],
panel,
self$renderer
)
})
)
})
)
})
},
# self$build() ------------------------------------
prepare_data_and_scales = function(plot_data) {
plot_data <- self$split_layer_data(plot_data)
plot_data <- self$map_data_columns(plot_data)
self$add_missing_scales(plot_data, self$renderer)
plot_data
},
compute_statistics = function(plot_data) {
plot_data <- self$scale_transform(plot_data)
self$scale_train_position(plot_data)
plot_data <- self$scale_map_position(plot_data)
plot_data <- self$setup_stat_data(plot_data, self$renderer)
plot_data <- self$stat_compute(plot_data, self$renderer)
plot_data <- self$map_stat_columns(plot_data)
plot_data
},
compute_positions = function(plot_data) {
self$add_position_scales(self$renderer)
plot_data <- self$position_compute(plot_data)
self$scale_retrain_position(plot_data)
self$finalize_panels()
plot_data <- self$scale_remap_position(plot_data)
plot_data
},
map_non_position_scales = function(plot_data) {
self$scale_train_non_position(plot_data)
plot_data <- self$scale_map_non_position(plot_data)
plot_data
},
finish_data = function(plot_data) {
plot_data <- self$setup_geom_data(plot_data, self$renderer)
plot_data
},
# self$build_init() -----------------------------------
train_facet = function() {
self$facet$reset()
for (layer in self$layers$lst) {
self$facet$train(layer$data_src)
}
invisible(self)
},
create_panels = function() {
self$panels <- self$facet$panels(self$coord, self$scales)
},
create_plot_data = function() {
panel_indices <- seq_along(self$panels)
layer_indices <- seq_along(self$layers$lst)
plot_data <- vector(
mode = "list",
length = length(panel_indices) * length(layer_indices)
)
dim(plot_data) <- c(length(panel_indices), length(layer_indices))
plot_data
},
# self$prepare_data() -----------------------------------
split_layer_data = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
self$facet$panel_data(layer$data_src, panel_index)
})
},
map_data_columns = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
layer$data(data)
})
},
add_missing_scales = function(plot_data, renderer) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$scales$add_missing(data, renderer)
})
invisible(self)
},
# self$compute_statistics() -----------------------------------
scale_transform = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$scales$transform_tbl(data)
})
},
scale_train_position = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$position_scales()$train_tbl(data)
})
},
scale_map_position = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$position_scales()$map_tbl(data)
})
},
setup_stat_data = function(plot_data, renderer) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
layer$stat$setup_data(data, panel, renderer)
})
},
stat_compute = function(plot_data, renderer) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
layer$stat$compute_panel(data, panel, renderer)
})
},
map_stat_columns = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
data_mapped_user <- layer$data_stat(data)
layer$stat$default_mapping$map_new(data_mapped_user)
})
},
# self$compute_positions() -------------------------
add_position_scales = function(renderer) {
position_aesthetics <- rlang::set_names(self$coord$aesthetics)
tbl_template <- tibble::as_tibble(purrr::map(position_aesthetics, ~numeric(0)))
for (panel in self$panels) {
panel$scales$add_missing(tbl_template, renderer)
}
invisible(self)
},
position_compute = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
layer$position$compute_panel(data, panel)
})
},
scale_retrain_position = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$position_scales()$reset()$train_tbl(data)
})
},
finalize_panels = function() {
purrr::walk(self$panels, function(panel) self$coord$finalize_panel(panel))
invisible(self)
},
scale_remap_position = function(plot_data) {
self$scale_map_position(plot_data)
},
# self$map_non_position_scales()
scale_train_non_position = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$non_position_scales()$train_tbl(data)
})
},
scale_map_non_position = function(plot_data) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
panel$non_position_scales()$map_tbl(data)
})
},
# self$finish_data() -----------------------
setup_geom_data = function(plot_data, renderer) {
self$modify_plot_data(plot_data, function(panel, layer, data) {
layer$geom$setup_data(data, panel, renderer)
})
},
# self$render() ----------------------------
train_guides = function() {
for (panel in self$panels) {
guides <- panel$scales$guides()
guides$merge_all()
guides$train_layers(self$layers, self$renderer)
panel$guides <- guides
}
},
# utility methods ------------------------------
modify_plot_data = function(plot_data, fun) {
plot_data_copy <- plot_data
purrr::iwalk(self$panels, function(panel, panel_index) {
purrr::iwalk(self$layers$lst, function(layer, layer_index) {
data <- plot_data_copy[panel_index, layer_index][[1]]
result <- fun(panel, layer, data)
plot_data_copy[panel_index, layer_index] <- list(result)
plot_data_copy <<- plot_data_copy
})
})
plot_data_copy
}
)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.