Skip to content

Commit

Permalink
Repurpose coord expansion (#6027)
Browse files Browse the repository at this point in the history
* `default_expansion()` handles longer `expand` arg

* Parser for expand argument

* insert parser at relevant places

* Use Coord$setup_params() to setup expand

* fix plot helper not including params

* Redocument parameter

* fix `coord_flip()` case

* add test

* add news bullet
  • Loading branch information
teunbrand committed Aug 28, 2024
1 parent a49bf1e commit 7ce155b
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 42 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ggplot2 (development version)

* `coord_*(expand)` can now take a logical vector to control expansion at any
side of the panel (top, right, bottom, left) (@teunbrand, #6020)
* (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.
Expand Down
25 changes: 23 additions & 2 deletions R/coord-.R
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,11 @@ Coord <- ggproto("Coord",
# Will generally have to return FALSE for coordinate systems that enforce a fixed aspect ratio.
is_free = function() FALSE,

setup_params = function(data) {
setup_params = function(self, data) {
list(
guide_default = guide_axis(),
guide_missing = guide_none()
guide_missing = guide_none(),
expand = parse_coord_expand(self$expand %||% TRUE)
)
},

Expand Down Expand Up @@ -243,6 +244,26 @@ render_axis <- function(panel_params, axis, scale, position, theme) {
}
}

# Elaborates an 'expand' argument for every side (top, right, bottom or left)
parse_coord_expand <- function(expand) {
check_logical(expand)
if (anyNA(expand)) {
cli::cli_abort("{.arg expand} cannot contain missing values.")
}

if (!is_named(expand)) {
return(rep_len(expand, 4))
}

# Match by top/right/bottom/left
out <- rep(TRUE, 4)
i <- match(names(expand), .trbl)
if (sum(!is.na(i)) > 0) {
out[i] <- unname(expand)[!is.na(i)]
}
out
}

# Utility function to check coord limits
check_coord_limits <- function(
limits, arg = caller_arg(limits), call = caller_env()
Expand Down
8 changes: 6 additions & 2 deletions R/coord-cartesian-.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#' @param expand If `TRUE`, the default, adds a small expansion factor to
#' the limits to ensure that data and axes don't overlap. If `FALSE`,
#' limits are taken exactly from the data or `xlim`/`ylim`.
#' Giving a logical vector will separately control the expansion for the four
#' directions (top, left, bottom and right). The `expand` argument will be
#' recycled to length 4 if necessary. Alternatively, can be a named logical
#' vector to control a single direction, e.g. `expand = c(bottom = FALSE)`.
#' @param default Is this the default coordinate system? If `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 `TRUE`, that warning
Expand Down Expand Up @@ -100,8 +104,8 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,

setup_panel_params = function(self, scale_x, scale_y, params = list()) {
c(
view_scales_from_scale(scale_x, self$limits$x, self$expand),
view_scales_from_scale(scale_y, self$limits$y, self$expand)
view_scales_from_scale(scale_x, self$limits$x, params$expand[c(4, 2)]),
view_scales_from_scale(scale_y, self$limits$y, params$expand[c(3, 1)])
)
},

Expand Down
1 change: 1 addition & 0 deletions R/coord-flip.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ CoordFlip <- ggproto("CoordFlip", CoordCartesian,
},

setup_panel_params = function(self, scale_x, scale_y, params = list()) {
params$expand <- params$expand[c(2, 1, 4, 3)]
parent <- ggproto_parent(CoordCartesian, self)
panel_params <- parent$setup_panel_params(scale_x, scale_y, params)
flip_axis_labels(panel_params)
Expand Down
37 changes: 18 additions & 19 deletions R/coord-radial.R
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ coord_radial <- function(theta = "x",
check_bool(r.axis.inside, allow_null = TRUE)
}

check_bool(expand)
check_bool(rotate.angle)
check_number_decimal(start, allow_infinite = FALSE)
check_number_decimal(end, allow_infinite = FALSE, allow_null = TRUE)
Expand Down Expand Up @@ -139,8 +138,8 @@ CoordRadial <- ggproto("CoordRadial", Coord,
setup_panel_params = function(self, scale_x, scale_y, params = list()) {

params <- c(
view_scales_polar(scale_x, self$theta, expand = self$expand),
view_scales_polar(scale_y, self$theta, expand = self$expand),
view_scales_polar(scale_x, self$theta, expand = params$expand[c(4, 2)]),
view_scales_polar(scale_y, self$theta, expand = params$expand[c(3, 1)]),
list(bbox = polar_bbox(self$arc, inner_radius = self$inner_radius),
arc = self$arc, inner_radius = self$inner_radius)
)
Expand Down Expand Up @@ -469,27 +468,27 @@ CoordRadial <- ggproto("CoordRadial", Coord,
},

setup_params = function(self, data) {
if (isFALSE(self$r_axis_inside)) {
place <- in_arc(c(0, 0.5, 1, 1.5) * pi, self$arc)
if (place[1]) {
return(list(r_axis = "left", fake_arc = c(0, 2) * pi))
}
if (place[3]) {
return(list(r_axis = "left", fake_arc = c(1, 3)* pi))
}
if (place[2]) {
return(list(r_axis = "bottom", fake_arc = c(0.5, 2.5) * pi))
}
if (place[4]) {
return(list(r_axis = "bottom", fake_arc = c(1.5, 3.5) * pi))
}
params <- ggproto_parent(Coord, self)$setup_params(data)
if (!isFALSE(self$r_axis_inside)) {
return(params)
}

place <- in_arc(c(0, 0.5, 1, 1.5) * pi, self$arc)
if (!any(place)) {
cli::cli_warn(c(
"No appropriate placement found for {.arg r_axis_inside}.",
i = "Axis will be placed at panel edge."
))
self$r_axis_inside <- TRUE
params$r_axis_inside <- TRUE
return(params)
}
return(NULL)

params$r_axis <- if (any(place[c(1, 3)])) "left" else "bottom"
params$fake_arc <- switch(
which(place[c(1, 3, 2, 4)])[1],
c(0, 2), c(1, 3), c(0.5, 2.5), c(1.5, 3.5)
) * pi
params
}
)

Expand Down
12 changes: 5 additions & 7 deletions R/coord-sf.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
},

setup_params = function(self, data) {
crs <- self$determine_crs(data)
params <- ggproto_parent(Coord, self)$setup_params(data)

params <- list(
crs = crs,
default_crs = self$default_crs
)
params$crs <- self$determine_crs(data)
params$default_crs <- self$default_crs
self$params <- params

params
Expand Down Expand Up @@ -170,8 +168,8 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,

setup_panel_params = function(self, scale_x, scale_y, params = list()) {
# expansion factors for scale limits
expansion_x <- default_expansion(scale_x, expand = self$expand)
expansion_y <- default_expansion(scale_y, expand = self$expand)
expansion_x <- default_expansion(scale_x, expand = params$expand[c(4, 2)])
expansion_y <- default_expansion(scale_y, expand = params$expand[c(3, 1)])

# get scale limits and coord limits and merge together
# coord limits take precedence over scale limits
Expand Down
4 changes: 2 additions & 2 deletions R/coord-transform.R
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ CoordTrans <- ggproto("CoordTrans", Coord,

setup_panel_params = function(self, scale_x, scale_y, params = list()) {
c(
view_scales_from_scale_with_coord_trans(scale_x, self$limits$x, self$trans$x, self$expand),
view_scales_from_scale_with_coord_trans(scale_y, self$limits$y, self$trans$y, self$expand)
view_scales_from_scale_with_coord_trans(scale_x, self$limits$x, self$trans$x, params$expand[c(4, 2)]),
view_scales_from_scale_with_coord_trans(scale_y, self$limits$y, self$trans$y, params$expand[c(3, 1)])
)
},

Expand Down
19 changes: 16 additions & 3 deletions R/scale-expansion.R
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,24 @@ expand_range4 <- function(limits, expand) {
#'
default_expansion <- function(scale, discrete = expansion(add = 0.6),
continuous = expansion(mult = 0.05), expand = TRUE) {
if (!expand) {
return(expansion(0, 0))
out <- expansion()
if (!any(expand)) {
return(out)
}
scale_expand <- scale$expand %|W|%
if (scale$is_discrete()) discrete else continuous

scale$expand %|W|% if (scale$is_discrete()) discrete else continuous
# for backward compatibility, we ensure expansions have expected length
expand <- rep_len(expand, 2L)
scale_expand <- rep_len(scale_expand, 4)

if (expand[1]) {
out[1:2] <- scale_expand[1:2]
}
if (expand[2]) {
out[3:4] <- scale_expand[3:4]
}
out
}

#' Expand limits in (possibly) transformed space
Expand Down
6 changes: 5 additions & 1 deletion man/coord_cartesian.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/coord_fixed.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/coord_flip.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/coord_map.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/coord_trans.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion man/ggsf.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/testthat/helper-plot-data.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cdata <- function(plot) {
lapply(pieces$data, function(d) {
dapply(d, "PANEL", function(panel_data) {
scales <- pieces$layout$get_scales(panel_data$PANEL[1])
panel_params <- plot$coordinates$setup_panel_params(scales$x, scales$y)
panel_params <- plot$coordinates$setup_panel_params(scales$x, scales$y, params = pieces$layout$coord_params)
plot$coordinates$transform(panel_data, panel_params)
})
})
Expand Down
16 changes: 16 additions & 0 deletions tests/testthat/test-coord-.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,19 @@ test_that("coords append a column to the layout correctly", {
expect_equal(test$COORD, c(1, 2, 1))
})

test_that("coord expand takes a vector", {

base <- ggplot() + lims(x = c(0, 10), y = c(0, 10))

p <- ggplot_build(base + coord_cartesian(expand = c(TRUE, FALSE, FALSE, TRUE)))
pp <- p$layout$panel_params[[1]]
expect_equal(pp$x.range, c(-0.5, 10))
expect_equal(pp$y.range, c(0, 10.5))

p <- ggplot_build(base + coord_cartesian(expand = c(top = FALSE, left = FALSE)))
pp <- p$layout$panel_params[[1]]
expect_equal(pp$x.range, c(0, 10.5))
expect_equal(pp$y.range, c(-0.5, 10))

})

0 comments on commit 7ce155b

Please sign in to comment.