Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Geom aesthetics based on theme #5833

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
08300bd
draft new `geom` element in themes
teunbrand Apr 5, 2024
8c24ab9
plumbing for providing theme to Geom$use_defaults()
teunbrand Apr 5, 2024
60af7a3
make `from_theme()` as eval helper
teunbrand Apr 5, 2024
f551c8b
Evaluate default aesthetics from theme
teunbrand Apr 5, 2024
ced8c15
allow user-specified `from_theme()`
teunbrand Apr 5, 2024
659fd09
plumbing for guides to observe theme
teunbrand Apr 5, 2024
2cec833
reoxygenate
teunbrand Apr 8, 2024
5637501
Add text settings
teunbrand Apr 17, 2024
2d28202
Add text settings
teunbrand Apr 17, 2024
5f2ebf9
accept minor fontsize difference (11.04pt -> 11.00pt)
teunbrand Apr 17, 2024
87af93e
Merge branch 'theme_geom_defaults' of https://github.com/teunbrand/gg…
teunbrand Apr 17, 2024
f503409
temporarily disable `sf_grob()` default lookups
teunbrand Apr 17, 2024
f08fcb6
temporary shim for colour mixing
teunbrand Apr 17, 2024
b5ffd32
Get all `colour`/`fill`/`linewidth` from theme
teunbrand Apr 17, 2024
93a9258
test theme has `geom` element
teunbrand Apr 17, 2024
5e9136f
adapt tests
teunbrand Apr 17, 2024
962004e
resolve merge conflict
teunbrand Apr 29, 2024
6185e82
`geom_sf()` has themed defaults
teunbrand Apr 29, 2024
df571c0
we don't expect complete themes anymore
teunbrand Apr 29, 2024
92d2bbb
shim auto-replaces itself when exported from scales
teunbrand Apr 30, 2024
124dd5f
theme defaults for point size/shape
teunbrand Apr 30, 2024
0ff6078
boxplot point shape/size default from `GeomBoxplot$default_aes`
teunbrand Apr 30, 2024
2dd3f37
accept larger points in large theme snapshots
teunbrand Apr 30, 2024
377a82c
document
teunbrand Apr 30, 2024
e584978
Point to theme setting in `update_geom_defaults()`
teunbrand Apr 30, 2024
1143a9f
document `from_theme()`
teunbrand Apr 30, 2024
afca781
add news bullet
teunbrand Apr 30, 2024
f0b4279
Update R/geom-pointrange.R
teunbrand Apr 30, 2024
b0251f3
Run revdepcheck
thomasp85 May 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ export(draw_key_vpath)
export(dup_axis)
export(el_def)
export(element_blank)
export(element_geom)
export(element_grob)
export(element_line)
export(element_rect)
Expand All @@ -365,6 +366,7 @@ export(find_panel)
export(flip_data)
export(flipped_names)
export(fortify)
export(from_theme)
export(geom_abline)
export(geom_area)
export(geom_bar)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# ggplot2 (development version)

