Skip to content
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

Repurpose coord expansion #6027

Merged
merged 10 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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 @@
# 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 @@
}
}

# 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.")

Check warning on line 251 in R/coord-.R

View check run for this annotation

Codecov / codecov/patch

R/coord-.R#L251

Added line #L251 was not covered by tests
}

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)]
Copy link
Member

Choose a reason for hiding this comment

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

Not sure flipping the coord params is the logical solution here

Copy link
Collaborator Author

@teunbrand teunbrand Aug 26, 2024

Choose a reason for hiding this comment

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

In this PR, coord_flip(expand = c(left = FALSE)) removes the left expansion, which feels correct to me. I don't know how much this intuition holds, but I feel like coord_flip() swaps x/y, but not left/bottom and right/top.
I'm unsure whether there is a more logical solution available here.

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))

})

Loading