diff --git a/DESCRIPTION b/DESCRIPTION index 50852c24e3..4d7d98248d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -221,6 +221,7 @@ Collate: 'scale-hue.r' 'scale-identity.r' 'scale-linetype.r' + 'scale-linewidth.R' 'scale-manual.r' 'scale-shape.r' 'scale-size.r' diff --git a/NAMESPACE b/NAMESPACE index a931b99665..15d7b9c658 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -557,6 +557,13 @@ export(scale_linetype_continuous) export(scale_linetype_discrete) export(scale_linetype_identity) export(scale_linetype_manual) +export(scale_linewidth) +export(scale_linewidth_binned) +export(scale_linewidth_continuous) +export(scale_linewidth_date) +export(scale_linewidth_datetime) +export(scale_linewidth_discrete) +export(scale_linewidth_ordinal) export(scale_radius) export(scale_shape) export(scale_shape_binned) diff --git a/NEWS.md b/NEWS.md index 41949e533b..6d8576201d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ # ggplot2 (development version) +* A `linewidth` aesthetic has been introduced and supersedes the `size` + aesthetic for scaling the width of lines in line based geoms. `size` will + remain functioning but deprecated for these geoms and it is recommended to + update all code to reflect the new aesthetic (@thomasp85, #3672) + * Secondary axis ticks are now positioned more precisely, removing small visual artefacts with alignment between grid and ticks (@thomasp85, #3576) diff --git a/R/geom-.r b/R/geom-.r index 9bf55ff501..9ec3691ab9 100644 --- a/R/geom-.r +++ b/R/geom-.r @@ -108,6 +108,11 @@ Geom <- ggproto("Geom", # Combine data with defaults and set aesthetics from parameters use_defaults = function(self, data, params = list(), modifiers = aes()) { + # Inherit size as linewidth if no linewidth aesthetic and param exist + if (self$rename_size && is.null(data$linewidth) && is.null(params$linewidth)) { + data$linewidth <- data$size + params$linewidth <- params$size + } # Fill in missing aesthetics with their defaults missing_aes <- setdiff(names(self$default_aes), names(data)) @@ -187,7 +192,10 @@ 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 ) diff --git a/R/geom-abline.r b/R/geom-abline.r index 3a5877fbda..ff7d359ab6 100644 --- a/R/geom-abline.r +++ b/R/geom-abline.r @@ -22,7 +22,7 @@ NULL #' @section Aesthetics: #' These geoms are drawn using with [geom_line()] so support the #' same aesthetics: `alpha`, `colour`, `linetype` and -#' `size`. They also each have aesthetics that control the position of +#' `linewidth`. They also each have aesthetics that control the position of #' the line: #' #' - `geom_vline()`: `xintercept` @@ -141,8 +141,10 @@ GeomAbline <- ggproto("GeomAbline", Geom, GeomSegment$draw_panel(unique(data), panel_params, coord, lineend = lineend) }, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = c("slope", "intercept"), - draw_key = draw_key_abline + draw_key = draw_key_abline, + + rename_size = TRUE ) diff --git a/R/geom-boxplot.r b/R/geom-boxplot.r index f891f6d498..d787f64b7a 100644 --- a/R/geom-boxplot.r +++ b/R/geom-boxplot.r @@ -223,7 +223,7 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom, common <- list( colour = data$colour, - size = data$size, + linewidth = data$linewidth, linetype = data$linetype, fill = alpha(data$fill, data$alpha), group = data$group @@ -293,8 +293,10 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom, draw_key = draw_key_boxplot, - default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = 0.5, - alpha = NA, shape = 19, linetype = "solid"), + default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = NULL, + alpha = NA, shape = 19, linetype = "solid", linewidth = 0.5), - required_aes = c("x|y", "lower|xlower", "upper|xupper", "middle|xmiddle", "ymin|xmin", "ymax|xmax") + required_aes = c("x|y", "lower|xlower", "upper|xupper", "middle|xmiddle", "ymin|xmin", "ymax|xmax"), + + rename_size = TRUE ) diff --git a/R/geom-col.r b/R/geom-col.r index 6ea87b3be6..b75aa34e18 100644 --- a/R/geom-col.r +++ b/R/geom-col.r @@ -66,5 +66,7 @@ GeomCol <- ggproto("GeomCol", GeomRect, lineend = lineend, linejoin = linejoin ) - } + }, + + rename_size = TRUE ) diff --git a/R/geom-contour.r b/R/geom-contour.r index 43b645f2ff..33822ba111 100644 --- a/R/geom-contour.r +++ b/R/geom-contour.r @@ -127,7 +127,7 @@ GeomContour <- ggproto("GeomContour", GeomPath, default_aes = aes( weight = 1, colour = "#3366FF", - size = 0.5, + linewidth = 0.5, linetype = 1, alpha = NA ) diff --git a/R/geom-crossbar.r b/R/geom-crossbar.r index 0f11d4a483..53af004a00 100644 --- a/R/geom-crossbar.r +++ b/R/geom-crossbar.r @@ -40,7 +40,7 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom, GeomErrorbar$setup_data(data, params) }, - default_aes = aes(colour = "black", fill = NA, size = 0.5, linetype = 1, + default_aes = aes(colour = "black", fill = NA, linewidth = 0.5, linetype = 1, alpha = NA), required_aes = c("x", "y", "ymin|xmin", "ymax|xmax"), @@ -52,7 +52,7 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom, flipped_aes = FALSE) { data <- flip_data(data, flipped_aes) - middle <- transform(data, x = xmin, xend = xmax, yend = y, size = size * fatten, alpha = NA) + middle <- transform(data, x = xmin, xend = xmax, yend = y, linewidth = linewidth * fatten, alpha = NA) has_notch <- !is.null(data$ynotchlower) && !is.null(data$ynotchupper) && !is.na(data$ynotchlower) && !is.na(data$ynotchupper) @@ -82,7 +82,7 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom, ), alpha = rep(data$alpha, 11), colour = rep(data$colour, 11), - size = rep(data$size, 11), + linewidth = rep(data$linewidth, 11), linetype = rep(data$linetype, 11), fill = rep(data$fill, 11), group = rep(seq_len(nrow(data)), 11) @@ -94,7 +94,7 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom, y = c(data$ymax, data$ymin, data$ymin, data$ymax, data$ymax), alpha = rep(data$alpha, 5), colour = rep(data$colour, 5), - size = rep(data$size, 5), + linewidth = rep(data$linewidth, 5), linetype = rep(data$linetype, 5), fill = rep(data$fill, 5), group = rep(seq_len(nrow(data)), 5) # each bar forms it's own group diff --git a/R/geom-curve.r b/R/geom-curve.r index 03c5e54a31..a79ad73085 100644 --- a/R/geom-curve.r +++ b/R/geom-curve.r @@ -40,7 +40,7 @@ geom_curve <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomCurve <- ggproto("GeomCurve", GeomSegment, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, 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) { @@ -60,7 +60,7 @@ GeomCurve <- ggproto("GeomCurve", GeomSegment, gp = gpar( col = alpha(trans$colour, trans$alpha), fill = alpha(arrow.fill, trans$alpha), - lwd = trans$size * .pt, + lwd = trans$linewidth * .pt, lty = trans$linetype, lineend = lineend), arrow = arrow diff --git a/R/geom-density2d.r b/R/geom-density2d.r index 6f7629472e..5e3bc652f4 100644 --- a/R/geom-density2d.r +++ b/R/geom-density2d.r @@ -104,7 +104,7 @@ geom_density2d <- geom_density_2d #' @usage NULL #' @export GeomDensity2d <- ggproto("GeomDensity2d", GeomPath, - default_aes = aes(colour = "#3366FF", size = 0.5, linetype = 1, alpha = NA) + default_aes = aes(colour = "#3366FF", linewidth = 0.5, linetype = 1, alpha = NA) ) #' @export diff --git a/R/geom-errorbar.r b/R/geom-errorbar.r index 2b33814b22..41e1cad5b5 100644 --- a/R/geom-errorbar.r +++ b/R/geom-errorbar.r @@ -28,7 +28,7 @@ geom_errorbar <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomErrorbar <- ggproto("GeomErrorbar", Geom, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, width = 0.5, + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, width = 0.5, alpha = NA), draw_key = draw_key_path, @@ -61,12 +61,14 @@ GeomErrorbar <- ggproto("GeomErrorbar", Geom, y = y, colour = rep(data$colour, each = 8), alpha = rep(data$alpha, each = 8), - size = rep(data$size, each = 8), + linewidth = rep(data$linewidth, each = 8), linetype = rep(data$linetype, each = 8), group = rep(1:(nrow(data)), each = 8), row.names = 1:(nrow(data) * 8) )) data <- flip_data(data, flipped_aes) GeomPath$draw_panel(data, panel_params, coord, lineend = lineend) - } + }, + + rename_size = TRUE ) diff --git a/R/geom-errorbarh.r b/R/geom-errorbarh.r index 02e5e44f04..93ceea200d 100644 --- a/R/geom-errorbarh.r +++ b/R/geom-errorbarh.r @@ -51,7 +51,7 @@ geom_errorbarh <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomErrorbarh <- ggproto("GeomErrorbarh", Geom, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, height = 0.5, + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, height = 0.5, alpha = NA), draw_key = draw_key_path, @@ -73,10 +73,12 @@ GeomErrorbarh <- ggproto("GeomErrorbarh", Geom, y = as.vector(rbind(data$ymin, data$ymax, NA, data$y, data$y, NA, data$ymin, data$ymax)), colour = rep(data$colour, each = 8), alpha = rep(data$alpha, each = 8), - size = rep(data$size, each = 8), + linewidth = rep(data$linewidth, each = 8), linetype = rep(data$linetype, each = 8), group = rep(1:(nrow(data)), each = 8), row.names = 1:(nrow(data) * 8) )), panel_params, coord, lineend = lineend) - } + }, + + rename_size = TRUE ) diff --git a/R/geom-hex.r b/R/geom-hex.r index 3f786d1080..421a3d0116 100644 --- a/R/geom-hex.r +++ b/R/geom-hex.r @@ -90,7 +90,7 @@ GeomHex <- ggproto("GeomHex", Geom, gp = gpar( col = coords$colour, fill = alpha(coords$fill, coords$alpha), - lwd = coords$size * .pt, + lwd = coords$linewidth * .pt, lty = coords$linetype, lineend = lineend, linejoin = linejoin, @@ -106,12 +106,14 @@ GeomHex <- ggproto("GeomHex", Geom, default_aes = aes( colour = NA, fill = "grey50", - size = 0.5, + linewidth = 0.5, linetype = 1, alpha = NA ), - draw_key = draw_key_polygon + draw_key = draw_key_polygon, + + rename_size = TRUE ) diff --git a/R/geom-hline.r b/R/geom-hline.r index d34e68b9aa..f1e4bb8286 100644 --- a/R/geom-hline.r +++ b/R/geom-hline.r @@ -55,8 +55,10 @@ GeomHline <- ggproto("GeomHline", Geom, GeomSegment$draw_panel(unique(data), panel_params, coord, lineend = lineend) }, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = "yintercept", - draw_key = draw_key_path + draw_key = draw_key_path, + + rename_size = TRUE ) diff --git a/R/geom-linerange.r b/R/geom-linerange.r index ed0e854ea7..beb1b23cc6 100644 --- a/R/geom-linerange.r +++ b/R/geom-linerange.r @@ -91,7 +91,7 @@ geom_linerange <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomLinerange <- ggproto("GeomLinerange", Geom, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), draw_key = draw_key_linerange, @@ -118,5 +118,7 @@ GeomLinerange <- ggproto("GeomLinerange", Geom, data <- transform(data, xend = x, y = ymin, yend = ymax) data <- flip_data(data, flipped_aes) ggname("geom_linerange", GeomSegment$draw_panel(data, panel_params, coord, lineend = lineend)) - } + }, + + rename_size = TRUE ) diff --git a/R/geom-map.r b/R/geom-map.r index c26178e4ac..0b390348c6 100644 --- a/R/geom-map.r +++ b/R/geom-map.r @@ -149,7 +149,7 @@ GeomMap <- ggproto("GeomMap", GeomPolygon, gp = gpar( col = data$colour, fill = alpha(data$fill, data$alpha), - lwd = data$size * .pt, + lwd = data$linewidth * .pt, lineend = lineend, linejoin = linejoin, linemitre = linemitre diff --git a/R/geom-path.r b/R/geom-path.r index b21caaf4f4..b8a76bf639 100644 --- a/R/geom-path.r +++ b/R/geom-path.r @@ -130,12 +130,12 @@ geom_path <- function(mapping = NULL, data = NULL, GeomPath <- ggproto("GeomPath", Geom, required_aes = c("x", "y"), - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), handle_na = function(self, data, params) { # Drop missing values at the start or end of a line - can't drop in the # middle since you expect those to be shown by a break in the line - complete <- stats::complete.cases(data[c("x", "y", "size", "colour", "linetype")]) + complete <- stats::complete.cases(data[c("x", "y", "linewidth", "colour", "linetype")]) kept <- stats::ave(complete, data$group, FUN = keep_mid_true) data <- data[kept, ] @@ -170,7 +170,7 @@ GeomPath <- ggproto("GeomPath", Geom, linetype <- unique(df$linetype) new_data_frame(list( solid = identical(linetype, 1) || identical(linetype, "solid"), - constant = nrow(unique(df[, c("alpha", "colour","size", "linetype")])) == 1 + constant = nrow(unique(df[, c("alpha", "colour","linewidth", "linetype")])) == 1 ), n = 1) }) solid_lines <- all(attr$solid) @@ -192,7 +192,7 @@ GeomPath <- ggproto("GeomPath", Geom, gp = gpar( col = alpha(munched$colour, munched$alpha)[!end], fill = alpha(munched$colour, munched$alpha)[!end], - lwd = munched$size[!end] * .pt, + lwd = munched$linewidth[!end] * .pt, lty = munched$linetype[!end], lineend = lineend, linejoin = linejoin, @@ -207,7 +207,7 @@ GeomPath <- ggproto("GeomPath", Geom, gp = gpar( col = alpha(munched$colour, munched$alpha)[start], fill = alpha(munched$colour, munched$alpha)[start], - lwd = munched$size[start] * .pt, + lwd = munched$linewidth[start] * .pt, lty = munched$linetype[start], lineend = lineend, linejoin = linejoin, @@ -217,7 +217,9 @@ GeomPath <- ggproto("GeomPath", Geom, } }, - draw_key = draw_key_path + draw_key = draw_key_path, + + rename_size = TRUE ) # Trim false values from left and right: keep all values from diff --git a/R/geom-pointrange.r b/R/geom-pointrange.r index 7291100a2f..9209f69d1b 100644 --- a/R/geom-pointrange.r +++ b/R/geom-pointrange.r @@ -30,8 +30,8 @@ geom_pointrange <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomPointrange <- ggproto("GeomPointrange", Geom, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, shape = 19, - fill = NA, alpha = NA, stroke = 1), + default_aes = aes(colour = "black", size = 0.5, linewidth = 0.5, linetype = 1, + shape = 19, fill = NA, alpha = NA, stroke = 1), draw_key = draw_key_pointrange, diff --git a/R/geom-polygon.r b/R/geom-polygon.r index c21a2031ae..9c771cced7 100644 --- a/R/geom-polygon.r +++ b/R/geom-polygon.r @@ -132,7 +132,7 @@ GeomPolygon <- ggproto("GeomPolygon", Geom, gp = gpar( col = first_rows$colour, fill = alpha(first_rows$fill, first_rows$alpha), - lwd = first_rows$size * .pt, + lwd = first_rows$linewidth * .pt, lty = first_rows$linetype, lineend = lineend, linejoin = linejoin, @@ -163,7 +163,7 @@ GeomPolygon <- ggproto("GeomPolygon", Geom, gp = gpar( col = first_rows$colour, fill = alpha(first_rows$fill, first_rows$alpha), - lwd = first_rows$size * .pt, + lwd = first_rows$linewidth * .pt, lty = first_rows$linetype, lineend = lineend, linejoin = linejoin, @@ -172,10 +172,9 @@ GeomPolygon <- ggproto("GeomPolygon", Geom, ) ) } - }, - default_aes = aes(colour = NA, fill = "grey20", size = 0.5, linetype = 1, + default_aes = aes(colour = NA, fill = "grey20", linewidth = 0.5, linetype = 1, alpha = NA, subgroup = NULL), handle_na = function(data, params) { @@ -184,7 +183,9 @@ GeomPolygon <- ggproto("GeomPolygon", Geom, required_aes = c("x", "y"), - draw_key = draw_key_polygon + draw_key = draw_key_polygon, + + rename_size = TRUE ) # Assigning pathGrob in .onLoad ensures that packages that subclass GeomPolygon diff --git a/R/geom-quantile.r b/R/geom-quantile.r index 4047d00799..06b3896a21 100644 --- a/R/geom-quantile.r +++ b/R/geom-quantile.r @@ -64,7 +64,7 @@ geom_quantile <- function(mapping = NULL, data = NULL, #' @include geom-path.r GeomQuantile <- ggproto("GeomQuantile", GeomPath, default_aes = defaults( - aes(weight = 1, colour = "#3366FF", size = 0.5), + aes(weight = 1, colour = "#3366FF", linewidth = 0.5), GeomPath$default_aes ) ) diff --git a/R/geom-rect.r b/R/geom-rect.r index 09de9fec32..9ee285f5d6 100644 --- a/R/geom-rect.r +++ b/R/geom-rect.r @@ -28,7 +28,7 @@ geom_rect <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomRect <- ggproto("GeomRect", Geom, - default_aes = aes(colour = NA, fill = "grey35", size = 0.5, linetype = 1, + default_aes = aes(colour = NA, fill = "grey35", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = c("xmin", "xmax", "ymin", "ymax"), @@ -58,7 +58,7 @@ GeomRect <- ggproto("GeomRect", Geom, gp = gpar( col = coords$colour, fill = alpha(coords$fill, coords$alpha), - lwd = coords$size * .pt, + lwd = coords$linewidth * .pt, lty = coords$linetype, linejoin = linejoin, lineend = lineend @@ -67,7 +67,9 @@ GeomRect <- ggproto("GeomRect", Geom, } }, - draw_key = draw_key_polygon + draw_key = draw_key_polygon, + + rename_size = TRUE ) diff --git a/R/geom-ribbon.r b/R/geom-ribbon.r index aecc1f29b8..081b21fb30 100644 --- a/R/geom-ribbon.r +++ b/R/geom-ribbon.r @@ -73,7 +73,7 @@ geom_ribbon <- function(mapping = NULL, data = NULL, #' @usage NULL #' @export GeomRibbon <- ggproto("GeomRibbon", Geom, - default_aes = aes(colour = NA, fill = "grey20", size = 0.5, linetype = 1, + default_aes = aes(colour = NA, fill = "grey20", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = c("x|y", "ymin|xmin", "ymax|xmax"), @@ -111,7 +111,7 @@ GeomRibbon <- ggproto("GeomRibbon", Geom, data <- data[order(data$group), ] # Check that aesthetics are constant - aes <- unique(data[c("colour", "fill", "size", "linetype", "alpha")]) + aes <- unique(data[c("colour", "fill", "linewidth", "linetype", "alpha")]) if (nrow(aes) > 1) { cli::cli_abort("Aesthetics can not vary along a ribbon") } @@ -158,7 +158,7 @@ GeomRibbon <- ggproto("GeomRibbon", Geom, gp = gpar( fill = alpha(aes$fill, aes$alpha), col = if (is_full_outline) aes$colour else NA, - lwd = if (is_full_outline) aes$size * .pt else 0, + lwd = if (is_full_outline) aes$linewidth * .pt else 0, lty = if (is_full_outline) aes$linetype else 1, lineend = lineend, linejoin = linejoin, @@ -187,7 +187,7 @@ GeomRibbon <- ggproto("GeomRibbon", Geom, default.units = "native", gp = gpar( col = aes$colour, - lwd = aes$size * .pt, + lwd = aes$linewidth * .pt, lty = aes$linetype, lineend = lineend, linejoin = linejoin, @@ -196,8 +196,9 @@ GeomRibbon <- ggproto("GeomRibbon", Geom, ) ggname("geom_ribbon", grobTree(g_poly, g_lines)) - } + }, + rename_size = TRUE ) #' @rdname geom_ribbon @@ -230,7 +231,7 @@ geom_area <- function(mapping = NULL, data = NULL, stat = "identity", #' @usage NULL #' @export GeomArea <- ggproto("GeomArea", GeomRibbon, - default_aes = aes(colour = NA, fill = "grey20", size = 0.5, linetype = 1, + default_aes = aes(colour = NA, fill = "grey20", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = c("x", "y"), diff --git a/R/geom-rug.r b/R/geom-rug.r index eaf56a0d90..6d4c3e7ac4 100644 --- a/R/geom-rug.r +++ b/R/geom-rug.r @@ -112,7 +112,7 @@ GeomRug <- ggproto("GeomRug", Geom, gp <- gpar( col = alpha(data$colour, data$alpha), lty = data$linetype, - lwd = data$size * .pt, + lwd = data$linewidth * .pt, lineend = lineend ) if (!is.null(data$x)) { @@ -154,7 +154,9 @@ GeomRug <- ggproto("GeomRug", Geom, gTree(children = do.call("gList", rugs)) }, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), - draw_key = draw_key_path + draw_key = draw_key_path, + + rename_size = TRUE ) diff --git a/R/geom-segment.r b/R/geom-segment.r index 6adb891fe1..ef511f35c2 100644 --- a/R/geom-segment.r +++ b/R/geom-segment.r @@ -103,13 +103,12 @@ geom_segment <- function(mapping = NULL, data = NULL, #' @export GeomSegment <- ggproto("GeomSegment", Geom, required_aes = c("x", "y", "xend", "yend"), - non_missing_aes = c("linetype", "size", "shape"), - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), - + non_missing_aes = c("linetype", "linewidth", "shape"), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), draw_panel = function(self, data, panel_params, coord, arrow = NULL, arrow.fill = NULL, lineend = "butt", linejoin = "round", na.rm = FALSE) { data <- remove_missing(data, na.rm = na.rm, - c("x", "y", "xend", "yend", "linetype", "size", "shape"), + c("x", "y", "xend", "yend", "linetype", "linewidth", "shape"), name = "geom_segment" ) @@ -123,7 +122,7 @@ GeomSegment <- ggproto("GeomSegment", Geom, gp = gpar( col = alpha(coord$colour, coord$alpha), fill = alpha(arrow.fill, coord$alpha), - lwd = coord$size * .pt, + lwd = coord$linewidth * .pt, lty = coord$linetype, lineend = lineend, linejoin = linejoin @@ -143,5 +142,7 @@ GeomSegment <- ggproto("GeomSegment", Geom, lineend = lineend) }, - draw_key = draw_key_path + draw_key = draw_key_path, + + rename_size = TRUE ) diff --git a/R/geom-sf.R b/R/geom-sf.R index 1b919f4c86..593ba18764 100644 --- a/R/geom-sf.R +++ b/R/geom-sf.R @@ -121,6 +121,7 @@ GeomSf <- ggproto("GeomSf", Geom, colour = NULL, fill = NULL, size = NULL, + linewidth = NULL, linetype = 1, alpha = NA, stroke = 0.5 @@ -200,10 +201,15 @@ sf_grob <- function(x, lineend = "butt", linejoin = "round", linemitre = 10, fill <- x$fill %||% defaults$fill[type_ind] fill <- alpha(fill, alpha) size <- x$size %||% defaults$size[type_ind] - point_size <- ifelse(is_collection, x$size %||% defaults$point_size[type_ind], size) + linewidth <- x$linewidth %||% defaults$linewidth[type_ind] + point_size <- ifelse( + is_collection, + x$size %||% defaults$point_size[type_ind], + ifelse(is_point, size, linewidth) + ) stroke <- (x$stroke %||% defaults$stroke[1]) * .stroke / 2 fontsize <- point_size * .pt + stroke - lwd <- ifelse(is_point, stroke, size * .pt) + lwd <- ifelse(is_point, stroke, linewidth * .pt) pch <- x$shape %||% defaults$shape[type_ind] lty <- x$linetype %||% defaults$linetype[type_ind] gp <- gpar( diff --git a/R/geom-smooth.r b/R/geom-smooth.r index 0980e56b06..6a4c735d44 100644 --- a/R/geom-smooth.r +++ b/R/geom-smooth.r @@ -159,6 +159,6 @@ GeomSmooth <- ggproto("GeomSmooth", Geom, required_aes = c("x", "y"), optional_aes = c("ymin", "ymax"), - default_aes = aes(colour = "#3366FF", fill = "grey60", size = 1, + default_aes = aes(colour = "#3366FF", fill = "grey60", linewidth = 1, linetype = 1, weight = 1, alpha = 0.4) ) diff --git a/R/geom-tile.r b/R/geom-tile.r index 231ae129dd..15d6aba054 100644 --- a/R/geom-tile.r +++ b/R/geom-tile.r @@ -109,7 +109,7 @@ GeomTile <- ggproto("GeomTile", GeomRect, ) }, - default_aes = aes(fill = "grey20", colour = NA, size = 0.1, linetype = 1, + default_aes = aes(fill = "grey20", colour = NA, linewidth = 0.1, linetype = 1, alpha = NA, width = NA, height = NA), required_aes = c("x", "y"), diff --git a/R/geom-violin.r b/R/geom-violin.r index a7d9129993..20d20f7c81 100644 --- a/R/geom-violin.r +++ b/R/geom-violin.r @@ -188,10 +188,12 @@ GeomViolin <- ggproto("GeomViolin", Geom, draw_key = draw_key_polygon, - default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = 0.5, + default_aes = aes(weight = 1, colour = "grey20", fill = "white", linewidth = 0.5, alpha = NA, linetype = "solid"), - required_aes = c("x", "y") + required_aes = c("x", "y"), + + rename_size = TRUE ) # Returns a data.frame with info needed to draw quantile segments. diff --git a/R/geom-vline.r b/R/geom-vline.r index 1c6f6cdd11..d81f089ad7 100644 --- a/R/geom-vline.r +++ b/R/geom-vline.r @@ -55,8 +55,10 @@ GeomVline <- ggproto("GeomVline", Geom, GeomSegment$draw_panel(unique(data), panel_params, coord, lineend = lineend) }, - default_aes = aes(colour = "black", size = 0.5, linetype = 1, alpha = NA), + default_aes = aes(colour = "black", linewidth = 0.5, linetype = 1, alpha = NA), required_aes = "xintercept", - draw_key = draw_key_vline + draw_key = draw_key_vline, + + rename_size = TRUE ) diff --git a/R/guides-.r b/R/guides-.r index 87e5fe6456..f752e033a3 100644 --- a/R/guides-.r +++ b/R/guides-.r @@ -384,6 +384,9 @@ guide_gengrob <- function(guide, theme) UseMethod("guide_gengrob") matched_aes <- function(layer, guide) { all <- names(c(layer$computed_mapping, layer$stat$default_aes)) geom <- c(layer$geom$required_aes, names(layer$geom$default_aes)) + + # Make sure that size guides are shown if a renaming layer is used + if (layer$geom$rename_size && "size" %in% all && !"linewidth" %in% all) geom <- c(geom, "size") matched <- intersect(intersect(all, geom), names(guide$key)) matched <- setdiff(matched, names(layer$computed_geom_params)) setdiff(matched, names(layer$aes_params)) diff --git a/R/layer.r b/R/layer.r index b065a2c10d..2970dacdea 100644 --- a/R/layer.r +++ b/R/layer.r @@ -125,6 +125,16 @@ layer <- function(geom = NULL, stat = NULL, # Warn about extra params and aesthetics extra_param <- setdiff(names(params), all) + # Take care of size->linewidth renaming in layer params + if (geom$rename_size && "size" %in% extra_param && !"linewidth" %in% mapped_aesthetics(mapping)) { + aes_params <- c(aes_params, params["size"]) + extra_param <- setdiff(extra_param, "size") + # TODO: move to cli_warn() + cli::cli_inform(c( + "{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0", + "i" = "Please use {.field linewidth} aesthetic instead" + ), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth") + } if (check.param && length(extra_param) > 0) { cli::cli_warn("Ignoring unknown parameters: {.arg {extra_param}}", call = call_env) } @@ -133,6 +143,15 @@ layer <- function(geom = NULL, stat = NULL, mapped_aesthetics(mapping), c(geom$aesthetics(), stat$aesthetics()) ) + # Take care of size->linewidth aes renaming + if (geom$rename_size && "size" %in% extra_aes && !"linewidth" %in% mapped_aesthetics(mapping)) { + extra_aes <- setdiff(extra_aes, "size") + # TODO: move to cli_warn() + cli::cli_inform(c( + "{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0", + "i" = "Please use {.field linewidth} aesthetic instead" + ), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth") + } if (check.aes && length(extra_aes) > 0) { cli::cli_warn("Ignoring unknown aesthetics: {.field {extra_aes}}", call = call_env) } @@ -221,6 +240,19 @@ Layer <- ggproto("Layer", NULL, # For annotation geoms, it is useful to be able to ignore the default aes if (isTRUE(self$inherit.aes)) { self$computed_mapping <- defaults(self$mapping, plot$mapping) + + # Inherit size as linewidth from global mapping + if (self$geom$rename_size && + "size" %in% names(plot$mapping) && + !"linewidth" %in% names(self$computed_mapping) && + "linewidth" %in% self$geom$aesthetics()) { + self$computed_mapping$size <- plot$mapping$size + # TODO: move to cli_warn() + cli::cli_inform(c( + "{.field size} aesthetic has been deprecated for use with lines as of ggplot2 3.4.0", + "i" = "Please use {.field linewidth} aesthetic instead" + ), .frequency = "regularly", .frequency_id = "ggplot-size-linewidth") + } # defaults() strips class, but it needs to be preserved for now class(self$computed_mapping) <- "uneval" } else { diff --git a/R/legend-draw.r b/R/legend-draw.r index aebcc83c36..86c7d36839 100644 --- a/R/legend-draw.r +++ b/R/legend-draw.r @@ -47,7 +47,7 @@ draw_key_abline <- function(data, params, size) { segmentsGrob(0, 0, 1, 1, gp = gpar( col = alpha(data$colour %||% data$fill %||% "black", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt" ) @@ -66,11 +66,11 @@ draw_key_rect <- function(data, params, size) { #' @export #' @rdname draw_key draw_key_polygon <- function(data, params, size) { - if (is.null(data$size)) { - data$size <- 0.5 + if (is.null(data$linewidth)) { + data$linewidth <- 0.5 } - lwd <- min(data$size, min(size) / 4) + lwd <- min(data$linewidth, min(size) / 4) rectGrob( width = unit(1, "npc") - unit(lwd, "mm"), @@ -102,7 +102,7 @@ draw_key_boxplot <- function(data, params, size) { gp = gpar( col = data$colour %||% "grey20", fill = alpha(data$fill %||% "white", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt", linejoin = params$linejoin %||% "mitre" @@ -120,7 +120,7 @@ draw_key_crossbar <- function(data, params, size) { gp = gpar( col = data$colour %||% "grey20", fill = alpha(data$fill %||% "white", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt", linejoin = params$linejoin %||% "mitre" @@ -143,7 +143,7 @@ draw_key_path <- function(data, params, size) { col = alpha(data$colour %||% data$fill %||% "black", data$alpha), fill = alpha(params$arrow.fill %||% data$colour %||% data$fill %||% "black", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt" ), @@ -157,7 +157,7 @@ draw_key_vpath <- function(data, params, size) { segmentsGrob(0.5, 0.1, 0.5, 0.9, gp = gpar( col = alpha(data$colour %||% data$fill %||% "black", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt" ), @@ -194,7 +194,7 @@ draw_key_linerange <- function(data, params, size) { draw_key_pointrange <- function(data, params, size) { grobTree( draw_key_linerange(data, params, size), - draw_key_point(transform(data, size = (data$size %||% 1.5) * 4), params) + draw_key_point(transform(data, params, size = (data$size %||% 1.5) * 4)) ) } @@ -206,7 +206,7 @@ draw_key_smooth <- function(data, params, size) { grobTree( if (isTRUE(params$se)) rectGrob(gp = gpar(col = NA, fill = data$fill)), - draw_key_path(data, params) + draw_key_path(data, params, size) ) } @@ -241,7 +241,7 @@ draw_key_vline <- function(data, params, size) { segmentsGrob(0.5, 0, 0.5, 1, gp = gpar( col = alpha(data$colour %||% data$fill %||% "black", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt" ) @@ -262,7 +262,7 @@ draw_key_timeseries <- function(data, params, size) { y = c(0.1, 0.6, 0.4, 0.9), gp = gpar( col = alpha(data$colour %||% data$fill %||% "black", data$alpha), - lwd = (data$size %||% 0.5) * .pt, + lwd = (data$linewidth %||% 0.5) * .pt, lty = data$linetype %||% 1, lineend = params$lineend %||% "butt", linejoin = params$linejoin %||% "round" diff --git a/R/scale-linewidth.R b/R/scale-linewidth.R new file mode 100644 index 0000000000..0c726e7016 --- /dev/null +++ b/R/scale-linewidth.R @@ -0,0 +1,85 @@ +#' Scales for line width +#' +#' `scale_linewidth` scales the width of lines and polygon strokes. Due to +#' historical reasons, it is also possible to control this with the `size` +#' aesthetic, but using `linewidth` is encourage to clearly differentiate area +#' aesthetics from stroke width aesthetics. +#' +#' @name scale_linewidth +#' @inheritParams continuous_scale +#' @inheritParams binned_scale +#' @param range a numeric vector of length 2 that specifies the minimum and +#' maximum size of the plotting symbol after transformation. +#' @examples +#' p <- ggplot(economics, aes(date, unemploy, linewidth = uempmed)) + +#' geom_line(lineend = "round") +#' p +#' p + scale_linewidth("Duration of\nunemployment") +#' p + scale_linewidth(range = c(0, 4)) +#' +#' # Binning can sometimes make it easier to match the scaled data to the legend +#' p + scale_linewidth_binned() +#' +NULL + +#' @rdname scale_linewidth +#' @export +#' @usage NULL +scale_linewidth_continuous <- function(name = waiver(), breaks = waiver(), + labels = waiver(), limits = NULL, + range = c(1, 6), trans = "identity", + guide = "legend") { + continuous_scale("linewidth", "linewidth_c", rescale_pal(range), name = name, + breaks = breaks, labels = labels, limits = limits, trans = trans, + guide = guide) +} + +#' @rdname scale_linewidth +#' @export +scale_linewidth <- scale_linewidth_continuous + +#' @rdname scale_linewidth +#' @export +scale_linewidth_binned <- function(name = waiver(), breaks = waiver(), labels = waiver(), + limits = NULL, range = c(1, 6), n.breaks = NULL, + nice.breaks = TRUE, trans = "identity", guide = "bins") { + binned_scale("linewidth", "linewidth_b", rescale_pal(range), name = name, + breaks = breaks, labels = labels, limits = limits, trans = trans, + n.breaks = n.breaks, nice.breaks = nice.breaks, guide = guide) +} + +#' @rdname scale_linewidth +#' @export +#' @usage NULL +scale_linewidth_discrete <- function(...) { + cli::cli_warn("Using {.field linewidth} for a discrete variable is not advised.") + scale_linewidth_ordinal(...) +} + +#' @rdname scale_linewidth +#' @export +#' @usage NULL +scale_linewidth_ordinal <- function(..., range = c(2, 6)) { + force(range) + + discrete_scale( + "linewidth", + "linewidth_d", + function(n) seq(range[1], range[2], length.out = n), + ... + ) +} + +#' @rdname scale_linewidth +#' @export +#' @usage NULL +scale_linewidth_datetime <- function(..., range = c(1, 6)) { + datetime_scale("linewidth", "time", palette = rescale_pal(range), ...) +} + +#' @rdname scale_linewidth +#' @export +#' @usage NULL +scale_linewidth_date <- function(..., range = c(1, 6)) { + datetime_scale("linewidth", "date", palette = rescale_pal(range), ...) +} diff --git a/R/scale-size.r b/R/scale-size.r index 84a0ff88cf..b56f01d555 100644 --- a/R/scale-size.r +++ b/R/scale-size.r @@ -8,13 +8,20 @@ #' scales by area (but does not ensure 0 equals an area of zero). For a binned #' equivalent of `scale_size_area()` use `scale_size_binned_area()`. #' +#' @note Historically the size aesthetic was used for two different things: +#' Scaling the size of object (like points and glyphs) and scaling the width +#' of lines. From ggplot2 3.4.0 the latter has been moved to its own linewidth +#' aesthetic. For backwards compatibility using size is still possible, but it +#' is highly advised to switch to the new linewidth aesthetic for these cases. +#' #' @name scale_size #' @inheritParams continuous_scale #' @inheritParams binned_scale #' @param range a numeric vector of length 2 that specifies the minimum and #' maximum size of the plotting symbol after transformation. #' @seealso [scale_size_area()] if you want 0 values to be mapped -#' to points with size 0. +#' to points with size 0. [scale_linewidth()] if you want to scale the width +#' of lines. #' @examples #' p <- ggplot(mpg, aes(displ, hwy, size = hwy)) + #' geom_point() diff --git a/man/geom_abline.Rd b/man/geom_abline.Rd index d018b98860..03baa1d9b2 100644 --- a/man/geom_abline.Rd +++ b/man/geom_abline.Rd @@ -92,7 +92,7 @@ commonly set in the plot. They also do not affect the x and y scales. These geoms are drawn using with \code{\link[=geom_line]{geom_line()}} so support the same aesthetics: \code{alpha}, \code{colour}, \code{linetype} and -\code{size}. They also each have aesthetics that control the position of +\code{linewidth}. They also each have aesthetics that control the position of the line: \itemize{ \item \code{geom_vline()}: \code{xintercept} diff --git a/man/geom_bar.Rd b/man/geom_bar.Rd index d6a03c24e9..734c0d42c1 100644 --- a/man/geom_bar.Rd +++ b/man/geom_bar.Rd @@ -140,7 +140,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. @@ -154,7 +154,7 @@ Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_boxplot.Rd b/man/geom_boxplot.Rd index 3f8684b2c2..f9b850bcf0 100644 --- a/man/geom_boxplot.Rd +++ b/man/geom_boxplot.Rd @@ -165,6 +165,7 @@ See McGill et al. (1978) for more details. \item \code{fill} \item \code{group} \item \code{linetype} +\item \code{linewidth} \item \code{shape} \item \code{size} \item \code{weight} diff --git a/man/geom_contour.Rd b/man/geom_contour.Rd index cd653ee332..8341670c2d 100644 --- a/man/geom_contour.Rd +++ b/man/geom_contour.Rd @@ -162,7 +162,7 @@ using \code{\link[interp:interp]{interp::interp()}}, \code{\link[akima:bilinear] \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{weight} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. @@ -177,7 +177,7 @@ Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{subgroup} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_density.Rd b/man/geom_density.Rd index dd8469635f..02fdc43627 100644 --- a/man/geom_density.Rd +++ b/man/geom_density.Rd @@ -135,7 +135,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{weight} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_density_2d.Rd b/man/geom_density_2d.Rd index 66639998ce..2b7337cfc4 100644 --- a/man/geom_density_2d.Rd +++ b/man/geom_density_2d.Rd @@ -170,7 +170,7 @@ bands. \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. @@ -184,7 +184,7 @@ Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{subgroup} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_errorbarh.Rd b/man/geom_errorbarh.Rd index 5ba6f86942..d6f2c135dd 100644 --- a/man/geom_errorbarh.Rd +++ b/man/geom_errorbarh.Rd @@ -80,7 +80,7 @@ A rotated version of \code{\link[=geom_errorbar]{geom_errorbar()}}. \item \code{group} \item \code{height} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_function.Rd b/man/geom_function.Rd index d5af1b54e0..2bbb8733c1 100644 --- a/man/geom_function.Rd +++ b/man/geom_function.Rd @@ -99,7 +99,7 @@ drawn (by default) with a line. \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_hex.Rd b/man/geom_hex.Rd index 97b41b321d..a6962f7cfd 100644 --- a/man/geom_hex.Rd +++ b/man/geom_hex.Rd @@ -101,7 +101,7 @@ the very regular alignment of \code{\link[=geom_bin2d]{geom_bin2d()}}. \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_linerange.Rd b/man/geom_linerange.Rd index 78d5957f78..d274ad2c1b 100644 --- a/man/geom_linerange.Rd +++ b/man/geom_linerange.Rd @@ -137,7 +137,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_map.Rd b/man/geom_map.Rd index 7ed400b902..442f2e93d7 100644 --- a/man/geom_map.Rd +++ b/man/geom_map.Rd @@ -82,7 +82,7 @@ it can be used in conjunction with \code{geom_sf()} layers and/or \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{subgroup} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_path.Rd b/man/geom_path.Rd index b5b5db79a9..9d2af717fc 100644 --- a/man/geom_path.Rd +++ b/man/geom_path.Rd @@ -138,7 +138,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_polygon.Rd b/man/geom_polygon.Rd index d94e333541..81d1eb61a6 100644 --- a/man/geom_polygon.Rd +++ b/man/geom_polygon.Rd @@ -91,7 +91,7 @@ polygon. \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{subgroup} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_quantile.Rd b/man/geom_quantile.Rd index fe7a8f4830..cd53c5ee08 100644 --- a/man/geom_quantile.Rd +++ b/man/geom_quantile.Rd @@ -112,7 +112,7 @@ with lines. This is as a continuous analogue to \code{\link[=geom_boxplot]{geom_ \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{weight} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_ribbon.Rd b/man/geom_ribbon.Rd index f8ef013641..21d0c9c21b 100644 --- a/man/geom_ribbon.Rd +++ b/man/geom_ribbon.Rd @@ -121,7 +121,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_rug.Rd b/man/geom_rug.Rd index 9a1723e074..91eed7baa0 100644 --- a/man/geom_rug.Rd +++ b/man/geom_rug.Rd @@ -95,7 +95,7 @@ any data points under the default settings. \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{x} \item \code{y} } diff --git a/man/geom_segment.Rd b/man/geom_segment.Rd index b93f35c05d..310daacc1f 100644 --- a/man/geom_segment.Rd +++ b/man/geom_segment.Rd @@ -131,7 +131,7 @@ need to connect points across multiple cases. \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_smooth.Rd b/man/geom_smooth.Rd index 7df592c77f..ba84914d29 100644 --- a/man/geom_smooth.Rd +++ b/man/geom_smooth.Rd @@ -165,7 +165,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{weight} \item \code{ymax} \item \code{ymin} diff --git a/man/geom_spoke.Rd b/man/geom_spoke.Rd index fbd2728756..d38bd3aa45 100644 --- a/man/geom_spoke.Rd +++ b/man/geom_spoke.Rd @@ -83,7 +83,7 @@ The angles start from east and increase counterclockwise. \item \code{colour} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_tile.Rd b/man/geom_tile.Rd index a07095f2b6..2da772536c 100644 --- a/man/geom_tile.Rd +++ b/man/geom_tile.Rd @@ -123,7 +123,7 @@ performance special case for when all the tiles are the same size. \item \code{group} \item \code{height} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{width} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/geom_violin.Rd b/man/geom_violin.Rd index 6f3500a0c1..c125383a95 100644 --- a/man/geom_violin.Rd +++ b/man/geom_violin.Rd @@ -133,7 +133,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{fill} \item \code{group} \item \code{linetype} -\item \code{size} +\item \code{linewidth} \item \code{weight} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. diff --git a/man/scale_linewidth.Rd b/man/scale_linewidth.Rd new file mode 100644 index 0000000000..eeb1ccb061 --- /dev/null +++ b/man/scale_linewidth.Rd @@ -0,0 +1,122 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/scale-linewidth.R +\name{scale_linewidth} +\alias{scale_linewidth} +\alias{scale_linewidth_continuous} +\alias{scale_linewidth_binned} +\alias{scale_linewidth_discrete} +\alias{scale_linewidth_ordinal} +\alias{scale_linewidth_datetime} +\alias{scale_linewidth_date} +\title{Scales for line width} +\usage{ +scale_linewidth( + name = waiver(), + breaks = waiver(), + labels = waiver(), + limits = NULL, + range = c(1, 6), + trans = "identity", + guide = "legend" +) + +scale_linewidth_binned( + name = waiver(), + breaks = waiver(), + labels = waiver(), + limits = NULL, + range = c(1, 6), + n.breaks = NULL, + nice.breaks = TRUE, + trans = "identity", + guide = "bins" +) +} +\arguments{ +\item{name}{The name of the scale. Used as the axis or legend title. If +\code{waiver()}, the default, the name of the scale is taken from the first +mapping used for that aesthetic. If \code{NULL}, the legend title will be +omitted.} + +\item{breaks}{One of: +\itemize{ +\item \code{NULL} for no breaks +\item \code{waiver()} for the default breaks computed by the +\link[scales:trans_new]{transformation object} +\item A numeric vector of positions +\item A function that takes the limits as input and returns breaks +as output (e.g., a function returned by \code{\link[scales:breaks_extended]{scales::extended_breaks()}}). +Also accepts rlang \link[rlang:as_function]{lambda} function notation. +}} + +\item{labels}{One of: +\itemize{ +\item \code{NULL} for no labels +\item \code{waiver()} for the default labels computed by the +transformation object +\item A character vector giving labels (must be same length as \code{breaks}) +\item An expression vector (must be the same length as breaks). See ?plotmath for details. +\item A function that takes the breaks as input and returns labels +as output. Also accepts rlang \link[rlang:as_function]{lambda} function +notation. +}} + +\item{limits}{One of: +\itemize{ +\item \code{NULL} to use the default scale range +\item A numeric vector of length two providing limits of the scale. +Use \code{NA} to refer to the existing minimum or maximum +\item A function that accepts the existing (automatic) limits and returns +new limits. Also accepts rlang \link[rlang:as_function]{lambda} function +notation. +Note that setting limits on positional scales will \strong{remove} data outside of the limits. +If the purpose is to zoom, use the limit argument in the coordinate system +(see \code{\link[=coord_cartesian]{coord_cartesian()}}). +}} + +\item{range}{a numeric vector of length 2 that specifies the minimum and +maximum size of the plotting symbol after transformation.} + +\item{trans}{For continuous scales, the name of a transformation object +or the object itself. Built-in transformations include "asn", "atanh", +"boxcox", "date", "exp", "hms", "identity", "log", "log10", "log1p", "log2", +"logit", "modulus", "probability", "probit", "pseudo_log", "reciprocal", +"reverse", "sqrt" and "time". + +A transformation object bundles together a transform, its inverse, +and methods for generating breaks and labels. Transformation objects +are defined in the scales package, and are called \verb{_trans} (e.g., +\code{\link[scales:boxcox_trans]{scales::boxcox_trans()}}). You can create your own +transformation with \code{\link[scales:trans_new]{scales::trans_new()}}.} + +\item{guide}{A function used to create a guide or its name. See +\code{\link[=guides]{guides()}} for more information.} + +\item{n.breaks}{An integer guiding the number of major breaks. The algorithm +may choose a slightly different number to ensure nice break labels. Will +only have an effect if \code{breaks = waiver()}. Use \code{NULL} to use the default +number of breaks given by the transformation.} + +\item{nice.breaks}{Logical. Should breaks be attempted placed at nice values +instead of exactly evenly spaced between the limits. If \code{TRUE} (default) +the scale will ask the transformation object to create breaks, and this +may result in a different number of breaks than requested. Ignored if +breaks are given explicitly.} +} +\description{ +\code{scale_linewidth} scales the width of lines and polygon strokes. Due to +historical reasons, it is also possible to control this with the \code{size} +aesthetic, but using \code{linewidth} is encourage to clearly differentiate area +aesthetics from stroke width aesthetics. +} +\examples{ +p <- ggplot(economics, aes(date, unemploy, linewidth = uempmed)) + + geom_line(lineend = "round") +p +p + scale_linewidth("Duration of\nunemployment") +p + scale_linewidth(range = c(0, 4)) + +# Binning can sometimes make it easier to match the scaled data to the legend +p + scale_linewidth_binned() + +} diff --git a/man/scale_size.Rd b/man/scale_size.Rd index 83ce8692e8..00a03998ce 100644 --- a/man/scale_size.Rd +++ b/man/scale_size.Rd @@ -165,6 +165,13 @@ to a size of 0. \code{scale_size_binned()} is a binned version of \code{scale_si scales by area (but does not ensure 0 equals an area of zero). For a binned equivalent of \code{scale_size_area()} use \code{scale_size_binned_area()}. } +\note{ +Historically the size aesthetic was used for two different things: +Scaling the size of object (like points and glyphs) and scaling the width +of lines. From ggplot2 3.4.0 the latter has been moved to its own linewidth +aesthetic. For backwards compatibility using size is still possible, but it +is highly advised to switch to the new linewidth aesthetic for these cases. +} \examples{ p <- ggplot(mpg, aes(displ, hwy, size = hwy)) + geom_point() @@ -188,5 +195,6 @@ p + scale_radius() } \seealso{ \code{\link[=scale_size_area]{scale_size_area()}} if you want 0 values to be mapped -to points with size 0. +to points with size 0. \code{\link[=scale_linewidth]{scale_linewidth()}} if you want to scale the width +of lines. } diff --git a/tests/testthat/_snaps/draw-key/time-series-and-polygon-key-glyphs.svg b/tests/testthat/_snaps/draw-key/time-series-and-polygon-key-glyphs.svg index 08f726f50e..90a5b755d2 100644 --- a/tests/testthat/_snaps/draw-key/time-series-and-polygon-key-glyphs.svg +++ b/tests/testthat/_snaps/draw-key/time-series-and-polygon-key-glyphs.svg @@ -59,11 +59,11 @@ z - + - + - + a b c diff --git a/tests/testthat/test-geom-hex.R b/tests/testthat/test-geom-hex.R index 82465e2183..08669fdb88 100644 --- a/tests/testthat/test-geom-hex.R +++ b/tests/testthat/test-geom-hex.R @@ -12,7 +12,7 @@ test_that("density and value summaries are available", { test_that("size and linetype are applied", { df <- data_frame(x = c(1, 1, 1, 2), y = c(1, 1, 1, 2)) plot <- ggplot(df, aes(x, y)) + - geom_hex(color = "red", size = 4, linetype = 2) + geom_hex(color = "red", linewidth = 4, linetype = 2) gpar <- layer_grob(plot)[[1]]$children[[1]]$gp expect_equal(gpar$lwd, rep(4, 12) * .pt, tolerance = 1e-7) diff --git a/tests/testthat/test-geom-tile.R b/tests/testthat/test-geom-tile.R index 00261386ca..d0c8a6f073 100644 --- a/tests/testthat/test-geom-tile.R +++ b/tests/testthat/test-geom-tile.R @@ -14,7 +14,7 @@ test_that("accepts width and height aesthetics", { df <- data_frame(x = 0, y = 0, width = c(2, 4), height = c(2, 4)) p <- ggplot(df, aes(x, y, width = width, height = height)) + - geom_tile(fill = NA, colour = "black", size = 1) + geom_tile(fill = NA, colour = "black", linewidth = 1) out <- layer_data(p) boundary <- as.data.frame(tibble::tribble( diff --git a/tests/testthat/test-scales.r b/tests/testthat/test-scales.r index 40bb1e5c8a..7cf2e8c528 100644 --- a/tests/testthat/test-scales.r +++ b/tests/testthat/test-scales.r @@ -229,6 +229,10 @@ test_that("size and alpha scales throw appropriate warnings for factors", { ggplot_build(p + geom_point(aes(alpha = d))), "Using alpha for a discrete variable is not advised." ) + expect_warning( + ggplot_build(p + geom_line(aes(linewidth = d, group = 1))), + "Using linewidth for a discrete variable is not advised." + ) # There should be no warnings for ordered factors expect_warning(ggplot_build(p + geom_point(aes(size = o))), NA) expect_warning(ggplot_build(p + geom_point(aes(alpha = o))), NA) diff --git a/tests/testthat/test-sec-axis.R b/tests/testthat/test-sec-axis.R index 68067df80b..d77ee102bf 100644 --- a/tests/testthat/test-sec-axis.R +++ b/tests/testthat/test-sec-axis.R @@ -242,7 +242,7 @@ test_that("sec_axis() respects custom transformations", { expect_doppelganger( "sec_axis, custom transform", ggplot(dat, aes(x = x, y = y)) + - geom_line(size = 1, na.rm = T) + + geom_line(linewidth = 1, na.rm = T) + scale_y_continuous( trans = magnify_trans_log(interval_low = 0.5, interval_high = 1, reducer = 0.5, reducer2 = 8), breaks = diff --git a/tests/testthat/test-stat-sum.R b/tests/testthat/test-stat-sum.R index 43734a0961..350665d561 100644 --- a/tests/testthat/test-stat-sum.R +++ b/tests/testthat/test-stat-sum.R @@ -47,7 +47,7 @@ test_that("summaries are drawn correctly", { expect_doppelganger("summary with color and lines", ggplot(mtcars, aes(x = cyl, y = mpg, colour = factor(vs))) + geom_point() + - stat_summary(fun = mean, geom = "line", size = 2) + stat_summary(fun = mean, geom = "line", linewidth = 2) ) expect_doppelganger("summary with crossbars, no grouping", ggplot(mtcars, aes(x = cyl, y = mpg)) + diff --git a/vignettes/extending-ggplot2.Rmd b/vignettes/extending-ggplot2.Rmd index 9719384769..666d5409c9 100644 --- a/vignettes/extending-ggplot2.Rmd +++ b/vignettes/extending-ggplot2.Rmd @@ -401,7 +401,7 @@ GeomSimplePolygon <- ggproto("GeomPolygon", Geom, required_aes = c("x", "y"), default_aes = aes( - colour = NA, fill = "grey20", size = 0.5, + colour = NA, fill = "grey20", linewidth = 0.5, linetype = 1, alpha = 1 ), @@ -421,7 +421,7 @@ GeomSimplePolygon <- ggproto("GeomPolygon", Geom, gp = grid::gpar( col = first_row$colour, fill = scales::alpha(first_row$fill, first_row$alpha), - lwd = first_row$size * .pt, + lwd = first_row$linewidth * .pt, lty = first_row$linetype ) ) @@ -465,7 +465,7 @@ Sometimes you just want to make a small modification to an existing geom. In thi ```{r} GeomPolygonHollow <- ggproto("GeomPolygonHollow", GeomPolygon, - default_aes = aes(colour = "black", fill = NA, size = 0.5, linetype = 1, + default_aes = aes(colour = "black", fill = NA, linewidth = 0.5, linetype = 1, alpha = NA) ) geom_chull <- function(mapping = NULL, data = NULL,