* (Breaking) The defaults for all geoms can be set at one in the theme.
(@teunbrand based on pioneering work by @dpseidel, #2239)
* A new `theme(geom)` argument is used to track these defaults.
* The `element_geom()` function can be used to populate that argument.
* The `from_theme()` function allows access to the theme default fields from
inside the `aes()` function.
* (Internal) Applying defaults in `geom_sf()` has moved from the internal
`sf_grob()` to `GeomSf$use_defaults()` (@teunbrand).
* `facet_wrap()` has new options for the `dir` argument to more precisely
Expand Down
20 changes: 20 additions & 0 deletions R/aes-evaluation.R
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@
#' fun.data = ~ round(data.frame(mean = mean(.x), sd = sd(.x)), 2)
#' )
#' ```
#'
#' ## Theme access
#' The `from_theme()` function can be used to acces the [`element_geom()`]
#' fields of the `theme(geom)` argument. Using `aes(colour = from_theme(ink))`
#' and `aes(colour = from_theme(accent))` allows swapping between foreground and
#' accent colours.
#'
#' @rdname aes_eval
#' @name aes_eval
#'
Expand Down Expand Up @@ -192,6 +199,13 @@ stat <- function(x) {
after_scale <- function(x) {
x
}

#' @rdname aes_eval
#' @export
from_theme <- function(x) {
x
}

#' @rdname aes_eval
#' @export
stage <- function(start = NULL, after_stat = NULL, after_scale = NULL) {
Expand Down Expand Up @@ -221,6 +235,9 @@ is_scaled_aes <- function(aesthetics) {
is_staged_aes <- function(aesthetics) {
vapply(aesthetics, is_staged, logical(1), USE.NAMES = FALSE)
}
is_themed_aes <- function(aesthetics) {
vapply(aesthetics, is_themed, logical(1), USE.NAMES = FALSE)
}
is_calculated <- function(x, warn = FALSE) {
if (is_call(get_expr(x), "after_stat")) {
return(TRUE)
Expand Down Expand Up @@ -263,6 +280,9 @@ is_scaled <- function(x) {
is_staged <- function(x) {
is_call(get_expr(x), "stage")
}
is_themed <- function(x) {
is_call(get_expr(x), "from_theme")
}

# Strip dots from expressions
strip_dots <- function(expr, env, strip_pronoun = FALSE) {
Expand Down
2 changes: 1 addition & 1 deletion R/annotation-logticks.R
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ GeomLogticks <- ggproto("GeomLogticks", Geom,
gTree(children = inject(gList(!!!ticks)))
},

default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = 1)
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = 1)
)


Expand Down
24 changes: 22 additions & 2 deletions R/geom-.R
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ Geom <- ggproto("Geom",
setup_data = function(data, params) data,

# Combine data with defaults and set aesthetics from parameters
use_defaults = function(self, data, params = list(), modifiers = aes(), default_aes = NULL) {
use_defaults = function(self, data, params = list(), modifiers = aes(),
default_aes = NULL, theme = NULL) {
default_aes <- default_aes %||% self$default_aes

# Inherit size as linewidth if no linewidth aesthetic and param exist
Expand All @@ -131,8 +132,11 @@ Geom <- ggproto("Geom",

# Fill in missing aesthetics with their defaults
missing_aes <- setdiff(names(default_aes), names(data))
default_aes <- default_aes[missing_aes]
themed_defaults <- eval_from_theme(default_aes, theme)
default_aes[names(themed_defaults)] <- themed_defaults

missing_eval <- lapply(default_aes[missing_aes], eval_tidy)
missing_eval <- lapply(default_aes, eval_tidy)
# Needed for geoms with defaults set to NULL (e.g. GeomSf)
missing_eval <- compact(missing_eval)

Expand All @@ -142,6 +146,13 @@ Geom <- ggproto("Geom",
data[names(missing_eval)] <- missing_eval
}

themed <- is_themed_aes(modifiers)
if (any(themed)) {
themed <- eval_from_theme(modifiers[themed], theme)
modifiers <- modifiers[setdiff(names(modifiers), names(themed))]
data[names(themed)] <- themed
}

# If any after_scale mappings are detected they will be resolved here
# This order means that they will have access to all default aesthetics
if (length(modifiers) != 0) {
Expand Down Expand Up @@ -223,6 +234,15 @@ Geom <- ggproto("Geom",
)


eval_from_theme <- function(aesthetics, theme) {
themed <- is_themed_aes(aesthetics)
if (!any(themed)) {
return(aesthetics)
}
settings <- calc_element("geom", theme) %||% .default_geom_element
lapply(aesthetics[themed], eval_tidy, data = settings)
}

#' Graphical units
#'
#' Multiply size in mm by these constants in order to convert to the units
Expand Down
2 changes: 1 addition & 1 deletion R/geom-abline.R
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ GeomAbline <- ggproto("GeomAbline", Geom,
GeomSegment$draw_panel(unique0(data), panel_params, coord, lineend = lineend)
},

default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA),
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = NA),
required_aes = c("slope", "intercept"),

draw_key = draw_key_abline,
Expand Down
16 changes: 10 additions & 6 deletions R/geom-boxplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ geom_boxplot <- function(mapping = NULL, data = NULL,
outlier.colour = NULL,
outlier.color = NULL,
outlier.fill = NULL,
outlier.shape = 19,
outlier.size = 1.5,
outlier.shape = NULL,
outlier.size = NULL,
outlier.stroke = 0.5,
outlier.alpha = NULL,
notch = FALSE,
Expand Down Expand Up @@ -223,8 +223,8 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,

draw_group = function(self, data, panel_params, coord, lineend = "butt",
linejoin = "mitre", fatten = 2, outlier.colour = NULL,
outlier.fill = NULL, outlier.shape = 19,
outlier.size = 1.5, outlier.stroke = 0.5,
outlier.fill = NULL, outlier.shape = NULL,
outlier.size = NULL, outlier.stroke = 0.5,
outlier.alpha = NULL, notch = FALSE, notchwidth = 0.5,
staplewidth = 0, varwidth = FALSE, flipped_aes = FALSE) {
data <- check_linewidth(data, snake_class(self))
Expand Down Expand Up @@ -327,8 +327,12 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,

draw_key = draw_key_boxplot,

default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = NULL,
alpha = NA, shape = 19, linetype = "solid", linewidth = 0.5),
default_aes = aes(
weight = 1, colour = from_theme(col_mix(ink, paper, 0.2)),
fill = from_theme(paper), size = from_theme(pointsize),
alpha = NA, shape = from_theme(pointshape), linetype = "solid",
linewidth = from_theme(thin)
),

required_aes = c("x|y", "lower|xlower", "upper|xupper", "middle|xmiddle", "ymin|xmin", "ymax|xmax"),

Expand Down
4 changes: 2 additions & 2 deletions R/geom-contour.R
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ geom_contour_filled <- function(mapping = NULL, data = NULL,
GeomContour <- ggproto("GeomContour", GeomPath,
default_aes = aes(
weight = 1,
colour = "#3366FF",
linewidth = 0.5,
colour = from_theme(accent),
linewidth = from_theme(thin),
linetype = 1,
alpha = NA
)
Expand Down
2 changes: 1 addition & 1 deletion R/geom-crossbar.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom,
GeomErrorbar$setup_data(data, params)
},

default_aes = aes(colour = "black", fill = NA, linewidth = 0.5, linetype = 1,
default_aes = aes(colour = from_theme(ink), fill = NA, linewidth = from_theme(thin), linetype = 1,
alpha = NA),

required_aes = c("x", "y", "ymin|xmin", "ymax|xmax"),
Expand Down
2 changes: 1 addition & 1 deletion R/geom-curve.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ geom_curve <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomCurve <- ggproto("GeomCurve", GeomSegment,
default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA),
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = NA),
draw_panel = function(data, panel_params, coord, curvature = 0.5, angle = 90,
ncp = 5, arrow = NULL, arrow.fill = NULL, lineend = "butt", na.rm = FALSE) {

Expand Down
3 changes: 3 additions & 0 deletions R/geom-defaults.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#' * A named list of aesthetics to serve as new defaults.
#' * `NULL` to reset the defaults.
#' @keywords internal
#' @note
#' Please note that geom defaults can be set *en masse* via the `theme(geom)`
#' argument.
#' @export
#' @examples
#'
Expand Down
2 changes: 1 addition & 1 deletion R/geom-density.R
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ geom_density <- function(mapping = NULL, data = NULL,
#' @include geom-ribbon.R
GeomDensity <- ggproto("GeomDensity", GeomArea,
default_aes = defaults(
aes(fill = NA, weight = 1, colour = "black", alpha = NA),
aes(fill = NA, weight = 1, colour = from_theme(ink), alpha = NA),
GeomArea$default_aes
)
)
2 changes: 1 addition & 1 deletion R/geom-density2d.R
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ geom_density2d <- geom_density_2d
#' @usage NULL
#' @export
GeomDensity2d <- ggproto("GeomDensity2d", GeomPath,
default_aes = aes(colour = "#3366FF", linewidth = 0.5, linetype = 1, alpha = NA)
default_aes = aes(colour = from_theme(accent), linewidth = from_theme(thin), linetype = 1, alpha = NA)
)

#' @export
Expand Down
2 changes: 1 addition & 1 deletion R/geom-dotplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ GeomDotplot <- ggproto("GeomDotplot", Geom,
required_aes = c("x", "y"),
non_missing_aes = c("size", "shape"),

default_aes = aes(colour = "black", fill = "black", alpha = NA,
default_aes = aes(colour = from_theme(ink), fill = from_theme(ink), alpha = NA,
stroke = 1, linetype = "solid", weight = 1),

setup_data = function(data, params) {
Expand Down
2 changes: 1 addition & 1 deletion R/geom-errorbar.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ geom_errorbar <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomErrorbar <- ggproto("GeomErrorbar", Geom,
default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, width = 0.5,
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, width = 0.5,
alpha = NA),

draw_key = draw_key_path,
Expand Down
2 changes: 1 addition & 1 deletion R/geom-errorbarh.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ geom_errorbarh <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomErrorbarh <- ggproto("GeomErrorbarh", Geom,
default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, height = 0.5,
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, height = 0.5,
alpha = NA),

draw_key = draw_key_path,
Expand Down
4 changes: 2 additions & 2 deletions R/geom-hex.R
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ GeomHex <- ggproto("GeomHex", Geom,

default_aes = aes(
colour = NA,
fill = "grey50",
linewidth = 0.5,
fill = from_theme(col_mix(ink, paper)),
linewidth = from_theme(thin),
linetype = 1,
alpha = NA
),
Expand Down
2 changes: 1 addition & 1 deletion R/geom-hline.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ GeomHline <- ggproto("GeomHline", Geom,
GeomSegment$draw_panel(unique0(data), panel_params, coord, lineend = lineend)
},

default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA),
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = NA),
required_aes = "yintercept",

draw_key = draw_key_path,
Expand Down
7 changes: 5 additions & 2 deletions R/geom-label.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ GeomLabel <- ggproto("GeomLabel", Geom,
required_aes = c("x", "y", "label"),

default_aes = aes(
colour = "black", fill = "white", size = 3.88, angle = 0,
hjust = 0.5, vjust = 0.5, alpha = NA, family = "", fontface = 1,
colour = from_theme(ink), fill = from_theme(paper),
family = from_theme(family),
size = from_theme(fontsize),
angle = 0,
hjust = 0.5, vjust = 0.5, alpha = NA, fontface = 1,
lineheight = 1.2
),

Expand Down
2 changes: 1 addition & 1 deletion R/geom-linerange.R
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ geom_linerange <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomLinerange <- ggproto("GeomLinerange", Geom,
default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA),
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = NA),

draw_key = draw_key_linerange,

Expand Down
2 changes: 1 addition & 1 deletion R/geom-path.R
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ geom_path <- function(mapping = NULL, data = NULL,
GeomPath <- ggproto("GeomPath", Geom,
required_aes = c("x", "y"),

default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA),
default_aes = aes(colour = from_theme(ink), linewidth = from_theme(thin), linetype = 1, alpha = NA),

non_missing_aes = c("linewidth", "colour", "linetype"),

Expand Down
3 changes: 2 additions & 1 deletion R/geom-point.R
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ GeomPoint <- ggproto("GeomPoint", Geom,
required_aes = c("x", "y"),
non_missing_aes = c("size", "shape", "colour"),
default_aes = aes(
shape = 19, colour = "black", size = 1.5, fill = NA,
shape = from_theme(pointshape),
colour = from_theme(ink), size = from_theme(pointsize), fill = NA,
alpha = NA, stroke = 0.5
),

Expand Down
7 changes: 5 additions & 2 deletions R/geom-pointrange.R
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ geom_pointrange <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomPointrange <- ggproto("GeomPointrange", Geom,
default_aes = aes(colour = "black", size = 0.5, linewidth = 0.5, linetype = 1,
shape = 19, fill = NA, alpha = NA, stroke = 1),
default_aes = aes(
colour = from_theme(ink), size = from_theme(pointsize / 3),
linewidth = from_theme(thin), linetype = 1,
shape = 19, fill = NA, alpha = NA, stroke = 1
teunbrand marked this conversation as resolved.
Show resolved Hide resolved
),

draw_key = draw_key_pointrange,

Expand Down
8 changes: 6 additions & 2 deletions R/geom-polygon.R
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,12 @@ GeomPolygon <- ggproto("GeomPolygon", Geom,
}
},

default_aes = aes(colour = NA, fill = "grey20", linewidth = 0.5, linetype = 1,
alpha = NA, subgroup = NULL),
default_aes = aes(
colour = NA,
fill = from_theme(col_mix(ink, paper, 0.2)),
linewidth = from_theme(thin), linetype = 1,
alpha = NA, subgroup = NULL
),

handle_na = function(data, params) {
data
Expand Down
2 changes: 1 addition & 1 deletion R/geom-quantile.R
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ geom_quantile <- function(mapping = NULL, data = NULL,
#' @include geom-path.R
GeomQuantile <- ggproto("GeomQuantile", GeomPath,
default_aes = defaults(
aes(weight = 1, colour = "#3366FF", linewidth = 0.5),
aes(weight = 1, colour = from_theme(accent), linewidth = from_theme(thin)),
GeomPath$default_aes
)
)
2 changes: 1 addition & 1 deletion R/geom-raster.R
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ geom_raster <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomRaster <- ggproto("GeomRaster", Geom,
default_aes = aes(fill = "grey20", alpha = NA),
default_aes = aes(fill = from_theme(col_mix(ink, paper, 0.2)), alpha = NA),
non_missing_aes = c("fill", "xmin", "xmax", "ymin", "ymax"),
required_aes = c("x", "y"),

Expand Down
7 changes: 5 additions & 2 deletions R/geom-rect.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ geom_rect <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomRect <- ggproto("GeomRect", Geom,
default_aes = aes(colour = NA, fill = "grey35", linewidth = 0.5, linetype = 1,
alpha = NA),
default_aes = aes(
colour = NA, fill = from_theme(col_mix(ink, paper, 0.35)),
linewidth = from_theme(thin), linetype = 1,
alpha = NA
),

required_aes = c("xmin", "xmax", "ymin", "ymax"),

Expand Down