diff --git a/NEWS.md b/NEWS.md index 272f551ca3..1caca611c1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # ggplot2 (development version) +* New `coord_cartesian(ratio)` argument that supersedes `coord_fixed()` and + `coord_equal()`. * (internal) New `Facet$draw_panel_content()` method for delegating panel assembly (@Yunuuuu, #6406). * Facet gains a new method `setup_panel_params` to interact with the diff --git a/R/coord-cartesian-.R b/R/coord-cartesian-.R index 350e9bfd86..c3d719377e 100644 --- a/R/coord-cartesian-.R +++ b/R/coord-cartesian-.R @@ -29,6 +29,10 @@ #' (default) keeps directions as is. `"x"` and `"y"` can be used to reverse #' their respective directions. `"xy"` can be used to reverse both #' directions. +#' @param ratio aspect ratio, expressed as `y / x`. Can be `NULL` (default) to +#' not use an aspect ratio. Using `1` ensures that one unit on the x-axis +#' is the same length as one unit on the y-axis. Ratios higher than one make +#' units on the y-axis longer than units on the x-axis, and vice versa. #' @export #' @examples #' # There are two ways of zooming the plot display: with scales or @@ -55,6 +59,10 @@ #' # default limits #' p + coord_cartesian(expand = FALSE) #' +#' # Using a fixed ratio: 1 y-axis unit is 100 x-axis units +#' # Plot window can be resized and aspect ratio will be maintained +#' p + coord_cartesian(ratio = 100) +#' #' # You can see the same thing with this 2d histogram #' d <- ggplot(diamonds, aes(carat, price)) + #' stat_bin_2d(bins = 25, colour = "white") @@ -68,15 +76,18 @@ #' # displayed bigger #' d + coord_cartesian(xlim = c(0, 1)) coord_cartesian <- function(xlim = NULL, ylim = NULL, expand = TRUE, - default = FALSE, clip = "on", reverse = "none") { + default = FALSE, clip = "on", reverse = "none", + ratio = NULL) { check_coord_limits(xlim) check_coord_limits(ylim) + check_number_decimal(ratio, allow_infinite = FALSE, allow_null = TRUE) ggproto(NULL, CoordCartesian, limits = list(x = xlim, y = ylim), reverse = reverse, expand = expand, default = default, - clip = clip + clip = clip, + ratio = ratio ) } @@ -87,7 +98,13 @@ coord_cartesian <- function(xlim = NULL, ylim = NULL, expand = TRUE, CoordCartesian <- ggproto("CoordCartesian", Coord, is_linear = function() TRUE, - is_free = function() TRUE, + is_free = function(self) is.null(self$ratio), + aspect = function(self, ranges) { + if (is.null(self$ratio)) { + return(NULL) + } + diff(ranges$y.range) / diff(ranges$x.range) * self$ratio + }, distance = function(x, y, panel_params) { max_dist <- dist_euclidean(panel_params$x$dimension(), panel_params$y$dimension()) diff --git a/R/coord-fixed.R b/R/coord-fixed.R index d48824cfc4..db1a502b91 100644 --- a/R/coord-fixed.R +++ b/R/coord-fixed.R @@ -1,5 +1,7 @@ #' Cartesian coordinates with fixed "aspect ratio" #' +#' `r lifecycle::badge("superseded")` This coordinate system can be replaced by +#' using the `coord_cartesian(ratio)` argument.\cr\cr #' A fixed scale coordinate system forces a specified ratio between the #' physical representation of data units on the axes. The ratio represents the #' number of units on the y-axis equivalent to one unit on the x-axis. The @@ -10,7 +12,7 @@ #' #' @export #' @inheritParams coord_cartesian -#' @param ratio aspect ratio, expressed as `y / x` +#' @inheritDotParams coord_cartesian #' @examples #' # ensures that the ranges of axes are equal to the specified ratio by #' # adjusting the plot aspect ratio @@ -22,17 +24,8 @@ #' p + coord_fixed(xlim = c(15, 30)) #' #' # Resize the plot to see that the specified aspect ratio is maintained -coord_fixed <- function(ratio = 1, xlim = NULL, ylim = NULL, expand = TRUE, - clip = "on", reverse = "none") { - check_coord_limits(xlim) - check_coord_limits(ylim) - ggproto(NULL, CoordFixed, - limits = list(x = xlim, y = ylim), - ratio = ratio, - expand = expand, - reverse = reverse, - clip = clip - ) +coord_fixed <- function(ratio = 1, ...) { + coord_cartesian(ratio = ratio, ...) } #' @export diff --git a/R/facet-.R b/R/facet-.R index 94b75148ee..8acb95d2d5 100644 --- a/R/facet-.R +++ b/R/facet-.R @@ -161,7 +161,7 @@ Facet <- ggproto("Facet", NULL, params ) - # Draw individual panels, then call `$draw_panels()` method to + # Draw individual panels, then call `$draw_panels()` method to # assemble into gtable lapply(seq_along(panels[[1]]), function(i) { panel <- lapply(panels, `[[`, i) @@ -185,10 +185,12 @@ Facet <- ggproto("Facet", NULL, if (space$x && space$y) { aspect_ratio <- aspect_ratio %||% coord$ratio } else if (free$x || free$y) { - cli::cli_abort( - "{.fn {snake_class(self)}} can't use free scales with \\ - {.fn {snake_class(coord)}}." - ) + msg <- paste0("{.fn {snake_class(self)}} can't use free scales with ", + "{.fn {snake_class(coord)}}") + if (!is.null(coord$ratio)) { + msg <- paste0(msg, " with a fixed {.arg ratio} argument") + } + cli::cli_abort(paste0(msg, ".")) } } diff --git a/man/coord_cartesian.Rd b/man/coord_cartesian.Rd index 15afad523a..a670baff6c 100644 --- a/man/coord_cartesian.Rd +++ b/man/coord_cartesian.Rd @@ -10,7 +10,8 @@ coord_cartesian( expand = TRUE, default = FALSE, clip = "on", - reverse = "none" + reverse = "none", + ratio = NULL ) } \arguments{ @@ -42,6 +43,11 @@ legend, the plot title, or the plot margins.} (default) keeps directions as is. \code{"x"} and \code{"y"} can be used to reverse their respective directions. \code{"xy"} can be used to reverse both directions.} + +\item{ratio}{aspect ratio, expressed as \code{y / x}. Can be \code{NULL} (default) to +not use an aspect ratio. Using \code{1} ensures that one unit on the x-axis +is the same length as one unit on the y-axis. Ratios higher than one make +units on the y-axis longer than units on the x-axis, and vice versa.} } \description{ The Cartesian coordinate system is the most familiar, and common, type of @@ -74,6 +80,10 @@ p + coord_cartesian(xlim = c(325, 500), expand = FALSE) # default limits p + coord_cartesian(expand = FALSE) +# Using a fixed ratio: 1 y-axis unit is 100 x-axis units +# Plot window can be resized and aspect ratio will be maintained +p + coord_cartesian(ratio = 100) + # You can see the same thing with this 2d histogram d <- ggplot(diamonds, aes(carat, price)) + stat_bin_2d(bins = 25, colour = "white") diff --git a/man/coord_fixed.Rd b/man/coord_fixed.Rd index a3d8d358b7..4f918a9894 100644 --- a/man/coord_fixed.Rd +++ b/man/coord_fixed.Rd @@ -5,29 +5,30 @@ \alias{coord_equal} \title{Cartesian coordinates with fixed "aspect ratio"} \usage{ -coord_fixed( - ratio = 1, - xlim = NULL, - ylim = NULL, - expand = TRUE, - clip = "on", - reverse = "none" -) +coord_fixed(ratio = 1, ...) } \arguments{ -\item{ratio}{aspect ratio, expressed as \code{y / x}} +\item{ratio}{aspect ratio, expressed as \code{y / x}. Can be \code{NULL} (default) to +not use an aspect ratio. Using \code{1} ensures that one unit on the x-axis +is the same length as one unit on the y-axis. Ratios higher than one make +units on the y-axis longer than units on the x-axis, and vice versa.} -\item{xlim, ylim}{Limits for the x and y axes.} - -\item{expand}{If \code{TRUE}, the default, adds a small expansion factor to +\item{...}{ + Arguments passed on to \code{\link[=coord_cartesian]{coord_cartesian}} + \describe{ + \item{\code{xlim,ylim}}{Limits for the x and y axes.} + \item{\code{expand}}{If \code{TRUE}, the default, adds a small expansion factor to the limits to ensure that data and axes don't overlap. If \code{FALSE}, limits are taken exactly from the data or \code{xlim}/\code{ylim}. Giving a logical vector will separately control the expansion for the four directions (top, left, bottom and right). The \code{expand} argument will be recycled to length 4 if necessary. Alternatively, can be a named logical vector to control a single direction, e.g. \code{expand = c(bottom = FALSE)}.} - -\item{clip}{Should drawing be clipped to the extent of the plot panel? A + \item{\code{default}}{Is this the default coordinate system? If \code{FALSE} (the default), +then replacing this coordinate system with another one creates a message alerting +the user that the coordinate system is being replaced. If \code{TRUE}, that warning +is suppressed.} + \item{\code{clip}}{Should drawing be clipped to the extent of the plot panel? A setting of \code{"on"} (the default) means yes, and a setting of \code{"off"} means no. In most cases, the default of \code{"on"} should not be changed, as setting \code{clip = "off"} can cause unexpected results. It allows @@ -35,13 +36,15 @@ drawing of data points anywhere on the plot, including in the plot margins. If limits are set via \code{xlim} and \code{ylim} and some data points fall outside those limits, then those data points may show up in places such as the axes, the legend, the plot title, or the plot margins.} - -\item{reverse}{A string giving which directions to reverse. \code{"none"} + \item{\code{reverse}}{A string giving which directions to reverse. \code{"none"} (default) keeps directions as is. \code{"x"} and \code{"y"} can be used to reverse their respective directions. \code{"xy"} can be used to reverse both directions.} + }} } \description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} This coordinate system can be replaced by +using the \code{coord_cartesian(ratio)} argument.\cr\cr A fixed scale coordinate system forces a specified ratio between the physical representation of data units on the axes. The ratio represents the number of units on the y-axis equivalent to one unit on the x-axis. The diff --git a/tests/testthat/_snaps/facet-layout.md b/tests/testthat/_snaps/facet-layout.md index 1ab4474443..2df447c705 100644 --- a/tests/testthat/_snaps/facet-layout.md +++ b/tests/testthat/_snaps/facet-layout.md @@ -33,11 +33,11 @@ --- - `facet_wrap()` can't use free scales with `coord_fixed()`. + `facet_wrap()` can't use free scales with `coord_cartesian()` with a fixed `ratio` argument. # facet_grid throws errors at bad layout specs - `facet_grid()` can't use free scales with `coord_fixed()`. + `facet_grid()` can't use free scales with `coord_cartesian()` with a fixed `ratio` argument. ---