From 59dc42e51d4b8505e087be2a7860d1c110f89192 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 2 Apr 2025 10:08:03 +0200 Subject: [PATCH 1/8] cleanup branch --- man/stat_connect.Rd | 10 +++++----- .../{scale-colour-continuous.md => scale-colour.md} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename tests/testthat/_snaps/{scale-colour-continuous.md => scale-colour.md} (100%) diff --git a/man/stat_connect.Rd b/man/stat_connect.Rd index d62ba610a5..24166744d8 100644 --- a/man/stat_connect.Rd +++ b/man/stat_connect.Rd @@ -128,11 +128,11 @@ Connect successive points with lines of different shapes. } \section{Aesthetics}{ -\code{stat_connect()} understands the following aesthetics (required aesthetics are in bold): -\itemize{ -\item \strong{\code{\link[=aes_position]{x}} \emph{or} \code{\link[=aes_position]{xmin}} \emph{or} \code{\link[=aes_position]{xmax}}} -\item \strong{\code{\link[=aes_position]{y}} \emph{or} \code{\link[=aes_position]{ymin}} \emph{or} \code{\link[=aes_position]{ymax}}} -\item \code{\link[=aes_group_order]{group}} +\code{stat_connect()} understands the following aesthetics. Required aesthetics are displayed in bold and defaults are displayed for optional aesthetics: +\tabular{rll}{ +• \tab \strong{\code{\link[=aes_position]{x}} \emph{or} \code{\link[=aes_position]{xmin}} \emph{or} \code{\link[=aes_position]{xmax}}} \tab \cr\cr +• \tab \strong{\code{\link[=aes_position]{y}} \emph{or} \code{\link[=aes_position]{ymin}} \emph{or} \code{\link[=aes_position]{ymax}}} \tab \cr\cr +• \tab \code{\link[=aes_group_order]{group}} \tab → inferred \cr\cr } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/tests/testthat/_snaps/scale-colour-continuous.md b/tests/testthat/_snaps/scale-colour.md similarity index 100% rename from tests/testthat/_snaps/scale-colour-continuous.md rename to tests/testthat/_snaps/scale-colour.md From 3fcfbe374617cf04b93c3d8d6880ce1f1dae8c8d Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 2 Apr 2025 10:13:26 +0200 Subject: [PATCH 2/8] For backward compatibility, `zeroGrob()` returns a `nullGrob()` subclass --- NAMESPACE | 5 ----- R/grob-null.R | 20 ++------------------ R/utilities.R | 9 +++++++++ 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 088da2ba6c..b0d3fc0713 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -19,7 +19,6 @@ S3method(as.list,ggproto) S3method(autolayer,default) S3method(autoplot,default) S3method(c,mapped_discrete) -S3method(drawDetails,zeroGrob) S3method(element_grob,element_blank) S3method(element_grob,element_line) S3method(element_grob,element_point) @@ -76,9 +75,7 @@ S3method(ggplot_gtable,ggplot_built) S3method(grid.draw,absoluteGrob) S3method(grid.draw,ggplot) S3method(grobHeight,absoluteGrob) -S3method(grobHeight,zeroGrob) S3method(grobWidth,absoluteGrob) -S3method(grobWidth,zeroGrob) S3method(grobX,absoluteGrob) S3method(grobY,absoluteGrob) S3method(guide_gengrob,default) @@ -87,7 +84,6 @@ S3method(guide_merge,default) S3method(guide_train,default) S3method(guide_transform,default) S3method(heightDetails,titleGrob) -S3method(heightDetails,zeroGrob) S3method(limits,Date) S3method(limits,POSIXct) S3method(limits,POSIXlt) @@ -149,7 +145,6 @@ S3method(vec_ptype2,mapped_discrete.factor) S3method(vec_ptype2,mapped_discrete.integer) S3method(vec_ptype2,mapped_discrete.mapped_discrete) S3method(widthDetails,titleGrob) -S3method(widthDetails,zeroGrob) export("%+%") export("%+replace%") export(.data) diff --git a/R/grob-null.R b/R/grob-null.R index 217c5a7560..193255a824 100644 --- a/R/grob-null.R +++ b/R/grob-null.R @@ -5,22 +5,6 @@ zeroGrob <- function() .zeroGrob .zeroGrob <- NULL -on_load(.zeroGrob <- grob(cl = "zeroGrob", name = "NULL")) +on_load(.zeroGrob <- add_class(nullGrob(), "zeroGrob")) -#' @export -#' @method widthDetails zeroGrob -widthDetails.zeroGrob <- function(x) unit(0, "cm") -#' @export -#' @method heightDetails zeroGrob -heightDetails.zeroGrob <- function(x) unit(0, "cm") -#' @export -#' @method grobWidth zeroGrob -grobWidth.zeroGrob <- function(x) unit(0, "cm") -#' @export -#' @method grobHeight zeroGrob -grobHeight.zeroGrob <- function(x) unit(0, "cm") -#' @export -#' @method drawDetails zeroGrob -drawDetails.zeroGrob <- function(x, recording) {} - -is.zero <- function(x) is.null(x) || inherits(x, "zeroGrob") +is.zero <- function(x) is.null(x) || inherits(x, "null") diff --git a/R/utilities.R b/R/utilities.R index 8f0672c142..96ebd1e59c 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -959,3 +959,12 @@ compute_data_size <- function(data, size, default = 0.9, data[[target]] <- res * (default %||% 0.9) data } + +add_class <- function(x, new_class) { + new_class <- setdiff(new_class, class(x)) + if (length(new_class) < 1) { + return(x) + } + class(x) <- union(new_class, class(x)) + x +} From 5d55ae88b14d1c091ad5120832a08956961eb5a7 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 2 Apr 2025 10:14:29 +0200 Subject: [PATCH 3/8] adjust tests --- tests/testthat/test-facet-strips.R | 2 +- tests/testthat/test-stat-density2d.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-facet-strips.R b/tests/testthat/test-facet-strips.R index d13f8d500c..8bc684dbe7 100644 --- a/tests/testthat/test-facet-strips.R +++ b/tests/testthat/test-facet-strips.R @@ -132,7 +132,7 @@ test_that("strips can be removed", { theme(strip.background = element_blank(), strip.text = element_blank()) g_grobs <- ggplotGrob(g) strip_grobs <- g_grobs$grobs[grepl('strip-', g_grobs$layout$name)] - expect_true(all(sapply(strip_grobs, inherits, 'zeroGrob'))) + expect_true(all(sapply(strip_grobs, inherits, 'null'))) }) test_that("padding is only added if axis is present", { diff --git a/tests/testthat/test-stat-density2d.R b/tests/testthat/test-stat-density2d.R index 43a99e9513..2e2896cc47 100644 --- a/tests/testthat/test-stat-density2d.R +++ b/tests/testthat/test-stat-density2d.R @@ -100,5 +100,5 @@ test_that("stat_density_2d handles faulty bandwidth", { p <- ggplot(faithful, aes(eruptions, waiting)) + stat_density_2d(h = c(0, NA)) expect_snapshot_warning(b <- ggplot_build(p)) - expect_s3_class(layer_grob(b)[[1]], "zeroGrob") + expect_s3_class(layer_grob(b)[[1]], "null") }) From a8306d36ca109d50a6909a9abd8d94954dd63bd5 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 2 Apr 2025 11:05:57 +0200 Subject: [PATCH 4/8] add news bullet --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 363a20cc28..a33dc3a529 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # ggplot2 (development version) +* (internal) `zeroGrob()` now returns a `grid::nullGrob()` (#6390). * `position_fill()` avoids stacking observations of zero (@teunbrand, #6338) * New `layer(layout)` argument to interact with facets (@teunbrand, #3062) * New `stat_connect()` to connect points via steps or other shapes From 674b6d0bd79453fcd7d1d775e51cbaac25b4067e Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 2 Apr 2025 11:08:02 +0200 Subject: [PATCH 5/8] migrate to grob utilities --- DESCRIPTION | 1 - R/grob-null.R | 10 ---------- R/utilities-grid.R | 11 +++++++++++ man/zeroGrob.Rd | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 R/grob-null.R diff --git a/DESCRIPTION b/DESCRIPTION index 82266c740f..83ca78ce8e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -173,7 +173,6 @@ Collate: 'ggplot2-package.R' 'grob-absolute.R' 'grob-dotstack.R' - 'grob-null.R' 'grouping.R' 'theme-elements.R' 'guide-.R' diff --git a/R/grob-null.R b/R/grob-null.R deleted file mode 100644 index 193255a824..0000000000 --- a/R/grob-null.R +++ /dev/null @@ -1,10 +0,0 @@ -#' The zero grob draws nothing and has zero size. -#' -#' @keywords internal -#' @export -zeroGrob <- function() .zeroGrob - -.zeroGrob <- NULL -on_load(.zeroGrob <- add_class(nullGrob(), "zeroGrob")) - -is.zero <- function(x) is.null(x) || inherits(x, "null") diff --git a/R/utilities-grid.R b/R/utilities-grid.R index a935d5b38f..3bd29e9ceb 100644 --- a/R/utilities-grid.R +++ b/R/utilities-grid.R @@ -48,6 +48,17 @@ gg_par <- function(..., stroke = NULL, pointsize = NULL) { inject(gpar(!!!args)) } +#' The zero grob draws nothing and has zero size. +#' +#' @keywords internal +#' @export +zeroGrob <- function() .zeroGrob + +.zeroGrob <- NULL +on_load(.zeroGrob <- add_class(nullGrob(), "zeroGrob")) + +is.zero <- function(x) is.null(x) || inherits(x, "null") + width_cm <- function(x) { if (is.grob(x)) { convertWidth(grobWidth(x), "cm", TRUE) diff --git a/man/zeroGrob.Rd b/man/zeroGrob.Rd index e4aac88eb0..6fcfe56218 100644 --- a/man/zeroGrob.Rd +++ b/man/zeroGrob.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/grob-null.R +% Please edit documentation in R/utilities-grid.R \name{zeroGrob} \alias{zeroGrob} \title{The zero grob draws nothing and has zero size.} From 8ac14d6054b8f8e60f1acd451f7ac6f59834a32d Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 4 Dec 2025 13:28:54 +0100 Subject: [PATCH 6/8] avoid including fragile grobName in snapshot --- tests/testthat/_snaps/theme-elements.md | 4 +--- tests/testthat/test-theme-elements.R | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testthat/_snaps/theme-elements.md b/tests/testthat/_snaps/theme-elements.md index f3931ef6b0..936112d821 100644 --- a/tests/testthat/_snaps/theme-elements.md +++ b/tests/testthat/_snaps/theme-elements.md @@ -64,10 +64,8 @@ --- Code - element_grob(el, label = element_blank()) + x <- element_grob(el, label = element_blank()) Condition Warning: `label` cannot be a object. - Output - zeroGrob[NULL] diff --git a/tests/testthat/test-theme-elements.R b/tests/testthat/test-theme-elements.R index d74b35b5c3..76ef1ae7cd 100644 --- a/tests/testthat/test-theme-elements.R +++ b/tests/testthat/test-theme-elements.R @@ -100,7 +100,7 @@ test_that("element_text throws appropriate conditions", { # labs(y = element_blank()) for some reason el <- theme_get()$text expect_snapshot( - element_grob(el, label = element_blank()) + x <- element_grob(el, label = element_blank()) ) }) From fda95e13c56e7909c13a4c61b4923b0019b94023 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 4 Dec 2025 13:50:04 +0100 Subject: [PATCH 7/8] use `add_class()` in other places too --- R/aes-variants.R | 3 +-- R/theme.R | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/R/aes-variants.R b/R/aes-variants.R index 264151f2bb..12851c5cd6 100644 --- a/R/aes-variants.R +++ b/R/aes-variants.R @@ -113,8 +113,7 @@ aes_all <- function(vars) { # Quosure the symbols in the empty environment because they can only # refer to the data mask x <- class_mapping(lapply(vars, function(x) new_quosure(as.name(x), emptyenv()))) - class(x) <- union("unlabelled", class(x)) - x + add_class(x, "unlabelled") } #' Automatic aesthetic mapping diff --git a/R/theme.R b/R/theme.R index 79143ec0b5..d263ff0fe6 100644 --- a/R/theme.R +++ b/R/theme.R @@ -501,7 +501,7 @@ theme <- function(..., } t <- class_theme(elements, complete = complete, validate = validate) - class(t) <- union("theme", class(t)) + t <- add_class(t, "theme") t } From 961973f648b3fd4fb6c74d4655039315099cd167 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 4 Dec 2025 13:59:40 +0100 Subject: [PATCH 8/8] fix misresolution of merge conflict --- NEWS.md | 98 --------------------------------------------------------- 1 file changed, 98 deletions(-) diff --git a/NEWS.md b/NEWS.md index abc7c45e6b..7fab21a27a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,104 +1,6 @@ # ggplot2 (development version) * (internal) `zeroGrob()` now returns a `grid::nullGrob()` (#6390). -* `position_fill()` avoids stacking observations of zero (@teunbrand, #6338) -* New `layer(layout)` argument to interact with facets (@teunbrand, #3062) -* New `stat_connect()` to connect points via steps or other shapes - (@teunbrand, #6228) -* Fixed regression with incorrectly drawn gridlines when using `coord_flip()` - (@teunbrand, #6293). -* Deprecated functions and arguments prior to ggplot2 3.0.0 throw errors instead - of warnings. -* Functions and arguments that were soft-deprecated up to ggplot2 3.4.0 now - throw warnings. -* (internal) layer data can be attenuated with parameter attributes - (@teunbrand, #3175). -* Date scales silently coerce to and datetime scales silently - coerce to (@laurabrianna, #3533) -* New parameters for `geom_label()` (@teunbrand and @steveharoz, #5365): - * The `linewidth` aesthetic is now applied and replaces the `label.size` - argument. - * The `linetype` aesthetic is now applied. - * New `border.colour` argument to set the colour of borders. - * New `text.colour` argument to set the colour of text. -* New `element_point()` and `element_polygon()` that can be given to - `theme(point, polygon)` as an extension point (@teunbrand, #6248). -* Turned off fallback for `size` to `linewidth` translation in - `geom_bar()`/`geom_col()` (#4848). -* `coord_radial()` now displays no axis instead of throwing an error when - a scale has no breaks (@teunbrand, #6271). -* The `fatten` argument has been deprecated in `geom_boxplot()`, - `geom_crossbar()` and `geom_pointrange()` (@teunbrand, #4881). -* Axis labels are now preserved better when using `coord_sf(expand = TRUE)` and - graticule lines are straight but do not meet the edge (@teunbrand, #2985). -* Attempt to boost detail in `coord_polar()` and `coord_radial()` near the - center (@teunbrand, #5023) -* Scale names, guide titles and aesthetic labels can now accept functions - (@teunbrand, #4313) -* Binned scales with zero-width data expand the default limits by 0.1 - (@teunbrand, #5066) -* New default `geom_qq_line(geom = "abline")` for better clipping in the - vertical direction. In addition, `slope` and `intercept` are new computed - variables in `stat_qq_line()` (@teunbrand, #6087). -* Position adjustments can now have auxiliary aesthetics (@teunbrand). - * `position_nudge()` gains `nudge_x` and `nudge_y` aesthetics (#3026, #5445). - * `position_dodge()` gains `order` aesthetic (#3022, #3345) -* More stability for vctrs-based palettes (@teunbrand, #6117). -* Fixed regression in `guide_bins(reverse = TRUE)` (@teunbrand, #6183). -* New function family for setting parts of a theme. For example, you can now use - `theme_sub_axis(line, text, ticks, ticks.length, line)` as a substitute for - `theme(axis.line, axis.text, axis.ticks, axis.ticks.length, axis.line)`. This - should allow slightly terser and more organised theme declarations - (@teunbrand, #5301). -* `scale_{x/y}_discrete(continuous.limits)` is a new argument to control the - display range of discrete scales (@teunbrand, #4174, #6259). -* `geom_ribbon()` now appropriately warns about, and removes, missing values - (@teunbrand, #6243). -* `guide_*()` can now accept two inside legend theme elements: - `legend.position.inside` and `legend.justification.inside`, allowing inside - legends to be placed at different positions. Only inside legends with the same - position and justification will be merged (@Yunuuuu, #6210). -* New stat: `stat_manual()` for arbitrary computations (@teunbrand, #3501) -* Reversal of a dimension, typically 'x' or 'y', is now controlled by the - `reverse` argument in `coord_cartesian()`, `coord_fixed()`, `coord_radial()` - and `coord_sf()`. In `coord_radial()`, this replaces the older `direction` - argument (#4021, @teunbrand). -* `coord_radial()` displays minor gridlines now (@teunbrand). -* (internal) `continuous_scale()` and `binned_scale()` sort the `limits` - argument internally (@teunbrand). -* Theme margins can have NA-units to inherit from parent elements. The new - function `margin_part()` has NA-units as default (@teunbrand, #6115) -* New `margin_auto()` specification for theme margins. -* New argument `labs(dictionary)` to label based on variable name rather than - based on aesthetic (@teunbrand, #5178) -* Fixed bug in out-of-bounds binned breaks (@teunbrand, #6054) -* Binned guides now accept expressions as labels (@teunbrand, #6005) -* (internal) `Scale$get_labels()` format expressions as lists. -* In non-orthogonal coordinate systems (`coord_sf()`, `coord_polar()` and - `coord_radial()`), using 'AsIs' variables escape transformation when - both `x` and `y` is an 'AsIs' variable (@teunbrand, #6205). -* The following methods have been deprecated: `fortify.lm()`, `fortify.glht()`, - `fortify.confint.glht()`, `fortify.summary.glht()` and `fortify.cld()`. It - is recommend to use `broom::augment()` and `broom::tidy()` instead - (@teunbrand, #3816). -* Custom and raster annotation now respond to scale transformations, and can - use AsIs variables for relative placement (@teunbrand based on - @yutannihilation's prior work, #3120) -* When discrete breaks have names, they'll be used as labels by default - (@teunbrand, #6147). -* The helper function `is.waiver()` is now exported to help extensions to work - with `waiver()` objects (@arcresu, #6173). -* Date(time) scales now throw appropriate errors when `date_breaks`, - `date_minor_breaks` or `date_labels` are not strings (@RodDalBen, #5880) -* `geom_errorbarh()` is deprecated in favour of - `geom_errorbar(orientation = "y")` (@teunbrand, #5961). -* `geom_contour()` should be able to recognise a rotated grid of points - (@teunbrand, #4320) -* `geom_boxplot()` gains additional arguments to style the colour, linetype and - linewidths of the box, whiskers, median line and staples (@teunbrand, #5126) -* `geom_violin()` gains additional arguments to style the colour, linetype and - linewidths of the quantiles, which replace the now-deprecated `draw_quantiles` - argument (#5912). * `stat_ydensity()` now only requires the `x` or `y` aesthetic. The other will be populated with 0, similar to `stat_boxplot()` (@teunbrand, #6600) * Implemented `as.list()` and `S7::convert()` methods for lists and classes in ggplot2