diff --git a/R/annotation-custom.R b/R/annotation-custom.R index 8f060efab2..e727b48f1e 100644 --- a/R/annotation-custom.R +++ b/R/annotation-custom.R @@ -58,7 +58,7 @@ annotation_custom <- function(grob, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/annotation-logticks.R b/R/annotation-logticks.R index 600bd6ce7c..45af225c71 100644 --- a/R/annotation-logticks.R +++ b/R/annotation-logticks.R @@ -123,7 +123,7 @@ annotation_logticks <- function(base = 10, sides = "bl", outside = FALSE, scaled ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/annotation-map.R b/R/annotation-map.R index c6888f2add..0c9353d1b2 100644 --- a/R/annotation-map.R +++ b/R/annotation-map.R @@ -76,7 +76,7 @@ annotation_map <- function(map, ...) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/annotation-raster.R b/R/annotation-raster.R index 2635cf05de..594e30d557 100644 --- a/R/annotation-raster.R +++ b/R/annotation-raster.R @@ -61,7 +61,7 @@ annotation_raster <- function(raster, xmin, xmax, ymin, ymax, } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-.R b/R/coord-.R index 2b560292c4..c8c3d47e3a 100644 --- a/R/coord-.R +++ b/R/coord-.R @@ -1,105 +1,263 @@ -#' @section Coordinate systems: +#' Coords #' -#' All `coord_*()` functions (like `coord_trans()`) return a `Coord*` -#' object (like `CoordTrans`). +#' @description +#' All `coord_*()` functions (like `coord_trans()`) return a `Coord*` object +#' (like `CoordTrans`). These objects contain methods that support the +#' coordinate systems in ggplot2. #' -#' Each of the `Coord*` objects is a [ggproto()] object, -#' descended from the top-level `Coord`. To create a new type of Coord -#' object, you typically will want to implement one or more of the following: +#' @details +#' Each of the `Coord*` objects is a [ggproto()] object, descended from the +#' top-level `Coord`, and each implements various methods and fields. +#' The object and its parameters are chaperoned by the [Layout] class. #' -#' - `aspect`: Returns the desired aspect ratio for the plot. -#' - `labels`: Returns a list containing labels for x and y. -#' - `render_fg`: Renders foreground elements. -#' - `render_bg`: Renders background elements. -#' - `render_axis_h`: Renders the horizontal axes. -#' - `render_axis_v`: Renders the vertical axes. -#' - `backtransform_range(panel_params)`: Extracts the panel range provided -#' in `panel_params` (created by `setup_panel_params()`, see below) and -#' back-transforms to data coordinates. This back-transformation can be needed -#' for coords such as `coord_trans()` where the range in the transformed -#' coordinates differs from the range in the untransformed coordinates. Returns -#' a list of two ranges, `x` and `y`, and these correspond to the variables -#' mapped to the `x` and `y` aesthetics, even for coords such as `coord_flip()` -#' where the `x` aesthetic is shown along the y direction and vice versa. -#' - `range(panel_params)`: Extracts the panel range provided -#' in `panel_params` (created by `setup_panel_params()`, see below) and -#' returns it. Unlike `backtransform_range()`, this function does not perform -#' any back-transformation and instead returns final transformed coordinates. Returns -#' a list of two ranges, `x` and `y`, and these correspond to the variables -#' mapped to the `x` and `y` aesthetics, even for coords such as `coord_flip()` -#' where the `x` aesthetic is shown along the y direction and vice versa. -#' - `transform`: Transforms x and y coordinates. -#' - `distance`: Calculates distance. -#' - `is_linear`: Returns `TRUE` if the coordinate system is -#' linear; `FALSE` otherwise. -#' - `is_free`: Returns `TRUE` if the coordinate system supports free -#' positional scales; `FALSE` otherwise. -#' - `setup_panel_params(scale_x, scale_y, params)`: Determines the appropriate -#' x and y ranges for each panel, and also calculates anything else needed to -#' render the panel and axes, such as tick positions and labels for major -#' and minor ticks. Returns all this information in a named list. -#' - `setup_data(data, params)`: Allows the coordinate system to -#' manipulate the plot data. Should return list of data frames. -#' - `setup_layout(layout, params)`: Allows the coordinate -#' system to manipulate the `layout` data frame which assigns -#' data to panels and scales. +#' To create a new type of Coord object, it is recommended +#' to extend not the base `Coord` class, but one of its children like +#' `CoordCartesian`. #' -#' See also the `r link_book("new coords section", "extensions#sec-new-coords")` +#' When overriding the `transform()` method, it may be necessary to adapt the +#' implementation of `render_bg()` and possibly axis placement too. #' -#' @rdname ggplot2-ggproto +#' An important data structure that coordinate systems create is the +#' `panel_params` structure. When overriding that structure, many methods may +#' need to be adapted as well. +#' +#' @section Conventions: +#' +#' The object name that a new class is assigned to is typically the same as the +#' class name. Coord class names are in UpperCamelCase and start with the +#' `Coord*` prefix, like `CoordNew`. +#' +#' A constructor function is usually paired with a Coord class. The constructor +#' copies the coord class and populates parameters. The constructor function name +#' should take the Coord class name and be formatted with snake_case, +#' so that `CoordNew` becomes `coord_new()`. +#' +#' @export #' @format NULL +#' @family Layout components +#' @keywords internal #' @usage NULL -#' @export +#' @seealso The `r link_book("new coords section", "extensions#sec-new-coords")` +#' @examples +#' # Extending the class +#' CoordJitter <- ggproto( +#' "CoordJitter", CoordCartesian, +#' # Fields +#' amount = 0, +#' # Methods +#' is_linear = function() FALSE, +#' transform = function(self, data, panel_params) { +#' data <- ggproto_parent(CoordCartesian, self)$transform(data, panel_params) +#' data$x <- jitter(data$x, amount = self$amount) +#' data$y <- jitter(data$y, amount = self$amount) +#' data +#' } +#' ) +#' +#' # Building a constructor +#' coord_jitter <- function(amount = 0.005, xlim = NULL, ylim = NULL, expand = TRUE, +#' clip = "on", reverse = "none") { +#' ggproto( +#' NULL, CoordJitter, +#' amount = amount, +#' limits = list(x = xlim, y = ylim), +#' reverse = reverse, expand = expand, clip = clip +#' ) +#' } +#' +#' # Use new coord in plot +#' set.seed(42) +#' ggplot(mpg, aes(drv, displ)) + +#' geom_boxplot() + +#' coord_jitter() Coord <- ggproto("Coord", + # Fields ------------------------------------------------------------------ + # Is this the default coordinate system? + #' @field default Scaler boolean indicating whether this is the default + #' coordinate system. Non-default coordinate systems raise a message when + #' a new system replaces it. default = FALSE, - # should drawing be clipped to the extent of the plot panel? - # "on" = yes, "off" = no + #' @field clip A scalar string grid setting controlling whether layers should + #' be clipped to the extent of the plot panel extent. Can be `"on"` to + #' perform clipping, `"off"` to not clip, or `"inherit"` to take on the + #' setting of the parent viewport. clip = "on", - # Should any of the scales be reversed? + #' @field reverse A scalar string giving which directions to reverse. For + #' Cartesian systems, can be `"none`, `"x"`, `"y"` or `"xy"` for both. + #' Non-Cartesian may define their own settings. reverse = "none", - aspect = function(ranges) NULL, - - labels = function(self, labels, panel_params) { - labels - }, - - render_fg = function(panel_params, theme) { - element_render(theme, "panel.border", fill = NA) - }, - - render_bg = function(self, panel_params, theme) { - cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_bg} method.") - }, - - render_axis_h = function(self, panel_params, theme) { - cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_axis_h} method.") + # Methods ----------------------------------------------------------------- + + ## setup ------------------------------------------------------------------ + + #' @field setup_params + #' **Description** + #' + #' A function method for modifying or checking the parameters based on the + #' data. The default method parses the `expand` parameter. + #' + #' **Usage** + #' ```r + #' Coord$setup_params(data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames. The first item is the global data, + #' which is followed by layer data in subsequent items.} + #' } + #' + #' **Value** + #' + #' A list of parameters + setup_params = function(self, data) { + list(expand = parse_coord_expand(self$expand %||% TRUE)) }, - render_axis_v = function(self, panel_params, theme) { - cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_axis_v} method.") + #' @field setup_data + #' **Description** + #' + #' A function method for modifying or checking the data prior to adding + #' defaults. The default method returns data unaltered. + #' + #' **Usage** + #' ```r + #' Coord$setup_data(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames. The first item is the global data, + #' which is followed by layer data in subsequent items.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A list of data frames of the same length as the `data` argument + setup_data = function(data, params = list()) { + data }, - # transform range given in transformed coordinates - # back into range in given in (possibly scale-transformed) - # data coordinates - backtransform_range = function(self, panel_params) { - cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn backtransform_range} method.") + #' @field setup_layout + #' **Description** + #' + #' A function method that acts as a hook for the coordinate system to have + #' input on the layout computed by facets. + #' + #' **Usage** + #' ```r + #' Coord$setup_layout(layout, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`layout`}{A data frame computed by `Facet$compute_layout()`. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A data frame from the modified `layout` argument. The default creates a + #' new `COORD` column to identify unique combinations of x and y scales for + #' efficiency purposes. It should never remove columns. + setup_layout = function(layout, params) { + # We're appending a COORD variable to the layout that determines the + # uniqueness of panel parameters. The layout uses this to prevent redundant + # setups of these parameters. + scales <- layout[c("SCALE_X", "SCALE_Y")] + layout$COORD <- vec_match(scales, unique0(scales)) + layout }, - # return range stored in panel_params - range = function(self, panel_params) { - cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn range} method.") + ## setup_panel_params ------------------------------------------------------ + + #' @field modify_scales + #' **Description** + #' + #' A function method for modifying scales in place. This is optional and + #' currently used by CoordFlip and CoordPolar to ensure axis positions are + #' conforming to the coordinate system. + #' + #' **Usage** + #' ```r + #' Coord$modify_scales(scales_x, scales_y) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`scales_x`,`scales_y`}{A list of trained scales for the `x` and `y` + #' aesthetics respectively.} + #' } + #' + #' **Value** + #' + #' Nothing, this is called for the side effect of modifying scales. + modify_scales = function(scales_x, scales_y) { + invisible() }, + #' @field setup_panel_params + #' **Description** + #' + #' This function method is used to setup panel parameters per panel. + #' For efficiency reasons, this method is called once per combination of + #' `x` and `y` scales. It is used to instantiate ViewScale class objects and + #' ranges for position aesthetics and optionally append additional + #' parameters needed for the `transform()` method and rendering axes. + #' + #' **Usage** + #' ```r + #' Coord$setup_panel_params(scale_x, scale_y, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`scale_x`,`scale_y`}{A list of trained scales for the `x` and `y` + #' aesthetics respectively.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A named list of view scales, ranges and other optional parameters. setup_panel_params = function(scale_x, scale_y, params = list()) { list() }, + ## setup_panel_guides ------------------------------------------------------ + + #' @field setup_panel_guides + #' **Description** + #' + #' This function method is used to initiate position guides for each panel. + #' For efficiency reasons, this method is called once per combination of `x` + #' and `y` scales. For the primary and secondary positions, it should resolve + #' guides coming from the `plot$guides` field and `Scale$guide` fields and + #' set appropriate `Guide$params$position` parameters. + #' + #' **Usage** + #' ```r + #' Coord$setup_panel_guides(panel_params, guides, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' \item{`guides`}{A `` ggproto class.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' The `panel_params` object but with a Guides class object appended with + #' the name 'guides'. setup_panel_guides = function(self, panel_params, guides, params = list()) { aesthetics <- c("x", "y", "x.sec", "y.sec") names(aesthetics) <- aesthetics @@ -147,6 +305,30 @@ Coord <- ggproto("Coord", panel_params }, + #' @field setup_panel_guides + #' **Description** + #' + #' This function method is used to train and transform position guides for each + #' panel. For efficiency reasons, this method is called once per combination + #' of `x` and `y` scales. + #' + #' **Usage** + #' ```r + #' Coord$train_panel_guides(panel_params, layers, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects, a Guides class + #' object and additional parameters from the `setup_panel_params()` method.} + #' \item{`layers`}{A list of layers from `plot$layers`.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' The `panel_params` object, but with trained and transformed `guides` + #' parameter. train_panel_guides = function(self, panel_params, layers, params = list()) { aesthetics <- c("x", "y", "x.sec", "y.sec") @@ -177,39 +359,141 @@ Coord <- ggproto("Coord", panel_params }, - transform = function(data, range) NULL, - - distance = function(x, y, panel_params) NULL, - - is_linear = function() FALSE, - - # Does the coordinate system support free scaling of axes in a faceted plot? - # Will generally have to return FALSE for coordinate systems that enforce a fixed aspect ratio. - is_free = function() FALSE, - - setup_params = function(self, data) { - list(expand = parse_coord_expand(self$expand %||% TRUE)) + ## draw_geom --------------------------------------------------------------- + # The Layer$draw_geom() context + + #' @field transform + #' **Description** + #' + #' This function method is used to apply transformations and rescale position + #' aesthetics. This method is used in several places: + #' * The Geom drawing code, used through `coord_munch()` in many Geoms. + #' * The Guide transform method + #' * Panel grid transformation in `render_bg()` + #' + #' **Usage** + #' ```r + #' Coord$transform(data, panel_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with columns for numeric position aesthetics.} + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' } + #' + #' **Value** + #' + #' The `data` argument with rescaled and transformed position aesthetics. + transform = function(data, panel_params) { + NULL }, - setup_data = function(data, params = list()) { - data + #' @field distance + #' **Description** + #' + #' This function method is used to calculate distances between subsequent + #' data points. `coord_munch()` uses this method determine how many points + #' should be used to interpolate. + #' + #' **Usage** + #' ```r + #' Coord$distance(x, y, panel_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`x`,`y`}{x and y coordinates of a set of points in data space.} + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' } + #' + #' **Value** + #' + #' The `data` argument with rescaled and transformed position aesthetics. + distance = function(x, y, panel_params) { + NULL }, - setup_layout = function(layout, params) { - # We're appending a COORD variable to the layout that determines the - # uniqueness of panel parameters. The layout uses this to prevent redundant - # setups of these parameters. - scales <- layout[c("SCALE_X", "SCALE_Y")] - layout$COORD <- vec_match(scales, unique0(scales)) - layout + #' @field backtransform_range + #' **Description** + #' + #' This function method is used to convert ranges from transformed coordinates + #' back into data coordinates. The data coordinates may possibly be scale- + #' transformed. It is used in `coord_munch()` to ensure limits are in data + #' coordinates. + #' + #' The back-transformation may be needed for coords such as `coord_trans()`, + #' where the range in the transformed coordinates differs from the range in + #' the untransformed coordinates. + #' + #' **Usage** + #' ```r + #' Coord$backtransform_range(panel_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' } + #' + #' **Value** + #' + #' A list containing numeric ranges for `x` and `y` in data coordinates. + backtransform_range = function(self, panel_params) { + cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn backtransform_range} method.") }, - # Optionally, modify list of x and y scales in place. Currently - # used as a fudge for CoordFlip and CoordPolar - modify_scales = function(scales_x, scales_y) { - invisible() + # return range stored in panel_params + #' @field range + #' **Description** + #' + #' This function method is a small helper method to extract ranges from the + #' `panel_params` object. It exists because `panel_params` can be opaque at + #' times. + #' + #' **Usage** + #' ```r + #' Coord$range(panel_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' } + #' + #' **Value** + #' + #' A list containing numeric ranges for `x` and `y`. + range = function(self, panel_params) { + cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn range} method.") }, + ## render ----------------------------------------------------------------- + # The `Layout$render()` context + + #' @field draw_panel + #' **Description** + #' + #' This function method is used to orchestrate decorating panel drawings with + #' foreground and background drawings. It is called once per panel, invokes + #' the `render_fg()` and `render_bg()` methods and enforces the `clip` field. + #' + #' **Usage** + #' ```r + #' Coord$draw_panel(panel, params, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel`}{A grob containing drawn layers and facet foreground and + #' background.} + #' \item{`params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()]} + #' } + #' + #' **Value** + #' + #' A grob with panel content. draw_panel = function(self, panel, params, theme) { fg <- self$render_fg(params, theme) bg <- self$render_bg(params, theme) @@ -222,9 +506,188 @@ Coord <- ggproto("Coord", children = inject(gList(!!!panel)), vp = viewport(clip = self$clip) ) + }, + + #' @field render_fg + #' **Description** + #' + #' This function method is used to draw the panel foreground. For all + #' intents and purposes is just the `panel.border` theme element, but you can + #' repurpose this method. + #' + #' **Usage** + #' ```r + #' Coord$render_fg(panel_params, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()]} + #' } + #' + #' **Value** + #' + #' A grob with panel foreground. + render_fg = function(panel_params, theme) { + element_render(theme, "panel.border", fill = NA) + }, + + #' @field render_bg + #' **Description** + #' + #' This function method is used to draw the panel background. Typically + #' this is a combination of the `panel.background` and `panel.grid` theme + #' elements. + #' + #' **Usage** + #' ```r + #' Coord$render_bg(panel_params, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()]} + #' } + #' + #' **Value** + #' + #' A grob with panel background. + render_bg = function(self, panel_params, theme) { + cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_bg} method.") + }, + + #' @field labels + #' **Description** + #' + #' This function method is used to format axis titles. It is used in some + #' coordinate systems to (conditionally) swap x and y labels. + #' + #' **Usage** + #' ```r + #' Coord$labels(labels, panel_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`labels`}{A named list containing an `x` list and a `y` list. The + #' `x` and `y` lists have `primary` and `secondary` labels.} + #' \item{`panel_params`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method.} + #' } + #' + #' **Value** + #' + #' A list with the same structure and names as the `labels` argument. + labels = function(self, labels, panel_params) { + labels + }, + + ## draw panels ------------------------------------------------------------- + # In Facet$draw_panels() context + + #' @field aspect + #' **Description** + #' + #' This function method that gives the aspect ratio for panels. It allows for + #' `CoordFixed` to compute an aspect ratio based on data ranges. + #' + #' **Usage** + #' ```r + #' Coord$render_bg(panel_params, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`ranges`}{A list of ViewScale class objects and additional + #' parameters from the `setup_panel_params()` method. If there are + #' multiple panels, the parameters for the first panel is used.} + #' } + #' + #' **Value** + #' + #' A scalar numeric + aspect = function(ranges) { + NULL + }, + + #' @field render_axis_h,render_axis_v + #' **Description** + #' + #' These function methods are used to render axes to place at the outside edge + #' of panels. Interior axes should not be rendered here. The `render_axis_h()` + #' methods produces the horizontal axes for the top and bottom position. + #' The `render_axis_v()` method renders the vertical axes for the left and + #' right position. + #' + #' **Usage** + #' ```r + #' Coord$render_axis_h(panel_params, theme + #' Coord$render_axis_v(panel_params, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A list of ViewScale class objects, a Guides class + #' object and additional parameters from the `setup_panel_params()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()]} + #' } + #' + #' **Value** + #' + #' For `render_axis_h()` a named list where `"top"` and `"bottom"` are grobs + #' with an axis. For `render_axis_v()` a named list where `"left"` and + #' `"right"` are grobs with an axis. These grobs should be [`zeroGrob()`] + #' when no axes should be rendered. + render_axis_h = function(self, panel_params, theme) { + cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_axis_h} method.") + }, + + render_axis_v = function(self, panel_params, theme) { + cli::cli_abort("{.fn {snake_class(self)}} has not implemented a {.fn render_axis_v} method.") + }, + + ## Utilities -------------------------------------------------------------- + + #' @field is_linear + #' **Description** + #' + #' This function method is used to signal whether a coordinate system is + #' linear. In `coord_munch()` and several Geom drawing methods, it is used to + #' determine whether points should be interpolated. + #' + #' **Usage** + #' ```r + #' Coord$is_linear() + #' ``` + #' + #' **Value** + #' + #' A scalar boolean. + is_linear = function() { + FALSE + }, + + #' @field is_free + #' **Description** + #' + #' This function method is used to signal whether a coordinate system supports + #' free scaling of axes in faceted plots. This should generally return `FALSE` + #' for coordinate systems that enforce a fixed aspect ratio. + #' + #' **Usage** + #' ```r + #' Coord$is_free() + #' ``` + #' + #' **Value** + #' + #' A scalar boolean. + is_free = function() { + FALSE } ) +# Helpers ----------------------------------------------------------------- + #' @export #' @rdname is_tests is_coord <- function(x) inherits(x, "Coord") diff --git a/R/coord-cartesian-.R b/R/coord-cartesian-.R index 350e9bfd86..4fe6e6814c 100644 --- a/R/coord-cartesian-.R +++ b/R/coord-cartesian-.R @@ -80,7 +80,7 @@ coord_cartesian <- function(xlim = NULL, ylim = NULL, expand = TRUE, ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-fixed.R b/R/coord-fixed.R index d48824cfc4..4d6bae6db9 100644 --- a/R/coord-fixed.R +++ b/R/coord-fixed.R @@ -41,7 +41,7 @@ coord_fixed <- function(ratio = 1, xlim = NULL, ylim = NULL, expand = TRUE, coord_equal <- coord_fixed -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-flip.R b/R/coord-flip.R index 502ff56f88..914dd0d75c 100644 --- a/R/coord-flip.R +++ b/R/coord-flip.R @@ -66,7 +66,7 @@ coord_flip <- function(xlim = NULL, ylim = NULL, expand = TRUE, clip = "on") { ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-map.R b/R/coord-map.R index 3ba9260206..f0bc39b063 100644 --- a/R/coord-map.R +++ b/R/coord-map.R @@ -149,7 +149,7 @@ coord_map <- function(projection="mercator", ..., parameters = NULL, orientation ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-polar.R b/R/coord-polar.R index b8855f52b9..f276020616 100644 --- a/R/coord-polar.R +++ b/R/coord-polar.R @@ -74,7 +74,7 @@ coord_polar <- function(theta = "x", start = 0, direction = 1, clip = "on") { ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-quickmap.R b/R/coord-quickmap.R index 31367367a6..aec97f1472 100644 --- a/R/coord-quickmap.R +++ b/R/coord-quickmap.R @@ -11,7 +11,7 @@ coord_quickmap <- function(xlim = NULL, ylim = NULL, expand = TRUE, clip = "on") ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-radial.R b/R/coord-radial.R index 3cef9c2b5b..77562c0c61 100644 --- a/R/coord-radial.R +++ b/R/coord-radial.R @@ -130,7 +130,7 @@ coord_radial <- function(theta = "x", ) } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/coord-transform.R b/R/coord-transform.R index 18230a1742..f24cb77a6b 100644 --- a/R/coord-transform.R +++ b/R/coord-transform.R @@ -106,7 +106,7 @@ coord_trans <- function(x = "identity", y = "identity", xlim = NULL, ylim = NULL } -#' @rdname ggplot2-ggproto +#' @rdname Coord #' @format NULL #' @usage NULL #' @export diff --git a/R/facet-.R b/R/facet-.R index 94b75148ee..51a6ad5b2d 100644 --- a/R/facet-.R +++ b/R/facet-.R @@ -1,104 +1,227 @@ #' @include ggproto.R NULL -#' @section Facets: +#' Facets #' -#' All `facet_*` functions returns a `Facet` object or an object of a +#' @description +#' All `facet_*()` functions returns a `Facet` object or an object of a #' `Facet` subclass. This object describes how to assign data to different #' panels, how to apply positional scales and how to lay out the panels, once #' rendered. #' +#' @details #' Extending facets can range from the simple modifications of current facets, #' to very laborious rewrites with a lot of [`gtable()`][gtable::gtable()] -#' manipulation. For some examples of both, please see the extension vignette. +#' manipulation.For some examples of both, please see the extension vignette. +#' The object and its parameters are chaperoned by the [Layout] class. #' #' `Facet` subclasses, like other extendible ggproto classes, have a range #' of methods that can be modified. Some of these are required for all new #' subclasses, while other only need to be modified if need arises. #' #' The required methods are: +#' * `compute_layout` +#' * `map_data()` +#' * `draw_panels()` or its subsidiaries: +#' * `init_gtable()` +#' * `attach_axes()` +#' * `attach_strips()` #' -#' - `compute_layout`: Based on layer data compute a mapping between -#' panels, axes, and potentially other parameters such as faceting variable -#' level etc. This method must return a data.frame containing at least the -#' columns `PANEL`, `SCALE_X`, and `SCALE_Y` each containing -#' integer keys mapping a PANEL to which axes it should use. In addition the -#' data.frame can contain whatever other information is necessary to assign -#' observations to the correct panel as well as determining the position of -#' the panel. -#' -#' - `map_data`: This method is supplied the data for each layer in -#' turn and is expected to supply a `PANEL` column mapping each row to a -#' panel defined in the layout. Additionally this method can also add or -#' subtract data points as needed e.g. in the case of adding margins to -#' `facet_grid()`. -#' -#' - `draw_panels`: This is where the panels are assembled into a -#' `gtable` object. The method receives, among others, a list of grobs -#' defining the content of each panel as generated by the Geoms and Coord -#' objects. The responsibility of the method is to decorate the panels with -#' axes and strips as needed, as well as position them relative to each other -#' in a gtable. For some of the automatic functions to work correctly, each -#' panel, axis, and strip grob name must be prefixed with "panel", "axis", and -#' "strip" respectively. -#' -#' In addition to the methods described above, it is also possible to override -#' the default behaviour of one or more of the following methods: -#' -#' - `setup_params`: -#' -#' - `setup_panel_params`: modifies the x and y ranges for each panel. This is -#' used to allow the `Facet` to interact with the `panel_params`. -#' -#' - `init_scales`: Given a master scale for x and y, create panel -#' specific scales for each panel defined in the layout. The default is to -#' simply clone the master scale. -#' -#' - `train_scales`: Based on layer data train each set of panel -#' scales. The default is to train it on the data related to the panel. +#' In addition to the methods above, it can be useful to override the default +#' behaviour of one or more of the following methods: #' -#' - `finish_data`: Make last-minute modifications to layer data -#' before it is rendered by the Geoms. The default is to not modify it. -#' -#' - `draw_back`: Add a grob in between the background defined by the -#' Coord object (usually the axis grid) and the layer stack. The default is to -#' return an empty grob for each panel. -#' -#' - `draw_front`: As above except the returned grob is placed -#' between the layer stack and the foreground defined by the Coord object -#' (usually empty). The default is, as above, to return an empty grob. -#' -#' - `draw_panel_content`: Draws each panel for the facet. Should return a list -#' of grobs, one for each panel. The output is used by the `draw_panels` -#' method. -#' -#' - `draw_labels`: Given the gtable returned by `draw_panels`, -#' add axis titles to the gtable. The default is to add one title at each side -#' depending on the position and existence of axes. +#' * `setup_params()` +#' * `init_scales()` +#' * `train_scale()` +#' * `finish_data()` +#' * `draw_back()`, `draw_front()` or `draw_labels()` #' #' All extension methods receive the content of the params field as the params #' argument, so the constructor function will generally put all relevant -#' information into this field. The only exception is the `shrink` -#' parameter which is used to determine if scales are retrained after Stat -#' transformations has been applied. +#' information into this field. #' -#' See also the `r link_book("new facets section", "extensions#new-facets")` +#' @section Conventions: #' -#' @rdname ggplot2-ggproto +#' The object name that a new class is assigned to is typically the same as the +#' class name. Facet class names are in UpperCamelCase and start with the +#' `Facet*` prefix, like `FacetNew`. +#' +#' A constructor function is usually paired with a Facet class. The constructor +#' copies the facet class and populates the `params` field. The constructor +#' function name should take the Facet class name and be formatted with +#' snake_case, so that `FacetNew` becomes `facet_new()`. +#' +#' @export #' @format NULL #' @usage NULL -#' @export +#' @family Layout components +#' @keywords internal +#' @seealso The the `r link_book("new facets section", "extensions#new-facets")` +#' @seealso Run `vignette("extending-ggplot2")`, in particular the "Creating a +#' new faceting" section. +#' +#' @examples +#' # Please see extension vignette +#' NULL Facet <- ggproto("Facet", NULL, + + # Fields ------------------------------------------------------------------ + + #' @field shink A scalar boolean which when `TRUE`, will shrink scales to + #' fit output statistics rather than raw data. If `FALSE`, will only include + #' raw data before statistical summary. By exception this is not part of the + #' `params` field. + # TODO: should just put `shrink` in the params? shrink = FALSE, + + #' @field params A named list of parameters populated by the constructor + #' function. params = list(), + # Methods ----------------------------------------------------------------- + + ## Layout$setup() --------------------------------------------------------- + + #' @field setup_params + #' **Description** + #' + #' A function method for modifying or checking the parameters based on the + #' data. The default method includes a `.possible_columns` variable giving + #' column names. + #' + #' **Usage** + #' ```r + #' Facet$setup_params(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames. The first item is the global data, + #' which is followed by layer data in subsequent items.} + #' \item{`params`}{A list of current parameters.} + #' } + #' + #' **Value** + #' + #' A list of parameters + setup_params = function(data, params) { + params$.possible_columns <- unique0(unlist(lapply(data, names))) + params + }, + + #' @field setup_data + #' **Description** + #' + #' A function method for modifying or checking the data prior to adding + #' defaults. The default method returns data unaltered. + #' + #' **Usage** + #' ```r + #' Facet$setup_data(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames. The first item is the global data, + #' which is followed by layer data in subsequent items.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A list of data frames of the same length as the `data` argument + setup_data = function(data, params) { + data + }, + + #' @field compute_layout + #' **Description** + #' + #' A function method for creating the correspondence between faceting + #' variable levels, panels and position scales. It places panels like cells + #' in a matrix. + #' + #' **Usage** + #' ```r + #' Facet$compute_layout(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames. The first item is the global data, + #' which is followed by layer data in subsequent items.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A data frame with 1 row per panel, containing at least integer columns + #' `ROW`, `COL`, `PANEL`, `SCALE_X` and `SCALE_Y`. Can contain additional + #' information in terms of columns, typically faceting variables. compute_layout = function(data, params) { cli::cli_abort("Not implemented.") }, + + #' @field map_data + #' **Description** + #' + #' A function method for to create the `PANEL` variable in layer data. The + #' `PANEL` variable is a special variable that tracks the relationship between + #' rows in the layer data and the panels described in the `layout` input. + #' + #' In addition, #' this function may copy or discard rows as needed, for + #' example when adding margins in FacetGrid. + #' + #' **Usage** + #' ```r + #' Facet$map_data(data, layout, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A list of data frames containing layer data including a `PANEL` variable. map_data = function(data, layout, params) { cli::cli_abort("Not implemented.") }, - setup_panel_params = function(self, panel_params, coord, ...) panel_params, + + ## Layout$train_position() ----------------------------------------------- + + #' @field init_scales + #' **Description** + #' + #' A function method for initialising position scales. Given a prototype scale + #' for `x` and `y`, creates layout specific scales to accommodate + #' the relationships between panels and scales. By default, the prototype + #' scales are cloned for each `SCALE_X` and `SCALE_Y` level. The function is + #' called separately; once for `x` and once for `y`. + #' + #' **Usage** + #' ```r + #' Facet$init_scales(layout, x_scale, y_scale, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scale`,`y_scale`}{A position scale for the `x` and `y` + #' aesthetics respectively.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A named list with `x` and `y` elements containing a list of panel scales + #' for each `SCALE_X` and/or `SCALE_Y` level respectively. init_scales = function(layout, x_scale = NULL, y_scale = NULL, params) { scales <- list() if (!is.null(x_scale)) { @@ -109,6 +232,32 @@ Facet <- ggproto("Facet", NULL, } scales }, + + #' @field train_scales + #' **Description** + #' + #' A function method for training position scales. The default trains each + #' scale on the data related to its panels. + #' + #' **Usage** + #' ```r + #' Facet$train_scales(x_scales, y_scales, layout, data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' Nothing, this method is called for its side-effect of training the scales. train_scales = function(x_scales, y_scales, layout, data, params) { # loop over each layer, training x and y scales in turn for (layer_data in data) { @@ -136,12 +285,110 @@ Facet <- ggproto("Facet", NULL, } } }, - draw_back = function(data, layout, x_scales, y_scales, theme, params) { - rep(list(zeroGrob()), vec_unique_count(layout$PANEL)) + + ## Layout$setup_panel_params() -------------------------------------------- + + #' @field setup_panel_params + #' **Description** + #' + #' A function method as a hook to give facets input over panel parameters. By + #' default, returns panel parameters unaltered. + #' + #' **Usage** + #' ```r + #' Facet$setup_panel_params(panel_params, coord, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panel_params`}{A named list of view scales, ranges and other + #' optional parameters from `Coord$setup_panel_params()`.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`...`}{Currently not in use. For future expansion.} + #' } + #' + #' **Value** + #' + #' A list of panel parameters. + setup_panel_params = function(self, panel_params, coord, ...) { + panel_params }, - draw_front = function(data, layout, x_scales, y_scales, theme, params) { - rep(list(zeroGrob()), vec_unique_count(layout$PANEL)) + + ## Layout$finish_data() -------------------------------------------------- + + #' @field finish_data + #' **Description** + #' + #' A function method as a hook for making last-minute modifications to layer + #' data before it is rendered by Geoms. The default is to not modify the data. + #' + #' **Usage** + #' ```r + #' Facet$finish_data(data, layout, x_scales, y_scales, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame containing layer data of a single layer.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A data frame containing layer data. + finish_data = function(data, layout, x_scales, y_scales, params) { + data }, + + ## Layout$render() ------------------------------------------------------- + + #' @field draw_panel_content + #' **Description** + #' + #' A function method to assemble the panel contents. It delegates the + #' `draw_back()` and `draw_front()` methods, as well as `Coord$draw_panel()`. + #' + #' **Usage** + #' ```r + #' Facet$draw_panel_content( + #' panels, + #' layout, + #' x_scales, + #' y_scales, + #' ranges, + #' coord, + #' theme, + #' params, + #' ... + #' ) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panels`}{A list parallel to layers. Each element is another list + #' with grobs for each panel, generated by `Layer$draw_geom()`.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`ranges`}{A list of panel parameters from the + #' `setup_panel_params()` augmented with position guides.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' \item{`...`}{Currently not in use.} + #' } + #' + #' **Value** + #' + #' A list of grobs, one for each level of the `PANEL` layout variable. Grob + #' can be `zeroGrob()` to draw nothing. draw_panel_content = function(self, panels, layout, x_scales, y_scales, ranges, coord, data, theme, params, ...) { facet_bg <- self$draw_back( @@ -161,7 +408,7 @@ Facet <- ggproto("Facet", NULL, params ) - # Draw individual panels, then call `$draw_panels()` method to + # Draw individual panels, then call `$draw_panels()` method to # assemble into gtable lapply(seq_along(panels[[1]]), function(i) { panel <- lapply(panels, `[[`, i) @@ -170,6 +417,87 @@ Facet <- ggproto("Facet", NULL, ggname(paste("panel", i, sep = "-"), panel) }) }, + + #' @field draw_back,draw_front + #' **Description** + #' + #' A function method draw facet background (back) and foreground (front) for + #' panels. The front and back will sandwich the grobs created by layers. The + #' default methods draw nothing. + #' + #' **Usage** + #' ```r + #' Facet$draw_back(data, layout, x_scales, y_scales, theme, params) + #' Facet$draw_front(data, layout, x_scales, y_scales, theme, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A list of grobs, one for each level of the `PANEL` layout variable. Grob + #' can be `zeroGrob()` to draw nothing. + draw_back = function(data, layout, x_scales, y_scales, theme, params) { + rep(list(zeroGrob()), vec_unique_count(layout$PANEL)) + }, + + draw_front = function(data, layout, x_scales, y_scales, theme, params) { + rep(list(zeroGrob()), vec_unique_count(layout$PANEL)) + }, + + #' @field draw_panels + #' **Description** + #' + #' A function method that orchestrates the majority of facet drawing. It is + #' responsible for assembling a gtable with panel content decorated with axes + #' and strips. The resulting gtable is the basis for the plot in its entirety. + #' It delegates these tasks to the `init_gtable()`, `attach_axes()` and + #' `attach_strips()` methods. + #' + #' **Usage** + #' ```r + #' Facet$draw_panels( + #' panels, + #' layout, + #' x_scales, + #' y_scales, + #' ranges, + #' coord, + #' data, + #' theme, + #' params + #' ) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panels`}{A list of grobs, one per panel.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`ranges`}{A list of panel parameters from the + #' `setup_panel_params()` augmented with position guides.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A [`gtable`][gtable::gtable()] object. draw_panels = function(self, panels, layout, x_scales = NULL, y_scales = NULL, ranges, coord, data = NULL, theme, params) { @@ -200,43 +528,35 @@ Facet <- ggproto("Facet", NULL, table <- self$attach_axes(table, layout, ranges, coord, theme, params) self$attach_strips(table, layout, params, theme) }, - draw_labels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, labels, params) { - panel_dim <- find_panel(panels) - - xlab_height_top <- grobHeight(labels$x[[1]]) - panels <- gtable_add_rows(panels, xlab_height_top, pos = 0) - panels <- gtable_add_grob(panels, labels$x[[1]], name = "xlab-t", - l = panel_dim$l, r = panel_dim$r, t = 1, clip = "off") - - xlab_height_bottom <- grobHeight(labels$x[[2]]) - panels <- gtable_add_rows(panels, xlab_height_bottom, pos = -1) - panels <- gtable_add_grob(panels, labels$x[[2]], name = "xlab-b", - l = panel_dim$l, r = panel_dim$r, t = -1, clip = "off") - panel_dim <- find_panel(panels) - - ylab_width_left <- grobWidth(labels$y[[1]]) - panels <- gtable_add_cols(panels, ylab_width_left, pos = 0) - panels <- gtable_add_grob(panels, labels$y[[1]], name = "ylab-l", - l = 1, b = panel_dim$b, t = panel_dim$t, clip = "off") - - ylab_width_right <- grobWidth(labels$y[[2]]) - panels <- gtable_add_cols(panels, ylab_width_right, pos = -1) - panels <- gtable_add_grob(panels, labels$y[[2]], name = "ylab-r", - l = -1, b = panel_dim$b, t = panel_dim$t, clip = "off") - - panels - }, - setup_params = function(data, params) { - params$.possible_columns <- unique0(unlist(lapply(data, names))) - params - }, - setup_data = function(data, params) { - data - }, - finish_data = function(data, layout, x_scales, y_scales, params) { - data - }, + #' @field init_gtable + #' **Description** + #' + #' A function method that initiates a gtable object containing panels set + #' at the appropriate `ROW` and `COL` cells from the layout. The panels are + #' separated by the `panel.spacing.{x/y}` spacing. + #' + #' **Usage** + #' ```r + #' Facet$init_gtable(panels, layout, theme, ranges, params, aspect_ratio) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panels`}{A list of grobs, one per panel.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`ranges`}{A list of panel parameters from the + #' `setup_panel_params()` augmented with position guides.} + #' \item{`aspect_ratio`}{A scalar numeric for the panel aspect ratio or + #' `NULL` for no aspect ratio.} + #' } + #' + #' **Value** + #' + #' A [`gtable`][gtable::gtable()] object containing panel grobs prefixed with + #' `"panel"`. init_gtable = function(panels, layout, theme, ranges, params, aspect_ratio = NULL) { @@ -289,18 +609,117 @@ Facet <- ggproto("Facet", NULL, table <- gtable_add_row_space(table, spacing$y) table }, + + #' @field attach_axes + #' **Description** + #' + #' A function method that renders position guides (axes) and attaches these + #' to the gtable with panels. The default method returns the gtable unaltered. + #' + #' **Usage** + #' ```r + #' Facet$attach_axes(table, layout, ranges, coord, theme, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`table`}{A [`gtable`][gtable::gtable()] object populated with panels from the + #' `init_gtable()` method.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`ranges`}{A list of panel parameters from the + #' `setup_panel_params()` augmented with position guides.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A [`gtable`][gtable::gtable()] object. attach_axes = function(table, layout, ranges, coord, theme, params) { table }, + + #' @field attach_strips + #' **Description** + #' + #' A function method that renders strips and attaches these to the gtable + #' with panels and axes. The `format_strip_labels()` method is used to format + #' the strip text. The default method returns the gtable unaltered. + #' + #' **Usage** + #' ```r + #' Facet$attach_strips(table, layout, ranges, coord, theme, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`table`}{A [`gtable`][gtable::gtable()] object from the `attach_axes()` + #' method.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' } + #' + #' **Value** + #' + #' A [`gtable`][gtable::gtable()] object. attach_strips = function(table, layout, params, theme) { table }, - vars = function() { - character(0) - }, + + #' @field format_strip_labels + #' **Description** + #' + #' A function method that formats the text for strips. It is used in the + #' `attach_strips` methods, but also the `get_strip_labels()` function. + #' The default method returns `NULL`. + #' + #' **Usage** + #' ```r + #' Facet$format_strip_labels(layout, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A list containing a data frame with strip labels. format_strip_labels = function(layout, params) { return() }, + + #' @field set_panel_size + #' **Description** + #' + #' A function method that enforces the `panel.widths` and `panel.heights` + #' theme settings. + #' + #' **Usage** + #' ```r + #' Facet$set_panel_size(table, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`table`}{A [`gtable`][gtable::gtable()] object populated by the + #' `draw_panels()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' } + #' + #' **Value** + #' + #' The `table` object, optionally with different `widths` and `heights` + #' properties. set_panel_size = function(table, theme) { new_widths <- calc_element("panel.widths", theme) @@ -347,6 +766,99 @@ Facet <- ggproto("Facet", NULL, } table + }, + + #' @field attach_axes + #' **Description** + #' + #' A function method that renders axis titles and adds them to the gtable. + #' The default is to add one title at each side depending on the position + #' and presence of axes. + #' + #' **Usage** + #' ```r + #' Facet$draw_labels( + #' panels, + #' layout, + #' x_scales, + #' y_scales, + #' ranges, + #' coord, + #' data, + #' theme, + #' labels, + #' params + #' ) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`panels`}{A [`gtable`][gtable::gtable()] object initiated by the + #' `draw_panels()` method.} + #' \item{`layout`}{A data frame computed by the `compute_layout()` method. + #' Typically contains the faceting variables, `ROW`, `COL`, `PANEL`, + #' `SCALE_X` and `SCALE_Y` variables.} + #' \item{`x_scales`,`y_scales`}{A list of panel scales for each `SCALE_X` + #' and `SCALE_Y` level respectively.} + #' \item{`ranges`}{A list of panel parameters from the + #' `setup_panel_params()` augmented with position guides.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`data`}{A list of data frames containing layer data.} + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`labels`}{A named list containing an `x` list and `y` list. The + #' `x` and `y` lists have `primary` and `secondary` labels.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A [`gtable`][gtable::gtable()] object. + draw_labels = function(panels, layout, x_scales, y_scales, ranges, coord, data, theme, labels, params) { + panel_dim <- find_panel(panels) + + xlab_height_top <- grobHeight(labels$x[[1]]) + panels <- gtable_add_rows(panels, xlab_height_top, pos = 0) + panels <- gtable_add_grob(panels, labels$x[[1]], name = "xlab-t", + l = panel_dim$l, r = panel_dim$r, t = 1, clip = "off") + + xlab_height_bottom <- grobHeight(labels$x[[2]]) + panels <- gtable_add_rows(panels, xlab_height_bottom, pos = -1) + panels <- gtable_add_grob(panels, labels$x[[2]], name = "xlab-b", + l = panel_dim$l, r = panel_dim$r, t = -1, clip = "off") + + panel_dim <- find_panel(panels) + + ylab_width_left <- grobWidth(labels$y[[1]]) + panels <- gtable_add_cols(panels, ylab_width_left, pos = 0) + panels <- gtable_add_grob(panels, labels$y[[1]], name = "ylab-l", + l = 1, b = panel_dim$b, t = panel_dim$t, clip = "off") + + ylab_width_right <- grobWidth(labels$y[[2]]) + panels <- gtable_add_cols(panels, ylab_width_right, pos = -1) + panels <- gtable_add_grob(panels, labels$y[[2]], name = "ylab-r", + l = -1, b = panel_dim$b, t = panel_dim$t, clip = "off") + + panels + }, + + ## Utilities ------------------------------------------------------------- + + #' @field vars + #' **Description** + #' + #' A function method that returns the names of faceting variables. The + #' default method returns an character vector with 0 length. + #' + #' **Usage** + #' ```r + #' Facet$vars() + #' ``` + #' + #' **Value** + #' + #' A character vector + vars = function() { + character(0) } ) diff --git a/R/facet-grid-.R b/R/facet-grid-.R index 86bbad2b04..21d3fed9bd 100644 --- a/R/facet-grid-.R +++ b/R/facet-grid-.R @@ -234,7 +234,7 @@ grid_as_facets_list <- function(rows, cols) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Facet #' @format NULL #' @usage NULL #' @export diff --git a/R/facet-null.R b/R/facet-null.R index 26b610fdfa..e2c4156aa6 100644 --- a/R/facet-null.R +++ b/R/facet-null.R @@ -19,7 +19,7 @@ facet_null <- function(shrink = TRUE) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Facet #' @format NULL #' @usage NULL #' @export diff --git a/R/facet-wrap.R b/R/facet-wrap.R index e1eda21cdb..04ac484343 100644 --- a/R/facet-wrap.R +++ b/R/facet-wrap.R @@ -223,7 +223,7 @@ facet_wrap <- function(facets, nrow = NULL, ncol = NULL, scales = "fixed", ) } -#' @rdname ggplot2-ggproto +#' @rdname Facet #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-.R b/R/geom-.R index e2d8806b35..bcb8a49a21 100644 --- a/R/geom-.R +++ b/R/geom-.R @@ -2,118 +2,198 @@ #' @include utilities-checks.R NULL -#' @section Geoms: +#' Geoms #' +#' @description #' All `geom_*()` functions (like `geom_point()`) return a layer that #' contains a `Geom*` object (like `GeomPoint`). The `Geom*` #' object is responsible for rendering the data in the plot. #' +#' @details #' Each of the `Geom*` objects is a [ggproto()] object, descended #' from the top-level `Geom`, and each implements various methods and -#' fields. +#' fields. The object and its parameters are chaperoned by the [Layer] class. #' #' Compared to `Stat` and `Position`, `Geom` is a little #' different because the execution of the setup and compute functions is #' split up. `setup_data` runs before position adjustments, and #' `draw_layer()` is not run until render time, much later. #' -#' To create a new type of Geom object, you typically will want to -#' override one or more of the following: +#' When creating a new Geom class, you may want to consider override one or +#' more of the following: +#' * The `required_aes` and `default_aes` fields. +#' * The `setup_data()` and `setup_params()` functions. +#' * Either the `draw_panel()` or `draw_group()` function. +#' * The `draw_key` field. #' -#' - Either `draw_panel(self, data, panel_params, coord)` or -#' `draw_group(self, data, panel_params, coord)`. `draw_panel` is -#' called once per panel, `draw_group` is called once per group. +#' @section Conventions: #' -#' Use `draw_panel` if each row in the data represents a -#' single element. Use `draw_group` if each group represents -#' an element (e.g. a smooth, a violin). +#' The object name that a new class is assigned to is typically the same as +#' the class name. Geom class names are in UpperCamelCase and start with the +#' `Geom*` prefix, like `GeomNew`. #' -#' `data` is a data frame of scaled aesthetics. +#' A constructor function is usually paired with a Geom class. The constructor +#' wraps a call to `layer()`, where e.g. `layer(geom = GeomNew)`. The constructor +#' function name is formatted by taking the Geom class name and formatting it +#' with snake_case, so that `GeomNew` becomes `geom_new()`. #' -#' `panel_params` is a set of per-panel parameters for the -#' `coord`. Generally, you should consider `panel_params` -#' to be an opaque data structure that you pass along whenever you call -#' a coord method. -#' -#' You must always call `coord$transform(data, panel_params)` to -#' get the (position) scaled data for plotting. To work with -#' non-linear coordinate systems, you typically need to convert into a -#' primitive geom (e.g. point, path or polygon), and then pass on to the -#' corresponding draw method for munching. -#' -#' Must return a grob. Use [zeroGrob()] if there's nothing to -#' draw. -#' - `draw_key`: Renders a single legend key. -#' - `required_aes`: A character vector of aesthetics needed to -#' render the geom. -#' - `default_aes`: A list (generated by [aes()] of -#' default values for aesthetics. -#' - `setup_data`: Converts width and height to xmin and xmax, -#' and ymin and ymax values. It can potentially set other values as well. -#' -#' See also the `r link_book("new geoms section", "extensions#sec-new-geoms")` -#' @rdname ggplot2-ggproto +#' @export #' @format NULL #' @usage NULL -#' @export -Geom <- ggproto("Geom", - required_aes = character(), - non_missing_aes = character(), - optional_aes = character(), +#' @family Layer components +#' @keywords internal +#' @seealso The `r link_book("new geoms section", "extensions#sec-new-geoms")` +#' @seealso Run `vignette("extending-ggplot2")`, in particular the "Creating a +#' new geom" section. +#' @examples +#' # Extending the class +#' GeomSimplePoint <- ggproto( +#' "GeomSimplePoint", Geom, +#' # Fields +#' required_aes = c("x", "y"), +#' draw_key = draw_key_point, +#' # Methods +#' draw_panel = function(data, panel_params, coord) { +#' data <- coord$transform(data, panel_params) +#' grid::pointsGrob(data$x, data$y) +#' } +#' ) +#' +#' # Building a constructor +#' geom_simple_point <- function(mapping = NULL, data = NULL, stat = "identity", +#' position = "identity", ..., na.rm = FALSE, +#' show.legend = NA, inherit.aes = TRUE) { +#' layer( +#' mapping = mapping, data = data, +#' geom = GeomSimplePoint, stat = stat, position = position, +#' show.legend = show.legend, inherit.aes = inherit.aes, +#' params = list(na.rm = na.rm, ...) +#' ) +#' } +#' +#' # Use new geom in plot +#' ggplot(mpg, aes(displ, hwy)) + +#' geom_simple_point() +Geom <- ggproto( + "Geom", - default_aes = aes(), + # Fields ------------------------------------------------------------------ - draw_key = draw_key_point, + #' @field required_aes A character vector naming aesthetics that are necessary + #' to render the geom. + required_aes = character(), - handle_na = function(self, data, params) { - remove_missing(data, params$na.rm, - c(self$required_aes, self$non_missing_aes), - snake_class(self) - ) - }, + #' @field non_missing_aes A character vector naming aesthetics that will cause + #' removal if they have missing values. + non_missing_aes = character(), - draw_layer = function(self, data, params, layout, coord) { - if (empty(data)) { - n <- if (is.factor(data$PANEL)) nlevels(data$PANEL) else 1L - return(rep(list(zeroGrob()), n)) - } + #' @field optional_aes A character vector naming aesthetics that will be + #' accepted by `layer()`, but are not required or described in the + #' `default_aes` field. + optional_aes = character(), - # Trim off extra parameters - params <- params[intersect(names(params), self$parameters())] + #' @field default_aes A [mapping][aes()] of default values for aesthetics. + #' Aesthetics can be set to `NULL` to be included as optional aesthetic. + default_aes = aes(), - if (nlevels(as.factor(data$PANEL)) > 1L) { - data_panels <- split(data, data$PANEL) - } else { - data_panels <- list(data) - } - lapply(data_panels, function(data) { - if (empty(data)) return(zeroGrob()) + #' @field rename_size + #' A scalar boolean: whether to rename `size` aesthetics to `linewidth`. + rename_size = FALSE, - panel_params <- layout$panel_params[[data$PANEL[1]]] - inject(self$draw_panel(data, panel_params, coord, !!!params)) - }) - }, + #' @field extra_params A character vector of parameter names in addition to + #' those imputed from the `draw_panel()` or `draw_groups()` methods. This + #' field can be set to include parameters for `setup_data()` or `handle_na()` + #' methods. By default, this only contains `"na.rm"`. + extra_params = c("na.rm"), - draw_panel = function(self, data, panel_params, coord, ...) { - groups <- split(data, factor(data$group)) - grobs <- lapply(groups, function(group) { - self$draw_group(group, panel_params, coord, ...) - }) + #' @field draw_key A function generating a single legend glyph for the geom. + #' Typically one of the functions prefixed by [`draw_key_`][draw_key]. + draw_key = draw_key_point, - ggname(snake_class(self), gTree( - children = inject(gList(!!!grobs)) - )) + # Methods ----------------------------------------------------------------- + + ## compute_geom_1 --------------------------------------------------------- + + #' @field setup_params + #' **Description** + #' + #' A function method for modifying or checking the parameters based on the + #' data. The default method returns the parameters unaltered. + #' + #' **Usage** + #' ```r + #' Geom$setup_params(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of current parameters.} + #' } + #' + #' **Value** + #' + #' A list of parameters + setup_params = function(data, params) { + params }, - draw_group = function(self, data, panel_params, coord) { - cli::cli_abort("{.fn {snake_class(self)}}, has not implemented a {.fn draw_group} method") + #' @field setup_data + #' **Description** + #' + #' A function method for modifying or checking the data prior to adding + #' defaults. The default method returns data unaltered. + #' + #' **Usage** + #' ```r + #' Geom$setup_data(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data + setup_data = function(data, params) { + data }, - setup_params = function(data, params) params, - - setup_data = function(data, params) data, - - # Combine data with defaults and set aesthetics from parameters + ## compute_geom_2 ---------------------------------------------------------- + + #' @field use_defaults + #' **Description** + #' + #' A function method for completing the layer data by filling in default + #' aesthetics that are not present. It is not recommended to use as an + #' extension point. + #' + #' It takes on these tasks: + #' * Evaluation of default aesthetics from the `default_aes` field. + #' * Handling the [`after_scale()`]/`stage(after_scale)` stage of delayed + #' evaluation. + #' * Fill in fixed, unmapped aesthetics passed as parameters. + #' + #' **Usage** + #' ```r + #' Geom$use_defaults(data, params, modifiers, default_aes, theme, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame of the layer's data, coming from the + #' `setup_data()` method. Can be `NULL`, in which case resolved defaults + #' should be returned.} + #' \item{`params`}{A list of fixed aesthetic parameters} + #' \item{`modifiers`}{A [mapping][aes()] with delayed evaluations.} + #' \item{`default_aes`}{A [mapping][aes()] with default aesthetics.} + #' \item{`theme`}{A [completed theme][complete_theme()].} + #' } + #' + #' **Value** + #' + #' A data frame with completed layer data. use_defaults = function(self, data, params = list(), modifiers = aes(), default_aes = NULL, theme = NULL, ...) { default_aes <- default_aes %||% self$default_aes @@ -199,13 +279,152 @@ Geom <- ggproto("Geom", data }, - # Most parameters for the geom are taken automatically from draw_panel() or - # draw_groups(). However, some additional parameters may be needed - # for setup_data() or handle_na(). These can not be imputed automatically, - # so the slightly hacky "extra_params" field is used instead. By - # default it contains `na.rm` - extra_params = c("na.rm"), + ## draw_geom --------------------------------------------------------------- + + #' @field handle_na + #' **Description** + #' + #' A function method to handle missing values. The default method will + #' remove rows that have missing values for the aesthetics listed in the + #' `required_aes` and `non_missing_aes` fields. It is not recommended to + #' use this method as an extension point. + #' + #' **Usage** + #' ```r + #' Geom$handle_na(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data coming from the + #' `update_defaults()` method.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method} + #' } + #' + #' **Value** + #' + #' A data frame with layer data + handle_na = function(self, data, params) { + remove_missing(data, params$na.rm, + c(self$required_aes, self$non_missing_aes), + snake_class(self) + ) + }, + + #' @field draw_layer + #' **Description** + #' + #' A function method orchestrating the drawing of the entire layer. The + #' default method splits the data and passes on drawing tasks to the + #' panel-level `draw_panel()` method. It is not recommended to use this method + #' as an extension point. + #' + #' **Usage** + #' ```r + #' Geom$draw_layer(data, params, layout, coord) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters} + #' \item{`layout`}{A completed `` ggproto object.} + #' \item{`coord`}{A `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A list of grobs, one per panel + draw_layer = function(self, data, params, layout, coord) { + if (empty(data)) { + n <- if (is.factor(data$PANEL)) nlevels(data$PANEL) else 1L + return(rep(list(zeroGrob()), n)) + } + + # Trim off extra parameters + params <- params[intersect(names(params), self$parameters())] + + if (nlevels(as.factor(data$PANEL)) > 1L) { + data_panels <- split(data, data$PANEL) + } else { + data_panels <- list(data) + } + lapply(data_panels, function(data) { + if (empty(data)) return(zeroGrob()) + + panel_params <- layout$panel_params[[data$PANEL[1]]] + inject(self$draw_panel(data, panel_params, coord, !!!params)) + }) + }, + + #' @field draw_panel,draw_group + #' **Description** + #' + #' A function method orchestrating the drawing of the layer for a single + #' panel or group. The default `draw_panel()` method splits the data into groups, + #' passes on the drawing tasks to the group-level `draw_group()` method and + #' finally assembles these into a single grob. The default `draw_group` method + #' is not implemented. + #' + #' **Usage** + #' ```r + #' Geom$draw_panel(data, panel_params, coord, ...) + #' Geom$draw_group(data, panel_params, coord, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`panel_params`}{A list of per-panel parameters constructed by + #' `Coord$setup_panel_params()`. This should be considered an opaque data + #' structure that is just passed along when calling coord methods.} + #' \item{`coord`}{A `` ggproto object. To correctly scale the + #' position data, one should always call + #' `coord$transform(data, panel_params)`. When working with non-linear + #' coordinate systems, data should be converted to fit a primitive geom + #' (e.g. point, path or polygon) and passed on to the corresponding draw + #' method for [munching][coord_munch()].} + #' \item{`...`}{Reserved for extensions. By default, this is passed on to + #' the `draw_group()` method.} + #' } + #' + #' **Value** + #' + #' A single grob or [`zeroGrob()`] when there is nothing to draw. For + #' `draw_panel()` this can be a [gTree][grid::grob] holding individual grobs + #' from the `draw_group()` method. + draw_panel = function(self, data, panel_params, coord, ...) { + groups <- split(data, factor(data$group)) + grobs <- lapply(groups, function(group) { + self$draw_group(group, panel_params, coord, ...) + }) + + ggname(snake_class(self), gTree( + children = inject(gList(!!!grobs)) + )) + }, + draw_group = function(self, data, panel_params, coord) { + cli::cli_abort("{.fn {snake_class(self)}}, has not implemented a {.fn draw_group} method") + }, + + ## Utilities --------------------------------------------------------------- + + #' @field parameters + #' **Description** + #' + #' A function method for listing out all acceptable parameters for this geom. + #' + #' **Usage** + #' ```r + #' Geom$parameters(extra) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`extra`}{A boolean: whether to include the `extra_params` field.} + #' } + #' + #' **Value** + #' + #' A character vector of parameter names. parameters = function(self, extra = FALSE) { # Look first in draw_panel. If it contains ... then look in draw groups panel_args <- names(ggproto_formals(self$draw_panel)) @@ -221,6 +440,18 @@ Geom <- ggproto("Geom", args }, + #' @field aesthetics + #' **Description** + #' + #' A function method for listing out all acceptable aesthetics for this geom. + #' + #' **Usage** + #' ```r + #' Geom$aesthetics() + #' ``` + #' **Value** + #' + #' A character vector of aesthetic names. aesthetics = function(self) { if (is.null(self$required_aes)) { required_aes <- NULL @@ -228,13 +459,11 @@ Geom <- ggproto("Geom", required_aes <- unlist(strsplit(self$required_aes, '|', fixed = TRUE)) } c(union(required_aes, names(self$default_aes)), self$optional_aes, "group") - }, - - # Should the geom rename size to linewidth? - rename_size = FALSE - + } ) +# Helpers ----------------------------------------------------------------- + #' @export #' @rdname is_tests is_geom <- function(x) inherits(x, "Geom") diff --git a/R/geom-abline.R b/R/geom-abline.R index d87dfdc58b..72f71f490e 100644 --- a/R/geom-abline.R +++ b/R/geom-abline.R @@ -120,7 +120,7 @@ geom_abline <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-bar.R b/R/geom-bar.R index b0901502d9..3cfa495055 100644 --- a/R/geom-bar.R +++ b/R/geom-bar.R @@ -117,7 +117,7 @@ geom_bar <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-bin2d.R b/R/geom-bin2d.R index e0c78ab5e4..e9646701c5 100644 --- a/R/geom-bin2d.R +++ b/R/geom-bin2d.R @@ -56,7 +56,7 @@ geom_bin_2d <- function(mapping = NULL, data = NULL, #' @usage NULL geom_bin2d <- geom_bin_2d -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-blank.R b/R/geom-blank.R index e1d6986081..d51416a43d 100644 --- a/R/geom-blank.R +++ b/R/geom-blank.R @@ -29,7 +29,7 @@ geom_blank <- function(mapping = NULL, data = NULL, } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-boxplot.R b/R/geom-boxplot.R index 316dd6004b..dc1234c651 100644 --- a/R/geom-boxplot.R +++ b/R/geom-boxplot.R @@ -230,7 +230,7 @@ geom_boxplot <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-col.R b/R/geom-col.R index 77c756f573..29c4c64d61 100644 --- a/R/geom-col.R +++ b/R/geom-col.R @@ -24,7 +24,7 @@ geom_col <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-contour.R b/R/geom-contour.R index 00cddd51e5..652641eeec 100644 --- a/R/geom-contour.R +++ b/R/geom-contour.R @@ -118,7 +118,7 @@ geom_contour_filled <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -127,7 +127,7 @@ GeomContour <- ggproto("GeomContour", GeomPath, default_aes = aes(weight = 1, !!!GeomPath$default_aes) ) -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-crossbar.R b/R/geom-crossbar.R index c8452b2e9a..e0c98621fd 100644 --- a/R/geom-crossbar.R +++ b/R/geom-crossbar.R @@ -54,7 +54,7 @@ geom_crossbar <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-curve.R b/R/geom-curve.R index 6d6ec0027d..ccc8d89f7f 100644 --- a/R/geom-curve.R +++ b/R/geom-curve.R @@ -34,7 +34,7 @@ geom_curve <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @include geom-segment.R #' @format NULL #' @usage NULL diff --git a/R/geom-density.R b/R/geom-density.R index bc9bfd7b81..be14955c3a 100644 --- a/R/geom-density.R +++ b/R/geom-density.R @@ -86,7 +86,7 @@ geom_density <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-density2d.R b/R/geom-density2d.R index 778cac80fe..0dcff33191 100644 --- a/R/geom-density2d.R +++ b/R/geom-density2d.R @@ -101,7 +101,7 @@ geom_density_2d <- function(mapping = NULL, data = NULL, geom_density2d <- geom_density_2d -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -145,7 +145,7 @@ geom_density_2d_filled <- function(mapping = NULL, data = NULL, #' @usage NULL geom_density2d_filled <- geom_density_2d_filled -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-dotplot.R b/R/geom-dotplot.R index e912c44877..8fd501cb8b 100644 --- a/R/geom-dotplot.R +++ b/R/geom-dotplot.R @@ -180,7 +180,7 @@ geom_dotplot <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-errorbar.R b/R/geom-errorbar.R index f6102a04d6..fc0cc1d5fb 100644 --- a/R/geom-errorbar.R +++ b/R/geom-errorbar.R @@ -52,7 +52,7 @@ geom_errorbarh <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -114,7 +114,7 @@ GeomErrorbar <- ggproto("GeomErrorbar", Geom, rename_size = TRUE ) -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-function.R b/R/geom-function.R index c566731996..3d4bfcebbf 100644 --- a/R/geom-function.R +++ b/R/geom-function.R @@ -84,7 +84,7 @@ geom_function <- function(mapping = NULL, data = NULL, stat = "function", ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-hex.R b/R/geom-hex.R index 27db70f1d5..49c5f08c39 100644 --- a/R/geom-hex.R +++ b/R/geom-hex.R @@ -50,8 +50,7 @@ geom_hex <- function(mapping = NULL, data = NULL, ) } - -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-hline.R b/R/geom-hline.R index 0ebf2436fc..9d59f21b6e 100644 --- a/R/geom-hline.R +++ b/R/geom-hline.R @@ -40,7 +40,7 @@ geom_hline <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-label.R b/R/geom-label.R index a9d288996f..a49a1fbaed 100644 --- a/R/geom-label.R +++ b/R/geom-label.R @@ -53,8 +53,7 @@ geom_label <- function(mapping = NULL, data = NULL, ) } - -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-linerange.R b/R/geom-linerange.R index acaefbc9b2..531d6311bd 100644 --- a/R/geom-linerange.R +++ b/R/geom-linerange.R @@ -85,7 +85,7 @@ geom_linerange <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-map.R b/R/geom-map.R index 0632ba36ee..c75361b5f1 100644 --- a/R/geom-map.R +++ b/R/geom-map.R @@ -121,7 +121,7 @@ geom_map <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-path.R b/R/geom-path.R index fe930363a6..64379b098c 100644 --- a/R/geom-path.R +++ b/R/geom-path.R @@ -128,7 +128,7 @@ geom_path <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -283,7 +283,7 @@ geom_line <- function(mapping = NULL, data = NULL, stat = "identity", ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -330,7 +330,7 @@ geom_step <- function(mapping = NULL, data = NULL, stat = "identity", ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-point.R b/R/geom-point.R index a46a3a3245..fb51b260ca 100644 --- a/R/geom-point.R +++ b/R/geom-point.R @@ -134,7 +134,7 @@ geom_point <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-pointrange.R b/R/geom-pointrange.R index 0ffa1bf51f..b937be2173 100644 --- a/R/geom-pointrange.R +++ b/R/geom-pointrange.R @@ -25,7 +25,7 @@ geom_pointrange <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-polygon.R b/R/geom-polygon.R index 8ffdd05e58..c2f13a7610 100644 --- a/R/geom-polygon.R +++ b/R/geom-polygon.R @@ -102,7 +102,7 @@ geom_polygon <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-quantile.R b/R/geom-quantile.R index ecdf7f69fb..2df76090de 100644 --- a/R/geom-quantile.R +++ b/R/geom-quantile.R @@ -59,7 +59,7 @@ geom_quantile <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-raster.R b/R/geom-raster.R index 819692cea5..cc3caf3d9d 100644 --- a/R/geom-raster.R +++ b/R/geom-raster.R @@ -39,7 +39,7 @@ geom_raster <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-rect.R b/R/geom-rect.R index 9799f82cf2..9308d458ca 100644 --- a/R/geom-rect.R +++ b/R/geom-rect.R @@ -23,7 +23,7 @@ geom_rect <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-ribbon.R b/R/geom-ribbon.R index 20e01d36c4..575a8d9fbd 100644 --- a/R/geom-ribbon.R +++ b/R/geom-ribbon.R @@ -91,7 +91,7 @@ geom_ribbon <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export @@ -316,7 +316,7 @@ geom_area <- function(mapping = NULL, data = NULL, stat = "align", ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-rug.R b/R/geom-rug.R index bcc7adca7a..27adebd772 100644 --- a/R/geom-rug.R +++ b/R/geom-rug.R @@ -81,7 +81,7 @@ geom_rug <- function(mapping = NULL, data = NULL, } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-segment.R b/R/geom-segment.R index fb00f0481d..14ed7c8e3f 100644 --- a/R/geom-segment.R +++ b/R/geom-segment.R @@ -97,7 +97,7 @@ geom_segment <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-smooth.R b/R/geom-smooth.R index c386504fa8..dc81e1869e 100644 --- a/R/geom-smooth.R +++ b/R/geom-smooth.R @@ -118,7 +118,7 @@ geom_smooth <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-spoke.R b/R/geom-spoke.R index 9ffbe71144..c35e272206 100644 --- a/R/geom-spoke.R +++ b/R/geom-spoke.R @@ -50,7 +50,7 @@ stat_spoke <- function(...) { lifecycle::deprecate_stop("2.0.0", "stat_spoke()", "geom_spoke()") } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-text.R b/R/geom-text.R index 7ea074fc60..cdff59ca8c 100644 --- a/R/geom-text.R +++ b/R/geom-text.R @@ -180,7 +180,7 @@ geom_text <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-tile.R b/R/geom-tile.R index fabf70a4a9..f9d17a5f58 100644 --- a/R/geom-tile.R +++ b/R/geom-tile.R @@ -101,7 +101,7 @@ geom_tile <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-violin.R b/R/geom-violin.R index 24d8fd07d9..b186a9d828 100644 --- a/R/geom-violin.R +++ b/R/geom-violin.R @@ -156,7 +156,7 @@ geom_violin <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/geom-vline.R b/R/geom-vline.R index 23093fcbcd..872ac379f7 100644 --- a/R/geom-vline.R +++ b/R/geom-vline.R @@ -40,7 +40,7 @@ geom_vline <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Geom #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-.R b/R/guide-.R index 8d26a95628..63bdedfb87 100644 --- a/R/guide-.R +++ b/R/guide-.R @@ -70,107 +70,105 @@ new_guide <- function(..., available_aes = "any", super) { #' @rdname is_tests is_guide <- function(x) inherits(x, "Guide") -#' @section Guides: +#' Guides #' -#' The `guide_*()` functions, such as `guide_legend()` return an object that -#' is responsible for displaying how objects in the plotting panel are related -#' to actual values. +#' @description +#' The `guide_*` functions (like `guide_legend()`) return `Guide*` objects +#' (like `GuideLegend`). The `Guide*` object is responsible for rendering the +#' guide for at least one aesthetic. #' -#' Each of the `Guide*` object is a [ggproto()] object, descended from the -#' top-level `Guide`, and each implements their own methods for drawing. +#' @details +#' Each of the `Guide*` objects is a [ggproto()] object, descended from the +#' top-level `Guide`, and each implements various methods and fields. #' -#' To create a new type of Guide object, you typically will want to override -#' one or more of the following: +#' Building a guide has three stages: +#' 1. The guide extracts the relevant information from scales. +#' 2. The guide interacts with other parts of the plot, like coords or layers to +#' supplement information. +#' 3. The guide is rendered. #' -#' Properties: +#' When creating a new Guide class, you may want to consider overriding one or +#' more of the following: #' -#' - `available_aes` A `character` vector with aesthetics that this guide -#' supports. The value `"any"` indicates all non-position aesthetics. +#' * The `params`, `elements`, `hashables` and `available_aes` fields. +#' * The `extract_key()`, `extract_decor()` and `extract_params()` methods. +#' * The `transform()` or `get_layer_key()` methods. +#' * The `setup_params()` and `override_elements()` methods. +#' * Any of the `build_*` methods. +#' * As a last resort the `measure_grobs()`, `arrange_layout()`, and +#' `assemble_drawing()` methods. #' -#' - `params` A named `list` of parameters that the guide needs to function. -#' It has the following roles: +#' @section Conventions: #' -#' - `params` provides the defaults for a guide. -#' - `names(params)` determines what are valid arguments to `new_guide()`. -#' Some parameters are *required* to render the guide. These are: `title`, -#' `name`, `position`, `direction`, `order` and `hash`. -#' - During build stages, `params` holds information about the guide. +#' The object name that a new class is assigned to is typically the same as the +#' class name. Guide class names are in UpperCamelCase and start with the +#' `Guide*` prefix, like `GuideNew`. #' -#' - `elements` A named list of `character`s, giving the name of theme elements -#' that should be retrieved automatically, for example `"legend.text"`. +#' A constructor function is usually paired with a Guide class. The constructor +#' wraps a call to `new_guide()`, where e.g. `new_guide(super = GuideNew)`. The +#' constructor name is formatted by taking the Guide class name and formatting +#' it with snake_case, so that `GuideNew` becomes `guide_new()`. #' -#' - `hashables` An `expression` that can be evaluated in the context of -#' `params`. The hash of the evaluated expression determines the merge -#' compatibility of guides, and is stored in `params$hash`. -#' -#' Methods: -#' -#' - `extract_key()` Returns a `data.frame` with (mapped) breaks and labels -#' extracted from the scale, which will be stored in `params$key`. -#' -#' - `extract_decor()` Returns a `data.frame` containing other structured -#' information extracted from the scale, which will be stored in -#' `params$decor`. The `decor` has a guide-specific meaning: it is the bar in -#' `guide_colourbar()`, but specifies the `axis.line` in `guide_axis()`. -#' -#' - `extract_params()` Updates the `params` with other, unstructured -#' information from the scale. An example of this is inheriting the guide's -#' title from the `scale$name` field. -#' -#' - `transform()` Updates the `params$key` based on the coordinates. This -#' applies to position guides, as it rescales the aesthetic to the \[0, 1\] -#' range. -#' -#' - `merge()` Combines information from multiple guides with the same -#' `params$hash`. This ensures that e.g. `guide_legend()` can display both -#' `shape` and `colour` in the same guide. -#' -#' - `process_layers()` Extract information from layers. This acts mostly -#' as a filter for which layers to include and these are then (typically) -#' forwarded to `get_layer_key()`. -#' -#' - `get_layer_key()` This can be used to gather information about how legend -#' keys should be displayed. -#' -#' - `setup_params()` Set up parameters at the beginning of drawing stages. -#' It can be used to overrule user-supplied parameters or perform checks on -#' the `params` property. -#' -#' - `override_elements()` Take populated theme elements derived from the -#' `elements` property and allows overriding these theme settings. -#' -#' - `build_title()` Render the guide's title. -#' -#' - `build_labels()` Render the guide's labels. -#' -#' - `build_decor()` Render the `params$decor`, which is different for every -#' guide. +#' @export +#' @format NULL +#' @usage NULL +#' @keywords internal +#' @seealso Run `vignette("extending-ggplot2")`, in particular the "Creating +#' new guides" section. +#' @examples +#' # Extending the class +#' GuideDescribe <- ggproto( +#' "GuideDescribe", Guide, +#' # Fields +#' elements = list(text = "legend.text", margin = "legend.margin"), +#' hashables = rlang::exprs(key$.label), #' -#' - `build_ticks()` Render tick marks. +#' # Methods +#' build_title = function(...) zeroGrob(), # Turn off title #' -#' - `measure_grobs()` Measure dimensions of the graphical objects produced -#' by the `build_*()` methods to be used in the layout or assembly. +#' build_labels = function(key, elements, params) { +#' labels <- key$.label +#' n <- length(labels) +#' labels <- paste0(paste0(labels[-n], collapse = ", "), ", and ", labels[n]) +#' labels <- paste0("A guide showing ", labels, " categories") +#' element_grob(elements$text, label = labels, margin_x = TRUE, margin_y = TRUE) +#' }, #' -#' - `arrange_layout()` Set up a layout for how graphical objects produced by -#' the `build_*()` methods should be arranged. +#' measure_grobs = function(grobs, params, elements) { +#' # Measuring in centimetres is the convention +#' width <- grid::convertWidth(grid::grobWidth(grobs$labels), "cm", valueOnly = TRUE) +#' height <- grid::convertHeight(grid::grobHeight(grobs$labels), "cm", valueOnly = TRUE) +#' list(width = unit(width, "cm"), height = unit(height, "cm")) +#' }, #' -#' - `assemble_drawing()` Take the graphical objects produced by the `build_*()` -#' methods, the measurements from `measure_grobs()` and layout from -#' `arrange_layout()` to finalise the guide. +#' assemble_drawing = function(self, grobs, layout, sizes, params, elements) { +#' gt <- gtable::as.gtable(grobs$labels, width = sizes$width, height = sizes$height) +#' gt <- gtable::gtable_add_padding(gt, elements$margin) +#' gt +#' } +#' ) #' -#' - `add_title` Adds the title to a gtable, taking into account the size -#' of the title as well as the gtable size. +#' # Building a constructor +#' guide_describe <- function(position = NULL) { +#' new_guide(position = position, super = GuideDescribe) +#' } #' -#' @rdname ggplot2-ggproto -#' @format NULL -#' @usage NULL -#' @export +#' # Use new guide plot +#' ggplot(mpg, aes(displ, hwy, colour = class)) + +#' geom_point() + +#' guides(colour = guide_describe("bottom")) Guide <- ggproto( "Guide", - # `params` is a list of initial parameters that gets updated upon - # construction. After construction, parameters are manged by the - # `GuidesList` class. + # Fields ------------------------------------------------------------------ + + #' @field params A list of initial parameters that the guide needs to + #' function. The base `Guide$params` contains mandatory parameters, + #' but extensions can add new parameters. It has the following roles: + #' * It provides the default values for parameters. + #' * `names(params)` determines what are valid arguments for `new_guide()`. + #' * During build stages, a mutable copy of `params` holds information + #' about the guide. params = list( title = waiver(), theme = NULL, @@ -181,22 +179,57 @@ Guide <- ggproto( hash = character() ), - # A list of theme elements that should be calculated - elements = list(), - - # The aesthetics for which this guide is appropriate + #' @field available_aes A character vector of aesthetic names for which the + #' guide is appropriate. Can use keyword `"any"` to indicate all non-position + #' aesthetics. available_aes = character(), - # The `hashables` are the parameters of the guide that are used to generate a - # unique hash that determines whether other guides are compatible. + #' @field elements A named list of strings stating which theme elements this + #' guide uses. By default, strings will be translated in + #' `Guide$setup_elements()` using `calc_element()`. Strings are expected to + #' occur in `names(get_element_tree())`, like `"legend.text"` for example. + #' Position guides typically append the `{aes}.{position}` suffix in the + #' `setup_elements()` method when the position is known. + elements = list(), + + #' @field hashables A list of calls or names constructed by `rlang::exprs()` + #' indicating objects in the `params` field. These will be evaluated in the + #' context of the `params` field and the resulting list will be hashed. The + #' hash uniquely identify guides that can merge. Guides that have different + #' hashes will not merge. For extensions, you should include objects that + #' clearly mark two guides from one another that cannot be merged. hashables = exprs(title, name), - # Training has the task of updating parameters based the scale. - # There are 3 sub-tasks: - # 1. Extract a key from the scale - # 2. (Optionally) extract further decoration from the scale (e.g. the - # colour bar). - # 3. Extract further parameters + # Methods ----------------------------------------------------------------- + + ## Training --------------------------------------------------------------- + + #' @field train + #' **Description** + #' + #' A function method for orchestrating the training of a guide, which extracts + #' necessary information from a Scale object. As orchestrator, this method is + #' not intended for extension. + #' + #' **Usage** + #' ```r + #' Guide$train(params, scale, aesthetic, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`scale`}{A `` ggproto object. In the case of position + #' guides, can be a `` ggproto object.} + #' \item{`aesthetic`}{A scalar string specifying the aesthetic. + #' If missing (default), it will use the first aesthetic specified in the + #' scale.} + #' \item{`...`}{Additional parameters passed on to the `extract_params()` + #' method.} + #' } + #' + #' **Value** + #' + #' A modified list of parameters train = function(self, params = self$params, scale, aesthetic = NULL, ...) { params$aesthetic <- aesthetic %||% scale$aesthetics[1] params$key <- inject(self$extract_key(scale, !!!params)) @@ -211,12 +244,31 @@ Guide <- ggproto( params }, - # Setup parameters that are only available after training - extract_params = function(scale, params, ...) { - params - }, - - # Function for generating a `key` data.frame from the scale + #' @field extract_key + #' **Description** + #' + #' A function method for extracting break information from the scale called + #' the 'key'. It retrieves breaks, maps these breaks and derives labels. These + #' form the basis for tick marks and labels in some guides. It is appropriate + #' to override in extensions. + #' + #' **Usage** + #' ```r + #' Guide$extract_key(scale, aesthetic, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`scale`}{A `` ggproto object. In the case of position + #' guides, can be a `` ggproto object.} + #' \item{`aesthetic`}{A scalar string specifying the aesthetic.} + #' \item{`...`}{Optional arguments from the `params` field.} + #' } + #' + #' **Value** + #' + #' A 'key' data frame containing annotated scale breaks, including at least a + #' column for the aesthetic, `.label` and `.value`. If there are no breaks, + #' returns `NULL`. extract_key = function(scale, aesthetic, ...) { breaks <- scale$get_breaks() if (length(breaks) == 0) { @@ -238,23 +290,92 @@ Guide <- ggproto( } }, - # Function for extracting decoration from the scale. - # This is for `guide_colourbar` to extract the bar as well as the key, - # and might be a good extension point. + #' @field extract_decor + #' **Description** + #' + #' A function method for extracting 'decor' from the scale. The 'decor' acts + #' as a wildcard for anything the guide may need to render that is not based + #' on the key. For this reason, it has guide specific meaning that indicates + #' different things for different guides. In `guide_colourbar()` it is the + #' colour gradient, but in `guide_axis()` it is the axis line information. + #' It is appropriate to override in extensions. + #' + #' **Usage** + #' ```r + #' Guide$extract_decor(scale, aesthetic, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`scale`}{A `` ggproto object. In the case of position + #' guides, can be a `` ggproto object.} + #' \item{`aesthetic`}{A scalar string specifying the aesthetic.} + #' \item{`...`}{Optional arguments from the `params` field.} + #' } + #' + #' **Value** + #' + #' Undefined. `NULL` by default. extract_decor = function(scale, aesthetic, ...) { return(invisible()) # By default, nothing else needs to be extracted }, - # Function for merging multiple guides. - # Mostly applies to `guide_legend()` and `guide_binned()`. - # Defaults to returning the *other* guide, because this parent class is - # mostly a virtual class and children should implement their own merges. - merge = function(self, params, new_guide, new_params) { - return(list(guide = new_guide, params = new_params)) + #' @field extract_params + #' **Description** + #' + #' A function method for extracting any other information from the scale that + #' the guide may need. A typical example is to derive the title from the scale, + #' or apply any edits to the `key` or `decor` variables. + #' + #' **Usage** + #' ```r + #' Geom$extract_params(scale, params, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`scale`}{A `` ggproto object. In the case of position + #' guides, can be a `` ggproto object.} + #' \item{`params`}{A list of parameters initiated by the `params` field, + #' which at this point includes the `key` and `decor` from previous + #' extractors.} + #' \item{`...`}{Additional arguments passed from the `train()` method. For + #' non-position guides, often includes `title` as derived from the + #' `plot$labels` field.} + #' } + #' + #' **Value** + #' + #' A modified list of parameters + extract_params = function(scale, params, ...) { + params }, - # Function for applying coord-transformation. - # Mostly applied to position guides, such as `guide_axis()`. + ## Intermediate steps ------------------------------------------------------ + + #' @field transform + #' **Description** + #' + #' A function method to apply coord transformation and munching to the + #' 'key' and 'decor' parameters. This method only applies to position guides + #' like `guide_axis()` and is not called for non-position guides. It is + #' recommended to override this method if you have a position guide that + #' does not inherit from `GuideAxis` or has custom key' or 'decor' structures + #' that `GuideAxis$transform()` does not handle well. + #' + #' **Usage** + #' ```r + #' Guide$transform(params, coord, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`coord`}{A `` ggproto object.} + #' \item{`...`}{Optional arguments, typically `panel_params` for most + #' position guides.} + #' } + #' + #' **Value** + #' + #' A list of parameters. The default throws an error. transform = function(self, params, coord, ...) { cli::cli_abort(c( "{.fn {snake_class(self)}} does not implement a {.fn transform} method.", @@ -262,9 +383,81 @@ Guide <- ggproto( )) }, - # Function for extracting information from the layers. - # Mostly applies to `guide_legend()` and `guide_binned()` + #' @field merge + #' **Description** + #' + #' A function method for combining information from two guides. When two + #' guides have the same computed `hash` parameter derived from the `hashables` + #' field, this function will be called to merge them. If more than two guides + #' need to be merged, they are merged successively in a `Reduce()`-like + #' fashion. + #' + #' Merging guides is the mechanism by which `guide_legend()` can take one + #' guide trained on the `shape` scale, another trained on the `colour` scale + #' and display them together in the same guide, for example. + #' + #' Overriding this method is recommended if the extension descends directly + #' from `Guide` and not its children. Otherwise, it should be overridden if + #' presented with no superior options. + #' + #' **Usage** + #' ```r + #' Guide$merge(params, new_guide, new_params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters derived from the `params` field of + #' this guide.} + #' \item{`new_guide`}{A `` ggproto object representing the other guide class} + #' \item{`new_params`}{A list of parameters derived from the `params` field + #' of the other guide} + #' } + #' + #' **Value** + #' + #' A named list containing `guide` and `params`, where `guide` is a `` + #' ggproto object and `params` is a list with parameters. By default, returns + #' the new guide and its parameters. + merge = function(self, params, new_guide, new_params) { + return(list(guide = new_guide, params = new_params)) + }, + + #' @field process_layers,get_layer_key + #' **Description** + #' + #' These function methods extract information from layers that the guide may + #' need. The `process_layers()` method is tasked with selecting layers that + #' are represented by the guide and are to be included. The selected layers + #' should be passed on to the `get_layer_key()` method. + #' + #' Typical use of these methods is for `guide_legend()` to extract the + #' `Geom$draw_key` function to render glyphs in addition to any default or + #' fixed aesthetics. While these methods are called in position guides, + #' the `layers` and `data` arguments are empty as these are unavailable at + #' that point. + #' + #' You can override `get_layer_key()`, but `process_layers()` should + #' probably only be overridden if the extension does not inherit from + #' `GuideLegend`. + #' + #' **Usage** + #' ```r + #' Guide$process_layers(params, layers, data, theme) + #' Guide$get_layer_key(params, layers, data, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`layers`}{A list of layers from `plot$layers`.} + #' \item{`data`}{A list of layer data frames.} + #' \item{`theme`}{A [completed theme][complete_theme()] object.} + #' } + #' + #' **Value** + #' + #' A list of parameters process_layers = function(self, params, layers, data = NULL, theme = NULL) { + # TODO: should we just not call this method for position guides? self$get_layer_key(params, layers, data, theme) }, @@ -272,30 +465,38 @@ Guide <- ggproto( return(params) }, - # Called at start of the `draw` method. Typically used to either overrule - # user-specified parameters or populate extra parameters derived from - # the guide's direction or position. - setup_params = function(params) { - params - }, + ## Drawing ----------------------------------------------------------------- - # Converts the `elements` field to proper elements to be accepted by - # `element_grob()`. String-interpolates aesthetic/position dependent elements. - setup_elements = function(params, elements, theme) { - theme <- add_theme(theme, params$theme) - is_char <- vapply(elements, is.character, logical(1)) - elements[is_char] <- lapply(elements[is_char], calc_element, theme = theme) - elements - }, - - # Called after `setup_elements` to overrule any element defaults descended - # from the theme. - override_elements = function(params, elements, theme) { - elements - }, - - # Main drawing function that organises more specialised aspects of guide - # drawing. + #' @field draw + #' **Description** + #' + #' A function method is the main orchestrator for drawing the guide. It sets + #' up the final pieces in context of the position, direction and theme + #' and. Subsequenty, it renders the individual components like titles, ticks, + #' labels and decor. Finally, it arranges these components into a guide. + #' + #' This method should only be overridden if the extension has non standard + #' components that do not fit into 'decor' or when this method can be greatly + #' simplified for humble guides. All subsidiaries are fine to override. + #' + #' **Usage** + #' ```r + #' Geom$setup_params(theme, position, direction, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`theme`}{A [complete theme][complete_theme()] object.} + #' \item{`position`}{A scalar string indicating the position where + #' the guide should be drawn. Typically `"top"`, `"right"`, `"bottom"` + #' or `"left"`, unless it is a position guide for an exotic coord. Can be + #' `NULL`, in which case `params$position` should be used.} + #' \item{`direction`}{A scalar string indicating the legend direction. + #' Can be `NULL`, in which case `params$direction` should be used.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' } + #' **Value** + #' + #' A grob with the guide. draw = function(self, theme, position = NULL, direction = NULL, params = self$params) { @@ -332,23 +533,126 @@ Guide <- ggproto( self$assemble_drawing(grobs, layout, sizes, params, elems) }, - # Makes measurements of grobs that can be used in the layout or assembly - # stages of guide drawing. - measure_grobs = function(grobs, params, elements) { - return(invisible()) + #' @field draw_early_exit + #' **Description** + #' + #' A function method that determines what should be drawn when the guide 'key' + #' is empty. The default method returns [`zeroGrob()`]. You can override + #' this method if an empty key should draw anything. Used in `guide_axis()` + #' to render the `axis.line` part even if no ticks or labels should be drawn. + #' + #' **Usage** + #' ```r + #' Guide$draw_early_exit(params, elements) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method.} + #' } + #' + #' **Value** + #' + #' A grob. + draw_early_exit = function(self, params, elements) { + zeroGrob() }, - # Takes care of where grobs should be added to the output gtable. - arrange_layout = function(key, sizes, params, elements) { - return(invisible()) + ### Setup ------------------------------------------------------------------- + + #' @field setup_params + #' **Description** + #' + #' A function method for finalising parameters. Typically used to make checks + #' on the `params` object or to make any position or direction based + #' adjustments. + #' + #' **Usage** + #' ```r + #' Guide$setup_params(params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' } + #' + #' **Value** + #' + #' A list of parameters + setup_params = function(params) { + params }, - # Combines grobs into a single gtable. - assemble_drawing = function(grobs, layout, sizes, params, elements) { - zeroGrob() + #' @field setup_elements,override_elements + #' **Description** + #' + #' A function method for resolving required theme elements. The + #' `setup_elements()` method joins local guide theme with global theme and + #' calculates the necessary theme elements. The `override_elements()` method + #' is a hook to edit elements after they've been calculated. + #' + #' You can override the `setup_elements()` method if you need more complicated + #' theme handling before calculating elements or want to intervene in inheritance. + #' For example, `guide_legend()` has special handling of text margins and + #' `guide_axis()` suffixes `{aes}.{position}` to get the theme elements for + #' the correct position. + #' + #' For other purposes, you can override the `override_elements()` method. + #' + #' **Usage** + #' ```r + #' Guide$setup_elements(params, elements, theme) + #' Guide$override_elements(params, elements, theme) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`elements`}{A named list of strings initiated by the `elements` field.} + #' \item{`theme`}{A [complete theme][complete_theme()]} + #' } + #' + #' **Value** + #' + #' A list of elements or resolved theme settings. + setup_elements = function(params, elements, theme) { + theme <- add_theme(theme, params$theme) + is_char <- vapply(elements, is.character, logical(1)) + elements[is_char] <- lapply(elements[is_char], calc_element, theme = theme) + elements }, - # Renders the guide title + override_elements = function(params, elements, theme) { + elements + }, + + ### Building -------------------------------------------------------------- + + #' @field build_title + #' **Description** + #' + #' A function method for rendering the title. Note that titles for position + #' guides are rendered by the Facet class and not this method. + #' + #' You can override this method if you need to render more than one title + #' (or none) or adjust margin settings. + #' + #' **Usage** + #' ```r + #' Guide$build_title(label, elements, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`label`}{A single string or expression with the title text.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method. The default method expects + #' `elements$title` to inherit from the `` class.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' } + #' + #' **Value** + #' + #' A grob representing the title. build_title = function(label, elements, params) { ggname( "guide.title", @@ -361,21 +665,39 @@ Guide <- ggproto( ) }, - # Renders the guide labels - # TODO: See if we can generalise label drawing for many guides - build_labels = function(key, elements, params) { - zeroGrob() - }, - - # Renders 'decor', which can have different meanings for different guides. - # The other grobs are provided, as a colourbar might use the ticks for example - build_decor = function(decor, grobs, elements, params) { - zeroGrob() - }, - - # Renders tickmarks + #' @field build_ticks + #' **Description** + #' + #' A function method for rendering tick marks. + #' + #' You can override this function if you don't need ticks or have completely + #' different logic on how these should be drawn. + #' + #' **Usage** + #' ```r + #' Guide$build_ticks(key, elements, params, position, length) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`key`}{A data frame with the key information derived from the + #' `extract_key()` method.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method. The default method expects + #' `elements$ticks` to inherit from the `` class and + #' `elements$ticks_length` to be a scalar `` object.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`position`}{A scalar string indicating the position. Due to + #' historic error this works in the opposite way to intuition: if you want + #' ticks for an axis at the bottom of a plot, you should use `"top"` here.} + #' \item{`length`}{A scalar `` object giving the tick length.} + #' } + #' + #' **Value** + #' + #' A grob representing tick marks. build_ticks = function(key, elements, params, position = params$position, length = elements$ticks_length) { + # TODO: position logic is crooked, should this be reversed? if (!is_theme_element(elements)) { elements <- elements$ticks } @@ -416,10 +738,187 @@ Guide <- ggproto( ) }, - draw_early_exit = function(self, params, elements) { + #' @field build_labels + #' **Description** + #' + #' A function method for rendering labels. The default method returns an + #' empty grob. It is recommended to override this method when your extension + #' directly descends from Guide. + #' + #' **Usage** + #' ```r + #' Guide$build_labels(key, elements, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`key`}{A data frame with the key information derived from the + #' `extract_key()` method.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method. Most non-default methods expects + #' `elements$text` to inherit from the ``.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' } + #' + #' **Value** + #' + #' A grob representing labels. + build_labels = function(key, elements, params) { + # TODO: See if we can generalise label drawing for many guides zeroGrob() }, + #' @field build_decor + #' **Description** + #' + #' A function method for rendering decor. As the 'wildcard' component, this + #' can draw whatever component the guide needs that isn't already captured by + #' the key. The default method returns an empty grob. It is recommended to + #' override this method. + #' + #' For some examples: `guide_legend()` renders the keys with the glyphs, + #' `guide_colourbar()` renders the colour gradient rectangle and + #' `guide_axis()` renders the axis line. + #' + #' **Usage** + #' ```r + #' Guide$build_decor(decor, grobs, elements, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`decor`}{A data frame (or other structure) with information derived + #' from the `extract_decor()` method.} + #' \item{`grobs`}{A list with grobs generated by the other `build_*` + #' methods.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method. Most non-default methods expects + #' `elements$text` to inherit from the ``.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' } + #' + #' **Value** + #' + #' A grob. + build_decor = function(decor, grobs, elements, params) { + zeroGrob() + }, + + ### Assembly -------------------------------------------------------------- + + #' @field measure_grobs + #' **Description** + #' + #' A function method for measuring grobs. In preparation for arranging grobs, + #' they often need to be measured to determine their widths and heights. + #' It is convention that every measurement is converted to centimetres. + #' You can override this method if your extension directly descends from + #' Guide, or the parent class measurement is defective. + #' + #' **Usage** + #' ```r + #' Guide$measure_grobs(grobs, params, elements) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`grobs`}{A list with grobs generated by the `build_*` methods.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method.} + #' } + #' + #' **Value** + #' + #' A named list or `` vector giving sizes of components, coordinated + #' with `arrange_layout()` and `assemble_drawing()` methods. The default + #' method returns `NULL`. + measure_grobs = function(grobs, params, elements) { + return(invisible()) + }, + + #' @field arrange_layout + #' **Description** + #' + #' A function method for determining the location or order of grobs in a + #' gtable. Typically determines rows and columns where decor and labels are + #' placed. Titles are added seperately.You can override this method if your + #' extension directly descends from Guide. + #' + #' **Usage** + #' ```r + #' Guide$arrange_layout(key, sizes, params, elements) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`key`}{A data frame with the key information derived from the + #' `extract_key()` method.} + #' \item{`sizes`}{A list of `` vector from the `measure_grobs()` method.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method.} + #' } + #' + #' **Value** + #' + #' Any structure holding placement information coordinated with the + #' `assemble_drawing()` method. + arrange_layout = function(key, sizes, params, elements) { + return(invisible()) + }, + + #' @field assemble_drawing + #' **Description** + #' + #' A function method that takes measurements, placement information and grobs + #' and assembles these together in a gtable structure. You can override this + #' method if your extension directly descends from Guide, or the parent class + #' assembly does not work for your guide. + #' + #' **Usage** + #' ```r + #' Guide$assemble_drawing(grobs, layout, sizes, params, elements) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`grobs`}{A list with grobs generated by the `build_*` methods.} + #' \item{`layout`}{A data structure from the `arrange_layout()` method.} + #' \item{`sizes`}{A list of `` vector from the `measure_grobs()` method.} + #' \item{`params`}{A list of parameters initiated by the `params` field.} + #' \item{`elements`}{A list of elements or resolved theme settings from + #' the `override_elements()` method.} + #' } + #' + #' **Value** + #' + #' A finished gtable containing the guide. + assemble_drawing = function(grobs, layout, sizes, params, elements) { + zeroGrob() + }, + + #' @field arrange_layout + #' **Description** + #' + #' A function method for placing the title. It is a subsidiary method used + #' in the `assemble_drawing()` method for non-position guides. Titles are + #' typically added before `legend.margin` is applied. It is not + #' recommended to override this method. + #' + #' **Usage** + #' ```r + #' Guide$add_title(gtable, title, position, just) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`gtable`}{An unfinished gtable under construction in the + #' `assemble_drawing()` method.} + #' \item{`title`}{The grob resulting from the `build_title()` method.} + #' \item{`position`}{A scaler string, either `"top"`, `"right"`, `"bottom"` + #' or `"left"` corresponding to the `legend.title.position`.} + #' \item{`just`}{A named list having `hjust` and `vjust` components with + #' scalar numeric values between 0 and 1.} + #' } + #' + #' **Value** + #' + #' The `gtable` argument with added title. add_title = function(gtable, title, position, just) { if (is.zero(title)) { return(gtable) @@ -484,6 +983,8 @@ Guide <- ggproto( } ) +# Helpers ----------------------------------------------------------------- + # Helper function that may facilitate flipping theme elements by # swapping x/y related arguments to `element_grob()` flip_element_grob <- function(..., flip = FALSE) { diff --git a/R/guide-axis-logticks.R b/R/guide-axis-logticks.R index 37273cba06..b411cc2d6a 100644 --- a/R/guide-axis-logticks.R +++ b/R/guide-axis-logticks.R @@ -138,7 +138,7 @@ guide_axis_logticks <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-axis-stack.R b/R/guide-axis-stack.R index a58a4344cc..effa6a5eaf 100644 --- a/R/guide-axis-stack.R +++ b/R/guide-axis-stack.R @@ -75,7 +75,7 @@ guide_axis_stack <- function(first = "axis", ..., title = waiver(), theme = NULL ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-axis-theta.R b/R/guide-axis-theta.R index 5183e802fc..8abd11bce1 100644 --- a/R/guide-axis-theta.R +++ b/R/guide-axis-theta.R @@ -54,7 +54,7 @@ guide_axis_theta <- function(title = waiver(), theme = NULL, angle = waiver(), ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-axis.R b/R/guide-axis.R index d445900071..1b42917653 100644 --- a/R/guide-axis.R +++ b/R/guide-axis.R @@ -81,7 +81,7 @@ guide_axis <- function(title = waiver(), theme = NULL, check.overlap = FALSE, ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-bins.R b/R/guide-bins.R index b83494fb77..d8fed79e42 100644 --- a/R/guide-bins.R +++ b/R/guide-bins.R @@ -106,7 +106,7 @@ guide_bins <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-colorbar.R b/R/guide-colorbar.R index 096901e9a8..2710860333 100644 --- a/R/guide-colorbar.R +++ b/R/guide-colorbar.R @@ -170,7 +170,7 @@ guide_colourbar <- function( #' @rdname guide_colourbar guide_colorbar <- guide_colourbar -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-colorsteps.R b/R/guide-colorsteps.R index 14cca8563d..2a9f291477 100644 --- a/R/guide-colorsteps.R +++ b/R/guide-colorsteps.R @@ -83,7 +83,7 @@ guide_coloursteps <- function( #' @rdname guide_coloursteps guide_colorsteps <- guide_coloursteps -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-custom.R b/R/guide-custom.R index f602bfc843..12bd9705ad 100644 --- a/R/guide-custom.R +++ b/R/guide-custom.R @@ -64,7 +64,7 @@ guide_custom <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-legend.R b/R/guide-legend.R index b728752518..dfd06f25d3 100644 --- a/R/guide-legend.R +++ b/R/guide-legend.R @@ -144,7 +144,7 @@ guide_legend <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-none.R b/R/guide-none.R index 5c0b2d35e2..71a0f2f78e 100644 --- a/R/guide-none.R +++ b/R/guide-none.R @@ -18,7 +18,7 @@ guide_none <- function(title = waiver(), position = waiver()) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/guide-old.R b/R/guide-old.R index d20fec0e3e..8f8be86ba1 100644 --- a/R/guide-old.R +++ b/R/guide-old.R @@ -80,7 +80,7 @@ old_guide <- function(guide) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Guide #' @format NULL #' @usage NULL #' @export diff --git a/R/layer.R b/R/layer.R index b1029579bc..171dc4631e 100644 --- a/R/layer.R +++ b/R/layer.R @@ -217,36 +217,234 @@ validate_mapping <- function(mapping, call = caller_env()) { new_aes(mapping) } +#' Layers +#' @name Layer-class +#' +#' @description +#' The Layer class is a chaperone class not available for extension. The class +#' fulfils the following tasks. The class houses the Geom, Stat and Position +#' trinity and tracks their stateful parameters. Furthermore, its methods are +#' responsible for managing the layer data and exposing it to other components +#' of the plot at the right time. +#' +#' @details +#' The Layer class is an internal class that is not exported because the class +#' is not intended for extension. The `layer()` function instantiates the +#' LayerInstance class, which inherits from Layer, but has relevant fields +#' populated. +#' +#' The class is mostly used in `ggplot_build()`, with the notable exception +#' of the `draw_geom()` method, which is used in `ggplot_gtable()` instead. +#' +#' @section Layer data diagram: +#' +#' As the Layer class is a chaparone for the data, it makes sense to give a +#' small overview of how layer data flows through a plot. In the diagram below +#' we following the `layer(data)` argument over the course of plot building +#' through Layer class methods. When an outside class acts on the data without +#' the Layer class, this is indicated with the left arrow `<-`. Subcomponents +#' of a method that touch data are indicated with the right arrow `->`. +#' +#' ```r +#' # Inside `ggplot_build()` +#' | +#' layer(data) +#' | +#' | +#' | # Inherit plot data +#' | +#' Layer$layer_data() +#' | +#' | +#' | # Finalise mapping +#' | +#' Layer$setup_layer() +#' | +#' | +#' | # Append PANEL variable for facets +#' | +#' |<- Layout$setup() +#' | | +#' | +-> Facet$setup_data() +#' | | +#' | +-> Coord$setup_data() +#' | +#' | +#' | # Evaluate mappings to new data and infer group +#' | +#' Layer$compute_aesthetics() +#' | +#' | +#' | # Scale-transform all aesthetics +#' | +#' |<- ScalesList$transform_df() +#' | | +#' | +-> Scale$transform_df() +#' | +#' | +#' | # Map x/y aesthetics with initial scale +#' | +#' |<- Layout$map_position() +#' | | +#' | +-> Scale$map() +#' | +#' | +#' | # Compute stat part of layer +#' | +#' Layer$compute_statistic() +#' | | +#' | +-> Stat$setup_data() +#' | | +#' | +-> Stat$compute_layer() +#' | +#' | +#' | # Add `after_stat()` stage +#' | # Scale transform computed variables +#' | +#' Layer$map_statistic() +#' | +#' | +#' | # Setup geom part of layer +#' | +#' Layer$compute_geom_1() +#' | | +#' | +-> Geom$setup_data() +#' | +#' | +#' | # Apply position adjustments +#' | +#' Layer$compute_position() +#' | | +#' | +-> Position$use_defaults() +#' | | +#' | +-> Position$setup_data() +#' | | +#' | +-> Position$compute_layer() +#' | +#' | +#' | # Map x/y aesthetics with final scales +#' | +#' |<- Layout$map_position() +#' | | +#' | +-> Scale$map() +#' | +#' | # Map non-position aesthetics +#' | +#' |<- ScalesList$map_df() +#' | | +#' | +-> Scale$map() +#' | +#' | +#' | # Fill in defaults and fixed aesthetics +#' | +#' Layer$compute_geom_2() +#' | | +#' | +-> Geom$use_defaults() +#' | +#' | +#' | # Apply final Stat hook +#' | +#' Layer$finish_statistics() +#' | | +#' | +-> Stat$finish_layer() +#' | +#' | +#' | # Apply final Facet hook +#' | +#' |<- Layout$finish_data() +#' | | +#' | +-> Facet$finish_data() +#' | +#' V +#' # `ggplot_build()` is finished +#' # Hand off to `ggplot_gtable()` +#' | +#' | +#' | # Draw the geom part +#' | +#' Layer$draw_geom() +#' | +#' +-> Geom$handle_na() +#' | +#' +-> Geom$draw_layer() +#' ``` +#' @usage NULL +#' @format NULL +#' @family Layer components +#' @family chaperone classes +#' @keywords internal +#' @examples +#' # None: Layer is not intended to be extended Layer <- ggproto("Layer", NULL, + + # Fields ------------------------------------------------------------------ + + #' @field constructor A [call][call()] object with the user-facing + #' constructor function, for use in error messaging. This field is populated + #' by `layer()`. constructor = NULL, + + #' @field geom,stat,position These fields house the Geom, Stat and Position + #' trifecta in ggproto form and is populated by `layer()`. geom = NULL, - geom_params = NULL, stat = NULL, + position = NULL, + + #' @field stat_params,computed_stat_params These fields hold parameters + #' assigned to the Stat. The `stat_params` field is directly derived from + #' user input and is populated by `layer()`. The `computed_stat_params` + #' carries state and is constructed by the `Stat$setup_params()` method. stat_params = NULL, + computed_stat_params = NULL, - # These two fields carry state throughout rendering but will always be - # calculated before use + #' @field geom_params,computed_geom_params These fields hold parameters + #' assigned to the Geom. The `geom_params` field is directly derived from + #' user input and is populated by `layer()`. The `computed_geom_params` + #' carries state and is constructed by the `Geom$setup_params()` method. + geom_params = NULL, computed_geom_params = NULL, - computed_stat_params = NULL, + + #' @field mapping,computed_mapping These fields hold [mapping][aes()]s. + #' The `mapping` field holds the `layer(mapping)` argument. The + #' `computed_mapping` field carries state and is constructed in the + #' `setup_layer()` method. + mapping = NULL, computed_mapping = NULL, + #' @field data The fortified `layer(data)` argument. data = NULL, + + #' @field aes_params Holds the fixed, unmapped aesthetics passed to + #' `layer(params)` as determined by `Geom$aesthetics()`. aes_params = NULL, - mapping = NULL, - position = NULL, - inherit.aes = FALSE, - print = function(self) { - if (!is.null(self$mapping)) { - cat("mapping:", clist(self$mapping), "\n") - } - cat(snakeize(class(self$geom)[[1]]), ": ", clist(self$geom_params), "\n", - sep = "") - cat(snakeize(class(self$stat)[[1]]), ": ", clist(self$stat_params), "\n", - sep = "") - cat(snakeize(class(self$position)[[1]]), "\n") - }, + #' @field inherit.aes A scalar boolean used in the `setup_layer()` method to + #' indicate whether the `computed_mapping` should include the global mapping + #' (`TRUE`) or only the layer mapping (`FALSE`). This is populated by the + #' `layer(inherit.aes)` parameter. + inherit.aes = FALSE, + # Methods ----------------------------------------------------------------- + + #' @field layer_data + #' **Description** + #' + #' A function method for initially resolving layer data. If layer data is + #' missing or is a function, it will derive layer data from the global plot + #' data. + #' + #' **Usage** + #' ```r + #' Layer$layer_data(plot_data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`plot_data`}{The `data` field of the ggplot object.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data or `NULL` layer_data = function(self, plot_data) { if (is.waiver(self$data)) { data <- plot_data @@ -261,8 +459,28 @@ Layer <- ggproto("Layer", NULL, if (is.null(data) || is.waiver(data)) data else unrowname(data) }, - # hook to allow a layer access to the final layer data - # in input form and to global plot info + #' @field setup_layer + #' **Description** + #' + #' A function method is a hook to allow a final access to layer data in + #' input form. In addition, it allows a layer access to global plot + #' information. The latter is used to enforce the `inherit.aes` parameter by + #' supplementing the layer mapping with the global mapping when requested. + #' + #' **Usage** + #' ```r + #' Layer$setup_data(data, plot) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`plot`}{A ggplot object} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. As a side effect, the `computed_mapping` + #' field is populated. setup_layer = function(self, data, plot) { # For annotation geoms, it is useful to be able to ignore the default aes if (isTRUE(self$inherit.aes)) { @@ -286,6 +504,27 @@ Layer <- ggproto("Layer", NULL, data }, + #' @field compute_aesthetics + #' **Description** + #' + #' A function method that evaluates aesthetics and warns about any problems. + #' It also infers a `group` aesthetic if not provided. This method is also + #' the step where layer data becomes standardised to base data frames without + #' row names or additional attributes. + #' + #' **Usage** + #' ```r + #' Layer$compute_aesthetics(data, plot) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`plot`}{A ggplot object} + #' } + #' + #' **Value** + #' + #' A data frame with layer data compute_aesthetics = function(self, data, plot) { aesthetics <- self$computed_mapping @@ -342,12 +581,32 @@ Layer <- ggproto("Layer", NULL, } else { evaled$PANEL <- data$PANEL } - evaled <- lapply(evaled, vec_set_names, names = NULL) - evaled <- as_gg_data_frame(evaled) + evaled <- data_frame0(!!!lapply(evaled, vec_set_names, names = NULL)) + # evaled <- as_gg_data_frame(evaled) evaled <- add_group(evaled) evaled }, + #' @field compute_aesthetics + #' **Description** + #' + #' A function method that orchestrates computing statistics. It executes + #' methods from the Stat class to form new computed variables. + #' + #' **Usage** + #' ```r + #' Layer$compute_statistic(data, layout) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`layout`}{A `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. As a side effect the `computed_stat_params` + #' field is populated. compute_statistic = function(self, data, layout) { if (empty(data)) return(data_frame0()) @@ -358,6 +617,30 @@ Layer <- ggproto("Layer", NULL, merge_attrs(data, ptype) }, + # TODO: should this be merged with compute_aesthetics? + #' @field map_statistic + #' **Description** + #' + #' A function method that finishes the result of computed statistics. It has + #' several tasks: + #' * It evaluates the `after_stat()` stage of the mapping from both the + #' `computed_mapping` but also the `Stat$default_aes` fields. + #' * It ensures relevant scales are instantiated for computed aesthetics. + #' * It takes care that scale transformation is applied to computed aesthetics. + #' + #' **Usage** + #' ```r + #' Layer$map_statistic(data, plot) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`plot`}{A ggplot object.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data map_statistic = function(self, data, plot) { if (empty(data)) return(data_frame0()) @@ -403,6 +686,26 @@ Layer <- ggproto("Layer", NULL, data }, + #' @field compute_geom_1 + #' **Description** + #' + #' A function method that prepares data for drawing. It checks that all + #' required aesthetics are present and sets up parameters and data using the + #' Geom class. + #' + #' **Usage** + #' ```r + #' Layer$compute_geom_1(data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. As a side effect the `computed_geom_params` + #' field is populated. compute_geom_1 = function(self, data) { if (empty(data)) return(data_frame0()) ptype <- vec_ptype(data) @@ -417,6 +720,25 @@ Layer <- ggproto("Layer", NULL, merge_attrs(data, ptype) }, + #' @field compute_position + #' **Description** + #' + #' A function method that orchestrates the position adjustment. It executes + #' methods from the Position class. + #' + #' **Usage** + #' ```r + #' Layer$compute_position(data, layout) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`layout`}{A `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. compute_position = function(self, data, layout) { if (empty(data)) return(data_frame0()) ptype <- vec_ptype(data) @@ -427,6 +749,28 @@ Layer <- ggproto("Layer", NULL, merge_attrs(data, ptype) }, + #' @field compute_geom_2 + #' **Description** + #' + #' A function method that add defaults and fixed parameters. It wraps the + #' `Geom$use_defaults()` method. + #' + #' **Usage** + #' ```r + #' Layer$compute_geom_2(data, params, theme, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list with fixed aesthetic parameters, typically the + #' `aes_params` field.} + #' \item{`theme`}{A [theme][theme()] object} + #' \item{`...`}{Passed on to `Geom$use_defaults()`, not in use.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. compute_geom_2 = function(self, data, params = self$aes_params, theme = NULL, ...) { # Combine aesthetics, defaults, & params if (empty(data)) return(data) @@ -437,10 +781,48 @@ Layer <- ggproto("Layer", NULL, self$geom$use_defaults(data, params, modifiers, theme = theme, ...) }, + #' @field finish_statistics + #' **Description** + #' + #' A function method that wraps `Stat$finish_layer()`. + #' + #' **Usage** + #' ```r + #' Layer$finish_statistics(data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data. finish_statistics = function(self, data) { self$stat$finish_layer(data, self$computed_stat_params) }, + #' @field draw_geom + #' **Description** + #' + #' A function method that produces graphics for every panel. It uses Geom + #' class methods to handle missing data and produce grobs. In contrast to + #' other methods, this is called during the `ggplot_gtable()` stage, not the + #' `ggplot_build()` stage. + #' + #' **Usage** + #' ```r + #' Layer$draw_geom(data, layout) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`layout`}{A `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A list of grobs, one per panel. draw_geom = function(self, data, layout) { if (empty(data)) { n <- nrow(layout$layout) @@ -449,9 +831,36 @@ Layer <- ggproto("Layer", NULL, data <- self$geom$handle_na(data, self$computed_geom_params) self$geom$draw_layer(data, self$computed_geom_params, layout, layout$coord) + }, + + # Utilities --------------------------------------------------------------- + + #' @field print + #' **Description** + #' + #' A function method that prints information about the layer. + #' + #' **Usage** + #' ```r + #' Layer$print() + #' ``` + #' + #' **Value** + #' + #' Nothing (`NULL`), invisibly + print = function(self) { + if (!is.null(self$mapping)) { + cat("mapping:", clist(self$mapping), "\n") + } + cat(snakeize(class(self$geom)[[1]]), ": ", clist(self$geom_params), "\n", + sep = "") + cat(snakeize(class(self$stat)[[1]]), ": ", clist(self$stat_params), "\n", + sep = "") + cat(snakeize(class(self$position)[[1]]), "\n") } ) +# Helpers ----------------------------------------------------------------- #' @export #' @rdname is_tests diff --git a/R/layout.R b/R/layout.R index e2a27339e5..88fa8185a3 100644 --- a/R/layout.R +++ b/R/layout.R @@ -12,27 +12,105 @@ create_layout <- function(facet, coord, layout = NULL) { ggproto(NULL, layout, facet = facet, coord = coord) } -#' @rdname ggplot2-ggproto +#' Layout +#' +#' @description +#' The Layout class is a chaperone class discouraged for extension. The class +#' fulfils the following tasks. The class houses the Coord and Facet classes +#' and tracks their stateful parameters. In addition, it manages the position +#' scales for each panel. It is responsible for keeping track of panel +#' specifications and matching pieces of the data to scales and parameters in +#' panel-wise manners. +#' +#' @details +#' The Layout class is only exported for extensions that re-implement a +#' `ggplot_build()` method for their specific class of plots. It is discouraged +#' to subclass the Layout class and for all purposes be considered an internal +#' structure. It has no user-facing constructor to put an small barrier in the +#' way. +#' +#' The class is used throughout `ggplot_build()`, with the notable exception of +#' the `render()` method, which is used in `ggplot_gtable()` instead. +#' #' @format NULL #' @usage NULL +#' @keywords internal +#' @family Layout components +#' @family chaperone classes #' @export -Layout <- ggproto("Layout", NULL, - # The coordinate system and its parameters +#' @examples +#' # Some dummy layout components +#' facet <- facet_null() +#' coord <- coord_cartesian() +#' +#' # Use in custom `ggplot_build()` methods +#' layout <- ggproto(NULL, Layout, facet = facet, coord = coord) +Layout <- ggproto( + "Layout", NULL, + + # Fields ---------------------------------------------------------------- + + #' @field coord,coord_params A [``][Coord] ggproto object and a list + #' of the coordinate system's parameters. Parameters get populated by the + #' `Coord$setup_params()` method. coord = NULL, coord_params = list(), # The faceting specification and its parameters + #' @field facet,facet_params A [``][Facet] ggproto object and a list + #' of the faceting specification's parameters. Parameters get populated by + #' the `Facet$setup_params()` method. facet = NULL, facet_params = list(), - # A data frame giving the layout of the data into panels + #' @field layout A data frame with a row for each panel. The data frame + #' contains integer columns `PANEL`, `SCALE_X`, `SCALE_Y`, `ROW` and `COL` + #' representing a panel ID, scale indices and placement locations. In addition, + #' the layout may contain faceting variables or other additional information. + #' This field gets populated by the `Facet$compute_layout()` method. layout = NULL, - # Per panel scales and params + #' @field panel_scales_x,panel_scales_y A list of `x` and `y` position scales + #' parallel to the layout field's `SCALE_X` and `SCALE_Y` levels respectively. + #' This fields gets populated by the `Facet$init_scales()` method. panel_scales_x = NULL, panel_scales_y = NULL, + + #' @field panel_params A named list of parameters per panel populated by the + #' `Coord$setup_panel_params()` method. Contains `` entries for + #' the `x` and `y` variables in addition to ranges and other information the + #' coordinate system might need to transform or render guides and grids. panel_params = NULL, + # Methods ----------------------------------------------------------------- + + ## ggplot_build ----------------------------------------------------------- + + #' @field setup + #' **Description** + #' + #' A function method for setting up the relevant information for the layout + #' of the plot. It populates the `facet_params`, `coord_params` and `layout` + #' fields and appends a `PANEL` variable to the layer data. + #' + #' **Usage** + #' ```r + #' Layout$setup(data, plot_data, plot_env) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames with layer data.} + #' \item{`plot_data`}{The data frame in the `data` field of the ggplot + #' object.} + #' \item{`plot_env`}{The environment in the `plot_env` field of the + #' ggplot object.} + #' } + #' + #' **Value** + #' + #' A list of data frames from the `data` argument with a `PANEL` variable + #' corresponding to rows in the `layout` field. + #' Also called for the side effects of populating fields. setup = function(self, data, plot_data = data_frame0(), plot_env = emptyenv()) { data <- c(list(plot_data), data) @@ -57,56 +135,28 @@ Layout <- ggproto("Layout", NULL, ) }, - # Assemble the facet fg & bg, the coord fg & bg, and the layers - # Returns a gtable - render = function(self, panels, data, theme, labels) { - panels <- self$facet$draw_panel_content( - panels, - self$layout, - self$panel_scales_x, - self$panel_scales_y, - self$panel_params, - self$coord, - data, - theme, - self$facet_params - ) - plot_table <- self$facet$draw_panels( - panels, - self$layout, - self$panel_scales_x, - self$panel_scales_y, - self$panel_params, - self$coord, - data, - theme, - self$facet_params - ) - plot_table <- self$facet$set_panel_size(plot_table, theme) - - # Draw individual labels, then add to gtable - labels <- self$coord$labels( - list( - x = self$resolve_label(self$panel_scales_x[[1]], labels), - y = self$resolve_label(self$panel_scales_y[[1]], labels) - ), - self$panel_params[[1]] - ) - labels <- self$render_labels(labels, theme) - self$facet$draw_labels( - plot_table, - self$layout, - self$panel_scales_x, - self$panel_scales_y, - self$panel_params, - self$coord, - data, - theme, - labels, - self$params - ) - }, - + #' @field train_position + #' **Description** + #' + #' A function method for training position scales and optionally initiating + #' them. Implementation is via the `Facet$train_scales()` and + #' `Facet$init_scales()` methods. + #' + #' **Usage** + #' ```r + #' Layout$train_position(data, x_scale, y_scale) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames with layer data.} + #' \item{`x_scale`,`y_scale`}{A single prototype position scale for the `x` + #' and `y` aesthetics respectively.} + #' } + #' + #' **Value** + #' + #' Nothing, this method is called for the side effect of training scales and + #' optionally populating the `panel_scales_x` and `panel_scales_y` fields. train_position = function(self, data, x_scale, y_scale) { # Initialise scales if needed, and possible. layout <- self$layout @@ -128,6 +178,26 @@ Layout <- ggproto("Layout", NULL, ) }, + #' @field map_position + #' **Description** + #' + #' A function method for mapping position aesthetics. For discrete scales this + #' converts discrete levels to a numeric representation, usually integers. For + #' continuous scales, this applies out-of-bounds handling. + #' + #' **Usage** + #' ```r + #' Layout$map_position(data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames with layer data.} + #' } + #' + #' **Value** + #' + #' A list of data frames per the `data` argument with mapped position + #' aesthetics. map_position = function(self, data) { layout <- self$layout @@ -160,6 +230,23 @@ Layout <- ggproto("Layout", NULL, }) }, + #' @field reset_scales + #' **Description** + #' + #' A function method for resetting scale ranges. After computing stats and + #' position adjustments, scales need to be reset and re-trained to have an + #' accurate measure of the data limits. This goes through the + #' `panel_scales_x` and `panel_scales_y` fields and invokes the + #' `Scale$reset()` method. + #' + #' **Usage** + #' ```r + #' Layout$reset_scales() + #' ``` + #' + #' **Value** + #' + #' Nothing, it is called for the side-effect of resetting scale ranges. reset_scales = function(self) { if (!self$facet$shrink) return() lapply(self$panel_scales_x, function(s) s$reset()) @@ -167,24 +254,22 @@ Layout <- ggproto("Layout", NULL, invisible() }, - finish_data = function(self, data) { - lapply(data, self$facet$finish_data, - layout = self$layout, - x_scales = self$panel_scales_x, - y_scales = self$panel_scales_y, - params = self$facet_params - ) - }, - - get_scales = function(self, i) { - this_panel <- self$layout[self$layout$PANEL == i, ] - - list( - x = self$panel_scales_x[[this_panel$SCALE_X]], - y = self$panel_scales_y[[this_panel$SCALE_Y]] - ) - }, - + #' @field setup_panel_params + #' **Description** + #' + #' A function method for executing `Coord$setup_panel_params()` once per panel + #' with the appropriate scales. For efficiency reasons, the setup is invoked + #' once per unique combination of `x` and `y` scale. + #' + #' **Usage** + #' ```r + #' Layout$setup_panel_params() + #' ``` + #' + #' **Value** + #' + #' Nothing, it is called for the side effect of populating the `panel_params` + #' field. setup_panel_params = function(self) { # Fudge for CoordFlip and CoordPolar - in place modification of # scales is not elegant, but it is pragmatic @@ -210,6 +295,32 @@ Layout <- ggproto("Layout", NULL, invisible() }, + #' @field setup_panel_guides + #' **Description** + #' + #' A function method for setting up and training the position guides (axes) + #' once per panel with the appropriate scales. For efficiency reasons, + #' the guides are setup once per unique combination of `x` and `y` scale. + #' It calls the `Coord$setup_panel_guides()` and `Coord$train_panel_guides()` + #' methods. + #' + #' **Usage** + #' ```r + #' Layout$setup_panel_guides(guides, layers) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`guides`}{A `` ggproto object from the `guides` field of + #' the ggplot object.} + #' \item{`layers`}{A list of layers from the `layers` field of the ggplot + #' object.} + #' } + #' + #' **Value** + #' + #' Nothing, it is called for the side effect of augmenting each entry of the + #' `panel_params` field with position guides. setup_panel_guides = function(self, guides, layers) { # Like in `setup_panel_params`, we only need to setup guides for unique @@ -234,6 +345,129 @@ Layout <- ggproto("Layout", NULL, invisible() }, + #' @field setup_panel_guides + #' **Description** + #' + #' A function method for setting up the `Facet$finish_data()` hook. + #' + #' **Usage** + #' ```r + #' Layout$finish_data(data) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`data`}{A list of data frames with layer data.} + #' } + #' + #' **Value** + #' + #' A list of data frames with layer data. + finish_data = function(self, data) { + lapply(data, self$facet$finish_data, + layout = self$layout, + x_scales = self$panel_scales_x, + y_scales = self$panel_scales_y, + params = self$facet_params + ) + }, + + ## ggplot_gtable ---------------------------------------------------------- + + #' @field render + #' **Description** + #' + #' A function method for drawing and assembling the core plot. Mostly it + #' delegates tasks to the specific Facet methods for drawing components. + #' + #' **Usage** + #' ```r + #' Layout$render(panels, data, theme, labels) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`panels`}{A list parallel to layers. Each element is another list + #' with grobs for each panel, generated by `Layer$draw_geom()`.} + #' \item{`data`}{A list of data frames with layer data.} + #' \item{`theme`}{A [complete theme][complete_theme()].} + #' \item{`labels`}{A list of labels from the `labels` field of the ggplot + #' object.} + #' } + #' + #' **Value** + #' + #' A gtable containing a plot with panels, axes, axis titles and strips. + render = function(self, panels, data, theme, labels) { + panels <- self$facet$draw_panel_content( + panels, + self$layout, + self$panel_scales_x, + self$panel_scales_y, + self$panel_params, + self$coord, + data, + theme, + self$facet_params + ) + plot_table <- self$facet$draw_panels( + panels, + self$layout, + self$panel_scales_x, + self$panel_scales_y, + self$panel_params, + self$coord, + data, + theme, + self$facet_params + ) + plot_table <- self$facet$set_panel_size(plot_table, theme) + + # Draw individual labels, then add to gtable + labels <- self$coord$labels( + list( + x = self$resolve_label(self$panel_scales_x[[1]], labels), + y = self$resolve_label(self$panel_scales_y[[1]], labels) + ), + self$panel_params[[1]] + ) + labels <- self$render_labels(labels, theme) + self$facet$draw_labels( + plot_table, + self$layout, + self$panel_scales_x, + self$panel_scales_y, + self$panel_params, + self$coord, + data, + theme, + labels, + self$params + ) + }, + + #' @field resolve_label + #' **Description** + #' + #' A function method for prying the axis titles from guides, scales or plot + #' labels. + #' + #' **Usage** + #' ```r + #' Layout$resolve_label(scale, labels) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`scale`}{A single scale from the `panel_scales_x` or + #' `panel_scales_y` fields.} + #' \item{`labels`}{A list of labels from the `labels` field of the ggplot + #' object.} + #' } + #' + #' **Value** + #' + #' A named list containing a two titles named `"primary"` and `"secondary"`. resolve_label = function(self, scale, labels) { aes <- scale$aesthetics[[1]] @@ -271,6 +505,30 @@ Layout <- ggproto("Layout", NULL, list(primary = primary, secondary = secondary)[order] }, + #' @field render_labels + #' **Description** + #' + #' A function method for drawing axis title grobs. The position guides + #' themselves do not typically render the axis title grobs as they are + #' orchestrated by the layout to draw one title even for multiple axes. + #' + #' **Usage** + #' ```r + #' Layout$render_labels(labels, theme) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`labels`}{A named list containing an `x` list and a `y` list. The + #' `x` and `y` lists have `primary` and `secondary` labels. It originates + #' from the `Coord$labels()` method.} + #' \item{`theme`}{A [complete theme][complete_theme()].} + #' } + #' + #' **Value** + #' + #' A list with the same structure and names as the `labels` argument, but with + #' grobs instead of text. render_labels = function(self, labels, theme) { label_grobs <- lapply(names(labels), function(label) { lapply(c(1, 2), function(i) { @@ -293,6 +551,38 @@ Layout <- ggproto("Layout", NULL, }) names(label_grobs) <- names(labels) label_grobs + }, + + ## Utilities ------------------------------------------------------------ + + #' @field get_scales + #' **Description** + #' + #' A function method for retrieving panel specific scales. It is called in + #' the `Stat$compute_layer()` and `Position$compute_layer()` methods. The + #' `Geom` uses the `panel_params` field instead of the raw scales. + #' + #' **Usage** + #' ```r + #' Layout$get_scales(i) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`i`}{A scalar integer panel index giving the panel for which to + #' retrieve scales} + #' } + #' + #' **Value** + #' + #' A named list of scales giving the `x` and `y` scale for the panel. + get_scales = function(self, i) { + this_panel <- self$layout[self$layout$PANEL == i, ] + + list( + x = self$panel_scales_x[[this_panel$SCALE_X]], + y = self$panel_scales_y[[this_panel$SCALE_Y]] + ) } ) diff --git a/R/position-.R b/R/position-.R index fb339b6660..845b095862 100644 --- a/R/position-.R +++ b/R/position-.R @@ -1,62 +1,197 @@ -#' @section Positions: +#' Positions #' +#' @description #' All `position_*()` functions (like `position_dodge()`) return a #' `Position*` object (like `PositionDodge`). The `Position*` #' object is responsible for adjusting the position of overlapping geoms. #' +#' @details #' The way that the `position_*` functions work is slightly different from #' the `geom_*` and `stat_*` functions, because a `position_*` #' function actually "instantiates" the `Position*` object by creating a -#' descendant, and returns that. +#' descendant, and returns that. The object is chaperoned by the [Layer] class. #' -#' Each of the `Position*` objects is a [ggproto()] object, -#' descended from the top-level `Position`, and each implements the -#' following methods: +#' To create a new type of Position object, you typically will want to override +#' one or more of the following: #' -#' - `compute_layer(self, data, params, panel)` is called once -#' per layer. `panel` is currently an internal data structure, so -#' this method should not be overridden. +#' * The `required_aes` and `default_aes` fields. +#' * The `setup_params()` and `setup_data()` methods. +#' * One of the `compute_layer()` or `compute_panel()` methods. #' -#' - `compute_panel(self, data, params, scales)` is called once per -#' panel and should return a modified data frame. +#' @section Convention: #' -#' `data` is a data frame containing the variables named according -#' to the aesthetics that they're mapped to. `scales` is a list -#' containing the `x` and `y` scales. There functions are called -#' before the facets are trained, so they are global scales, not local -#' to the individual panels. `params` contains the parameters returned by -#' `setup_params()`. -#' - `setup_params(data, params)`: called once for each layer. -#' Used to setup defaults that need to complete dataset, and to inform -#' the user of important choices. Should return list of parameters. -#' - `setup_data(data, params)`: called once for each layer, -#' after `setup_params()`. Should return modified `data`. -#' Default checks that required aesthetics are present. +#' The object name that a new class is assigned to is typically the same as the +#' class name. Position class name are in UpperCamelCase and start with the +#' `Position*` prefix, like `PositionNew`. #' -#' And the following fields -#' - `required_aes`: a character vector giving the aesthetics -#' that must be present for this position adjustment to work. +#' A constructor functions is usually paired with a Position class. The +#' constructor copies the position class and populates parameters. The +#' constructor function name is formatted by taking the Position class name and +#' formatting it with snake_case, so that `PositionNew` becomes `position_new()`. #' -#' See also the `r link_book("new positions section", "extensions#new-positions")` -#' -#' @rdname ggplot2-ggproto +#' @export #' @format NULL #' @usage NULL -#' @export -Position <- ggproto("Position", +#' @seealso The `r link_book("new positions section", "extensions#new-positions")` +#' @keywords internal +#' @family Layer components +#' @examples +#' # Extending the class +#' PositionRank <- ggproto( +#' "PositionRank", Position, +#' # Fields +#' required_aes = c("x", "y"), +#' # Methods +#' setup_params = function(self, data) list(width = self$width), +#' compute_panel = function(data, params, scales) { +#' width <- params$width +#' if (is.null(width)) { +#' width <- resolution(data$x, zero = FALSE, TRUE) * 0.4 +#' } +#' rank <- stats::ave(data$y, data$group, FUN = rank) +#' rank <- scales::rescale(rank, to = c(-width, width) / 2) +#' data$x <- data$x + rank +#' data +#' } +#' ) +#' +#' # Building a constructor +#' position_rank <- function(width = NULL) { +#' ggproto(NULL, PositionRank, width = width) +#' } +#' +#' # Use new position in plot +#' ggplot(mpg, aes(drv, displ)) + +#' geom_point(position = position_rank(width = 0.5)) +Position <- ggproto( + "Position", + + # Fields ------------------------------------------------------------------ + + #' @field required_aes A character vector naming aesthetics that are necessary + #' to compute the position adjustment. required_aes = character(), + #' @field default_aes A [mapping][aes()] of default values for aesthetics. default_aes = aes(), + # Methods ----------------------------------------------------------------- + + ## compute_position ------------------------------------------------------- + + #' @field use_defaults + #' **Description** + #' + #' A function method for completing the layer data by filling in default + #' position aesthetics that are not present. These can come from two sources: + #' either from the layer parameters as static, unmapped aesthetics or from + #' the `default_aes` field. + #' + #' **Usage** + #' ```r + #' Position$use_defaults(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame of the layer's data} + #' \item{`params`}{A list of fixed aesthetic parameters} + #' } + #' + #' **Value** + #' + #' A data frame with completed layer data + use_defaults = function(self, data, params = list()) { + + aes <- self$aesthetics() + defaults <- self$default_aes + + params <- params[intersect(names(params), aes)] + params <- params[setdiff(names(params), names(data))] + defaults <- defaults[setdiff(names(defaults), c(names(params), names(data)))] + + if ((length(params) + length(defaults)) < 1) { + return(data) + } + + new <- compact(lapply(defaults, eval_tidy, data = data)) + new[names(params)] <- params + check_aesthetics(new, nrow(data)) + + data[names(new)] <- new + data + + }, + + #' @field setup_params + #' **Description** + #' + #' A function method for modifying or checking the parameters based on the + #' data. The default method returns an empty list. + #' + #' **Usage** + #' ```r + #' Position$setup_params(data) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' } + #' + #' **Value** + #' + #' A list of parameters setup_params = function(self, data) { list() }, + #' @field setup_data + #' **Description** + #' + #' A function method for modifying or checking the data. The default method + #' checks for the presence of required aesthetics. + #' + #' **Usage** + #' ```r + #' Position$setup_data(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method} + #' } + #' + #' **Value** + #' + #' A data frame with layer data setup_data = function(self, data, params) { check_required_aesthetics(self$required_aes, names(data), snake_class(self)) data }, + #' @field compute_layer + #' **Description** + #' + #' A function method orchestrating the position adjust of the entire layer. + #' The default method splits the data and passes on adjustment tasks to the + #' panel-level `compute_panel()`. In addition, it finds the correct scales + #' in the layout object to pass to the panel computation. + #' + #' **Usage** + #' ```r + #' Position$compute_layer(data, params, layout) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method} + #' \item{`layout`}{A `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data compute_layer = function(self, data, params, layout) { dapply(data, "PANEL", function(data) { if (empty(data)) return(data_frame0()) @@ -66,38 +201,54 @@ Position <- ggproto("Position", }) }, + #' @field compute_panel + #' **Description** + #' + #' A function method executing the position adjustment at the panel level. + #' The default method is not implemented. + #' + #' **Usage** + #' ```r + #' Position$compute_panel(data, params, scales) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method} + #' \item{`scales`}{A list of pre-trained `x` and `y` scales. Note that the + #' position scales are not finalised at this point and reflect the initial + #' data range before computing stats.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data compute_panel = function(self, data, params, scales) { cli::cli_abort("Not implemented.") }, + ## Utilities --------------------------------------------------------------- + + #' @field aesthetics + #' **Description** + #' + #' A function method for listing out custom position aesthetics for this + #' position adjustment. + #' + #' **Usage** + #' ```r + #' Position$aesthetics() + #' ``` + #' **Value** + #' + #' A character vector of aesthetic names. aesthetics = function(self) { required_aes <- self$required_aes if (!is.null(required_aes)) { required_aes <- unlist(strsplit(self$required_aes, "|", fixed = TRUE)) } c(union(required_aes, names(self$default_aes))) - }, - - use_defaults = function(self, data, params = list()) { - - aes <- self$aesthetics() - defaults <- self$default_aes - - params <- params[intersect(names(params), aes)] - params <- params[setdiff(names(params), names(data))] - defaults <- defaults[setdiff(names(defaults), c(names(params), names(data)))] - - if ((length(params) + length(defaults)) < 1) { - return(data) - } - - new <- compact(lapply(defaults, eval_tidy, data = data)) - new[names(params)] <- params - check_aesthetics(new, nrow(data)) - - data[names(new)] <- new - data - } ) diff --git a/R/position-dodge.R b/R/position-dodge.R index bd816eecc9..e947950de7 100644 --- a/R/position-dodge.R +++ b/R/position-dodge.R @@ -97,7 +97,7 @@ position_dodge <- function(width = NULL, preserve = "total", orientation = "x", ) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-dodge2.R b/R/position-dodge2.R index a670ffc349..168bc6c287 100644 --- a/R/position-dodge2.R +++ b/R/position-dodge2.R @@ -12,7 +12,7 @@ position_dodge2 <- function(width = NULL, preserve = "total", ) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-identity.R b/R/position-identity.R index 6942cca418..baa50ee113 100644 --- a/R/position-identity.R +++ b/R/position-identity.R @@ -6,7 +6,7 @@ position_identity <- function() { PositionIdentity } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-jitter.R b/R/position-jitter.R index 1dfcc422eb..e4b98e175c 100644 --- a/R/position-jitter.R +++ b/R/position-jitter.R @@ -53,7 +53,7 @@ position_jitter <- function(width = NULL, height = NULL, seed = NA) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-jitterdodge.R b/R/position-jitterdodge.R index 10cb7c853f..35fed2cd72 100644 --- a/R/position-jitterdodge.R +++ b/R/position-jitterdodge.R @@ -36,7 +36,7 @@ position_jitterdodge <- function(jitter.width = NULL, jitter.height = 0, ) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-nudge.R b/R/position-nudge.R index cd28360d79..aa9b4d8e52 100644 --- a/R/position-nudge.R +++ b/R/position-nudge.R @@ -39,7 +39,7 @@ position_nudge <- function(x = NULL, y = NULL) { ) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/position-stack.R b/R/position-stack.R index 0e35f1191e..f2bc62ac34 100644 --- a/R/position-stack.R +++ b/R/position-stack.R @@ -140,7 +140,7 @@ position_fill <- function(vjust = 1, reverse = FALSE) { ggproto(NULL, PositionFill, vjust = vjust, reverse = reverse) } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export @@ -244,7 +244,7 @@ pos_stack <- function(df, width, vjust = 1, fill = FALSE) { } -#' @rdname ggplot2-ggproto +#' @rdname Position #' @format NULL #' @usage NULL #' @export diff --git a/R/scale-.R b/R/scale-.R index fa37bf5571..040bf7bacb 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -372,113 +372,94 @@ binned_scale <- function(aesthetics, scale_name = deprecated(), palette, name = #' @rdname is_tests is_scale <- function(x) inherits(x, "Scale") -#' @section Scales: -#' -#' All `scale_*` functions like [scale_x_continuous()] return a `Scale*` -#' object like `ScaleContinuous`. Each of the `Scale*` objects is a [ggproto()] -#' object, descended from the top-level `Scale`. -#' -#' Properties not documented in [continuous_scale()] or [discrete_scale()]: -#' -#' - `call` The call to [continuous_scale()] or [discrete_scale()] that constructed -#' the scale. -#' -#' - `range` One of `continuous_range()` or `discrete_range()`. -#' -#' -#' Methods: -#' -#' - `is_discrete()` Returns `TRUE` if the scale is a discrete scale -#' -#' - `is_empty()` Returns `TRUE` if the scale contains no information (i.e., -#' it has no information with which to calculate its `limits`). -#' -#' - `clone()` Returns a copy of the scale that can be trained -#' independently without affecting the original scale. -#' -#' - `transform()` Transforms a vector of values using `self$trans`. -#' This occurs before the `Stat` is calculated. -#' -#' - `train()` Update the `self$range` of observed (transformed) data values with -#' a vector of (possibly) new values. -#' -#' - `reset()` Reset the `self$range` of observed data values. For discrete -#' position scales, only the continuous range is reset. -#' -#' - `map()` Map transformed data values to some output value as -#' determined by `self$rescale()` and `self$palette` (except for position scales, -#' which do not use the default implementation of this method). The output corresponds -#' to the transformed data value in aesthetic space (e.g., a color, line width, or size). -#' -#' - `rescale()` Rescale transformed data to the range 0, 1. This is most useful for -#' position scales. For continuous scales, `rescale()` uses the `rescaler` that -#' was provided to the constructor. `rescale()` does not apply `self$oob()` to -#' its input, which means that discrete values outside `limits` will be `NA`, and -#' values that are outside `range` will have values less than 0 or greater than 1. -#' This allows guides more control over how out-of-bounds values are displayed. -#' -#' - `transform_df()`, `train_df()`, `map_df()` These `_df` variants -#' accept a data frame, and apply the `transform`, `train`, and `map` methods -#' (respectively) to the columns whose names are in `self$aesthetics`. -#' -#' - `get_limits()` Calculates the final scale limits in transformed data space -#' based on the combination of `self$limits` and/or the range of observed values -#' (`self$range`). +# Scale ------------------------------------------------------------------- + +#' Scales #' -#' - `get_breaks()` Calculates the final scale breaks in transformed data space -#' based on on the combination of `self$breaks`, `self$trans$breaks()` (for -#' continuous scales), and `limits`. Breaks outside of `limits` are assigned -#' a value of `NA` (continuous scales) or dropped (discrete scales). +#' @description +#' All `scale_*()` functions (like `scale_fill_continuous()`) return a `Scale*` +#' object. The main purpose of these objects is to translate data values to +#' aesthetic values and populating breaks and labels. #' -#' - `get_labels()` Calculates labels for a given set of (transformed) `breaks` -#' based on the combination of `self$labels` and `breaks`. +#' @details +#' All `scale_*` functions like [scale_x_continuous()] return a `Scale*` object +#' like `ScaleContinuous`. Each of the `Scale*` objects is a [ggproto()] object +#' descended from the top-level `Scale`. #' -#' - `get_breaks_minor()` For continuous scales, calculates the final scale minor breaks -#' in transformed data space based on the rescaled `breaks`, the value of `self$minor_breaks`, -#' and the value of `self$trans$minor_breaks()`. Discrete scales always return `NULL`. +#' Scales generally need to track three types of spaces: +#' 1. Data space. These are values as they are evaluated from the plot +#' or layer mapping, prior to any transformation. +#' 2. Transformed space. This is the space after original data has been +#' transformed. Effectively, scales internally operate in transformed space +#' in that ranges and breaks get passed around in this space. Discrete scales +#' don't do transformations, so for these scales, transformed space is the +#' same as untransformed space. +#' 3. Aesthetic space. Graphic values that are mapped from the transformed +#' space. This is dependent on the `palette` field for most scales and on the +#' `rescaler` field for continuous position scales. #' -#' - `get_transformation()` Returns the scale's transformation object. +#' The user is expected to give any vector-based `minor_breaks`, `breaks` or +#' `limits` in data space. When `breaks`, `limits` or `labels` is a function, +#' its input is expected to be in data space. #' -#' - `make_title()` Hook to modify the title that is calculated during guide construction -#' (for non-position scales) or when the `Layout` calculates the x and y labels -#' (position scales). +#' Before you attempt to create a new `Scale*` class of your own, it is +#' recommended to think through whether your aims cannot be implemented with +#' one of the existing classes. Making a wrapper for the `continuous_scale()`, +#' `discrete_scale()` and `binned_scale()` should cover many cases, and should +#' be considered prior to commiting to build a `Scale*` extension. #' -#' These methods are only valid for position (x and y) scales: +#' For example, if you aim to develop a scale for a new data type, it should +#' generally be possible to create a new [transformation][scales::new_transform] +#' instead. One reason to implement your own `Scale*` class is to accommodate +#' a data type does not lend itself for continuous or discrete range training. #' -#' - `dimension()` For continuous scales, the dimension is the same concept as the limits. -#' For discrete scales, `dimension()` returns a continuous range, where the limits -#' would be placed at integer positions. `dimension()` optionally expands -#' this range given an expansion of length 4 (see [expansion()]). +#' In such case, you can override the following: +#' * The `range` field. +#' * The `transform`, `train` and `map` methods. +#' * The `get_limits()`, `get_breaks()` and `get_labels()` methods. #' -#' - `break_info()` Returns a `list()` with calculated values needed for the `Coord` -#' to transform values in transformed data space. Axis and grid guides also use -#' these values to draw guides. This is called with -#' a (usually expanded) continuous range, such as that returned by `self$dimension()` -#' (even for discrete scales). The list has components `major_source` -#' (`self$get_breaks()` for continuous scales, or `seq_along(self$get_breaks())` -#' for discrete scales), `major` (the rescaled value of `major_source`, ignoring -#' `self$rescaler`), `minor` (the rescaled value of `minor_source`, ignoring -#' `self$rescaler`), `range` (the range that was passed in to `break_info()`), -#' `labels` (the label values, one for each element in `breaks`). +#' @section Conventions: #' -#' - `axis_order()` One of `c("primary", "secondary")` or `c("secondary", "primary")` +#' The object name that a new class is assigned to is typically the same as the +#' class name. Scale class names are in UpperCamelCase and start with the +#' `Scale*` prefix, like `ScaleNew`. #' -#' - `make_sec_title()` Hook to modify the title for the second axis that is calculated -#' when the `Layout` calculates the x and y labels. +#' In scales, there is a difference between user-facing and developer-facing +#' constructors. Developer facing constructors have the shape +#' `{foundation}_scale()`, like `discrete_scale()` corresponding to +#' `ScaleDiscrete`. User-facing constructors have the `scale_{aesthetic}_{type}` +#' as name. If you implement a new `Scale*` class, you like want both these +#' types of constructor. #' -#' @rdname ggplot2-ggproto +#' @export #' @format NULL #' @usage NULL -#' @export +#' @keywords internal +#' @examples +#' # TODO: find easy to digest example +#' NULL Scale <- ggproto("Scale", NULL, + ## Fields ------------------------------------------------------------------ + + #' @field call A [call][call()] object with the user-facing constructor + #' function, for use in error messaging. This field is populated by scale + #' constructors. call = NULL, - aesthetics = aes(), - palette = function() { - cli::cli_abort("Not implemented.") - }, + #' @field range A [`Range`][scales::Range] class object, like + #' `scales::ContinuousRange` or `scales::DiscreteRange`. These are 'trained' + #' to keep track of the data range (continuous) or data levels (discrete). + #' Continuous ranges are tracked in transformed space. range = Range$new(), + + #' @field aesthetics,palette,name,breaks,labels,limits,name,guide,position,na.value,expand + #' Fields populated by the scale constructor that can take on the same values + #' as described in e.g. [`?continuous_scale`][continuous_scale]. + #' Note that `limits` is expected in transformed space. + aesthetics = character(), + palette = function() cli::cli_abort("Not implemented."), + limits = NULL, na.value = NA, expand = waiver(), @@ -489,33 +470,34 @@ Scale <- ggproto("Scale", NULL, guide = "legend", position = "left", - - is_discrete = function() { - cli::cli_abort("Not implemented.") - }, - - train_df = function(self, df) { - if (empty(df)) return() - - aesthetics <- intersect(self$aesthetics, names(df)) - for (aesthetic in aesthetics) { - self$train(df[[aesthetic]]) - } - invisible() - }, - - train = function(self, x) { - cli::cli_abort("Not implemented.", call = self$call) - }, - - reset = function(self) { - self$range$reset() - }, - - is_empty = function(self) { - is.null(self$range$range) && is.null(self$limits) - }, - + ## Methods ----------------------------------------------------------------- + + ### Transformation ---------------------------------------------------------- + + #' @field transform_df,transform + #' **Description** + #' + #' A function method for orchestrating the transformation of aesthetics in a + #' data frame. Data transformation occurs before stats are computed. + #' The `transform_df()` method ensures the `transform()` method is applied + #' to the correct columns. + #' + #' **Usage** + #' ```r + #' Scale$transform_df(df) + #' Scale$transform(x) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`df`}{A data frame with the layer's data.} + #' \item{`x`}{A vector of the relevant aesthetic.} + #' } + #' + #' **Value** + #' + #' For `transform()` a vector of transformed values. + #' For `transform_df()`, a named list with transformed values for each + #' transformed aesthetic. transform_df = function(self, df) { if (empty(df)) { return() @@ -533,6 +515,74 @@ Scale <- ggproto("Scale", NULL, cli::cli_abort("Not implemented.", call = self$call) }, + ### Training ---------------------------------------------------------------- + + #' @field train_df,train + #' **Description** + #' + #' A function method for orchestrating scale training for keeping track of + #' the data range or levels. The `train_df()` method ensures the `train()` + #' method is applied to the correct columns. + #' + #' **Usage** + #' ```r + #' Scale$train_df(df) + #' Scale$train(x) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`df`}{A data frame with the layer's data.} + #' \item{`x`}{A vector of the relevant aesthetic.} + #' } + #' + #' **Value** + #' + #' Nothing, these are called for their side effect of updating the `range` + #' field. + train_df = function(self, df) { + if (empty(df)) return() + + aesthetics <- intersect(self$aesthetics, names(df)) + for (aesthetic in aesthetics) { + self$train(df[[aesthetic]]) + } + invisible() + }, + + train = function(self, x) { + cli::cli_abort("Not implemented.", call = self$call) + }, + + ### Mapping ----------------------------------------------------------------- + + #' @field map_df,map + #' **Description** + #' + #' A function method for orchestrating the mapping of data values to + #' aesthetics. The `map_df()` method ensures the `map()` method is applied + #' to the correct columns. When the scale uses a `palette()` function, it is + #' applied in the `map()` method. + #' + #' **Usage** + #' ```r + #' Scale$map_df(df, i) + #' Scale$map(x, limits) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`df`}{A data frame with the layer's data.} + #' \item{`i`}{An integer vector giving an index to map a subset of data. + #' The default, `NULL`, will map all rows.} + #' \item{`x`}{A vector of the relevant aesthetic.} + #' \item{`limits`}{A vector of the relevant aesthetic, usually via + #' the `get_limits()` method.} + #' } + #' + #' **Value** + #' + #' For `map()` a vector of mapped values in aesthetics space. + #' For `map_df()`, a named list with mapped values for each + #' aesthetic. map_df = function(self, df, i = NULL) { if (empty(df)) { return() @@ -556,10 +606,50 @@ Scale <- ggproto("Scale", NULL, cli::cli_abort("Not implemented.", call = self$call) }, + #' @field recale + #' **Description** + #' + #' A function method for applying the recale function in the `rescaler` field. + #' It is used during the continuous `map()` and `Coord$transform()` methods + #' to ensure values are in the 0-1 range. + #' + #' **Usage** + #' ```r + #' Scale$rescale(x, limits, range) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`x`}{A vector of values to rescale. Can contain out-of-bounds + #' or missing values depending on the `map()` method.} + #' \item{`limits`}{A length two vector giving the limits of the relevant + #' aesthetic, usually via the `get_limits()` method.} + #' \item{`range`}{A length two vector giving the range that should coincide + #' with the 0-1 points. For most purpuses, this should be the same as the + #' `limits` argument.} + #' } + #' + #' **Value** + #' + #' A vector of values between 0 and 1 for in-bounds values of `x`. rescale = function(self, x, limits = self$get_limits(), range = self$dimension()) { cli::cli_abort("Not implemented.", call = self$call) }, + ### Getters ----------------------------------------------------------------- + + #' @field get_limits + #' **Description** + #' + #' A function method for resolving user input and getting the scale limits. + #' + #' **Usage** + #' ```r + #' Scale$get_limits() + #' ``` + #' + #' **Value** + #' + #' The scale limits, without any expansion applied, in transformed space. get_limits = function(self) { if (self$is_empty()) { return(c(0, 1)) @@ -574,46 +664,195 @@ Scale <- ggproto("Scale", NULL, } }, + #' @field dimension + #' **Description** + #' + #' A function method for getting a continuous representation of the limits of + #' position scales. For continuous scales, the dimension is the same concept + #' as the limits. For discrete scales the dimension is the continuous range + #' occupied by the mapped breaks, which by default take integer positions. + #' + #' **Usage** + #' ```r + #' Scale$dimension(expand, limits) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`expand`}{A length 4 vector giving scale [expansion][expansion()]. + #' This is optional and defaults to no expansion.} + #' \item{`limits`}{A vector of the relevant aesthetic, usually via + #' the `get_limits()` method.} + #' } + #' + #' **Value** + #' + #' A numeric vector of length 2 dimension = function(self, expand = expansion(0, 0), limits = self$get_limits()) { cli::cli_abort("Not implemented.", call = self$call) }, + #' @field get_breaks,get_breaks_minor + #' **Description** + #' + #' A function method for resolving user input and getting the scale breaks + #' or minor breaks. Note that these may return out-of-bounds values for the + #' purpose of coordinating with the `get_labels()` method. + #' + #' **Usage** + #' ```r + #' Scale$get_breaks(limits) + #' Scale$get_breaks_minor(n, b, limits) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`limits`}{A vector of the relevant aesthetic, usually via + #' the `get_limits()` method.} + #' \item{`n`}{An integer setting the desired number of minor breaks per + #' major break. Note that the resulting minor breaks may coincide with + #' major breaks.} + #' \item{`b`}{A vector of mapped major breaks from the `get_breaks()` + #' method.} + #' } + #' + #' **Value** + #' + #' A vector of breaks in transformed space. get_breaks = function(self, limits = self$get_limits()) { cli::cli_abort("Not implemented.", call = self$call) }, - break_positions = function(self, range = self$get_limits()) { - self$map(self$get_breaks(range)) - }, - get_breaks_minor = function(self, n = 2, b = self$break_positions(), limits = self$get_limits()) { cli::cli_abort("Not implemented.", call = self$call) }, + #' @field get_labels + #' **Description** + #' + #' A function method for resolving user input and getting the scale labels for + #' a set of breaks. + #' + #' **Usage** + #' ```r + #' Scale$get_labels(breaks) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`breaks`}{A vector of unmapped major breaks from the `get_breaks()` + #' method, in transformed space.} + #' } + #' + #' **Value** + #' + #' A vector of labels of the same length as `breaks`. get_labels = function(self, breaks = self$get_breaks()) { cli::cli_abort("Not implemented.", call = self$call) }, + #' @field get_transformation + #' **Description** + #' + #' A helper method to access the scale's transformation object. + #' + #' **Usage** + #' ```r + #' Scale$get_transformation() + #' ``` + #' + #' **Value** + #' + #' A [transform][scales::new_transform] object. get_transformation = function(self) { self$trans }, - clone = function(self) { - cli::cli_abort("Not implemented.", call = self$call) - }, - + #' @field break_info + #' **Description** + #' + #' A function method for getting all break related information for position + #' scales. It is in use by coords that do not use the modern Guide system + #' and secondary axes. + #' + #' **Usage** + #' ```r + #' Scale$break_info(range) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`range`}{A vector of the relevant aesthetic.} + #' } + #' + #' **Value** + #' + #' A named list with the following structure: + #' * `range` a length 2 vector giving continuous range + #' * `labels` a character or expression vector of the same length as major breaks. + #' * `major` a numeric vector with mapped numeric values for major breaks. + #' * `major_source` a numeric vector with (transformed) data values for major breaks. + #' * `minor` a numeric vector with mapped numeric values for minor breaks. + #' * `minor_source` a numeric vector with (transformed) data values for minor breaks. break_info = function(self, range = NULL) { cli::cli_abort("Not implemented.", call = self$call) }, - axis_order = function(self) { - ord <- c("primary", "secondary") - if (self$position %in% c("right", "bottom")) { - ord <- rev(ord) - } - ord + #' @field break_position + #' **Description** + #' + #' A function method for getting mapped break positions. It is in use as a + #' default value in `get_breaks_minor()`, but is otherwise vestigial. + #' + #' **Usage** + #' ```r + #' Scale$break_info(range) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`range`}{A vector of the relevant aesthetic.} + #' } + #' + #' **Value** + #' + #' A vector with mapped break positions + break_positions = function(self, range = self$get_limits()) { + # TODO: should just retire this method? + self$map(self$get_breaks(range)) }, + ### Titles ---------------------------------------------------------------- + + #' @field make_title,make_sec_title + #' **Description** + #' + #' A function method for picking the title to use. This is usually called in + #' the `Guide$extract_params()` or `Layout$resolve_label()` methods. + #' The hierarchy of titles goes from guide (highest priority), to scale, to + #' labs (lowest priority). + #' When the guide or scale title are functions, they're applied to the next + #' in line. The `make_sec_title()` method by default re-uses the primary + #' `make_title()` method and only applies to position aesthetics. + #' + #' **Usage** + #' ```r + #' Scale$make_title(guide_title, scale_title, label_title) + #' Scale$make_sec_title(...) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`guide_title`}{The `title` parameter coming from a guide.} + #' \item{`scale_title`}{The `name` field of the Scale.} + #' \item{`label_title`}{The relevant entry in the `plot$labels` field.} + #' \item{`...`}{By default, arguments forwarded to the `make_title()` + #' method} + #' } + #' + #' **Value** + #' + #' A scalar character or expression title make_title = function(self, guide_title = waiver(), scale_title = waiver(), label_title = waiver()) { title <- label_title scale_title <- allow_lambda(scale_title) @@ -633,26 +872,112 @@ Scale <- ggproto("Scale", NULL, make_sec_title = function(self, ...) { self$make_title(...) - } -) + }, -check_breaks_labels <- function(breaks, labels, call = NULL) { - if (is.null(breaks) || is.null(labels)) { - return(invisible()) - } + #' @field axis_order + #' **Description** + #' + #' A function method for setting the order of axes titles used to coordinate + #' with `Facet$draw_labels()`. + #' + #' **Usage** + #' ```r + #' Scale$axis_order() + #' ``` + #' + #' **Value** + #' + #' Either `c("primary", "secondary")` or `c("secondary", "primary")`. + axis_order = function(self) { + # TODO: it feels like this method shouldn't be needed. Can we replace it? + ord <- c("primary", "secondary") + if (self$position %in% c("right", "bottom")) { + ord <- rev(ord) + } + ord + }, - bad_labels <- is.atomic(breaks) && is.atomic(labels) && - length(breaks) != length(labels) - if (bad_labels) { - cli::cli_abort( - "{.arg breaks} and {.arg labels} must have the same length.", - call = call - ) + ### Utilities --------------------------------------------------------------- + + #' @field clone + #' **Description** + #' + #' A function method for making an untrained copy of the scale. Due to + #' reference semantics of ggproto objects, in contrast to copy-on-modify + #' semantics, scales need to be cloned at the start of plot building. + #' The cloned scale can be trained independently of the original. + #' + #' **Usage** + #' ```r + #' Scale$clone() + #' ``` + #' + #' **Value** + #' + #' A Scale object. + clone = function(self) { + cli::cli_abort("Not implemented.", call = self$call) + }, + + #' @field reset + #' **Description** + #' + #' A function method for to reset the `range` field, effectively 'untraining' + #' the scale. This is used in the `Layout$reset_scales()` method, so that + #' scales can be re-trained on data with final position aesthetics. + #' For discrete scales, only the continuous range (`range_c`) is reset. + #' + #' **Usage** + #' ```r + #' Scale$clone() + #' ``` + #' + #' **Value** + #' + #' None, called for the side-effect of resetting the range. + reset = function(self) { + self$range$reset() + }, + + #' @field is_empty + #' **Description** + #' + #' A function method for determining whether a scale is empty, i.e. when no + #' information with which to calculate limits. + #' + #' **Usage** + #' ```r + #' Scale$is_empty() + #' ``` + #' + #' **Value** + #' + #' A scalar boolean value. + is_empty = function(self) { + is.null(self$range$range) && is.null(self$limits) + }, + + #' @field is_empty + #' **Description** + #' + #' A function method for determining whether a scale is discrete. + #' + #' **Usage** + #' ```r + #' Scale$is_discrete() + #' ``` + #' + #' **Value** + #' + #' A scalar boolean value. + is_discrete = function() { + cli::cli_abort("Not implemented.") } +) - invisible() -} +# ScaleContinuous --------------------------------------------------------- +# This needs to be defined prior to the Scale subclasses. default_transform <- function(self, x) { transformation <- self$get_transformation() new_x <- transformation$transform(x) @@ -660,12 +985,7 @@ default_transform <- function(self, x) { new_x } -has_default_transform <- function(scale) { - transform_method <- environment(scale$transform)$f - identical(default_transform, transform_method) || identical(identity, transform_method) -} - -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export @@ -947,8 +1267,9 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale, } ) +# ScaleDiscrete ----------------------------------------------------------- -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export @@ -1178,7 +1499,9 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale, } ) -#' @rdname ggplot2-ggproto +# ScaleBinned ------------------------------------------------------------- + +#' @rdname Scale #' @format NULL #' @usage NULL #' @export @@ -1420,6 +1743,30 @@ ScaleBinned <- ggproto("ScaleBinned", Scale, } ) +# Helpers ----------------------------------------------------------------- + +check_breaks_labels <- function(breaks, labels, call = NULL) { + if (is.null(breaks) || is.null(labels)) { + return(invisible()) + } + + bad_labels <- is.atomic(breaks) && is.atomic(labels) && + length(breaks) != length(labels) + if (bad_labels) { + cli::cli_abort( + "{.arg breaks} and {.arg labels} must have the same length.", + call = call + ) + } + + invisible() +} + +has_default_transform <- function(scale) { + transform_method <- environment(scale$transform)$f + identical(default_transform, transform_method) || identical(identity, transform_method) +} + # In place modification of a scale to change the primary axis scale_flip_position <- function(scale) { scale$position <- opposite_position(scale$position) diff --git a/R/scale-binned.R b/R/scale-binned.R index 4db4f1a916..5f9ec913e3 100644 --- a/R/scale-binned.R +++ b/R/scale-binned.R @@ -61,7 +61,7 @@ scale_y_binned <- function(name = waiver(), n.breaks = 10, nice.breaks = TRUE, ) } -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export diff --git a/R/scale-continuous.R b/R/scale-continuous.R index 9b0c8ec82c..14d7c8e725 100644 --- a/R/scale-continuous.R +++ b/R/scale-continuous.R @@ -131,7 +131,7 @@ scale_y_continuous <- function(name = waiver(), breaks = waiver(), } -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export diff --git a/R/scale-date.R b/R/scale-date.R index ac0c314e18..249bf891f8 100644 --- a/R/scale-date.R +++ b/R/scale-date.R @@ -374,7 +374,7 @@ datetime_scale <- function(aesthetics, transform, trans = deprecated(), sc } -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export @@ -427,7 +427,7 @@ ScaleContinuousDatetime <- ggproto("ScaleContinuousDatetime", ScaleContinuous, ) -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export diff --git a/R/scale-discrete-.R b/R/scale-discrete-.R index adaebb96d7..ce1dd893d5 100644 --- a/R/scale-discrete-.R +++ b/R/scale-discrete-.R @@ -111,7 +111,7 @@ scale_y_discrete <- function(name = waiver(), ..., palette = seq_len, # mapping, but makes it possible to place objects at non-integer positions, # as is necessary for jittering etc. -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export diff --git a/R/scale-identity.R b/R/scale-identity.R index 3ab2de5c43..29023868c6 100644 --- a/R/scale-identity.R +++ b/R/scale-identity.R @@ -170,7 +170,7 @@ scale_continuous_identity <- function(aesthetics, name = waiver(), ..., ) } -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export @@ -191,7 +191,7 @@ ScaleDiscreteIdentity <- ggproto("ScaleDiscreteIdentity", ScaleDiscrete, ) -#' @rdname ggplot2-ggproto +#' @rdname Scale #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-.R b/R/stat-.R index 186341eb78..cb4fc1a983 100644 --- a/R/stat-.R +++ b/R/stat-.R @@ -1,86 +1,190 @@ -#' @section Stats: +#' Stats #' +#' @description #' All `stat_*()` functions (like `stat_bin()`) return a layer that #' contains a `Stat*` object (like `StatBin`). The `Stat*` #' object is responsible for rendering the data in the plot. #' +#' @details #' Each of the `Stat*` objects is a [ggproto()] object, descended #' from the top-level `Stat`, and each implements various methods and -#' fields. To create a new type of Stat object, you typically will want to +#' fields. The object and its parameters are chaperoned by the [Layer] class. +#' +#' To create a new type of Stat object, you typically will want to #' override one or more of the following: #' -#' - One of : -#' `compute_layer(self, data, scales, ...)`, -#' `compute_panel(self, data, scales, ...)`, or -#' `compute_group(self, data, scales, ...)`. +#' * The `required_aes` and `default_aes` fields. +#' * One of the `compute_layer()`, `compute_panel()` or `compute_group()` +#' functions. Typically it best to implement `compute_group()` and use the +#' higher-up methods when there are substantial performance improvements to +#' be gained. +#' * The `finish_layer()` method #' -#' `compute_layer()` is called once per layer, `compute_panel()` -#' is called once per panel, and `compute_group()` is called once per -#' group. All must return a data frame. +#' @section Conventions: #' -#' It's usually best to start by overriding `compute_group`: if -#' you find substantial performance optimisations, override higher up. -#' You'll need to read the source code of the default methods to see -#' what else you should be doing. +#' The object name that a new class is assigned to is typically the same as the +#' class name. Stat class names are in UpperCamelCase and start with the `Stat*` +#' prefix, like `StatNew`. #' -#' `data` is a data frame containing the variables named according -#' to the aesthetics that they're mapped to. `scales` is a list -#' containing the `x` and `y` scales. There functions are called -#' before the facets are trained, so they are global scales, not local -#' to the individual panels.`...` contains the parameters returned by -#' `setup_params()`. -#' - `finish_layer(data, params)`: called once for each layer. Used -#' to modify the data after scales has been applied, but before the data is -#' handed of to the geom for rendering. The default is to not modify the -#' data. Use this hook if the stat needs access to the actual aesthetic -#' values rather than the values that are mapped to the aesthetic. -#' - `setup_params(data, params)`: called once for each layer. -#' Used to setup defaults that need to complete dataset, and to inform -#' the user of important choices. Should return list of parameters. -#' - `setup_data(data, params)`: called once for each layer, -#' after `setup_params()`. Should return modified `data`. -#' Default methods removes all rows containing a missing value in -#' required aesthetics (with a warning if `!na.rm`). -#' - `required_aes`: A character vector of aesthetics needed to -#' render the geom. -#' - `default_aes`: A list (generated by [aes()] of -#' default values for aesthetics. -#' - `dropped_aes` is a vecor of aesthetic names that are safe to drop after -#' statistical transformation. A classic example is the `weight` aesthetic -#' that is consumed during computation of the stat. +#' A constructor function is usually paired wih a Stat class. The constructor +#' wraps a call to `layer()`, where e.g. `layer(stat = StatNew)`. The +#' constructor function name is formatted by taking the Stat class name and +#' formatting it with snake_case, so that `StatNew` becomes `stat_new()`. #' -#' See also the `r link_book("new stats section", "extensions#sec-new-stats")` -#' @rdname ggplot2-ggproto +#' @export +#' @family Layer components #' @format NULL #' @usage NULL -#' @export -Stat <- ggproto("Stat", - # Should the values produced by the statistic also be transformed - # in the second pass when recently added statistics are trained to - # the scales - retransform = TRUE, +#' @keywords internal +#' @seealso The `r link_book("new stats section", "extensions#sec-new-stats")`. +#' @seealso Run `vignette("extending-ggplot2")`, in particular the "Creating a +#' new stat" section. +#' @examples +#' # Extending the class +#' StatKmeans <- ggproto( +#' "StatKmeans", Stat, +#' # Fields +#' required_aes = c("x", "y"), +#' # You can relate computed variables to aesthetics using `after_stat()` +#' # in defaults +#' default_aes = aes(colour = after_stat(cluster)), +#' # Methods +#' compute_panel = function(data, scales, k = 2L) { +#' km <- kmeans(cbind(scale(data$x), scale(data$y)), centers = k) +#' data$cluster <- factor(km$cluster) +#' data +#' } +#' ) +#' +#' # Building a constructor +#' stat_kmeans <- function(mapping = NULL, data = NULL, geom = "point", +#' position = "identity", ..., k = 2L, na.rm = FALSE, +#' show.legend = NA, inherit.aes = TRUE) { +#' layer( +#' mapping = mapping, data = data, +#' geom = geom, stat = StatKmeans, position = position, +#' show.legend = show.legend, inherit.aes = inherit.aes, +#' params = list(na.rm = na.rm, k = k, ...) +#' ) +#' } +#' +#' # Use new stat in plot +#' ggplot(mpg, aes(displ, hwy)) + +#' stat_kmeans(k = 3) +Stat <- ggproto( + "Stat", - default_aes = aes(), + # Fields ------------------------------------------------------------------ + #' @field required_aes A character vector naming aesthetics that are necessary + #' to compute the stat. required_aes = character(), + #' @field non_missing_aes A character vector naming aesthetics that will cause + #' removal if they have missing values. non_missing_aes = character(), - # Any aesthetics that are dropped from the data frame during the - # statistical transformation should be listed here to suppress a - # warning about dropped aesthetics + #' @field optional_aes A character vector naming aesthetics that will be + #' accepted by `layer()`, but are not required or dscribed in the `default_aes` + #' field. + optional_aes = character(), + + #' @field default_aes A [mapping][aes()] of default values for aesthetics. + #' Aesthetics can be set to `NULL` to be included as optional aesthetic. + default_aes = aes(), + + #' @field dropped_aes A character vector naming aesthetics that can be dropped + #' from the data without warning. Typically used for aesthetics that are + #' 'consumed' during computation like `"weight"`. dropped_aes = character(), - optional_aes = character(), + #' @field extra_params A character vector of parameter names in addition to + #' those imputed from the `compute_panel()` or `compute_groups()` methods. + #' This field can be set to include parameters for `setup_data()` methods. + #' By default, this only contains `"na.rm"`. + extra_params = "na.rm", + #' @field retransform A scalar boolean: should the values produced by the + #' statistic also be transformed in the second pass when recently added + #' statistics are trained to the scales + retransform = TRUE, + + # Methods ----------------------------------------------------------------- + + ## compute_statistic ------------------------------------------------------ + + #' @field setup_params + #' **Description** + #' + #' A function method for modifying or checking the parameters based on the + #' data. The default method returns the parameters unaltered. + #' + #' **Usage** + #' ```r + #' Stat$setup_params(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of current parameters} + #' } + #' + #' **Value** + #' + #' A list of parameters setup_params = function(data, params) { params }, + #' @field setup_data + #' **Description** + #' + #' A function method for modifying or checking the data. The default method + #' returns data unaltered. + #' + #' **Usage** + #' ```r + #' Stat$setup_data(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters coming from the `setup_params()` + #' method} + #' } + #' + #' **Value** + #' + #' A data frame with layer data setup_data = function(data, params) { data }, + #' @field compute_layer + #' **Description** + #' + #' A function method for orchestrating the computation of the statistic. The + #' default method splits the data and passes on computation tasks to the + #' panel-level `compute_panel()` method. In addition, the default method + #' handles missing values by removing rows that have missing values for the + #' aesthetics listed in the `required_aes` and `non_missing_aes` fields. It is + #' not recommended to use this method as an extension point. + #' + #' **Usage** + #' ```r + #' Stat$compute_layer(data, params, layout) + #' ``` + #' + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`params`}{A list of parameters} + #' \item{`layout`}{A pre-trained `` ggproto object.} + #' } + #' + #' **Value** + #' + #' A data frame with computed data compute_layer = function(self, data, params, layout) { check_required_aesthetics( self$required_aes, @@ -88,6 +192,7 @@ Stat <- ggproto("Stat", snake_class(self) ) + # TODO: for symmetry with Geom, should Stat have separate `handle_na()` method? # Make sure required_aes consists of the used set of aesthetics in case of # "|" notation in self$required_aes required_aes <- intersect( @@ -117,6 +222,34 @@ Stat <- ggproto("Stat", }) }, + #' @field compute_panel,compute_group + #' **Description** + #' + #' A function method orchestrating the computation of statistics for a single + #' panel or group. The default `compute_panel()` method splits the data into + #' groups, and passes on computation tasks to the `compute_group()` method. + #' In addition, `compute_panel()` is tasked with preserving aesthetics that + #' are constant within a group and preserving these if the computation loses + #' them. The default `compute_group()` is not implemented. + #' + #' **Usage** + #' ```r + #' Stat$compute_panel(data, scales, ...) + #' Stat$compute_group(data, scales, ...) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with the layer's data.} + #' \item{`scales`}{A list of pre-trained `x` and `y` scales. Note that the + #' position scales are not finalised at this point and reflect the initial + #' data range before computing stats.} + #' \item{`...`}{Reserved for extensions. By default, this passes parameters + #' to the `compute_group()` method.} + #' } + #' + #' **Value** + #' + #' A data frame with layer data compute_panel = function(self, data, scales, ...) { if (empty(data)) return(data_frame0()) @@ -186,13 +319,52 @@ Stat <- ggproto("Stat", cli::cli_abort("Not implemented.") }, + # finish_statistics ------------------------------------------------------- + + #' @field finish_layer + #' **Description** + #' + #' A function method acting as a hook to modify data after scales have been + #' applied, but before geoms have to render. The default is to pass the data + #' unaltered. This can be used as an extension point when actual aesthetic + #' values rather than values mapped to the aesthetic are needed. + #' + #' **Usage** + #' ```r + #' Stat$finish_layer(data, params) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`data`}{A data frame with layer data} + #' \item{`params`}{A list of parameters} + #' } + #' + #' **Value** + #' + #' A data frame with layer data finish_layer = function(self, data, params) { data }, - - # See discussion at Geom$parameters() - extra_params = "na.rm", + ## Utilities --------------------------------------------------------------- + + #' @field parameters + #' **Description** + #' + #' A function method for listing out all acceptable parameters for this stat. + #' + #' **Usage** + #' ```r + #' Stat$parameters(extra) + #' ``` + #' **Arguments** + #' \describe{ + #' \item{`extra`}{A boolean: whether to include the `extra_params` field.} + #' } + #' + #' **Value** + #' + #' A character vector of parameter names. parameters = function(self, extra = FALSE) { # Look first in compute_panel. If it contains ... then look in compute_group panel_args <- names(ggproto_formals(self$compute_panel)) @@ -208,6 +380,18 @@ Stat <- ggproto("Stat", args }, + #' @field aesthetics + #' **Description** + #' + #' A function method for listing out all acceptable aesthetics for this stat. + #' + #' **Usage** + #' ```r + #' Stat$aesthetics() + #' ``` + #' **Value** + #' + #' A character vector of aesthetic names. aesthetics = function(self) { if (is.null(self$required_aes)) { required_aes <- NULL diff --git a/R/stat-align.R b/R/stat-align.R index 3187ca28c0..d4aa50e8e3 100644 --- a/R/stat-align.R +++ b/R/stat-align.R @@ -23,7 +23,7 @@ stat_align <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-bin.R b/R/stat-bin.R index f65b54857b..5eaf025268 100644 --- a/R/stat-bin.R +++ b/R/stat-bin.R @@ -89,7 +89,7 @@ stat_bin <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-bin2d.R b/R/stat-bin2d.R index fe27a41162..c44bdb9780 100644 --- a/R/stat-bin2d.R +++ b/R/stat-bin2d.R @@ -54,7 +54,7 @@ stat_bin_2d <- function(mapping = NULL, data = NULL, #' @usage NULL stat_bin2d <- stat_bin_2d -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @include stat-summary-2d.R #' @format NULL #' @usage NULL diff --git a/R/stat-bindot.R b/R/stat-bindot.R index 66184a527c..5367a2d99a 100644 --- a/R/stat-bindot.R +++ b/R/stat-bindot.R @@ -1,4 +1,4 @@ -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-binhex.R b/R/stat-binhex.R index be5b61daf7..9724e2a3a9 100644 --- a/R/stat-binhex.R +++ b/R/stat-binhex.R @@ -44,7 +44,7 @@ stat_bin_hex <- function(mapping = NULL, data = NULL, #' @usage NULL stat_binhex <- stat_bin_hex -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-boxplot.R b/R/stat-boxplot.R index 46ce14879f..2404166d6b 100644 --- a/R/stat-boxplot.R +++ b/R/stat-boxplot.R @@ -41,8 +41,7 @@ stat_boxplot <- function(mapping = NULL, data = NULL, ) } - -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-connect.R b/R/stat-connect.R index 48a193fdf1..b805e9801d 100644 --- a/R/stat-connect.R +++ b/R/stat-connect.R @@ -58,7 +58,7 @@ stat_connect <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-contour.R b/R/stat-contour.R index 0602ed3899..a4eb4ecd5a 100644 --- a/R/stat-contour.R +++ b/R/stat-contour.R @@ -82,7 +82,7 @@ stat_contour_filled <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export @@ -122,7 +122,7 @@ StatContour <- ggproto("StatContour", Stat, } ) -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-count.R b/R/stat-count.R index fd78d1beaa..c325d75cf7 100644 --- a/R/stat-count.R +++ b/R/stat-count.R @@ -36,7 +36,7 @@ stat_count <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-density-2d.R b/R/stat-density-2d.R index 74a5539bdb..c9b0040110 100644 --- a/R/stat-density-2d.R +++ b/R/stat-density-2d.R @@ -116,7 +116,7 @@ stat_density_2d_filled <- function(mapping = NULL, data = NULL, stat_density2d_filled <- stat_density_2d_filled -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export @@ -204,9 +204,7 @@ StatDensity2d <- ggproto("StatDensity2d", Stat, } ) - - -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-density.R b/R/stat-density.R index 5b948f5d88..b89456f9b9 100644 --- a/R/stat-density.R +++ b/R/stat-density.R @@ -68,7 +68,7 @@ stat_density <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-ecdf.R b/R/stat-ecdf.R index 96430b1e32..0cd1d5a57f 100644 --- a/R/stat-ecdf.R +++ b/R/stat-ecdf.R @@ -87,8 +87,7 @@ stat_ecdf <- function(mapping = NULL, data = NULL, ) } - -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-ellipse.R b/R/stat-ellipse.R index cc38cbbd0d..94ca539924 100644 --- a/R/stat-ellipse.R +++ b/R/stat-ellipse.R @@ -71,7 +71,7 @@ stat_ellipse <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-function.R b/R/stat-function.R index bf6d2e4b74..8ea365b708 100644 --- a/R/stat-function.R +++ b/R/stat-function.R @@ -45,7 +45,7 @@ stat_function <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-identity.R b/R/stat-identity.R index 86c8c09b95..3cf4d6583a 100644 --- a/R/stat-identity.R +++ b/R/stat-identity.R @@ -28,7 +28,7 @@ stat_identity <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-manual.R b/R/stat-manual.R index 994c8d622e..ac9d03259f 100644 --- a/R/stat-manual.R +++ b/R/stat-manual.R @@ -112,7 +112,7 @@ stat_manual <- function( ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-qq-line.R b/R/stat-qq-line.R index ab6c194cfe..8ffe65cb5e 100644 --- a/R/stat-qq-line.R +++ b/R/stat-qq-line.R @@ -39,7 +39,7 @@ geom_qq_line <- function(mapping = NULL, #' @rdname geom_qq stat_qq_line <- geom_qq_line -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-qq.R b/R/stat-qq.R index 71068bbd04..c260490a89 100644 --- a/R/stat-qq.R +++ b/R/stat-qq.R @@ -81,7 +81,7 @@ geom_qq <- function(mapping = NULL, data = NULL, #' @rdname geom_qq stat_qq <- geom_qq -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-quantilemethods.R b/R/stat-quantilemethods.R index 9afb7e0b92..1d608259f3 100644 --- a/R/stat-quantilemethods.R +++ b/R/stat-quantilemethods.R @@ -39,7 +39,7 @@ stat_quantile <- function(mapping = NULL, data = NULL, } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-smooth.R b/R/stat-smooth.R index 147bd06e41..8ada08f88c 100644 --- a/R/stat-smooth.R +++ b/R/stat-smooth.R @@ -87,7 +87,7 @@ stat_smooth <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-sum.R b/R/stat-sum.R index 0dff7c07f2..deacf0ec08 100644 --- a/R/stat-sum.R +++ b/R/stat-sum.R @@ -27,7 +27,7 @@ stat_sum <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-summary-2d.R b/R/stat-summary-2d.R index 41d0c5b588..342ba939a1 100644 --- a/R/stat-summary-2d.R +++ b/R/stat-summary-2d.R @@ -83,7 +83,7 @@ stat_summary2d <- function(...) { stat_summary_2d(...) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-summary-bin.R b/R/stat-summary-bin.R index e3db18b102..e55fcedfb3 100644 --- a/R/stat-summary-bin.R +++ b/R/stat-summary-bin.R @@ -57,7 +57,7 @@ stat_summary_bin <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-summary-hex.R b/R/stat-summary-hex.R index 959630b4ac..89f58d069c 100644 --- a/R/stat-summary-hex.R +++ b/R/stat-summary-hex.R @@ -32,7 +32,7 @@ stat_summary_hex <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-summary.R b/R/stat-summary.R index a32eda8ca0..f4217833fa 100644 --- a/R/stat-summary.R +++ b/R/stat-summary.R @@ -174,7 +174,7 @@ stat_summary <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-unique.R b/R/stat-unique.R index 38483a2d7b..b73c582c9b 100644 --- a/R/stat-unique.R +++ b/R/stat-unique.R @@ -30,7 +30,7 @@ stat_unique <- function(mapping = NULL, data = NULL, ) } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/R/stat-ydensity.R b/R/stat-ydensity.R index 6b0e4f0ff8..ed6abdf774 100644 --- a/R/stat-ydensity.R +++ b/R/stat-ydensity.R @@ -67,7 +67,7 @@ stat_ydensity <- function(mapping = NULL, data = NULL, } -#' @rdname ggplot2-ggproto +#' @rdname Stat #' @format NULL #' @usage NULL #' @export diff --git a/man/Coord.Rd b/man/Coord.Rd new file mode 100644 index 0000000000..340e0a15c7 --- /dev/null +++ b/man/Coord.Rd @@ -0,0 +1,539 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/coord-.R, R/coord-cartesian-.R, +% R/coord-fixed.R, R/coord-flip.R, R/coord-map.R, R/coord-polar.R, +% R/coord-quickmap.R, R/coord-radial.R, R/coord-transform.R +\docType{data} +\name{Coord} +\alias{Coord} +\alias{CoordCartesian} +\alias{CoordFixed} +\alias{CoordFlip} +\alias{CoordMap} +\alias{CoordPolar} +\alias{CoordQuickmap} +\alias{CoordRadial} +\alias{CoordTrans} +\title{Coords} +\description{ +All \verb{coord_*()} functions (like \code{coord_trans()}) return a \verb{Coord*} object +(like \code{CoordTrans}). These objects contain methods that support the +coordinate systems in ggplot2. +} +\details{ +Each of the \verb{Coord*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended from the +top-level \code{Coord}, and each implements various methods and fields. +The object and its parameters are chaperoned by the \link{Layout} class. + +To create a new type of Coord object, it is recommended +to extend not the base \code{Coord} class, but one of its children like +\code{CoordCartesian}. + +When overriding the \code{transform()} method, it may be necessary to adapt the +implementation of \code{render_bg()} and possibly axis placement too. + +An important data structure that coordinate systems create is the +\code{panel_params} structure. When overriding that structure, many methods may +need to be adapted as well. +} +\section{Fields}{ + +\describe{ +\item{\code{default}}{Scaler boolean indicating whether this is the default +coordinate system. Non-default coordinate systems raise a message when +a new system replaces it.} + +\item{\code{clip}}{A scalar string grid setting controlling whether layers should +be clipped to the extent of the plot panel extent. Can be \code{"on"} to +perform clipping, \code{"off"} to not clip, or \code{"inherit"} to take on the +setting of the parent viewport.} + +\item{\code{reverse}}{A scalar string giving which directions to reverse. For +Cartesian systems, can be \verb{"none}, \code{"x"}, \code{"y"} or \code{"xy"} for both. +Non-Cartesian may define their own settings.} + +\item{\code{setup_params}}{\strong{Description} + +A function method for modifying or checking the parameters based on the +data. The default method parses the \code{expand} parameter. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$setup_params(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames. The first item is the global data, +which is followed by layer data in subsequent items.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_data}}{\strong{Description} + +A function method for modifying or checking the data prior to adding +defaults. The default method returns data unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$setup_data(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames. The first item is the global data, +which is followed by layer data in subsequent items.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A list of data frames of the same length as the \code{data} argument} + +\item{\code{setup_layout}}{\strong{Description} + +A function method that acts as a hook for the coordinate system to have +input on the layout computed by facets. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$setup_layout(layout, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{layout}}{A data frame computed by \code{Facet$compute_layout()}. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A data frame from the modified \code{layout} argument. The default creates a +new \code{COORD} column to identify unique combinations of x and y scales for +efficiency purposes. It should never remove columns.} + +\item{\code{modify_scales}}{\strong{Description} + +A function method for modifying scales in place. This is optional and +currently used by CoordFlip and CoordPolar to ensure axis positions are +conforming to the coordinate system. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$modify_scales(scales_x, scales_y) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scales_x},\code{scales_y}}{A list of trained scales for the \code{x} and \code{y} +aesthetics respectively.} +} + +\strong{Value} + +Nothing, this is called for the side effect of modifying scales.} + +\item{\code{setup_panel_params}}{\strong{Description} + +This function method is used to setup panel parameters per panel. +For efficiency reasons, this method is called once per combination of +\code{x} and \code{y} scales. It is used to instantiate ViewScale class objects and +ranges for position aesthetics and optionally append additional +parameters needed for the \code{transform()} method and rendering axes. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$setup_panel_params(scale_x, scale_y, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scale_x},\code{scale_y}}{A list of trained scales for the \code{x} and \code{y} +aesthetics respectively.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A named list of view scales, ranges and other optional parameters.} + +\item{\code{setup_panel_guides}}{\strong{Description} + +This function method is used to initiate position guides for each panel. +For efficiency reasons, this method is called once per combination of \code{x} +and \code{y} scales. For the primary and secondary positions, it should resolve +guides coming from the \code{plot$guides} field and \code{Scale$guide} fields and +set appropriate \code{Guide$params$position} parameters. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$setup_panel_guides(panel_params, guides, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +\item{\code{guides}}{A \verb{} ggproto class.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +The \code{panel_params} object but with a Guides class object appended with +the name 'guides'.} + +\item{\code{setup_panel_guides}}{\strong{Description} + +This function method is used to train and transform position guides for each +panel. For efficiency reasons, this method is called once per combination +of \code{x} and \code{y} scales. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$train_panel_guides(panel_params, layers, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects, a Guides class +object and additional parameters from the \code{setup_panel_params()} method.} +\item{\code{layers}}{A list of layers from \code{plot$layers}.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +The \code{panel_params} object, but with trained and transformed \code{guides} +parameter.} + +\item{\code{transform}}{\strong{Description} + +This function method is used to apply transformations and rescale position +aesthetics. This method is used in several places: +\itemize{ +\item The Geom drawing code, used through \code{coord_munch()} in many Geoms. +\item The Guide transform method +\item Panel grid transformation in \code{render_bg()} +} + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$transform(data, panel_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with columns for numeric position aesthetics.} +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +} + +\strong{Value} + +The \code{data} argument with rescaled and transformed position aesthetics.} + +\item{\code{distance}}{\strong{Description} + +This function method is used to calculate distances between subsequent +data points. \code{coord_munch()} uses this method determine how many points +should be used to interpolate. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$distance(x, y, panel_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{x},\code{y}}{x and y coordinates of a set of points in data space.} +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +} + +\strong{Value} + +The \code{data} argument with rescaled and transformed position aesthetics.} + +\item{\code{backtransform_range}}{\strong{Description} + +This function method is used to convert ranges from transformed coordinates +back into data coordinates. The data coordinates may possibly be scale- +transformed. It is used in \code{coord_munch()} to ensure limits are in data +coordinates. + +The back-transformation may be needed for coords such as \code{coord_trans()}, +where the range in the transformed coordinates differs from the range in +the untransformed coordinates. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$backtransform_range(panel_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +} + +\strong{Value} + +A list containing numeric ranges for \code{x} and \code{y} in data coordinates.} + +\item{\code{range}}{\strong{Description} + +This function method is a small helper method to extract ranges from the +\code{panel_params} object. It exists because \code{panel_params} can be opaque at +times. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$range(panel_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +} + +\strong{Value} + +A list containing numeric ranges for \code{x} and \code{y}.} + +\item{\code{draw_panel}}{\strong{Description} + +This function method is used to orchestrate decorating panel drawings with +foreground and background drawings. It is called once per panel, invokes +the \code{render_fg()} and \code{render_bg()} methods and enforces the \code{clip} field. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$draw_panel(panel, params, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel}}{A grob containing drawn layers and facet foreground and +background.} +\item{\code{params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}} +} + +\strong{Value} + +A grob with panel content.} + +\item{\code{render_fg}}{\strong{Description} + +This function method is used to draw the panel foreground. For all +intents and purposes is just the \code{panel.border} theme element, but you can +repurpose this method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$render_fg(panel_params, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}} +} + +\strong{Value} + +A grob with panel foreground.} + +\item{\code{render_bg}}{\strong{Description} + +This function method is used to draw the panel background. Typically +this is a combination of the \code{panel.background} and \code{panel.grid} theme +elements. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$render_bg(panel_params, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}} +} + +\strong{Value} + +A grob with panel background.} + +\item{\code{labels}}{\strong{Description} + +This function method is used to format axis titles. It is used in some +coordinate systems to (conditionally) swap x and y labels. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$labels(labels, panel_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{labels}}{A named list containing an \code{x} list and a \code{y} list. The +\code{x} and \code{y} lists have \code{primary} and \code{secondary} labels.} +\item{\code{panel_params}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method.} +} + +\strong{Value} + +A list with the same structure and names as the \code{labels} argument.} + +\item{\code{aspect}}{\strong{Description} + +This function method that gives the aspect ratio for panels. It allows for +\code{CoordFixed} to compute an aspect ratio based on data ranges. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$render_bg(panel_params, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{ranges}}{A list of ViewScale class objects and additional +parameters from the \code{setup_panel_params()} method. If there are +multiple panels, the parameters for the first panel is used.} +} + +\strong{Value} + +A scalar numeric} + +\item{\code{render_axis_h,render_axis_v}}{\strong{Description} + +These function methods are used to render axes to place at the outside edge +of panels. Interior axes should not be rendered here. The \code{render_axis_h()} +methods produces the horizontal axes for the top and bottom position. +The \code{render_axis_v()} method renders the vertical axes for the left and +right position. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$render_axis_h(panel_params, theme +Coord$render_axis_v(panel_params, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A list of ViewScale class objects, a Guides class +object and additional parameters from the \code{setup_panel_params()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}} +} + +\strong{Value} + +For \code{render_axis_h()} a named list where \code{"top"} and \code{"bottom"} are grobs +with an axis. For \code{render_axis_v()} a named list where \code{"left"} and +\code{"right"} are grobs with an axis. These grobs should be \code{\link[=zeroGrob]{zeroGrob()}} +when no axes should be rendered.} + +\item{\code{is_linear}}{\strong{Description} + +This function method is used to signal whether a coordinate system is +linear. In \code{coord_munch()} and several Geom drawing methods, it is used to +determine whether points should be interpolated. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$is_linear() +}\if{html}{\out{
}} + +\strong{Value} + +A scalar boolean.} + +\item{\code{is_free}}{\strong{Description} + +This function method is used to signal whether a coordinate system supports +free scaling of axes in faceted plots. This should generally return \code{FALSE} +for coordinate systems that enforce a fixed aspect ratio. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Coord$is_free() +}\if{html}{\out{
}} + +\strong{Value} + +A scalar boolean.} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Coord class names are in UpperCamelCase and start with the +\verb{Coord*} prefix, like \code{CoordNew}. + +A constructor function is usually paired with a Coord class. The constructor +copies the coord class and populates parameters. The constructor function name +should take the Coord class name and be formatted with snake_case, +so that \code{CoordNew} becomes \code{coord_new()}. +} + +\examples{ +# Extending the class +CoordJitter <- ggproto( + "CoordJitter", CoordCartesian, + # Fields + amount = 0, + # Methods + is_linear = function() FALSE, + transform = function(self, data, panel_params) { + data <- ggproto_parent(CoordCartesian, self)$transform(data, panel_params) + data$x <- jitter(data$x, amount = self$amount) + data$y <- jitter(data$y, amount = self$amount) + data + } +) + +# Building a constructor +coord_jitter <- function(amount = 0.005, xlim = NULL, ylim = NULL, expand = TRUE, + clip = "on", reverse = "none") { + ggproto( + NULL, CoordJitter, + amount = amount, + limits = list(x = xlim, y = ylim), + reverse = reverse, expand = expand, clip = clip + ) +} + +# Use new coord in plot +set.seed(42) +ggplot(mpg, aes(drv, displ)) + + geom_boxplot() + + coord_jitter() +} +\seealso{ +The \href{https://ggplot2-book.org/extensions#sec-new-coords}{new coords section} of the online ggplot2 book. + +Other Layout components: +\code{\link{Facet}}, +\code{\link{Layout}} +} +\concept{Layout components} +\keyword{datasets} +\keyword{internal} diff --git a/man/Facet.Rd b/man/Facet.Rd new file mode 100644 index 0000000000..b7e9e2bc81 --- /dev/null +++ b/man/Facet.Rd @@ -0,0 +1,603 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/facet-.R, R/facet-grid-.R, R/facet-null.R, +% R/facet-wrap.R +\docType{data} +\name{Facet} +\alias{Facet} +\alias{FacetGrid} +\alias{FacetNull} +\alias{FacetWrap} +\title{Facets} +\description{ +All \verb{facet_*()} functions returns a \code{Facet} object or an object of a +\code{Facet} subclass. This object describes how to assign data to different +panels, how to apply positional scales and how to lay out the panels, once +rendered. +} +\details{ +Extending facets can range from the simple modifications of current facets, +to very laborious rewrites with a lot of \code{\link[gtable:gtable]{gtable()}} +manipulation.For some examples of both, please see the extension vignette. +The object and its parameters are chaperoned by the \link{Layout} class. + +\code{Facet} subclasses, like other extendible ggproto classes, have a range +of methods that can be modified. Some of these are required for all new +subclasses, while other only need to be modified if need arises. + +The required methods are: +\itemize{ +\item \code{compute_layout} +\item \code{map_data()} +\item \code{draw_panels()} or its subsidiaries: +\itemize{ +\item \code{init_gtable()} +\item \code{attach_axes()} +\item \code{attach_strips()} +} +} + +In addition to the methods above, it can be useful to override the default +behaviour of one or more of the following methods: +\itemize{ +\item \code{setup_params()} +\item \code{init_scales()} +\item \code{train_scale()} +\item \code{finish_data()} +\item \code{draw_back()}, \code{draw_front()} or \code{draw_labels()} +} + +All extension methods receive the content of the params field as the params +argument, so the constructor function will generally put all relevant +information into this field. +} +\section{Fields}{ + +\describe{ +\item{\code{shink}}{A scalar boolean which when \code{TRUE}, will shrink scales to +fit output statistics rather than raw data. If \code{FALSE}, will only include +raw data before statistical summary. By exception this is not part of the +\code{params} field.} + +\item{\code{params}}{A named list of parameters populated by the constructor +function.} + +\item{\code{setup_params}}{\strong{Description} + +A function method for modifying or checking the parameters based on the +data. The default method includes a \code{.possible_columns} variable giving +column names. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$setup_params(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames. The first item is the global data, +which is followed by layer data in subsequent items.} +\item{\code{params}}{A list of current parameters.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_data}}{\strong{Description} + +A function method for modifying or checking the data prior to adding +defaults. The default method returns data unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$setup_data(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames. The first item is the global data, +which is followed by layer data in subsequent items.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A list of data frames of the same length as the \code{data} argument} + +\item{\code{compute_layout}}{\strong{Description} + +A function method for creating the correspondence between faceting +variable levels, panels and position scales. It places panels like cells +in a matrix. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$compute_layout(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames. The first item is the global data, +which is followed by layer data in subsequent items.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A data frame with 1 row per panel, containing at least integer columns +\code{ROW}, \code{COL}, \code{PANEL}, \code{SCALE_X} and \code{SCALE_Y}. Can contain additional +information in terms of columns, typically faceting variables.} + +\item{\code{map_data}}{\strong{Description} + +A function method for to create the \code{PANEL} variable in layer data. The +\code{PANEL} variable is a special variable that tracks the relationship between +rows in the layer data and the panels described in the \code{layout} input. + +In addition, #' this function may copy or discard rows as needed, for +example when adding margins in FacetGrid. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$map_data(data, layout, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A list of data frames containing layer data including a \code{PANEL} variable.} + +\item{\code{init_scales}}{\strong{Description} + +A function method for initialising position scales. Given a prototype scale +for \code{x} and \code{y}, creates layout specific scales to accommodate +the relationships between panels and scales. By default, the prototype +scales are cloned for each \code{SCALE_X} and \code{SCALE_Y} level. The function is +called separately; once for \code{x} and once for \code{y}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$init_scales(layout, x_scale, y_scale, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scale},\code{y_scale}}{A position scale for the \code{x} and \code{y} +aesthetics respectively.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A named list with \code{x} and \code{y} elements containing a list of panel scales +for each \code{SCALE_X} and/or \code{SCALE_Y} level respectively.} + +\item{\code{train_scales}}{\strong{Description} + +A function method for training position scales. The default trains each +scale on the data related to its panels. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$train_scales(x_scales, y_scales, layout, data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +Nothing, this method is called for its side-effect of training the scales.} + +\item{\code{setup_panel_params}}{\strong{Description} + +A function method as a hook to give facets input over panel parameters. By +default, returns panel parameters unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$setup_panel_params(panel_params, coord, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panel_params}}{A named list of view scales, ranges and other +optional parameters from \code{Coord$setup_panel_params()}.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{...}}{Currently not in use. For future expansion.} +} + +\strong{Value} + +A list of panel parameters.} + +\item{\code{finish_data}}{\strong{Description} + +A function method as a hook for making last-minute modifications to layer +data before it is rendered by Geoms. The default is to not modify the data. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$finish_data(data, layout, x_scales, y_scales, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame containing layer data of a single layer.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A data frame containing layer data.} + +\item{\code{draw_panel_content}}{\strong{Description} + +A function method to assemble the panel contents. It delegates the +\code{draw_back()} and \code{draw_front()} methods, as well as \code{Coord$draw_panel()}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$draw_panel_content( + panels, + layout, + x_scales, + y_scales, + ranges, + coord, + theme, + params, + ... +) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panels}}{A list parallel to layers. Each element is another list +with grobs for each panel, generated by \code{Layer$draw_geom()}.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{ranges}}{A list of panel parameters from the +\code{setup_panel_params()} augmented with position guides.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +\item{\code{...}}{Currently not in use.} +} + +\strong{Value} + +A list of grobs, one for each level of the \code{PANEL} layout variable. Grob +can be \code{zeroGrob()} to draw nothing.} + +\item{\code{draw_back,draw_front}}{\strong{Description} + +A function method draw facet background (back) and foreground (front) for +panels. The front and back will sandwich the grobs created by layers. The +default methods draw nothing. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$draw_back(data, layout, x_scales, y_scales, theme, params) +Facet$draw_front(data, layout, x_scales, y_scales, theme, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A list of grobs, one for each level of the \code{PANEL} layout variable. Grob +can be \code{zeroGrob()} to draw nothing.} + +\item{\code{draw_panels}}{\strong{Description} + +A function method that orchestrates the majority of facet drawing. It is +responsible for assembling a gtable with panel content decorated with axes +and strips. The resulting gtable is the basis for the plot in its entirety. +It delegates these tasks to the \code{init_gtable()}, \code{attach_axes()} and +\code{attach_strips()} methods. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$draw_panels( + panels, + layout, + x_scales, + y_scales, + ranges, + coord, + data, + theme, + params +) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panels}}{A list of grobs, one per panel.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{ranges}}{A list of panel parameters from the +\code{setup_panel_params()} augmented with position guides.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A \code{\link[gtable:gtable]{gtable}} object.} + +\item{\code{init_gtable}}{\strong{Description} + +A function method that initiates a gtable object containing panels set +at the appropriate \code{ROW} and \code{COL} cells from the layout. The panels are +separated by the \verb{panel.spacing.\{x/y\}} spacing. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$init_gtable(panels, layout, theme, ranges, params, aspect_ratio) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panels}}{A list of grobs, one per panel.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{ranges}}{A list of panel parameters from the +\code{setup_panel_params()} augmented with position guides.} +\item{\code{aspect_ratio}}{A scalar numeric for the panel aspect ratio or +\code{NULL} for no aspect ratio.} +} + +\strong{Value} + +A \code{\link[gtable:gtable]{gtable}} object containing panel grobs prefixed with +\code{"panel"}.} + +\item{\code{attach_axes}}{\strong{Description} + +A function method that renders position guides (axes) and attaches these +to the gtable with panels. The default method returns the gtable unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$attach_axes(table, layout, ranges, coord, theme, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{table}}{A \code{\link[gtable:gtable]{gtable}} object populated with panels from the +\code{init_gtable()} method.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{ranges}}{A list of panel parameters from the +\code{setup_panel_params()} augmented with position guides.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A \code{\link[gtable:gtable]{gtable}} object.} + +\item{\code{attach_strips}}{\strong{Description} + +A function method that renders strips and attaches these to the gtable +with panels and axes. The \code{format_strip_labels()} method is used to format +the strip text. The default method returns the gtable unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$attach_strips(table, layout, ranges, coord, theme, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{table}}{A \code{\link[gtable:gtable]{gtable}} object from the \code{attach_axes()} +method.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +} + +\strong{Value} + +A \code{\link[gtable:gtable]{gtable}} object.} + +\item{\code{format_strip_labels}}{\strong{Description} + +A function method that formats the text for strips. It is used in the +\code{attach_strips} methods, but also the \code{get_strip_labels()} function. +The default method returns \code{NULL}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$format_strip_labels(layout, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A list containing a data frame with strip labels.} + +\item{\code{set_panel_size}}{\strong{Description} + +A function method that enforces the \code{panel.widths} and \code{panel.heights} +theme settings. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$set_panel_size(table, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{table}}{A \code{\link[gtable:gtable]{gtable}} object populated by the +\code{draw_panels()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +} + +\strong{Value} + +The \code{table} object, optionally with different \code{widths} and \code{heights} +properties.} + +\item{\code{attach_axes}}{\strong{Description} + +A function method that renders axis titles and adds them to the gtable. +The default is to add one title at each side depending on the position +and presence of axes. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$draw_labels( + panels, + layout, + x_scales, + y_scales, + ranges, + coord, + data, + theme, + labels, + params +) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panels}}{A \code{\link[gtable:gtable]{gtable}} object initiated by the +\code{draw_panels()} method.} +\item{\code{layout}}{A data frame computed by the \code{compute_layout()} method. +Typically contains the faceting variables, \code{ROW}, \code{COL}, \code{PANEL}, +\code{SCALE_X} and \code{SCALE_Y} variables.} +\item{\code{x_scales},\code{y_scales}}{A list of panel scales for each \code{SCALE_X} +and \code{SCALE_Y} level respectively.} +\item{\code{ranges}}{A list of panel parameters from the +\code{setup_panel_params()} augmented with position guides.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{data}}{A list of data frames containing layer data.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{labels}}{A named list containing an \code{x} list and \code{y} list. The +\code{x} and \code{y} lists have \code{primary} and \code{secondary} labels.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A \code{\link[gtable:gtable]{gtable}} object.} + +\item{\code{vars}}{\strong{Description} + +A function method that returns the names of faceting variables. The +default method returns an character vector with 0 length. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Facet$vars() +}\if{html}{\out{
}} + +\strong{Value} + +A character vector} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Facet class names are in UpperCamelCase and start with the +\verb{Facet*} prefix, like \code{FacetNew}. + +A constructor function is usually paired with a Facet class. The constructor +copies the facet class and populates the \code{params} field. The constructor +function name should take the Facet class name and be formatted with +snake_case, so that \code{FacetNew} becomes \code{facet_new()}. +} + +\examples{ +# Please see extension vignette +NULL +} +\seealso{ +The the \href{https://ggplot2-book.org/extensions#new-facets}{new facets section} of the online ggplot2 book. + +Run \code{vignette("extending-ggplot2")}, in particular the "Creating a +new faceting" section. + +Other Layout components: +\code{\link{Coord}}, +\code{\link{Layout}} +} +\concept{Layout components} +\keyword{datasets} +\keyword{internal} diff --git a/man/Geom.Rd b/man/Geom.Rd new file mode 100644 index 0000000000..cabc4ee41a --- /dev/null +++ b/man/Geom.Rd @@ -0,0 +1,359 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geom-.R, R/annotation-custom.R, +% R/annotation-logticks.R, R/geom-polygon.R, R/geom-map.R, R/annotation-map.R, +% R/geom-raster.R, R/annotation-raster.R, R/geom-abline.R, R/geom-rect.R, +% R/geom-bar.R, R/geom-tile.R, R/geom-bin2d.R, R/geom-blank.R, +% R/geom-boxplot.R, R/geom-col.R, R/geom-path.R, R/geom-contour.R, +% R/geom-crossbar.R, R/geom-segment.R, R/geom-curve.R, R/geom-ribbon.R, +% R/geom-density.R, R/geom-density2d.R, R/geom-dotplot.R, R/geom-errorbar.R, +% R/geom-function.R, R/geom-hex.R, R/geom-hline.R, R/geom-label.R, +% R/geom-linerange.R, R/geom-point.R, R/geom-pointrange.R, R/geom-quantile.R, +% R/geom-rug.R, R/geom-smooth.R, R/geom-spoke.R, R/geom-text.R, +% R/geom-violin.R, R/geom-vline.R +\docType{data} +\name{Geom} +\alias{Geom} +\alias{GeomCustomAnn} +\alias{GeomLogticks} +\alias{GeomPolygon} +\alias{GeomMap} +\alias{GeomAnnotationMap} +\alias{GeomRaster} +\alias{GeomRasterAnn} +\alias{GeomAbline} +\alias{GeomRect} +\alias{GeomBar} +\alias{GeomTile} +\alias{GeomBin2d} +\alias{GeomBlank} +\alias{GeomBoxplot} +\alias{GeomCol} +\alias{GeomPath} +\alias{GeomLine} +\alias{GeomStep} +\alias{GeomContour} +\alias{GeomContourFilled} +\alias{GeomCrossbar} +\alias{GeomSegment} +\alias{GeomCurve} +\alias{GeomRibbon} +\alias{GeomArea} +\alias{GeomDensity} +\alias{GeomDensity2d} +\alias{GeomDensity2dFilled} +\alias{GeomDotplot} +\alias{GeomErrorbar} +\alias{GeomErrorbarh} +\alias{GeomFunction} +\alias{GeomHex} +\alias{GeomHline} +\alias{GeomLabel} +\alias{GeomLinerange} +\alias{GeomPoint} +\alias{GeomPointrange} +\alias{GeomQuantile} +\alias{GeomRug} +\alias{GeomSmooth} +\alias{GeomSpoke} +\alias{GeomText} +\alias{GeomViolin} +\alias{GeomVline} +\title{Geoms} +\description{ +All \verb{geom_*()} functions (like \code{geom_point()}) return a layer that +contains a \verb{Geom*} object (like \code{GeomPoint}). The \verb{Geom*} +object is responsible for rendering the data in the plot. +} +\details{ +Each of the \verb{Geom*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended +from the top-level \code{Geom}, and each implements various methods and +fields. The object and its parameters are chaperoned by the \link{Layer} class. + +Compared to \code{Stat} and \code{Position}, \code{Geom} is a little +different because the execution of the setup and compute functions is +split up. \code{setup_data} runs before position adjustments, and +\code{draw_layer()} is not run until render time, much later. + +When creating a new Geom class, you may want to consider override one or +more of the following: +\itemize{ +\item The \code{required_aes} and \code{default_aes} fields. +\item The \code{setup_data()} and \code{setup_params()} functions. +\item Either the \code{draw_panel()} or \code{draw_group()} function. +\item The \code{draw_key} field. +} +} +\section{Fields}{ + +\describe{ +\item{\code{required_aes}}{A character vector naming aesthetics that are necessary +to render the geom.} + +\item{\code{non_missing_aes}}{A character vector naming aesthetics that will cause +removal if they have missing values.} + +\item{\code{optional_aes}}{A character vector naming aesthetics that will be +accepted by \code{layer()}, but are not required or described in the +\code{default_aes} field.} + +\item{\code{default_aes}}{A \link[=aes]{mapping} of default values for aesthetics. +Aesthetics can be set to \code{NULL} to be included as optional aesthetic.} + +\item{\code{rename_size}}{A scalar boolean: whether to rename \code{size} aesthetics to \code{linewidth}.} + +\item{\code{extra_params}}{A character vector of parameter names in addition to +those imputed from the \code{draw_panel()} or \code{draw_groups()} methods. This +field can be set to include parameters for \code{setup_data()} or \code{handle_na()} +methods. By default, this only contains \code{"na.rm"}.} + +\item{\code{draw_key}}{A function generating a single legend glyph for the geom. +Typically one of the functions prefixed by \code{\link[=draw_key]{draw_key_}}.} + +\item{\code{setup_params}}{\strong{Description} + +A function method for modifying or checking the parameters based on the +data. The default method returns the parameters unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$setup_params(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of current parameters.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_data}}{\strong{Description} + +A function method for modifying or checking the data prior to adding +defaults. The default method returns data unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$setup_data(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method.} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{use_defaults}}{\strong{Description} + +A function method for completing the layer data by filling in default +aesthetics that are not present. It is not recommended to use as an +extension point. + +It takes on these tasks: +\itemize{ +\item Evaluation of default aesthetics from the \code{default_aes} field. +\item Handling the \code{\link[=after_scale]{after_scale()}}/\code{stage(after_scale)} stage of delayed +evaluation. +\item Fill in fixed, unmapped aesthetics passed as parameters. +} + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$use_defaults(data, params, modifiers, default_aes, theme, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame of the layer's data, coming from the +\code{setup_data()} method. Can be \code{NULL}, in which case resolved defaults +should be returned.} +\item{\code{params}}{A list of fixed aesthetic parameters} +\item{\code{modifiers}}{A \link[=aes]{mapping} with delayed evaluations.} +\item{\code{default_aes}}{A \link[=aes]{mapping} with default aesthetics.} +\item{\code{theme}}{A \link[=complete_theme]{completed theme}.} +} + +\strong{Value} + +A data frame with completed layer data.} + +\item{\code{handle_na}}{\strong{Description} + +A function method to handle missing values. The default method will +remove rows that have missing values for the aesthetics listed in the +\code{required_aes} and \code{non_missing_aes} fields. It is not recommended to +use this method as an extension point. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$handle_na(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data coming from the +\code{update_defaults()} method.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{draw_layer}}{\strong{Description} + +A function method orchestrating the drawing of the entire layer. The +default method splits the data and passes on drawing tasks to the +panel-level \code{draw_panel()} method. It is not recommended to use this method +as an extension point. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$draw_layer(data, params, layout, coord) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters} +\item{\code{layout}}{A completed \verb{} ggproto object.} +\item{\code{coord}}{A \verb{} ggproto object.} +} + +\strong{Value} + +A list of grobs, one per panel} + +\item{\code{draw_panel,draw_group}}{\strong{Description} + +A function method orchestrating the drawing of the layer for a single +panel or group. The default \code{draw_panel()} method splits the data into groups, +passes on the drawing tasks to the group-level \code{draw_group()} method and +finally assembles these into a single grob. The default \code{draw_group} method +is not implemented. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$draw_panel(data, panel_params, coord, ...) +Geom$draw_group(data, panel_params, coord, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{panel_params}}{A list of per-panel parameters constructed by +\code{Coord$setup_panel_params()}. This should be considered an opaque data +structure that is just passed along when calling coord methods.} +\item{\code{coord}}{A \verb{} ggproto object. To correctly scale the +position data, one should always call +\code{coord$transform(data, panel_params)}. When working with non-linear +coordinate systems, data should be converted to fit a primitive geom +(e.g. point, path or polygon) and passed on to the corresponding draw +method for \link[=coord_munch]{munching}.} +\item{\code{...}}{Reserved for extensions. By default, this is passed on to +the \code{draw_group()} method.} +} + +\strong{Value} + +A single grob or \code{\link[=zeroGrob]{zeroGrob()}} when there is nothing to draw. For +\code{draw_panel()} this can be a \link[grid:grid.grob]{gTree} holding individual grobs +from the \code{draw_group()} method.} + +\item{\code{parameters}}{\strong{Description} + +A function method for listing out all acceptable parameters for this geom. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$parameters(extra) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{extra}}{A boolean: whether to include the \code{extra_params} field.} +} + +\strong{Value} + +A character vector of parameter names.} + +\item{\code{aesthetics}}{\strong{Description} + +A function method for listing out all acceptable aesthetics for this geom. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$aesthetics() +}\if{html}{\out{
}} + +\strong{Value} + +A character vector of aesthetic names.} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as +the class name. Geom class names are in UpperCamelCase and start with the +\verb{Geom*} prefix, like \code{GeomNew}. + +A constructor function is usually paired with a Geom class. The constructor +wraps a call to \code{layer()}, where e.g. \code{layer(geom = GeomNew)}. The constructor +function name is formatted by taking the Geom class name and formatting it +with snake_case, so that \code{GeomNew} becomes \code{geom_new()}. +} + +\examples{ +# Extending the class +GeomSimplePoint <- ggproto( + "GeomSimplePoint", Geom, + # Fields + required_aes = c("x", "y"), + draw_key = draw_key_point, + # Methods + draw_panel = function(data, panel_params, coord) { + data <- coord$transform(data, panel_params) + grid::pointsGrob(data$x, data$y) + } +) + +# Building a constructor +geom_simple_point <- function(mapping = NULL, data = NULL, stat = "identity", + position = "identity", ..., na.rm = FALSE, + show.legend = NA, inherit.aes = TRUE) { + layer( + mapping = mapping, data = data, + geom = GeomSimplePoint, stat = stat, position = position, + show.legend = show.legend, inherit.aes = inherit.aes, + params = list(na.rm = na.rm, ...) + ) +} + +# Use new geom in plot +ggplot(mpg, aes(displ, hwy)) + + geom_simple_point() +} +\seealso{ +The \href{https://ggplot2-book.org/extensions#sec-new-geoms}{new geoms section} of the online ggplot2 book. + +Run \code{vignette("extending-ggplot2")}, in particular the "Creating a +new geom" section. + +Other Layer components: +\code{\link{Layer-class}}, +\code{\link{Position}}, +\code{\link{Stat}} +} +\concept{Layer components} +\keyword{datasets} +\keyword{internal} diff --git a/man/Guide.Rd b/man/Guide.Rd new file mode 100644 index 0000000000..9c4ad623dd --- /dev/null +++ b/man/Guide.Rd @@ -0,0 +1,673 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/guide-.R, R/guide-axis.R, +% R/guide-axis-logticks.R, R/guide-axis-stack.R, R/guide-axis-theta.R, +% R/guide-legend.R, R/guide-bins.R, R/guide-colorbar.R, R/guide-colorsteps.R, +% R/guide-custom.R, R/guide-none.R, R/guide-old.R +\docType{data} +\name{Guide} +\alias{Guide} +\alias{GuideAxis} +\alias{GuideAxisLogticks} +\alias{GuideAxisStack} +\alias{GuideAxisTheta} +\alias{GuideLegend} +\alias{GuideBins} +\alias{GuideColourbar} +\alias{GuideColoursteps} +\alias{GuideCustom} +\alias{GuideNone} +\alias{GuideOld} +\title{Guides} +\description{ +The \verb{guide_*} functions (like \code{guide_legend()}) return \verb{Guide*} objects +(like \code{GuideLegend}). The \verb{Guide*} object is responsible for rendering the +guide for at least one aesthetic. +} +\details{ +Each of the \verb{Guide*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended from the +top-level \code{Guide}, and each implements various methods and fields. + +Building a guide has three stages: +\enumerate{ +\item The guide extracts the relevant information from scales. +\item The guide interacts with other parts of the plot, like coords or layers to +supplement information. +\item The guide is rendered. +} + +When creating a new Guide class, you may want to consider overriding one or +more of the following: +\itemize{ +\item The \code{params}, \code{elements}, \code{hashables} and \code{available_aes} fields. +\item The \code{extract_key()}, \code{extract_decor()} and \code{extract_params()} methods. +\item The \code{transform()} or \code{get_layer_key()} methods. +\item The \code{setup_params()} and \code{override_elements()} methods. +\item Any of the \verb{build_*} methods. +\item As a last resort the \code{measure_grobs()}, \code{arrange_layout()}, and +\code{assemble_drawing()} methods. +} +} +\section{Fields}{ + +\describe{ +\item{\code{params}}{A list of initial parameters that the guide needs to +function. The base \code{Guide$params} contains mandatory parameters, +but extensions can add new parameters. It has the following roles: +\itemize{ +\item It provides the default values for parameters. +\item \code{names(params)} determines what are valid arguments for \code{new_guide()}. +\item During build stages, a mutable copy of \code{params} holds information +about the guide. +}} + +\item{\code{available_aes}}{A character vector of aesthetic names for which the +guide is appropriate. Can use keyword \code{"any"} to indicate all non-position +aesthetics.} + +\item{\code{elements}}{A named list of strings stating which theme elements this +guide uses. By default, strings will be translated in +\code{Guide$setup_elements()} using \code{calc_element()}. Strings are expected to +occur in \code{names(get_element_tree())}, like \code{"legend.text"} for example. +Position guides typically append the \verb{\{aes\}.\{position\}} suffix in the +\code{setup_elements()} method when the position is known.} + +\item{\code{hashables}}{A list of calls or names constructed by \code{rlang::exprs()} +indicating objects in the \code{params} field. These will be evaluated in the +context of the \code{params} field and the resulting list will be hashed. The +hash uniquely identify guides that can merge. Guides that have different +hashes will not merge. For extensions, you should include objects that +clearly mark two guides from one another that cannot be merged.} + +\item{\code{train}}{\strong{Description} + +A function method for orchestrating the training of a guide, which extracts +necessary information from a Scale object. As orchestrator, this method is +not intended for extension. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$train(params, scale, aesthetic, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{scale}}{A \verb{} ggproto object. In the case of position +guides, can be a \verb{} ggproto object.} +\item{\code{aesthetic}}{A scalar string specifying the aesthetic. +If missing (default), it will use the first aesthetic specified in the +scale.} +\item{\code{...}}{Additional parameters passed on to the \code{extract_params()} +method.} +} + +\strong{Value} + +A modified list of parameters} + +\item{\code{extract_key}}{\strong{Description} + +A function method for extracting break information from the scale called +the 'key'. It retrieves breaks, maps these breaks and derives labels. These +form the basis for tick marks and labels in some guides. It is appropriate +to override in extensions. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$extract_key(scale, aesthetic, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scale}}{A \verb{} ggproto object. In the case of position +guides, can be a \verb{} ggproto object.} +\item{\code{aesthetic}}{A scalar string specifying the aesthetic.} +\item{\code{...}}{Optional arguments from the \code{params} field.} +} + +\strong{Value} + +A 'key' data frame containing annotated scale breaks, including at least a +column for the aesthetic, \code{.label} and \code{.value}. If there are no breaks, +returns \code{NULL}.} + +\item{\code{extract_decor}}{\strong{Description} + +A function method for extracting 'decor' from the scale. The 'decor' acts +as a wildcard for anything the guide may need to render that is not based +on the key. For this reason, it has guide specific meaning that indicates +different things for different guides. In \code{guide_colourbar()} it is the +colour gradient, but in \code{guide_axis()} it is the axis line information. +It is appropriate to override in extensions. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$extract_decor(scale, aesthetic, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scale}}{A \verb{} ggproto object. In the case of position +guides, can be a \verb{} ggproto object.} +\item{\code{aesthetic}}{A scalar string specifying the aesthetic.} +\item{\code{...}}{Optional arguments from the \code{params} field.} +} + +\strong{Value} + +Undefined. \code{NULL} by default.} + +\item{\code{extract_params}}{\strong{Description} + +A function method for extracting any other information from the scale that +the guide may need. A typical example is to derive the title from the scale, +or apply any edits to the \code{key} or \code{decor} variables. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$extract_params(scale, params, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scale}}{A \verb{} ggproto object. In the case of position +guides, can be a \verb{} ggproto object.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field, +which at this point includes the \code{key} and \code{decor} from previous +extractors.} +\item{\code{...}}{Additional arguments passed from the \code{train()} method. For +non-position guides, often includes \code{title} as derived from the +\code{plot$labels} field.} +} + +\strong{Value} + +A modified list of parameters} + +\item{\code{transform}}{\strong{Description} + +A function method to apply coord transformation and munching to the +'key' and 'decor' parameters. This method only applies to position guides +like \code{guide_axis()} and is not called for non-position guides. It is +recommended to override this method if you have a position guide that +does not inherit from \code{GuideAxis} or has custom key' or 'decor' structures +that \code{GuideAxis$transform()} does not handle well. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$transform(params, coord, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{coord}}{A \verb{} ggproto object.} +\item{\code{...}}{Optional arguments, typically \code{panel_params} for most +position guides.} +} + +\strong{Value} + +A list of parameters. The default throws an error.} + +\item{\code{merge}}{\strong{Description} + +A function method for combining information from two guides. When two +guides have the same computed \code{hash} parameter derived from the \code{hashables} +field, this function will be called to merge them. If more than two guides +need to be merged, they are merged successively in a \code{Reduce()}-like +fashion. + +Merging guides is the mechanism by which \code{guide_legend()} can take one +guide trained on the \code{shape} scale, another trained on the \code{colour} scale +and display them together in the same guide, for example. + +Overriding this method is recommended if the extension descends directly +from \code{Guide} and not its children. Otherwise, it should be overridden if +presented with no superior options. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$merge(params, new_guide, new_params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters derived from the \code{params} field of +this guide.} +\item{\code{new_guide}}{A \verb{} ggproto object representing the other guide class} +\item{\code{new_params}}{A list of parameters derived from the \code{params} field +of the other guide} +} + +\strong{Value} + +A named list containing \code{guide} and \code{params}, where \code{guide} is a \verb{} +ggproto object and \code{params} is a list with parameters. By default, returns +the new guide and its parameters.} + +\item{\code{process_layers,get_layer_key}}{\strong{Description} + +These function methods extract information from layers that the guide may +need. The \code{process_layers()} method is tasked with selecting layers that +are represented by the guide and are to be included. The selected layers +should be passed on to the \code{get_layer_key()} method. + +Typical use of these methods is for \code{guide_legend()} to extract the +\code{Geom$draw_key} function to render glyphs in addition to any default or +fixed aesthetics. While these methods are called in position guides, +the \code{layers} and \code{data} arguments are empty as these are unavailable at +that point. + +You can override \code{get_layer_key()}, but \code{process_layers()} should +probably only be overridden if the extension does not inherit from +\code{GuideLegend}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$process_layers(params, layers, data, theme) +Guide$get_layer_key(params, layers, data, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{layers}}{A list of layers from \code{plot$layers}.} +\item{\code{data}}{A list of layer data frames.} +\item{\code{theme}}{A \link[=complete_theme]{completed theme} object.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{draw}}{\strong{Description} + +A function method is the main orchestrator for drawing the guide. It sets +up the final pieces in context of the position, direction and theme +and. Subsequenty, it renders the individual components like titles, ticks, +labels and decor. Finally, it arranges these components into a guide. + +This method should only be overridden if the extension has non standard +components that do not fit into 'decor' or when this method can be greatly +simplified for humble guides. All subsidiaries are fine to override. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Geom$setup_params(theme, position, direction, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{theme}}{A \link[=complete_theme]{complete theme} object.} +\item{\code{position}}{A scalar string indicating the position where +the guide should be drawn. Typically \code{"top"}, \code{"right"}, \code{"bottom"} +or \code{"left"}, unless it is a position guide for an exotic coord. Can be +\code{NULL}, in which case \code{params$position} should be used.} +\item{\code{direction}}{A scalar string indicating the legend direction. +Can be \code{NULL}, in which case \code{params$direction} should be used.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +} +\strong{Value} + +A grob with the guide.} + +\item{\code{draw_early_exit}}{\strong{Description} + +A function method that determines what should be drawn when the guide 'key' +is empty. The default method returns \code{\link[=zeroGrob]{zeroGrob()}}. You can override +this method if an empty key should draw anything. Used in \code{guide_axis()} +to render the \code{axis.line} part even if no ticks or labels should be drawn. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$draw_early_exit(params, elements) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method.} +} + +\strong{Value} + +A grob.} + +\item{\code{setup_params}}{\strong{Description} + +A function method for finalising parameters. Typically used to make checks +on the \code{params} object or to make any position or direction based +adjustments. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$setup_params(params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_elements,override_elements}}{\strong{Description} + +A function method for resolving required theme elements. The +\code{setup_elements()} method joins local guide theme with global theme and +calculates the necessary theme elements. The \code{override_elements()} method +is a hook to edit elements after they've been calculated. + +You can override the \code{setup_elements()} method if you need more complicated +theme handling before calculating elements or want to intervene in inheritance. +For example, \code{guide_legend()} has special handling of text margins and +\code{guide_axis()} suffixes \verb{\{aes\}.\{position\}} to get the theme elements for +the correct position. + +For other purposes, you can override the \code{override_elements()} method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$setup_elements(params, elements, theme) +Guide$override_elements(params, elements, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{elements}}{A named list of strings initiated by the \code{elements} field.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}} +} + +\strong{Value} + +A list of elements or resolved theme settings.} + +\item{\code{build_title}}{\strong{Description} + +A function method for rendering the title. Note that titles for position +guides are rendered by the Facet class and not this method. + +You can override this method if you need to render more than one title +(or none) or adjust margin settings. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$build_title(label, elements, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{label}}{A single string or expression with the title text.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method. The default method expects +\code{elements$title} to inherit from the \verb{} class.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +} + +\strong{Value} + +A grob representing the title.} + +\item{\code{build_ticks}}{\strong{Description} + +A function method for rendering tick marks. + +You can override this function if you don't need ticks or have completely +different logic on how these should be drawn. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$build_ticks(key, elements, params, position, length) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{key}}{A data frame with the key information derived from the +\code{extract_key()} method.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method. The default method expects +\code{elements$ticks} to inherit from the \verb{} class and +\code{elements$ticks_length} to be a scalar \verb{} object.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{position}}{A scalar string indicating the position. Due to +historic error this works in the opposite way to intuition: if you want +ticks for an axis at the bottom of a plot, you should use \code{"top"} here.} +\item{\code{length}}{A scalar \verb{} object giving the tick length.} +} + +\strong{Value} + +A grob representing tick marks.} + +\item{\code{build_labels}}{\strong{Description} + +A function method for rendering labels. The default method returns an +empty grob. It is recommended to override this method when your extension +directly descends from Guide. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$build_labels(key, elements, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{key}}{A data frame with the key information derived from the +\code{extract_key()} method.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method. Most non-default methods expects +\code{elements$text} to inherit from the \verb{}.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +} + +\strong{Value} + +A grob representing labels.} + +\item{\code{build_decor}}{\strong{Description} + +A function method for rendering decor. As the 'wildcard' component, this +can draw whatever component the guide needs that isn't already captured by +the key. The default method returns an empty grob. It is recommended to +override this method. + +For some examples: \code{guide_legend()} renders the keys with the glyphs, +\code{guide_colourbar()} renders the colour gradient rectangle and +\code{guide_axis()} renders the axis line. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$build_decor(decor, grobs, elements, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{decor}}{A data frame (or other structure) with information derived +from the \code{extract_decor()} method.} +\item{\code{grobs}}{A list with grobs generated by the other \verb{build_*} +methods.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method. Most non-default methods expects +\code{elements$text} to inherit from the \verb{}.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +} + +\strong{Value} + +A grob.} + +\item{\code{measure_grobs}}{\strong{Description} + +A function method for measuring grobs. In preparation for arranging grobs, +they often need to be measured to determine their widths and heights. +It is convention that every measurement is converted to centimetres. +You can override this method if your extension directly descends from +Guide, or the parent class measurement is defective. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$measure_grobs(grobs, params, elements) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{grobs}}{A list with grobs generated by the \verb{build_*} methods.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method.} +} + +\strong{Value} + +A named list or \verb{} vector giving sizes of components, coordinated +with \code{arrange_layout()} and \code{assemble_drawing()} methods. The default +method returns \code{NULL}.} + +\item{\code{arrange_layout}}{\strong{Description} + +A function method for determining the location or order of grobs in a +gtable. Typically determines rows and columns where decor and labels are +placed. Titles are added seperately.You can override this method if your +extension directly descends from Guide. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$arrange_layout(key, sizes, params, elements) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{key}}{A data frame with the key information derived from the +\code{extract_key()} method.} +\item{\code{sizes}}{A list of \verb{} vector from the \code{measure_grobs()} method.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method.} +} + +\strong{Value} + +Any structure holding placement information coordinated with the +\code{assemble_drawing()} method.} + +\item{\code{assemble_drawing}}{\strong{Description} + +A function method that takes measurements, placement information and grobs +and assembles these together in a gtable structure. You can override this +method if your extension directly descends from Guide, or the parent class +assembly does not work for your guide. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$assemble_drawing(grobs, layout, sizes, params, elements) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{grobs}}{A list with grobs generated by the \verb{build_*} methods.} +\item{\code{layout}}{A data structure from the \code{arrange_layout()} method.} +\item{\code{sizes}}{A list of \verb{} vector from the \code{measure_grobs()} method.} +\item{\code{params}}{A list of parameters initiated by the \code{params} field.} +\item{\code{elements}}{A list of elements or resolved theme settings from +the \code{override_elements()} method.} +} + +\strong{Value} + +A finished gtable containing the guide.} + +\item{\code{arrange_layout}}{\strong{Description} + +A function method for placing the title. It is a subsidiary method used +in the \code{assemble_drawing()} method for non-position guides. Titles are +typically added before \code{legend.margin} is applied. It is not +recommended to override this method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Guide$add_title(gtable, title, position, just) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{gtable}}{An unfinished gtable under construction in the +\code{assemble_drawing()} method.} +\item{\code{title}}{The grob resulting from the \code{build_title()} method.} +\item{\code{position}}{A scaler string, either \code{"top"}, \code{"right"}, \code{"bottom"} +or \code{"left"} corresponding to the \code{legend.title.position}.} +\item{\code{just}}{A named list having \code{hjust} and \code{vjust} components with +scalar numeric values between 0 and 1.} +} + +\strong{Value} + +The \code{gtable} argument with added title.} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Guide class names are in UpperCamelCase and start with the +\verb{Guide*} prefix, like \code{GuideNew}. + +A constructor function is usually paired with a Guide class. The constructor +wraps a call to \code{new_guide()}, where e.g. \code{new_guide(super = GuideNew)}. The +constructor name is formatted by taking the Guide class name and formatting +it with snake_case, so that \code{GuideNew} becomes \code{guide_new()}. +} + +\examples{ +# Extending the class +GuideDescribe <- ggproto( + "GuideDescribe", Guide, + # Fields + elements = list(text = "legend.text", margin = "legend.margin"), + hashables = rlang::exprs(key$.label), + + # Methods + build_title = function(...) zeroGrob(), # Turn off title + + build_labels = function(key, elements, params) { + labels <- key$.label + n <- length(labels) + labels <- paste0(paste0(labels[-n], collapse = ", "), ", and ", labels[n]) + labels <- paste0("A guide showing ", labels, " categories") + element_grob(elements$text, label = labels, margin_x = TRUE, margin_y = TRUE) + }, + + measure_grobs = function(grobs, params, elements) { + # Measuring in centimetres is the convention + width <- grid::convertWidth(grid::grobWidth(grobs$labels), "cm", valueOnly = TRUE) + height <- grid::convertHeight(grid::grobHeight(grobs$labels), "cm", valueOnly = TRUE) + list(width = unit(width, "cm"), height = unit(height, "cm")) + }, + + assemble_drawing = function(self, grobs, layout, sizes, params, elements) { + gt <- gtable::as.gtable(grobs$labels, width = sizes$width, height = sizes$height) + gt <- gtable::gtable_add_padding(gt, elements$margin) + gt + } +) + +# Building a constructor +guide_describe <- function(position = NULL) { + new_guide(position = position, super = GuideDescribe) +} + +# Use new guide plot +ggplot(mpg, aes(displ, hwy, colour = class)) + + geom_point() + + guides(colour = guide_describe("bottom")) +} +\seealso{ +Run \code{vignette("extending-ggplot2")}, in particular the "Creating +new guides" section. +} +\keyword{datasets} +\keyword{internal} diff --git a/man/Layer-class.Rd b/man/Layer-class.Rd new file mode 100644 index 0000000000..f5bc2a2022 --- /dev/null +++ b/man/Layer-class.Rd @@ -0,0 +1,437 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/layer.R +\docType{data} +\name{Layer-class} +\alias{Layer-class} +\alias{Layer} +\title{Layers} +\description{ +The Layer class is a chaperone class not available for extension. The class +fulfils the following tasks. The class houses the Geom, Stat and Position +trinity and tracks their stateful parameters. Furthermore, its methods are +responsible for managing the layer data and exposing it to other components +of the plot at the right time. +} +\details{ +The Layer class is an internal class that is not exported because the class +is not intended for extension. The \code{layer()} function instantiates the +LayerInstance class, which inherits from Layer, but has relevant fields +populated. + +The class is mostly used in \code{ggplot_build()}, with the notable exception +of the \code{draw_geom()} method, which is used in \code{ggplot_gtable()} instead. +} +\section{Fields}{ + +\describe{ +\item{\code{constructor}}{A \link[=call]{call} object with the user-facing +constructor function, for use in error messaging. This field is populated +by \code{layer()}.} + +\item{\code{geom,stat,position}}{These fields house the Geom, Stat and Position +trifecta in ggproto form and is populated by \code{layer()}.} + +\item{\code{stat_params,computed_stat_params}}{These fields hold parameters +assigned to the Stat. The \code{stat_params} field is directly derived from +user input and is populated by \code{layer()}. The \code{computed_stat_params} +carries state and is constructed by the \code{Stat$setup_params()} method.} + +\item{\code{geom_params,computed_geom_params}}{These fields hold parameters +assigned to the Geom. The \code{geom_params} field is directly derived from +user input and is populated by \code{layer()}. The \code{computed_geom_params} +carries state and is constructed by the \code{Geom$setup_params()} method.} + +\item{\code{mapping,computed_mapping}}{These fields hold \link[=aes]{mapping}s. +The \code{mapping} field holds the \code{layer(mapping)} argument. The +\code{computed_mapping} field carries state and is constructed in the +\code{setup_layer()} method.} + +\item{\code{data}}{The fortified \code{layer(data)} argument.} + +\item{\code{aes_params}}{Holds the fixed, unmapped aesthetics passed to +\code{layer(params)} as determined by \code{Geom$aesthetics()}.} + +\item{\code{inherit.aes}}{A scalar boolean used in the \code{setup_layer()} method to +indicate whether the \code{computed_mapping} should include the global mapping +(\code{TRUE}) or only the layer mapping (\code{FALSE}). This is populated by the +\code{layer(inherit.aes)} parameter.} + +\item{\code{layer_data}}{\strong{Description} + +A function method for initially resolving layer data. If layer data is +missing or is a function, it will derive layer data from the global plot +data. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$layer_data(plot_data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{plot_data}}{The \code{data} field of the ggplot object.} +} + +\strong{Value} + +A data frame with layer data or \code{NULL}} + +\item{\code{setup_layer}}{\strong{Description} + +A function method is a hook to allow a final access to layer data in +input form. In addition, it allows a layer access to global plot +information. The latter is used to enforce the \code{inherit.aes} parameter by +supplementing the layer mapping with the global mapping when requested. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$setup_data(data, plot) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{plot}}{A ggplot object} +} + +\strong{Value} + +A data frame with layer data. As a side effect, the \code{computed_mapping} +field is populated.} + +\item{\code{compute_aesthetics}}{\strong{Description} + +A function method that evaluates aesthetics and warns about any problems. +It also infers a \code{group} aesthetic if not provided. This method is also +the step where layer data becomes standardised to base data frames without +row names or additional attributes. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$compute_aesthetics(data, plot) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{plot}}{A ggplot object} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{compute_aesthetics}}{\strong{Description} + +A function method that orchestrates computing statistics. It executes +methods from the Stat class to form new computed variables. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$compute_statistic(data, layout) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{layout}}{A \verb{} ggproto object.} +} + +\strong{Value} + +A data frame with layer data. As a side effect the \code{computed_stat_params} +field is populated.} + +\item{\code{map_statistic}}{\strong{Description} + +A function method that finishes the result of computed statistics. It has +several tasks: +\itemize{ +\item It evaluates the \code{after_stat()} stage of the mapping from both the +\code{computed_mapping} but also the \code{Stat$default_aes} fields. +\item It ensures relevant scales are instantiated for computed aesthetics. +\item It takes care that scale transformation is applied to computed aesthetics. +} + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$map_statistic(data, plot) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{plot}}{A ggplot object.} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{compute_geom_1}}{\strong{Description} + +A function method that prepares data for drawing. It checks that all +required aesthetics are present and sets up parameters and data using the +Geom class. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$compute_geom_1(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +} + +\strong{Value} + +A data frame with layer data. As a side effect the \code{computed_geom_params} +field is populated.} + +\item{\code{compute_position}}{\strong{Description} + +A function method that orchestrates the position adjustment. It executes +methods from the Position class. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$compute_position(data, layout) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{layout}}{A \verb{} ggproto object.} +} + +\strong{Value} + +A data frame with layer data.} + +\item{\code{compute_geom_2}}{\strong{Description} + +A function method that add defaults and fixed parameters. It wraps the +\code{Geom$use_defaults()} method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$compute_geom_2(data, params, theme, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list with fixed aesthetic parameters, typically the +\code{aes_params} field.} +\item{\code{theme}}{A \link[=theme]{theme} object} +\item{\code{...}}{Passed on to \code{Geom$use_defaults()}, not in use.} +} + +\strong{Value} + +A data frame with layer data.} + +\item{\code{finish_statistics}}{\strong{Description} + +A function method that wraps \code{Stat$finish_layer()}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$finish_statistics(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +} + +\strong{Value} + +A data frame with layer data.} + +\item{\code{draw_geom}}{\strong{Description} + +A function method that produces graphics for every panel. It uses Geom +class methods to handle missing data and produce grobs. In contrast to +other methods, this is called during the \code{ggplot_gtable()} stage, not the +\code{ggplot_build()} stage. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$draw_geom(data, layout) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{layout}}{A \verb{} ggproto object.} +} + +\strong{Value} + +A list of grobs, one per panel.} + +\item{\code{print}}{\strong{Description} + +A function method that prints information about the layer. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layer$print() +}\if{html}{\out{
}} + +\strong{Value} + +Nothing (\code{NULL}), invisibly} +}} + +\section{Layer data diagram}{ + + +As the Layer class is a chaparone for the data, it makes sense to give a +small overview of how layer data flows through a plot. In the diagram below +we following the \code{layer(data)} argument over the course of plot building +through Layer class methods. When an outside class acts on the data without +the Layer class, this is indicated with the left arrow \verb{<-}. Subcomponents +of a method that touch data are indicated with the right arrow \verb{->}. + +\if{html}{\out{
}}\preformatted{# Inside `ggplot_build()` + | +layer(data) + | + | + | # Inherit plot data + | +Layer$layer_data() + | + | + | # Finalise mapping + | +Layer$setup_layer() + | + | + | # Append PANEL variable for facets + | + |<- Layout$setup() + | | + | +-> Facet$setup_data() + | | + | +-> Coord$setup_data() + | + | + | # Evaluate mappings to new data and infer group + | +Layer$compute_aesthetics() + | + | + | # Scale-transform all aesthetics + | + |<- ScalesList$transform_df() + | | + | +-> Scale$transform_df() + | + | + | # Map x/y aesthetics with initial scale + | + |<- Layout$map_position() + | | + | +-> Scale$map() + | + | + | # Compute stat part of layer + | +Layer$compute_statistic() + | | + | +-> Stat$setup_data() + | | + | +-> Stat$compute_layer() + | + | + | # Add `after_stat()` stage + | # Scale transform computed variables + | +Layer$map_statistic() + | + | + | # Setup geom part of layer + | +Layer$compute_geom_1() + | | + | +-> Geom$setup_data() + | + | + | # Apply position adjustments + | +Layer$compute_position() + | | + | +-> Position$use_defaults() + | | + | +-> Position$setup_data() + | | + | +-> Position$compute_layer() + | + | + | # Map x/y aesthetics with final scales + | + |<- Layout$map_position() + | | + | +-> Scale$map() + | + | # Map non-position aesthetics + | + |<- ScalesList$map_df() + | | + | +-> Scale$map() + | + | + | # Fill in defaults and fixed aesthetics + | +Layer$compute_geom_2() + | | + | +-> Geom$use_defaults() + | + | + | # Apply final Stat hook + | +Layer$finish_statistics() + | | + | +-> Stat$finish_layer() + | + | + | # Apply final Facet hook + | + |<- Layout$finish_data() + | | + | +-> Facet$finish_data() + | + V +# `ggplot_build()` is finished +# Hand off to `ggplot_gtable()` + | + | + | # Draw the geom part + | +Layer$draw_geom() + | + +-> Geom$handle_na() + | + +-> Geom$draw_layer() +}\if{html}{\out{
}} +} + +\examples{ +# None: Layer is not intended to be extended +} +\seealso{ +Other Layer components: +\code{\link{Geom}}, +\code{\link{Position}}, +\code{\link{Stat}} + +Other chaperone classes: +\code{\link{Layout}} +} +\concept{Layer components} +\concept{chaperone classes} +\keyword{internal} diff --git a/man/Layout.Rd b/man/Layout.Rd new file mode 100644 index 0000000000..1f0b7e71c9 --- /dev/null +++ b/man/Layout.Rd @@ -0,0 +1,308 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/layout.R +\docType{data} +\name{Layout} +\alias{Layout} +\title{Layout} +\description{ +The Layout class is a chaperone class discouraged for extension. The class +fulfils the following tasks. The class houses the Coord and Facet classes +and tracks their stateful parameters. In addition, it manages the position +scales for each panel. It is responsible for keeping track of panel +specifications and matching pieces of the data to scales and parameters in +panel-wise manners. +} +\details{ +The Layout class is only exported for extensions that re-implement a +\code{ggplot_build()} method for their specific class of plots. It is discouraged +to subclass the Layout class and for all purposes be considered an internal +structure. It has no user-facing constructor to put an small barrier in the +way. + +The class is used throughout \code{ggplot_build()}, with the notable exception of +the \code{render()} method, which is used in \code{ggplot_gtable()} instead. +} +\section{Fields}{ + +\describe{ +\item{\code{coord,coord_params}}{A \code{\link[=Coord]{}} ggproto object and a list +of the coordinate system's parameters. Parameters get populated by the +\code{Coord$setup_params()} method.} + +\item{\code{facet,facet_params}}{A \code{\link[=Facet]{}} ggproto object and a list +of the faceting specification's parameters. Parameters get populated by +the \code{Facet$setup_params()} method.} + +\item{\code{layout}}{A data frame with a row for each panel. The data frame +contains integer columns \code{PANEL}, \code{SCALE_X}, \code{SCALE_Y}, \code{ROW} and \code{COL} +representing a panel ID, scale indices and placement locations. In addition, +the layout may contain faceting variables or other additional information. +This field gets populated by the \code{Facet$compute_layout()} method.} + +\item{\code{panel_scales_x,panel_scales_y}}{A list of \code{x} and \code{y} position scales +parallel to the layout field's \code{SCALE_X} and \code{SCALE_Y} levels respectively. +This fields gets populated by the \code{Facet$init_scales()} method.} + +\item{\code{panel_params}}{A named list of parameters per panel populated by the +\code{Coord$setup_panel_params()} method. Contains \verb{} entries for +the \code{x} and \code{y} variables in addition to ranges and other information the +coordinate system might need to transform or render guides and grids.} + +\item{\code{setup}}{\strong{Description} + +A function method for setting up the relevant information for the layout +of the plot. It populates the \code{facet_params}, \code{coord_params} and \code{layout} +fields and appends a \code{PANEL} variable to the layer data. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$setup(data, plot_data, plot_env) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames with layer data.} +\item{\code{plot_data}}{The data frame in the \code{data} field of the ggplot +object.} +\item{\code{plot_env}}{The environment in the \code{plot_env} field of the +ggplot object.} +} + +\strong{Value} + +A list of data frames from the \code{data} argument with a \code{PANEL} variable +corresponding to rows in the \code{layout} field. +Also called for the side effects of populating fields.} + +\item{\code{train_position}}{\strong{Description} + +A function method for training position scales and optionally initiating +them. Implementation is via the \code{Facet$train_scales()} and +\code{Facet$init_scales()} methods. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$train_position(data, x_scale, y_scale) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames with layer data.} +\item{\code{x_scale},\code{y_scale}}{A single prototype position scale for the \code{x} +and \code{y} aesthetics respectively.} +} + +\strong{Value} + +Nothing, this method is called for the side effect of training scales and +optionally populating the \code{panel_scales_x} and \code{panel_scales_y} fields.} + +\item{\code{map_position}}{\strong{Description} + +A function method for mapping position aesthetics. For discrete scales this +converts discrete levels to a numeric representation, usually integers. For +continuous scales, this applies out-of-bounds handling. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$map_position(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames with layer data.} +} + +\strong{Value} + +A list of data frames per the \code{data} argument with mapped position +aesthetics.} + +\item{\code{reset_scales}}{\strong{Description} + +A function method for resetting scale ranges. After computing stats and +position adjustments, scales need to be reset and re-trained to have an +accurate measure of the data limits. This goes through the +\code{panel_scales_x} and \code{panel_scales_y} fields and invokes the +\code{Scale$reset()} method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$reset_scales() +}\if{html}{\out{
}} + +\strong{Value} + +Nothing, it is called for the side-effect of resetting scale ranges.} + +\item{\code{setup_panel_params}}{\strong{Description} + +A function method for executing \code{Coord$setup_panel_params()} once per panel +with the appropriate scales. For efficiency reasons, the setup is invoked +once per unique combination of \code{x} and \code{y} scale. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$setup_panel_params() +}\if{html}{\out{
}} + +\strong{Value} + +Nothing, it is called for the side effect of populating the \code{panel_params} +field.} + +\item{\code{setup_panel_guides}}{\strong{Description} + +A function method for setting up and training the position guides (axes) +once per panel with the appropriate scales. For efficiency reasons, +the guides are setup once per unique combination of \code{x} and \code{y} scale. +It calls the \code{Coord$setup_panel_guides()} and \code{Coord$train_panel_guides()} +methods. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$setup_panel_guides(guides, layers) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{guides}}{A \verb{} ggproto object from the \code{guides} field of +the ggplot object.} +\item{\code{layers}}{A list of layers from the \code{layers} field of the ggplot +object.} +} + +\strong{Value} + +Nothing, it is called for the side effect of augmenting each entry of the +\code{panel_params} field with position guides.} + +\item{\code{setup_panel_guides}}{\strong{Description} + +A function method for setting up the \code{Facet$finish_data()} hook. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$finish_data(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A list of data frames with layer data.} +} + +\strong{Value} + +A list of data frames with layer data.} + +\item{\code{render}}{\strong{Description} + +A function method for drawing and assembling the core plot. Mostly it +delegates tasks to the specific Facet methods for drawing components. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$render(panels, data, theme, labels) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{panels}}{A list parallel to layers. Each element is another list +with grobs for each panel, generated by \code{Layer$draw_geom()}.} +\item{\code{data}}{A list of data frames with layer data.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}.} +\item{\code{labels}}{A list of labels from the \code{labels} field of the ggplot +object.} +} + +\strong{Value} + +A gtable containing a plot with panels, axes, axis titles and strips.} + +\item{\code{resolve_label}}{\strong{Description} + +A function method for prying the axis titles from guides, scales or plot +labels. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$resolve_label(scale, labels) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{scale}}{A single scale from the \code{panel_scales_x} or +\code{panel_scales_y} fields.} +\item{\code{labels}}{A list of labels from the \code{labels} field of the ggplot +object.} +} + +\strong{Value} + +A named list containing a two titles named \code{"primary"} and \code{"secondary"}.} + +\item{\code{render_labels}}{\strong{Description} + +A function method for drawing axis title grobs. The position guides +themselves do not typically render the axis title grobs as they are +orchestrated by the layout to draw one title even for multiple axes. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$render_labels(labels, theme) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{labels}}{A named list containing an \code{x} list and a \code{y} list. The +\code{x} and \code{y} lists have \code{primary} and \code{secondary} labels. It originates +from the \code{Coord$labels()} method.} +\item{\code{theme}}{A \link[=complete_theme]{complete theme}.} +} + +\strong{Value} + +A list with the same structure and names as the \code{labels} argument, but with +grobs instead of text.} + +\item{\code{get_scales}}{\strong{Description} + +A function method for retrieving panel specific scales. It is called in +the \code{Stat$compute_layer()} and \code{Position$compute_layer()} methods. The +\code{Geom} uses the \code{panel_params} field instead of the raw scales. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Layout$get_scales(i) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{i}}{A scalar integer panel index giving the panel for which to +retrieve scales} +} + +\strong{Value} + +A named list of scales giving the \code{x} and \code{y} scale for the panel.} +}} + +\examples{ +# Some dummy layout components +facet <- facet_null() +coord <- coord_cartesian() + +# Use in custom `ggplot_build()` methods +layout <- ggproto(NULL, Layout, facet = facet, coord = coord) +} +\seealso{ +Other Layout components: +\code{\link{Coord}}, +\code{\link{Facet}} + +Other chaperone classes: +\code{\link{Layer-class}} +} +\concept{Layout components} +\concept{chaperone classes} +\keyword{internal} diff --git a/man/Position.Rd b/man/Position.Rd new file mode 100644 index 0000000000..c3bb3e741c --- /dev/null +++ b/man/Position.Rd @@ -0,0 +1,221 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/position-.R, R/position-dodge.R, +% R/position-dodge2.R, R/position-identity.R, R/position-jitter.R, +% R/position-jitterdodge.R, R/position-nudge.R, R/position-stack.R +\docType{data} +\name{Position} +\alias{Position} +\alias{PositionDodge} +\alias{PositionDodge2} +\alias{PositionIdentity} +\alias{PositionJitter} +\alias{PositionJitterdodge} +\alias{PositionNudge} +\alias{PositionStack} +\alias{PositionFill} +\title{Positions} +\description{ +All \verb{position_*()} functions (like \code{position_dodge()}) return a +\verb{Position*} object (like \code{PositionDodge}). The \verb{Position*} +object is responsible for adjusting the position of overlapping geoms. +} +\details{ +The way that the \verb{position_*} functions work is slightly different from +the \verb{geom_*} and \verb{stat_*} functions, because a \verb{position_*} +function actually "instantiates" the \verb{Position*} object by creating a +descendant, and returns that. The object is chaperoned by the \link{Layer} class. + +To create a new type of Position object, you typically will want to override +one or more of the following: +\itemize{ +\item The \code{required_aes} and \code{default_aes} fields. +\item The \code{setup_params()} and \code{setup_data()} methods. +\item One of the \code{compute_layer()} or \code{compute_panel()} methods. +} +} +\section{Fields}{ + +\describe{ +\item{\code{required_aes}}{A character vector naming aesthetics that are necessary +to compute the position adjustment.} + +\item{\code{default_aes}}{A \link[=aes]{mapping} of default values for aesthetics.} + +\item{\code{use_defaults}}{\strong{Description} + +A function method for completing the layer data by filling in default +position aesthetics that are not present. These can come from two sources: +either from the layer parameters as static, unmapped aesthetics or from +the \code{default_aes} field. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$use_defaults(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame of the layer's data} +\item{\code{params}}{A list of fixed aesthetic parameters} +} + +\strong{Value} + +A data frame with completed layer data} + +\item{\code{setup_params}}{\strong{Description} + +A function method for modifying or checking the parameters based on the +data. The default method returns an empty list. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$setup_params(data) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_data}}{\strong{Description} + +A function method for modifying or checking the data. The default method +checks for the presence of required aesthetics. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$setup_data(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{compute_layer}}{\strong{Description} + +A function method orchestrating the position adjust of the entire layer. +The default method splits the data and passes on adjustment tasks to the +panel-level \code{compute_panel()}. In addition, it finds the correct scales +in the layout object to pass to the panel computation. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$compute_layer(data, params, layout) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method} +\item{\code{layout}}{A \verb{} ggproto object.} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{compute_panel}}{\strong{Description} + +A function method executing the position adjustment at the panel level. +The default method is not implemented. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$compute_panel(data, params, scales) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method} +\item{\code{scales}}{A list of pre-trained \code{x} and \code{y} scales. Note that the +position scales are not finalised at this point and reflect the initial +data range before computing stats.} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{aesthetics}}{\strong{Description} + +A function method for listing out custom position aesthetics for this +position adjustment. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Position$aesthetics() +}\if{html}{\out{
}} + +\strong{Value} + +A character vector of aesthetic names.} +}} + +\section{Convention}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Position class name are in UpperCamelCase and start with the +\verb{Position*} prefix, like \code{PositionNew}. + +A constructor functions is usually paired with a Position class. The +constructor copies the position class and populates parameters. The +constructor function name is formatted by taking the Position class name and +formatting it with snake_case, so that \code{PositionNew} becomes \code{position_new()}. +} + +\examples{ +# Extending the class +PositionRank <- ggproto( + "PositionRank", Position, + # Fields + required_aes = c("x", "y"), + # Methods + setup_params = function(self, data) list(width = self$width), + compute_panel = function(data, params, scales) { + width <- params$width + if (is.null(width)) { + width <- resolution(data$x, zero = FALSE, TRUE) * 0.4 + } + rank <- stats::ave(data$y, data$group, FUN = rank) + rank <- scales::rescale(rank, to = c(-width, width) / 2) + data$x <- data$x + rank + data + } +) + +# Building a constructor +position_rank <- function(width = NULL) { + ggproto(NULL, PositionRank, width = width) +} + +# Use new position in plot +ggplot(mpg, aes(drv, displ)) + + geom_point(position = position_rank(width = 0.5)) +} +\seealso{ +The \href{https://ggplot2-book.org/extensions#new-positions}{new positions section} of the online ggplot2 book. + +Other Layer components: +\code{\link{Geom}}, +\code{\link{Layer-class}}, +\code{\link{Stat}} +} +\concept{Layer components} +\keyword{datasets} +\keyword{internal} diff --git a/man/Scale.Rd b/man/Scale.Rd new file mode 100644 index 0000000000..321fde0a4b --- /dev/null +++ b/man/Scale.Rd @@ -0,0 +1,451 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/scale-.R, R/scale-binned.R, +% R/scale-continuous.R, R/scale-date.R, R/scale-discrete-.R, +% R/scale-identity.R +\docType{data} +\name{Scale} +\alias{Scale} +\alias{ScaleContinuous} +\alias{ScaleDiscrete} +\alias{ScaleBinned} +\alias{ScaleBinnedPosition} +\alias{ScaleContinuousPosition} +\alias{ScaleContinuousDatetime} +\alias{ScaleContinuousDate} +\alias{ScaleDiscretePosition} +\alias{ScaleDiscreteIdentity} +\alias{ScaleContinuousIdentity} +\title{Scales} +\description{ +All \verb{scale_*()} functions (like \code{scale_fill_continuous()}) return a \verb{Scale*} +object. The main purpose of these objects is to translate data values to +aesthetic values and populating breaks and labels. +} +\details{ +All \verb{scale_*} functions like \code{\link[=scale_x_continuous]{scale_x_continuous()}} return a \verb{Scale*} object +like \code{ScaleContinuous}. Each of the \verb{Scale*} objects is a \code{\link[=ggproto]{ggproto()}} object +descended from the top-level \code{Scale}. + +Scales generally need to track three types of spaces: +\enumerate{ +\item Data space. These are values as they are evaluated from the plot +or layer mapping, prior to any transformation. +\item Transformed space. This is the space after original data has been +transformed. Effectively, scales internally operate in transformed space +in that ranges and breaks get passed around in this space. Discrete scales +don't do transformations, so for these scales, transformed space is the +same as untransformed space. +\item Aesthetic space. Graphic values that are mapped from the transformed +space. This is dependent on the \code{palette} field for most scales and on the +\code{rescaler} field for continuous position scales. +} + +The user is expected to give any vector-based \code{minor_breaks}, \code{breaks} or +\code{limits} in data space. When \code{breaks}, \code{limits} or \code{labels} is a function, +its input is expected to be in data space. + +Before you attempt to create a new \verb{Scale*} class of your own, it is +recommended to think through whether your aims cannot be implemented with +one of the existing classes. Making a wrapper for the \code{continuous_scale()}, +\code{discrete_scale()} and \code{binned_scale()} should cover many cases, and should +be considered prior to commiting to build a \verb{Scale*} extension. + +For example, if you aim to develop a scale for a new data type, it should +generally be possible to create a new \link[scales:new_transform]{transformation} +instead. One reason to implement your own \verb{Scale*} class is to accommodate +a data type does not lend itself for continuous or discrete range training. + +In such case, you can override the following: +\itemize{ +\item The \code{range} field. +\item The \code{transform}, \code{train} and \code{map} methods. +\item The \code{get_limits()}, \code{get_breaks()} and \code{get_labels()} methods. +} +} +\section{Fields}{ + +\describe{ +\item{\code{call}}{A \link[=call]{call} object with the user-facing constructor +function, for use in error messaging. This field is populated by scale +constructors.} + +\item{\code{range}}{A \code{\link[scales:Range]{Range}} class object, like +\code{scales::ContinuousRange} or \code{scales::DiscreteRange}. These are 'trained' +to keep track of the data range (continuous) or data levels (discrete). +Continuous ranges are tracked in transformed space.} + +\item{\code{aesthetics,palette,name,breaks,labels,limits,name,guide,position,na.value,expand}}{Fields populated by the scale constructor that can take on the same values +as described in e.g. \code{\link[=continuous_scale]{?continuous_scale}}. +Note that \code{limits} is expected in transformed space.} + +\item{\code{transform_df,transform}}{\strong{Description} + +A function method for orchestrating the transformation of aesthetics in a +data frame. Data transformation occurs before stats are computed. +The \code{transform_df()} method ensures the \code{transform()} method is applied +to the correct columns. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$transform_df(df) +Scale$transform(x) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{df}}{A data frame with the layer's data.} +\item{\code{x}}{A vector of the relevant aesthetic.} +} + +\strong{Value} + +For \code{transform()} a vector of transformed values. +For \code{transform_df()}, a named list with transformed values for each +transformed aesthetic.} + +\item{\code{train_df,train}}{\strong{Description} + +A function method for orchestrating scale training for keeping track of +the data range or levels. The \code{train_df()} method ensures the \code{train()} +method is applied to the correct columns. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$train_df(df) +Scale$train(x) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{df}}{A data frame with the layer's data.} +\item{\code{x}}{A vector of the relevant aesthetic.} +} + +\strong{Value} + +Nothing, these are called for their side effect of updating the \code{range} +field.} + +\item{\code{map_df,map}}{\strong{Description} + +A function method for orchestrating the mapping of data values to +aesthetics. The \code{map_df()} method ensures the \code{map()} method is applied +to the correct columns. When the scale uses a \code{palette()} function, it is +applied in the \code{map()} method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$map_df(df, i) +Scale$map(x, limits) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{df}}{A data frame with the layer's data.} +\item{\code{i}}{An integer vector giving an index to map a subset of data. +The default, \code{NULL}, will map all rows.} +\item{\code{x}}{A vector of the relevant aesthetic.} +\item{\code{limits}}{A vector of the relevant aesthetic, usually via +the \code{get_limits()} method.} +} + +\strong{Value} + +For \code{map()} a vector of mapped values in aesthetics space. +For \code{map_df()}, a named list with mapped values for each +aesthetic.} + +\item{\code{recale}}{\strong{Description} + +A function method for applying the recale function in the \code{rescaler} field. +It is used during the continuous \code{map()} and \code{Coord$transform()} methods +to ensure values are in the 0-1 range. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$rescale(x, limits, range) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{x}}{A vector of values to rescale. Can contain out-of-bounds +or missing values depending on the \code{map()} method.} +\item{\code{limits}}{A length two vector giving the limits of the relevant +aesthetic, usually via the \code{get_limits()} method.} +\item{\code{range}}{A length two vector giving the range that should coincide +with the 0-1 points. For most purpuses, this should be the same as the +\code{limits} argument.} +} + +\strong{Value} + +A vector of values between 0 and 1 for in-bounds values of \code{x}.} + +\item{\code{get_limits}}{\strong{Description} + +A function method for resolving user input and getting the scale limits. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$get_limits() +}\if{html}{\out{
}} + +\strong{Value} + +The scale limits, without any expansion applied, in transformed space.} + +\item{\code{dimension}}{\strong{Description} + +A function method for getting a continuous representation of the limits of +position scales. For continuous scales, the dimension is the same concept +as the limits. For discrete scales the dimension is the continuous range +occupied by the mapped breaks, which by default take integer positions. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$dimension(expand, limits) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{expand}}{A length 4 vector giving scale \link[=expansion]{expansion}. +This is optional and defaults to no expansion.} +\item{\code{limits}}{A vector of the relevant aesthetic, usually via +the \code{get_limits()} method.} +} + +\strong{Value} + +A numeric vector of length 2} + +\item{\code{get_breaks,get_breaks_minor}}{\strong{Description} + +A function method for resolving user input and getting the scale breaks +or minor breaks. Note that these may return out-of-bounds values for the +purpose of coordinating with the \code{get_labels()} method. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$get_breaks(limits) +Scale$get_breaks_minor(n, b, limits) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{limits}}{A vector of the relevant aesthetic, usually via +the \code{get_limits()} method.} +\item{\code{n}}{An integer setting the desired number of minor breaks per +major break. Note that the resulting minor breaks may coincide with +major breaks.} +\item{\code{b}}{A vector of mapped major breaks from the \code{get_breaks()} +method.} +} + +\strong{Value} + +A vector of breaks in transformed space.} + +\item{\code{get_labels}}{\strong{Description} + +A function method for resolving user input and getting the scale labels for +a set of breaks. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$get_labels(breaks) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{breaks}}{A vector of unmapped major breaks from the \code{get_breaks()} +method, in transformed space.} +} + +\strong{Value} + +A vector of labels of the same length as \code{breaks}.} + +\item{\code{get_transformation}}{\strong{Description} + +A helper method to access the scale's transformation object. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$get_transformation() +}\if{html}{\out{
}} + +\strong{Value} + +A \link[scales:new_transform]{transform} object.} + +\item{\code{break_info}}{\strong{Description} + +A function method for getting all break related information for position +scales. It is in use by coords that do not use the modern Guide system +and secondary axes. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$break_info(range) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{range}}{A vector of the relevant aesthetic.} +} + +\strong{Value} + +A named list with the following structure: +\itemize{ +\item \code{range} a length 2 vector giving continuous range +\item \code{labels} a character or expression vector of the same length as major breaks. +\item \code{major} a numeric vector with mapped numeric values for major breaks. +\item \code{major_source} a numeric vector with (transformed) data values for major breaks. +\item \code{minor} a numeric vector with mapped numeric values for minor breaks. +\item \code{minor_source} a numeric vector with (transformed) data values for minor breaks. +}} + +\item{\code{break_position}}{\strong{Description} + +A function method for getting mapped break positions. It is in use as a +default value in \code{get_breaks_minor()}, but is otherwise vestigial. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$break_info(range) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{range}}{A vector of the relevant aesthetic.} +} + +\strong{Value} + +A vector with mapped break positions} + +\item{\code{make_title,make_sec_title}}{\strong{Description} + +A function method for picking the title to use. This is usually called in +the \code{Guide$extract_params()} or \code{Layout$resolve_label()} methods. +The hierarchy of titles goes from guide (highest priority), to scale, to +labs (lowest priority). +When the guide or scale title are functions, they're applied to the next +in line. The \code{make_sec_title()} method by default re-uses the primary +\code{make_title()} method and only applies to position aesthetics. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$make_title(guide_title, scale_title, label_title) +Scale$make_sec_title(...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{guide_title}}{The \code{title} parameter coming from a guide.} +\item{\code{scale_title}}{The \code{name} field of the Scale.} +\item{\code{label_title}}{The relevant entry in the \code{plot$labels} field.} +\item{\code{...}}{By default, arguments forwarded to the \code{make_title()} +method} +} + +\strong{Value} + +A scalar character or expression title} + +\item{\code{axis_order}}{\strong{Description} + +A function method for setting the order of axes titles used to coordinate +with \code{Facet$draw_labels()}. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$axis_order() +}\if{html}{\out{
}} + +\strong{Value} + +Either \code{c("primary", "secondary")} or \code{c("secondary", "primary")}.} + +\item{\code{clone}}{\strong{Description} + +A function method for making an untrained copy of the scale. Due to +reference semantics of ggproto objects, in contrast to copy-on-modify +semantics, scales need to be cloned at the start of plot building. +The cloned scale can be trained independently of the original. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$clone() +}\if{html}{\out{
}} + +\strong{Value} + +A Scale object.} + +\item{\code{reset}}{\strong{Description} + +A function method for to reset the \code{range} field, effectively 'untraining' +the scale. This is used in the \code{Layout$reset_scales()} method, so that +scales can be re-trained on data with final position aesthetics. +For discrete scales, only the continuous range (\code{range_c}) is reset. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$clone() +}\if{html}{\out{
}} + +\strong{Value} + +None, called for the side-effect of resetting the range.} + +\item{\code{is_empty}}{\strong{Description} + +A function method for determining whether a scale is empty, i.e. when no +information with which to calculate limits. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$is_empty() +}\if{html}{\out{
}} + +\strong{Value} + +A scalar boolean value.} + +\item{\code{is_empty}}{\strong{Description} + +A function method for determining whether a scale is discrete. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Scale$is_discrete() +}\if{html}{\out{
}} + +\strong{Value} + +A scalar boolean value.} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Scale class names are in UpperCamelCase and start with the +\verb{Scale*} prefix, like \code{ScaleNew}. + +In scales, there is a difference between user-facing and developer-facing +constructors. Developer facing constructors have the shape +\verb{\{foundation\}_scale()}, like \code{discrete_scale()} corresponding to +\code{ScaleDiscrete}. User-facing constructors have the \verb{scale_\{aesthetic\}_\{type\}} +as name. If you implement a new \verb{Scale*} class, you like want both these +types of constructor. +} + +\examples{ +# TODO: find easy to digest example +NULL +} +\keyword{datasets} +\keyword{internal} diff --git a/man/Stat.Rd b/man/Stat.Rd new file mode 100644 index 0000000000..5291b47d13 --- /dev/null +++ b/man/Stat.Rd @@ -0,0 +1,301 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/stat-.R, R/stat-align.R, R/stat-bin.R, +% R/stat-summary-2d.R, R/stat-bin2d.R, R/stat-bindot.R, R/stat-binhex.R, +% R/stat-boxplot.R, R/stat-connect.R, R/stat-contour.R, R/stat-count.R, +% R/stat-density-2d.R, R/stat-density.R, R/stat-ecdf.R, R/stat-ellipse.R, +% R/stat-function.R, R/stat-identity.R, R/stat-manual.R, R/stat-qq-line.R, +% R/stat-qq.R, R/stat-quantilemethods.R, R/stat-smooth.R, R/stat-sum.R, +% R/stat-summary-bin.R, R/stat-summary-hex.R, R/stat-summary.R, +% R/stat-unique.R, R/stat-ydensity.R +\docType{data} +\name{Stat} +\alias{Stat} +\alias{StatAlign} +\alias{StatBin} +\alias{StatSummary2d} +\alias{StatBin2d} +\alias{StatBindot} +\alias{StatBinhex} +\alias{StatBoxplot} +\alias{StatConnect} +\alias{StatContour} +\alias{StatContourFilled} +\alias{StatCount} +\alias{StatDensity2d} +\alias{StatDensity2dFilled} +\alias{StatDensity} +\alias{StatEcdf} +\alias{StatEllipse} +\alias{StatFunction} +\alias{StatIdentity} +\alias{StatManual} +\alias{StatQqLine} +\alias{StatQq} +\alias{StatQuantile} +\alias{StatSmooth} +\alias{StatSum} +\alias{StatSummaryBin} +\alias{StatSummaryHex} +\alias{StatSummary} +\alias{StatUnique} +\alias{StatYdensity} +\title{Stats} +\description{ +All \verb{stat_*()} functions (like \code{stat_bin()}) return a layer that +contains a \verb{Stat*} object (like \code{StatBin}). The \verb{Stat*} +object is responsible for rendering the data in the plot. +} +\details{ +Each of the \verb{Stat*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended +from the top-level \code{Stat}, and each implements various methods and +fields. The object and its parameters are chaperoned by the \link{Layer} class. + +To create a new type of Stat object, you typically will want to +override one or more of the following: +\itemize{ +\item The \code{required_aes} and \code{default_aes} fields. +\item One of the \code{compute_layer()}, \code{compute_panel()} or \code{compute_group()} +functions. Typically it best to implement \code{compute_group()} and use the +higher-up methods when there are substantial performance improvements to +be gained. +\item The \code{finish_layer()} method +} +} +\section{Fields}{ + +\describe{ +\item{\code{required_aes}}{A character vector naming aesthetics that are necessary +to compute the stat.} + +\item{\code{non_missing_aes}}{A character vector naming aesthetics that will cause +removal if they have missing values.} + +\item{\code{optional_aes}}{A character vector naming aesthetics that will be +accepted by \code{layer()}, but are not required or dscribed in the \code{default_aes} +field.} + +\item{\code{default_aes}}{A \link[=aes]{mapping} of default values for aesthetics. +Aesthetics can be set to \code{NULL} to be included as optional aesthetic.} + +\item{\code{dropped_aes}}{A character vector naming aesthetics that can be dropped +from the data without warning. Typically used for aesthetics that are +'consumed' during computation like \code{"weight"}.} + +\item{\code{extra_params}}{A character vector of parameter names in addition to +those imputed from the \code{compute_panel()} or \code{compute_groups()} methods. +This field can be set to include parameters for \code{setup_data()} methods. +By default, this only contains \code{"na.rm"}.} + +\item{\code{retransform}}{A scalar boolean: should the values produced by the +statistic also be transformed in the second pass when recently added +statistics are trained to the scales} + +\item{\code{setup_params}}{\strong{Description} + +A function method for modifying or checking the parameters based on the +data. The default method returns the parameters unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$setup_params(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of current parameters} +} + +\strong{Value} + +A list of parameters} + +\item{\code{setup_data}}{\strong{Description} + +A function method for modifying or checking the data. The default method +returns data unaltered. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$setup_data(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters coming from the \code{setup_params()} +method} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{compute_layer}}{\strong{Description} + +A function method for orchestrating the computation of the statistic. The +default method splits the data and passes on computation tasks to the +panel-level \code{compute_panel()} method. In addition, the default method +handles missing values by removing rows that have missing values for the +aesthetics listed in the \code{required_aes} and \code{non_missing_aes} fields. It is +not recommended to use this method as an extension point. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$compute_layer(data, params, layout) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{params}}{A list of parameters} +\item{\code{layout}}{A pre-trained \verb{} ggproto object.} +} + +\strong{Value} + +A data frame with computed data} + +\item{\code{compute_panel,compute_group}}{\strong{Description} + +A function method orchestrating the computation of statistics for a single +panel or group. The default \code{compute_panel()} method splits the data into +groups, and passes on computation tasks to the \code{compute_group()} method. +In addition, \code{compute_panel()} is tasked with preserving aesthetics that +are constant within a group and preserving these if the computation loses +them. The default \code{compute_group()} is not implemented. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$compute_panel(data, scales, ...) +Stat$compute_group(data, scales, ...) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with the layer's data.} +\item{\code{scales}}{A list of pre-trained \code{x} and \code{y} scales. Note that the +position scales are not finalised at this point and reflect the initial +data range before computing stats.} +\item{\code{...}}{Reserved for extensions. By default, this passes parameters +to the \code{compute_group()} method.} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{finish_layer}}{\strong{Description} + +A function method acting as a hook to modify data after scales have been +applied, but before geoms have to render. The default is to pass the data +unaltered. This can be used as an extension point when actual aesthetic +values rather than values mapped to the aesthetic are needed. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$finish_layer(data, params) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{data}}{A data frame with layer data} +\item{\code{params}}{A list of parameters} +} + +\strong{Value} + +A data frame with layer data} + +\item{\code{parameters}}{\strong{Description} + +A function method for listing out all acceptable parameters for this stat. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$parameters(extra) +}\if{html}{\out{
}} + +\strong{Arguments} +\describe{ +\item{\code{extra}}{A boolean: whether to include the \code{extra_params} field.} +} + +\strong{Value} + +A character vector of parameter names.} + +\item{\code{aesthetics}}{\strong{Description} + +A function method for listing out all acceptable aesthetics for this stat. + +\strong{Usage} + +\if{html}{\out{
}}\preformatted{Stat$aesthetics() +}\if{html}{\out{
}} + +\strong{Value} + +A character vector of aesthetic names.} +}} + +\section{Conventions}{ + + +The object name that a new class is assigned to is typically the same as the +class name. Stat class names are in UpperCamelCase and start with the \verb{Stat*} +prefix, like \code{StatNew}. + +A constructor function is usually paired wih a Stat class. The constructor +wraps a call to \code{layer()}, where e.g. \code{layer(stat = StatNew)}. The +constructor function name is formatted by taking the Stat class name and +formatting it with snake_case, so that \code{StatNew} becomes \code{stat_new()}. +} + +\examples{ +# Extending the class +StatKmeans <- ggproto( + "StatKmeans", Stat, + # Fields + required_aes = c("x", "y"), + # You can relate computed variables to aesthetics using `after_stat()` + # in defaults + default_aes = aes(colour = after_stat(cluster)), + # Methods + compute_panel = function(data, scales, k = 2L) { + km <- kmeans(cbind(scale(data$x), scale(data$y)), centers = k) + data$cluster <- factor(km$cluster) + data + } +) + +# Building a constructor +stat_kmeans <- function(mapping = NULL, data = NULL, geom = "point", + position = "identity", ..., k = 2L, na.rm = FALSE, + show.legend = NA, inherit.aes = TRUE) { + layer( + mapping = mapping, data = data, + geom = geom, stat = StatKmeans, position = position, + show.legend = show.legend, inherit.aes = inherit.aes, + params = list(na.rm = na.rm, k = k, ...) + ) +} + +# Use new stat in plot +ggplot(mpg, aes(displ, hwy)) + + stat_kmeans(k = 3) +} +\seealso{ +The \href{https://ggplot2-book.org/extensions#sec-new-stats}{new stats section} of the online ggplot2 book.. + +Run \code{vignette("extending-ggplot2")}, in particular the "Creating a +new stat" section. + +Other Layer components: +\code{\link{Geom}}, +\code{\link{Layer-class}}, +\code{\link{Position}} +} +\concept{Layer components} +\keyword{datasets} +\keyword{internal} diff --git a/man/ggplot2-ggproto.Rd b/man/ggplot2-ggproto.Rd index dcd57c12a1..d608a38c55 100644 --- a/man/ggplot2-ggproto.Rd +++ b/man/ggplot2-ggproto.Rd @@ -1,604 +1,15 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/aaa-.R, R/geom-.R, R/annotation-custom.R, -% R/annotation-logticks.R, R/geom-polygon.R, R/geom-map.R, R/annotation-map.R, -% R/geom-raster.R, R/annotation-raster.R, R/axis-secondary.R, R/coord-.R, -% R/coord-cartesian-.R, R/coord-fixed.R, R/coord-flip.R, R/coord-map.R, -% R/coord-polar.R, R/coord-quickmap.R, R/coord-radial.R, R/coord-transform.R, -% R/facet-.R, R/facet-grid-.R, R/facet-null.R, R/facet-wrap.R, R/stat-.R, -% R/geom-abline.R, R/geom-rect.R, R/geom-bar.R, R/geom-tile.R, R/geom-bin2d.R, -% R/geom-blank.R, R/geom-boxplot.R, R/geom-col.R, R/geom-path.R, -% R/geom-contour.R, R/geom-crossbar.R, R/geom-segment.R, R/geom-curve.R, -% R/geom-ribbon.R, R/geom-density.R, R/geom-density2d.R, R/geom-dotplot.R, -% R/geom-errorbar.R, R/geom-function.R, R/geom-hex.R, R/geom-hline.R, -% R/geom-label.R, R/geom-linerange.R, R/geom-point.R, R/geom-pointrange.R, -% R/geom-quantile.R, R/geom-rug.R, R/geom-smooth.R, R/geom-spoke.R, -% R/geom-text.R, R/geom-violin.R, R/geom-vline.R, R/guide-.R, R/guide-axis.R, -% R/guide-axis-logticks.R, R/guide-axis-stack.R, R/guide-axis-theta.R, -% R/guide-legend.R, R/guide-bins.R, R/guide-colorbar.R, R/guide-colorsteps.R, -% R/guide-custom.R, R/guide-none.R, R/guide-old.R, R/layout.R, R/position-.R, -% R/position-dodge.R, R/position-dodge2.R, R/position-identity.R, -% R/position-jitter.R, R/position-jitterdodge.R, R/position-nudge.R, -% R/position-stack.R, R/scale-.R, R/scale-binned.R, R/scale-continuous.R, -% R/scale-date.R, R/scale-discrete-.R, R/scale-identity.R, R/stat-align.R, -% R/stat-bin.R, R/stat-summary-2d.R, R/stat-bin2d.R, R/stat-bindot.R, -% R/stat-binhex.R, R/stat-boxplot.R, R/stat-connect.R, R/stat-contour.R, -% R/stat-count.R, R/stat-density-2d.R, R/stat-density.R, R/stat-ecdf.R, -% R/stat-ellipse.R, R/stat-function.R, R/stat-identity.R, R/stat-manual.R, -% R/stat-qq-line.R, R/stat-qq.R, R/stat-quantilemethods.R, R/stat-smooth.R, -% R/stat-sum.R, R/stat-summary-bin.R, R/stat-summary-hex.R, R/stat-summary.R, -% R/stat-unique.R, R/stat-ydensity.R +% Please edit documentation in R/aaa-.R, R/axis-secondary.R \docType{data} \name{ggplot2-ggproto} \alias{ggplot2-ggproto} -\alias{Geom} -\alias{GeomCustomAnn} -\alias{GeomLogticks} -\alias{GeomPolygon} -\alias{GeomMap} -\alias{GeomAnnotationMap} -\alias{GeomRaster} -\alias{GeomRasterAnn} \alias{AxisSecondary} -\alias{Coord} -\alias{CoordCartesian} -\alias{CoordFixed} -\alias{CoordFlip} -\alias{CoordMap} -\alias{CoordPolar} -\alias{CoordQuickmap} -\alias{CoordRadial} -\alias{CoordTrans} -\alias{Facet} -\alias{FacetGrid} -\alias{FacetNull} -\alias{FacetWrap} -\alias{Stat} -\alias{GeomAbline} -\alias{GeomRect} -\alias{GeomBar} -\alias{GeomTile} -\alias{GeomBin2d} -\alias{GeomBlank} -\alias{GeomBoxplot} -\alias{GeomCol} -\alias{GeomPath} -\alias{GeomLine} -\alias{GeomStep} -\alias{GeomContour} -\alias{GeomContourFilled} -\alias{GeomCrossbar} -\alias{GeomSegment} -\alias{GeomCurve} -\alias{GeomRibbon} -\alias{GeomArea} -\alias{GeomDensity} -\alias{GeomDensity2d} -\alias{GeomDensity2dFilled} -\alias{GeomDotplot} -\alias{GeomErrorbar} -\alias{GeomErrorbarh} -\alias{GeomFunction} -\alias{GeomHex} -\alias{GeomHline} -\alias{GeomLabel} -\alias{GeomLinerange} -\alias{GeomPoint} -\alias{GeomPointrange} -\alias{GeomQuantile} -\alias{GeomRug} -\alias{GeomSmooth} -\alias{GeomSpoke} -\alias{GeomText} -\alias{GeomViolin} -\alias{GeomVline} -\alias{Guide} -\alias{GuideAxis} -\alias{GuideAxisLogticks} -\alias{GuideAxisStack} -\alias{GuideAxisTheta} -\alias{GuideLegend} -\alias{GuideBins} -\alias{GuideColourbar} -\alias{GuideColoursteps} -\alias{GuideCustom} -\alias{GuideNone} -\alias{GuideOld} -\alias{Layout} -\alias{Position} -\alias{PositionDodge} -\alias{PositionDodge2} -\alias{PositionIdentity} -\alias{PositionJitter} -\alias{PositionJitterdodge} -\alias{PositionNudge} -\alias{PositionStack} -\alias{PositionFill} -\alias{Scale} -\alias{ScaleContinuous} -\alias{ScaleDiscrete} -\alias{ScaleBinned} -\alias{ScaleBinnedPosition} -\alias{ScaleContinuousPosition} -\alias{ScaleContinuousDatetime} -\alias{ScaleContinuousDate} -\alias{ScaleDiscretePosition} -\alias{ScaleDiscreteIdentity} -\alias{ScaleContinuousIdentity} -\alias{StatAlign} -\alias{StatBin} -\alias{StatSummary2d} -\alias{StatBin2d} -\alias{StatBindot} -\alias{StatBinhex} -\alias{StatBoxplot} -\alias{StatConnect} -\alias{StatContour} -\alias{StatContourFilled} -\alias{StatCount} -\alias{StatDensity2d} -\alias{StatDensity2dFilled} -\alias{StatDensity} -\alias{StatEcdf} -\alias{StatEllipse} -\alias{StatFunction} -\alias{StatIdentity} -\alias{StatManual} -\alias{StatQqLine} -\alias{StatQq} -\alias{StatQuantile} -\alias{StatSmooth} -\alias{StatSum} -\alias{StatSummaryBin} -\alias{StatSummaryHex} -\alias{StatSummary} -\alias{StatUnique} -\alias{StatYdensity} \title{Base ggproto classes for ggplot2} \description{ If you are creating a new geom, stat, position, or scale in another package, you'll need to extend from \code{ggplot2::Geom}, \code{ggplot2::Stat}, \code{ggplot2::Position}, or \code{ggplot2::Scale}. } -\section{Geoms}{ - - -All \verb{geom_*()} functions (like \code{geom_point()}) return a layer that -contains a \verb{Geom*} object (like \code{GeomPoint}). The \verb{Geom*} -object is responsible for rendering the data in the plot. - -Each of the \verb{Geom*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended -from the top-level \code{Geom}, and each implements various methods and -fields. - -Compared to \code{Stat} and \code{Position}, \code{Geom} is a little -different because the execution of the setup and compute functions is -split up. \code{setup_data} runs before position adjustments, and -\code{draw_layer()} is not run until render time, much later. - -To create a new type of Geom object, you typically will want to -override one or more of the following: -\itemize{ -\item Either \code{draw_panel(self, data, panel_params, coord)} or -\code{draw_group(self, data, panel_params, coord)}. \code{draw_panel} is -called once per panel, \code{draw_group} is called once per group. - -Use \code{draw_panel} if each row in the data represents a -single element. Use \code{draw_group} if each group represents -an element (e.g. a smooth, a violin). - -\code{data} is a data frame of scaled aesthetics. - -\code{panel_params} is a set of per-panel parameters for the -\code{coord}. Generally, you should consider \code{panel_params} -to be an opaque data structure that you pass along whenever you call -a coord method. - -You must always call \code{coord$transform(data, panel_params)} to -get the (position) scaled data for plotting. To work with -non-linear coordinate systems, you typically need to convert into a -primitive geom (e.g. point, path or polygon), and then pass on to the -corresponding draw method for munching. - -Must return a grob. Use \code{\link[=zeroGrob]{zeroGrob()}} if there's nothing to -draw. -\item \code{draw_key}: Renders a single legend key. -\item \code{required_aes}: A character vector of aesthetics needed to -render the geom. -\item \code{default_aes}: A list (generated by \code{\link[=aes]{aes()}} of -default values for aesthetics. -\item \code{setup_data}: Converts width and height to xmin and xmax, -and ymin and ymax values. It can potentially set other values as well. -} - -See also the \href{https://ggplot2-book.org/extensions#sec-new-geoms}{new geoms section} of the online ggplot2 book. -} - -\section{Coordinate systems}{ - - -All \verb{coord_*()} functions (like \code{coord_trans()}) return a \verb{Coord*} -object (like \code{CoordTrans}). - -Each of the \verb{Coord*} objects is a \code{\link[=ggproto]{ggproto()}} object, -descended from the top-level \code{Coord}. To create a new type of Coord -object, you typically will want to implement one or more of the following: -\itemize{ -\item \code{aspect}: Returns the desired aspect ratio for the plot. -\item \code{labels}: Returns a list containing labels for x and y. -\item \code{render_fg}: Renders foreground elements. -\item \code{render_bg}: Renders background elements. -\item \code{render_axis_h}: Renders the horizontal axes. -\item \code{render_axis_v}: Renders the vertical axes. -\item \code{backtransform_range(panel_params)}: Extracts the panel range provided -in \code{panel_params} (created by \code{setup_panel_params()}, see below) and -back-transforms to data coordinates. This back-transformation can be needed -for coords such as \code{coord_trans()} where the range in the transformed -coordinates differs from the range in the untransformed coordinates. Returns -a list of two ranges, \code{x} and \code{y}, and these correspond to the variables -mapped to the \code{x} and \code{y} aesthetics, even for coords such as \code{coord_flip()} -where the \code{x} aesthetic is shown along the y direction and vice versa. -\item \code{range(panel_params)}: Extracts the panel range provided -in \code{panel_params} (created by \code{setup_panel_params()}, see below) and -returns it. Unlike \code{backtransform_range()}, this function does not perform -any back-transformation and instead returns final transformed coordinates. Returns -a list of two ranges, \code{x} and \code{y}, and these correspond to the variables -mapped to the \code{x} and \code{y} aesthetics, even for coords such as \code{coord_flip()} -where the \code{x} aesthetic is shown along the y direction and vice versa. -\item \code{transform}: Transforms x and y coordinates. -\item \code{distance}: Calculates distance. -\item \code{is_linear}: Returns \code{TRUE} if the coordinate system is -linear; \code{FALSE} otherwise. -\item \code{is_free}: Returns \code{TRUE} if the coordinate system supports free -positional scales; \code{FALSE} otherwise. -\item \code{setup_panel_params(scale_x, scale_y, params)}: Determines the appropriate -x and y ranges for each panel, and also calculates anything else needed to -render the panel and axes, such as tick positions and labels for major -and minor ticks. Returns all this information in a named list. -\item \code{setup_data(data, params)}: Allows the coordinate system to -manipulate the plot data. Should return list of data frames. -\item \code{setup_layout(layout, params)}: Allows the coordinate -system to manipulate the \code{layout} data frame which assigns -data to panels and scales. -} - -See also the \href{https://ggplot2-book.org/extensions#sec-new-coords}{new coords section} of the online ggplot2 book. -} - -\section{Facets}{ - - -All \verb{facet_*} functions returns a \code{Facet} object or an object of a -\code{Facet} subclass. This object describes how to assign data to different -panels, how to apply positional scales and how to lay out the panels, once -rendered. - -Extending facets can range from the simple modifications of current facets, -to very laborious rewrites with a lot of \code{\link[gtable:gtable]{gtable()}} -manipulation. For some examples of both, please see the extension vignette. - -\code{Facet} subclasses, like other extendible ggproto classes, have a range -of methods that can be modified. Some of these are required for all new -subclasses, while other only need to be modified if need arises. - -The required methods are: -\itemize{ -\item \code{compute_layout}: Based on layer data compute a mapping between -panels, axes, and potentially other parameters such as faceting variable -level etc. This method must return a data.frame containing at least the -columns \code{PANEL}, \code{SCALE_X}, and \code{SCALE_Y} each containing -integer keys mapping a PANEL to which axes it should use. In addition the -data.frame can contain whatever other information is necessary to assign -observations to the correct panel as well as determining the position of -the panel. -\item \code{map_data}: This method is supplied the data for each layer in -turn and is expected to supply a \code{PANEL} column mapping each row to a -panel defined in the layout. Additionally this method can also add or -subtract data points as needed e.g. in the case of adding margins to -\code{facet_grid()}. -\item \code{draw_panels}: This is where the panels are assembled into a -\code{gtable} object. The method receives, among others, a list of grobs -defining the content of each panel as generated by the Geoms and Coord -objects. The responsibility of the method is to decorate the panels with -axes and strips as needed, as well as position them relative to each other -in a gtable. For some of the automatic functions to work correctly, each -panel, axis, and strip grob name must be prefixed with "panel", "axis", and -"strip" respectively. -} - -In addition to the methods described above, it is also possible to override -the default behaviour of one or more of the following methods: -\itemize{ -\item \code{setup_params}: -\item \code{setup_panel_params}: modifies the x and y ranges for each panel. This is -used to allow the \code{Facet} to interact with the \code{panel_params}. -\item \code{init_scales}: Given a master scale for x and y, create panel -specific scales for each panel defined in the layout. The default is to -simply clone the master scale. -\item \code{train_scales}: Based on layer data train each set of panel -scales. The default is to train it on the data related to the panel. -\item \code{finish_data}: Make last-minute modifications to layer data -before it is rendered by the Geoms. The default is to not modify it. -\item \code{draw_back}: Add a grob in between the background defined by the -Coord object (usually the axis grid) and the layer stack. The default is to -return an empty grob for each panel. -\item \code{draw_front}: As above except the returned grob is placed -between the layer stack and the foreground defined by the Coord object -(usually empty). The default is, as above, to return an empty grob. -\item \code{draw_facet_panels}: Draws each panel for the facet. Should return a list -of grobs, one for each panel. The output is used by the \code{draw_panels} -method. -\item \code{draw_labels}: Given the gtable returned by \code{draw_panels}, -add axis titles to the gtable. The default is to add one title at each side -depending on the position and existence of axes. -} - -All extension methods receive the content of the params field as the params -argument, so the constructor function will generally put all relevant -information into this field. The only exception is the \code{shrink} -parameter which is used to determine if scales are retrained after Stat -transformations has been applied. - -See also the \href{https://ggplot2-book.org/extensions#new-facets}{new facets section} of the online ggplot2 book. -} - -\section{Stats}{ - - -All \verb{stat_*()} functions (like \code{stat_bin()}) return a layer that -contains a \verb{Stat*} object (like \code{StatBin}). The \verb{Stat*} -object is responsible for rendering the data in the plot. - -Each of the \verb{Stat*} objects is a \code{\link[=ggproto]{ggproto()}} object, descended -from the top-level \code{Stat}, and each implements various methods and -fields. To create a new type of Stat object, you typically will want to -override one or more of the following: -\itemize{ -\item One of : -\code{compute_layer(self, data, scales, ...)}, -\code{compute_panel(self, data, scales, ...)}, or -\code{compute_group(self, data, scales, ...)}. - -\code{compute_layer()} is called once per layer, \code{compute_panel()} -is called once per panel, and \code{compute_group()} is called once per -group. All must return a data frame. - -It's usually best to start by overriding \code{compute_group}: if -you find substantial performance optimisations, override higher up. -You'll need to read the source code of the default methods to see -what else you should be doing. - -\code{data} is a data frame containing the variables named according -to the aesthetics that they're mapped to. \code{scales} is a list -containing the \code{x} and \code{y} scales. There functions are called -before the facets are trained, so they are global scales, not local -to the individual panels.\code{...} contains the parameters returned by -\code{setup_params()}. -\item \code{finish_layer(data, params)}: called once for each layer. Used -to modify the data after scales has been applied, but before the data is -handed of to the geom for rendering. The default is to not modify the -data. Use this hook if the stat needs access to the actual aesthetic -values rather than the values that are mapped to the aesthetic. -\item \code{setup_params(data, params)}: called once for each layer. -Used to setup defaults that need to complete dataset, and to inform -the user of important choices. Should return list of parameters. -\item \code{setup_data(data, params)}: called once for each layer, -after \code{setup_params()}. Should return modified \code{data}. -Default methods removes all rows containing a missing value in -required aesthetics (with a warning if \code{!na.rm}). -\item \code{required_aes}: A character vector of aesthetics needed to -render the geom. -\item \code{default_aes}: A list (generated by \code{\link[=aes]{aes()}} of -default values for aesthetics. -\item \code{dropped_aes} is a vecor of aesthetic names that are safe to drop after -statistical transformation. A classic example is the \code{weight} aesthetic -that is consumed during computation of the stat. -} - -See also the \href{https://ggplot2-book.org/extensions#sec-new-stats}{new stats section} of the online ggplot2 book. -} - -\section{Guides}{ - - -The \verb{guide_*()} functions, such as \code{guide_legend()} return an object that -is responsible for displaying how objects in the plotting panel are related -to actual values. - -Each of the \verb{Guide*} object is a \code{\link[=ggproto]{ggproto()}} object, descended from the -top-level \code{Guide}, and each implements their own methods for drawing. - -To create a new type of Guide object, you typically will want to override -one or more of the following: - -Properties: -\itemize{ -\item \code{available_aes} A \code{character} vector with aesthetics that this guide -supports. The value \code{"any"} indicates all non-position aesthetics. -\item \code{params} A named \code{list} of parameters that the guide needs to function. -It has the following roles: -\itemize{ -\item \code{params} provides the defaults for a guide. -\item \code{names(params)} determines what are valid arguments to \code{new_guide()}. -Some parameters are \emph{required} to render the guide. These are: \code{title}, -\code{name}, \code{position}, \code{direction}, \code{order} and \code{hash}. -\item During build stages, \code{params} holds information about the guide. -} -\item \code{elements} A named list of \code{character}s, giving the name of theme elements -that should be retrieved automatically, for example \code{"legend.text"}. -\item \code{hashables} An \code{expression} that can be evaluated in the context of -\code{params}. The hash of the evaluated expression determines the merge -compatibility of guides, and is stored in \code{params$hash}. -} - -Methods: -\itemize{ -\item \code{extract_key()} Returns a \code{data.frame} with (mapped) breaks and labels -extracted from the scale, which will be stored in \code{params$key}. -\item \code{extract_decor()} Returns a \code{data.frame} containing other structured -information extracted from the scale, which will be stored in -\code{params$decor}. The \code{decor} has a guide-specific meaning: it is the bar in -\code{guide_colourbar()}, but specifies the \code{axis.line} in \code{guide_axis()}. -\item \code{extract_params()} Updates the \code{params} with other, unstructured -information from the scale. An example of this is inheriting the guide's -title from the \code{scale$name} field. -\item \code{transform()} Updates the \code{params$key} based on the coordinates. This -applies to position guides, as it rescales the aesthetic to the [0, 1] -range. -\item \code{merge()} Combines information from multiple guides with the same -\code{params$hash}. This ensures that e.g. \code{guide_legend()} can display both -\code{shape} and \code{colour} in the same guide. -\item \code{process_layers()} Extract information from layers. This acts mostly -as a filter for which layers to include and these are then (typically) -forwarded to \code{get_layer_key()}. -\item \code{get_layer_key()} This can be used to gather information about how legend -keys should be displayed. -\item \code{setup_params()} Set up parameters at the beginning of drawing stages. -It can be used to overrule user-supplied parameters or perform checks on -the \code{params} property. -\item \code{override_elements()} Take populated theme elements derived from the -\code{elements} property and allows overriding these theme settings. -\item \code{build_title()} Render the guide's title. -\item \code{build_labels()} Render the guide's labels. -\item \code{build_decor()} Render the \code{params$decor}, which is different for every -guide. -\item \code{build_ticks()} Render tick marks. -\item \code{measure_grobs()} Measure dimensions of the graphical objects produced -by the \verb{build_*()} methods to be used in the layout or assembly. -\item \code{arrange_layout()} Set up a layout for how graphical objects produced by -the \verb{build_*()} methods should be arranged. -\item \code{assemble_drawing()} Take the graphical objects produced by the \verb{build_*()} -methods, the measurements from \code{measure_grobs()} and layout from -\code{arrange_layout()} to finalise the guide. -\item \code{add_title} Adds the title to a gtable, taking into account the size -of the title as well as the gtable size. -} -} - -\section{Positions}{ - - -All \verb{position_*()} functions (like \code{position_dodge()}) return a -\verb{Position*} object (like \code{PositionDodge}). The \verb{Position*} -object is responsible for adjusting the position of overlapping geoms. - -The way that the \verb{position_*} functions work is slightly different from -the \verb{geom_*} and \verb{stat_*} functions, because a \verb{position_*} -function actually "instantiates" the \verb{Position*} object by creating a -descendant, and returns that. - -Each of the \verb{Position*} objects is a \code{\link[=ggproto]{ggproto()}} object, -descended from the top-level \code{Position}, and each implements the -following methods: -\itemize{ -\item \code{compute_layer(self, data, params, panel)} is called once -per layer. \code{panel} is currently an internal data structure, so -this method should not be overridden. -\item \code{compute_panel(self, data, params, scales)} is called once per -panel and should return a modified data frame. - -\code{data} is a data frame containing the variables named according -to the aesthetics that they're mapped to. \code{scales} is a list -containing the \code{x} and \code{y} scales. There functions are called -before the facets are trained, so they are global scales, not local -to the individual panels. \code{params} contains the parameters returned by -\code{setup_params()}. -\item \code{setup_params(data, params)}: called once for each layer. -Used to setup defaults that need to complete dataset, and to inform -the user of important choices. Should return list of parameters. -\item \code{setup_data(data, params)}: called once for each layer, -after \code{setup_params()}. Should return modified \code{data}. -Default checks that required aesthetics are present. -} - -And the following fields -\itemize{ -\item \code{required_aes}: a character vector giving the aesthetics -that must be present for this position adjustment to work. -} - -See also the \href{https://ggplot2-book.org/extensions#new-positions}{new positions section} of the online ggplot2 book. -} - -\section{Scales}{ - - -All \verb{scale_*} functions like \code{\link[=scale_x_continuous]{scale_x_continuous()}} return a \verb{Scale*} -object like \code{ScaleContinuous}. Each of the \verb{Scale*} objects is a \code{\link[=ggproto]{ggproto()}} -object, descended from the top-level \code{Scale}. - -Properties not documented in \code{\link[=continuous_scale]{continuous_scale()}} or \code{\link[=discrete_scale]{discrete_scale()}}: -\itemize{ -\item \code{call} The call to \code{\link[=continuous_scale]{continuous_scale()}} or \code{\link[=discrete_scale]{discrete_scale()}} that constructed -the scale. -\item \code{range} One of \code{continuous_range()} or \code{discrete_range()}. -} - -Methods: -\itemize{ -\item \code{is_discrete()} Returns \code{TRUE} if the scale is a discrete scale -\item \code{is_empty()} Returns \code{TRUE} if the scale contains no information (i.e., -it has no information with which to calculate its \code{limits}). -\item \code{clone()} Returns a copy of the scale that can be trained -independently without affecting the original scale. -\item \code{transform()} Transforms a vector of values using \code{self$trans}. -This occurs before the \code{Stat} is calculated. -\item \code{train()} Update the \code{self$range} of observed (transformed) data values with -a vector of (possibly) new values. -\item \code{reset()} Reset the \code{self$range} of observed data values. For discrete -position scales, only the continuous range is reset. -\item \code{map()} Map transformed data values to some output value as -determined by \code{self$rescale()} and \code{self$palette} (except for position scales, -which do not use the default implementation of this method). The output corresponds -to the transformed data value in aesthetic space (e.g., a color, line width, or size). -\item \code{rescale()} Rescale transformed data to the range 0, 1. This is most useful for -position scales. For continuous scales, \code{rescale()} uses the \code{rescaler} that -was provided to the constructor. \code{rescale()} does not apply \code{self$oob()} to -its input, which means that discrete values outside \code{limits} will be \code{NA}, and -values that are outside \code{range} will have values less than 0 or greater than 1. -This allows guides more control over how out-of-bounds values are displayed. -\item \code{transform_df()}, \code{train_df()}, \code{map_df()} These \verb{_df} variants -accept a data frame, and apply the \code{transform}, \code{train}, and \code{map} methods -(respectively) to the columns whose names are in \code{self$aesthetics}. -\item \code{get_limits()} Calculates the final scale limits in transformed data space -based on the combination of \code{self$limits} and/or the range of observed values -(\code{self$range}). -\item \code{get_breaks()} Calculates the final scale breaks in transformed data space -based on on the combination of \code{self$breaks}, \code{self$trans$breaks()} (for -continuous scales), and \code{limits}. Breaks outside of \code{limits} are assigned -a value of \code{NA} (continuous scales) or dropped (discrete scales). -\item \code{get_labels()} Calculates labels for a given set of (transformed) \code{breaks} -based on the combination of \code{self$labels} and \code{breaks}. -\item \code{get_breaks_minor()} For continuous scales, calculates the final scale minor breaks -in transformed data space based on the rescaled \code{breaks}, the value of \code{self$minor_breaks}, -and the value of \code{self$trans$minor_breaks()}. Discrete scales always return \code{NULL}. -\item \code{get_transformation()} Returns the scale's transformation object. -\item \code{make_title()} Hook to modify the title that is calculated during guide construction -(for non-position scales) or when the \code{Layout} calculates the x and y labels -(position scales). -} - -These methods are only valid for position (x and y) scales: -\itemize{ -\item \code{dimension()} For continuous scales, the dimension is the same concept as the limits. -For discrete scales, \code{dimension()} returns a continuous range, where the limits -would be placed at integer positions. \code{dimension()} optionally expands -this range given an expansion of length 4 (see \code{\link[=expansion]{expansion()}}). -\item \code{break_info()} Returns a \code{list()} with calculated values needed for the \code{Coord} -to transform values in transformed data space. Axis and grid guides also use -these values to draw guides. This is called with -a (usually expanded) continuous range, such as that returned by \code{self$dimension()} -(even for discrete scales). The list has components \code{major_source} -(\code{self$get_breaks()} for continuous scales, or \code{seq_along(self$get_breaks())} -for discrete scales), \code{major} (the rescaled value of \code{major_source}, ignoring -\code{self$rescaler}), \code{minor} (the rescaled value of \code{minor_source}, ignoring -\code{self$rescaler}), \code{range} (the range that was passed in to \code{break_info()}), -\code{labels} (the label values, one for each element in \code{breaks}). -\item \code{axis_order()} One of \code{c("primary", "secondary")} or \code{c("secondary", "primary")} -\item \code{make_sec_title()} Hook to modify the title for the second axis that is calculated -when the \code{Layout} calculates the x and y labels. -} -} - \seealso{ ggproto } diff --git a/vignettes/extending-ggplot2.Rmd b/vignettes/extending-ggplot2.Rmd index 6204321605..1c99075235 100644 --- a/vignettes/extending-ggplot2.Rmd +++ b/vignettes/extending-ggplot2.Rmd @@ -47,10 +47,12 @@ A$x The majority of ggplot2 classes are immutable and static: the methods neither use nor modify state in the class. They're mostly used as a convenient way of bundling related methods together. -To create a new geom or stat, you will just create a new ggproto that inherits from `Stat`, `Geom` and override the methods described below. +When you create new functionality, like a new geom or stat, the first decision to make is whether you want to build these from scratch or you want to re-use parts of pre-exiting classes. To create a new geom or stat from scratch, you will just create a new ggproto object that inherits from `Stat` or `Geom` and override the methods described below. ## Creating a new stat +Stats are an important part of layers: they instruct what is displayed, not how it is displayed. They are declared in the `layer(stat)` argument which has a correspondence to `Stat*` ggproto classes which all inherit from the root `Stat` class. + ### The simplest stat We'll start by creating a very simple stat: one that gives the convex hull (the _c_ hull) of a set of points. First we create a new ggproto object that inherits from `Stat`: @@ -68,7 +70,7 @@ StatChull <- ggproto("StatChull", Stat, The two most important components are the `compute_group()` method (which does the computation), and the `required_aes` field, which lists which aesthetics must be present in order for the stat to work. -Next we write a layer function. Unfortunately, due to an early design mistake I called these either `stat_()` or `geom_()`. A better decision would have been to call them `layer_()` functions: that's a more accurate description because every layer involves a stat _and_ a geom. +Next we write a layer function. Unfortunately, due to an early design mistake I called these either `stat_()` or `geom_()`. A better decision would have been to call them `layer_()` functions: that's a more accurate description because every layer involves a stat _and_ a geom. Currently it is the convention to have `stat_()` wrappers with fixed `layer(stat)` arguments and `geom_()` wrappers with fixed `layer(geom)` arguments. The `stat_()` and `geom_()` functions both have the same ingredients and cook up new layers. All layer functions follow the same form - you specify defaults in the function arguments and then call the `layer()` function, sending `...` into the `params` argument. The arguments in `...` will either be arguments for the geom (if you're making a stat wrapper), arguments for the stat (if you're making a geom wrapper), or aesthetics to be set. `layer()` takes care of teasing the different parameters apart and making sure they're stored in the right place: @@ -371,6 +373,8 @@ ggplot(mpg, aes(displ, drv, fill = after_stat(density))) + ## Creating a new geom +Geoms are also important parts of layers: they instruct how data is displayed. They are declared in the `layer(geom)` argument which has a correspondence to `Geom*` ggproto classes which all inherit from the root `Geom` class. + It's harder to create a new geom than a new stat because you also need to know some grid. ggplot2 is built on top of grid, so you'll need to know the basics of drawing with grid. If you're serious about adding a new geom, I'd recommend buying [R graphics](https://www.routledge.com/R-Graphics-Third-Edition/Murrell/p/book/9781498789059) by Paul Murrell. It tells you everything you need to know about drawing with grid. ### A simple geom @@ -592,6 +596,7 @@ GeomLine$setup_params ## Creating your own theme +Themes govern how non-data elements of the plot are displayed. If you're going to create your own complete theme, there are a few things you need to know: * Overriding existing elements, rather than modifying them @@ -662,7 +667,7 @@ Complete and incomplete themes behave somewhat differently when added to a ggplo ## Creating a new faceting -One of the more daunting exercises in ggplot2 extensions is to create a new faceting system. The reason for this is that when creating new facetings you take on the responsibility of how (almost) everything is drawn on the screen, and many do not have experience with directly using [gtable](https://cran.r-project.org/package=gtable) and [grid](https://cran.r-project.org/package=grid) upon which the ggplot2 rendering is built. If you decide to venture into faceting extensions, it is highly recommended to gain proficiency with the above-mentioned packages. +Facets are a way to draw small multiples of the data in different panels. One of the more daunting exercises in ggplot2 extensions is to create a new faceting system. The reason for this is that when creating new facetings you take on the responsibility of how (almost) everything is drawn on the screen, and many do not have experience with directly using [gtable](https://cran.r-project.org/package=gtable) and [grid](https://cran.r-project.org/package=grid) upon which the ggplot2 rendering is built. If you decide to venture into faceting extensions, it is highly recommended to gain proficiency with the above-mentioned packages. The `Facet` class in ggplot2 is very powerful as it takes on responsibility of a wide range of tasks. The main tasks of a `Facet` object are: