Skip to content

Allow functional limits in continuous scales #2334

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

Merged
merged 5 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## New features

* Continuous scale limits now accept a function, which must accept the default
/ automatic limits and return adjusted limits. This makes it possible to write
a function that e.g. ensures the limits are always a multiple of 100,
regardless of the data. It somewhat parallels the behaviour of breaks and
labels in accepting functions (@econandrew, #2307).

* ggplot2 now works on R 3.1 onwards, and uses the
[vdiffr](https://github.com/lionel-/vdiffr) package for visual testing.

Expand Down
17 changes: 12 additions & 5 deletions R/scale-.r
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,17 @@ Scale <- ggproto("Scale", NULL,
stop("Not implemented", call. = FALSE)
},

# if scale contains a function, apply it to the default (inverted) scale range
# if scale contains a NULL, use the default scale range
# if scale contains a NA, use the default range for that axis, otherwise
# use the user defined limit for that axis
get_limits = function(self) {
if (self$is_empty()) return(c(0, 1))

if (!is.null(self$limits)) {
if (is.function(self$limits)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're adding another condition here, can you please reframe the if statement along these lines:

if (is.null()) {

} else if (is.function()) {

} else if (is.numeric()) {
  
} else {
  stop("Informative error message", call. = FALSE) 
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did rework this conditional to be more intuitive but since get_limits() is called by both discrete and continuous scales and can be passed character, numeric, POSIXt and now function classes, I ultimately did not add an explicit is.numeric conditional nor an error message. Happy to update further if necessary.

# if limits is a function, it expects to work in data space
self$trans$transform(self$limits(self$trans$inverse(self$range$range)))
} else if (!is.null(self$limits)) {
ifelse(!is.na(self$limits), self$limits, self$range$range)
} else {
self$range$range
Expand Down Expand Up @@ -526,8 +530,11 @@ ScaleDiscrete <- ggproto("ScaleDiscrete", Scale,
#' - A character vector giving labels (must be same length as `breaks`)
#' - A function that takes the breaks as input and returns labels
#' as output
#' @param limits A numeric vector of length two providing limits of the scale.
#' Use `NA` to refer to the existing minimum or maximum.
#' @param limits One of:
#' - A numeric vector of length two providing limits of the scale.
#' Use `NA` to refer to the existing minimum or maximum
#' - A function that accepts the existing (automatic) limits and returns
#' new limits
#' @param rescaler Used by diverging and n colour gradients
#' (i.e. [scale_colour_gradient2()], [scale_colour_gradientn()]).
#' A function used to scale the input values to the range \eqn{[0, 1]}.
Expand Down Expand Up @@ -565,7 +572,7 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
}

trans <- as.trans(trans)
if (!is.null(limits)) {
if (!is.null(limits) && !is.function(limits)) {
limits <- trans$transform(limits)
}

Expand Down Expand Up @@ -598,7 +605,7 @@ continuous_scale <- function(aesthetics, scale_name, palette, name = waiver(),
#'
#' @export
#' @inheritParams continuous_scale
#' @param breaks One of:
#' @param breaks One of:
#' - `NULL` for no breaks
#' - `waiver()` for the default breaks computed by the
#' transformation object
Expand Down