Skip to content

Commit 7ce155b

Browse files
authored
Repurpose coord expansion (#6027)
* `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
1 parent a49bf1e commit 7ce155b

16 files changed

+120
-42
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# ggplot2 (development version)
22

3+
* `coord_*(expand)` can now take a logical vector to control expansion at any
4+
side of the panel (top, right, bottom, left) (@teunbrand, #6020)
35
* (Breaking) The defaults for all geoms can be set at one in the theme.
46
(@teunbrand based on pioneering work by @dpseidel, #2239)
57
* A new `theme(geom)` argument is used to track these defaults.

R/coord-.R

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,11 @@ Coord <- ggproto("Coord",
184184
# Will generally have to return FALSE for coordinate systems that enforce a fixed aspect ratio.
185185
is_free = function() FALSE,
186186

187-
setup_params = function(data) {
187+
setup_params = function(self, data) {
188188
list(
189189
guide_default = guide_axis(),
190-
guide_missing = guide_none()
190+
guide_missing = guide_none(),
191+
expand = parse_coord_expand(self$expand %||% TRUE)
191192
)
192193
},
193194

@@ -243,6 +244,26 @@ render_axis <- function(panel_params, axis, scale, position, theme) {
243244
}
244245
}
245246

247+
# Elaborates an 'expand' argument for every side (top, right, bottom or left)
248+
parse_coord_expand <- function(expand) {
249+
check_logical(expand)
250+
if (anyNA(expand)) {
251+
cli::cli_abort("{.arg expand} cannot contain missing values.")
252+
}
253+
254+
if (!is_named(expand)) {
255+
return(rep_len(expand, 4))
256+
}
257+
258+
# Match by top/right/bottom/left
259+
out <- rep(TRUE, 4)
260+
i <- match(names(expand), .trbl)
261+
if (sum(!is.na(i)) > 0) {
262+
out[i] <- unname(expand)[!is.na(i)]
263+
}
264+
out
265+
}
266+
246267
# Utility function to check coord limits
247268
check_coord_limits <- function(
248269
limits, arg = caller_arg(limits), call = caller_env()

R/coord-cartesian-.R

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#' @param expand If `TRUE`, the default, adds a small expansion factor to
1010
#' the limits to ensure that data and axes don't overlap. If `FALSE`,
1111
#' limits are taken exactly from the data or `xlim`/`ylim`.
12+
#' Giving a logical vector will separately control the expansion for the four
13+
#' directions (top, left, bottom and right). The `expand` argument will be
14+
#' recycled to length 4 if necessary. Alternatively, can be a named logical
15+
#' vector to control a single direction, e.g. `expand = c(bottom = FALSE)`.
1216
#' @param default Is this the default coordinate system? If `FALSE` (the default),
1317
#' then replacing this coordinate system with another one creates a message alerting
1418
#' the user that the coordinate system is being replaced. If `TRUE`, that warning
@@ -100,8 +104,8 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,
100104

101105
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
102106
c(
103-
view_scales_from_scale(scale_x, self$limits$x, self$expand),
104-
view_scales_from_scale(scale_y, self$limits$y, self$expand)
107+
view_scales_from_scale(scale_x, self$limits$x, params$expand[c(4, 2)]),
108+
view_scales_from_scale(scale_y, self$limits$y, params$expand[c(3, 1)])
105109
)
106110
},
107111

R/coord-flip.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ CoordFlip <- ggproto("CoordFlip", CoordCartesian,
8989
},
9090

9191
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
92+
params$expand <- params$expand[c(2, 1, 4, 3)]
9293
parent <- ggproto_parent(CoordCartesian, self)
9394
panel_params <- parent$setup_panel_params(scale_x, scale_y, params)
9495
flip_axis_labels(panel_params)

R/coord-radial.R

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ coord_radial <- function(theta = "x",
6666
check_bool(r.axis.inside, allow_null = TRUE)
6767
}
6868

69-
check_bool(expand)
7069
check_bool(rotate.angle)
7170
check_number_decimal(start, allow_infinite = FALSE)
7271
check_number_decimal(end, allow_infinite = FALSE, allow_null = TRUE)
@@ -139,8 +138,8 @@ CoordRadial <- ggproto("CoordRadial", Coord,
139138
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
140139

141140
params <- c(
142-
view_scales_polar(scale_x, self$theta, expand = self$expand),
143-
view_scales_polar(scale_y, self$theta, expand = self$expand),
141+
view_scales_polar(scale_x, self$theta, expand = params$expand[c(4, 2)]),
142+
view_scales_polar(scale_y, self$theta, expand = params$expand[c(3, 1)]),
144143
list(bbox = polar_bbox(self$arc, inner_radius = self$inner_radius),
145144
arc = self$arc, inner_radius = self$inner_radius)
146145
)
@@ -469,27 +468,27 @@ CoordRadial <- ggproto("CoordRadial", Coord,
469468
},
470469

471470
setup_params = function(self, data) {
472-
if (isFALSE(self$r_axis_inside)) {
473-
place <- in_arc(c(0, 0.5, 1, 1.5) * pi, self$arc)
474-
if (place[1]) {
475-
return(list(r_axis = "left", fake_arc = c(0, 2) * pi))
476-
}
477-
if (place[3]) {
478-
return(list(r_axis = "left", fake_arc = c(1, 3)* pi))
479-
}
480-
if (place[2]) {
481-
return(list(r_axis = "bottom", fake_arc = c(0.5, 2.5) * pi))
482-
}
483-
if (place[4]) {
484-
return(list(r_axis = "bottom", fake_arc = c(1.5, 3.5) * pi))
485-
}
471+
params <- ggproto_parent(Coord, self)$setup_params(data)
472+
if (!isFALSE(self$r_axis_inside)) {
473+
return(params)
474+
}
475+
476+
place <- in_arc(c(0, 0.5, 1, 1.5) * pi, self$arc)
477+
if (!any(place)) {
486478
cli::cli_warn(c(
487479
"No appropriate placement found for {.arg r_axis_inside}.",
488480
i = "Axis will be placed at panel edge."
489481
))
490-
self$r_axis_inside <- TRUE
482+
params$r_axis_inside <- TRUE
483+
return(params)
491484
}
492-
return(NULL)
485+
486+
params$r_axis <- if (any(place[c(1, 3)])) "left" else "bottom"
487+
params$fake_arc <- switch(
488+
which(place[c(1, 3, 2, 4)])[1],
489+
c(0, 2), c(1, 3), c(0.5, 2.5), c(1.5, 3.5)
490+
) * pi
491+
params
493492
}
494493
)
495494

R/coord-sf.R

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
1818
},
1919

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

23-
params <- list(
24-
crs = crs,
25-
default_crs = self$default_crs
26-
)
23+
params$crs <- self$determine_crs(data)
24+
params$default_crs <- self$default_crs
2725
self$params <- params
2826

2927
params
@@ -170,8 +168,8 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
170168

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

176174
# get scale limits and coord limits and merge together
177175
# coord limits take precedence over scale limits

R/coord-transform.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ CoordTrans <- ggproto("CoordTrans", Coord,
153153

154154
setup_panel_params = function(self, scale_x, scale_y, params = list()) {
155155
c(
156-
view_scales_from_scale_with_coord_trans(scale_x, self$limits$x, self$trans$x, self$expand),
157-
view_scales_from_scale_with_coord_trans(scale_y, self$limits$y, self$trans$y, self$expand)
156+
view_scales_from_scale_with_coord_trans(scale_x, self$limits$x, self$trans$x, params$expand[c(4, 2)]),
157+
view_scales_from_scale_with_coord_trans(scale_y, self$limits$y, self$trans$y, params$expand[c(3, 1)])
158158
)
159159
},
160160

R/scale-expansion.R

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,24 @@ expand_range4 <- function(limits, expand) {
9898
#'
9999
default_expansion <- function(scale, discrete = expansion(add = 0.6),
100100
continuous = expansion(mult = 0.05), expand = TRUE) {
101-
if (!expand) {
102-
return(expansion(0, 0))
101+
out <- expansion()
102+
if (!any(expand)) {
103+
return(out)
103104
}
105+
scale_expand <- scale$expand %|W|%
106+
if (scale$is_discrete()) discrete else continuous
104107

105-
scale$expand %|W|% if (scale$is_discrete()) discrete else continuous
108+
# for backward compatibility, we ensure expansions have expected length
109+
expand <- rep_len(expand, 2L)
110+
scale_expand <- rep_len(scale_expand, 4)
111+
112+
if (expand[1]) {
113+
out[1:2] <- scale_expand[1:2]
114+
}
115+
if (expand[2]) {
116+
out[3:4] <- scale_expand[3:4]
117+
}
118+
out
106119
}
107120

108121
#' Expand limits in (possibly) transformed space

man/coord_cartesian.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/coord_fixed.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/coord_flip.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/coord_map.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/coord_trans.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/ggsf.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/helper-plot-data.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cdata <- function(plot) {
55
lapply(pieces$data, function(d) {
66
dapply(d, "PANEL", function(panel_data) {
77
scales <- pieces$layout$get_scales(panel_data$PANEL[1])
8-
panel_params <- plot$coordinates$setup_panel_params(scales$x, scales$y)
8+
panel_params <- plot$coordinates$setup_panel_params(scales$x, scales$y, params = pieces$layout$coord_params)
99
plot$coordinates$transform(panel_data, panel_params)
1010
})
1111
})

tests/testthat/test-coord-.R

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,19 @@ test_that("coords append a column to the layout correctly", {
7575
expect_equal(test$COORD, c(1, 2, 1))
7676
})
7777

78+
test_that("coord expand takes a vector", {
79+
80+
base <- ggplot() + lims(x = c(0, 10), y = c(0, 10))
81+
82+
p <- ggplot_build(base + coord_cartesian(expand = c(TRUE, FALSE, FALSE, TRUE)))
83+
pp <- p$layout$panel_params[[1]]
84+
expect_equal(pp$x.range, c(-0.5, 10))
85+
expect_equal(pp$y.range, c(0, 10.5))
86+
87+
p <- ggplot_build(base + coord_cartesian(expand = c(top = FALSE, left = FALSE)))
88+
pp <- p$layout$panel_params[[1]]
89+
expect_equal(pp$x.range, c(0, 10.5))
90+
expect_equal(pp$y.range, c(-0.5, 10))
91+
92+
})
93+

0 commit comments

Comments
 (0)