Skip to content

Commit f8dac9d

Browse files
authored
Gradients in ribbons (#5699)
* support alpha/fill gradients in `geom_ribbon()` * add test * Add news bullet * Fix error messages * More aggressively enforce device capabilities * add missing newline
1 parent e22cff2 commit f8dac9d

File tree

4 files changed

+57
-6
lines changed

4 files changed

+57
-6
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
calculated as the density times the sum of weights (@teunbrand, #4176).
3838
* `theme()` gets new `spacing` and `margins` arguments that all other spacings
3939
and (non-text) margins inherit from (@teunbrand, #5622).
40+
* `geom_ribbon()` can have varying `fill` or `alpha` in linear coordinate
41+
systems (@teunbrand, #4690)
4042

4143
# ggplot2 3.5.1
4244

R/geom-ribbon.R

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,44 @@ GeomRibbon <- ggproto("GeomRibbon", Geom,
135135
data <- data[order(data$group), ]
136136

137137
# Check that aesthetics are constant
138-
aes <- unique0(data[names(data) %in% c("colour", "fill", "linewidth", "linetype", "alpha")])
139-
if (nrow(aes) > 1) {
140-
cli::cli_abort("Aesthetics can not vary along a ribbon.")
138+
aes <- lapply(
139+
data[names(data) %in% c("colour", "fill", "linewidth", "linetype", "alpha")],
140+
unique0
141+
)
142+
non_constant <- names(aes)[lengths(aes) > 1]
143+
if (coord$is_linear()) {
144+
if (any(c("fill", "alpha") %in% non_constant)) {
145+
check_device("gradients", action = "abort", maybe = TRUE)
146+
}
147+
# For linear coords, we can make a fill/alpha gradient, so we allow
148+
# these to vary
149+
non_constant <- setdiff(non_constant, c("fill", "alpha"))
150+
}
151+
if (length(non_constant) > 0) {
152+
cli::cli_abort(
153+
"Aesthetics can not vary along a ribbon: {.and {.field {non_constant}}}."
154+
)
155+
}
156+
if ((length(aes$fill) > 1 || length(aes$alpha) > 1)) {
157+
transformed <- coord$transform(flip_data(data, flipped_aes), panel_params)
158+
if (flipped_aes) {
159+
keep <- is.finite(tranformed$y)
160+
args <- list(
161+
colours = alpha(data$fill, data$alpha)[keep],
162+
stops = rescale(transformed$y)[keep],
163+
y1 = 0, y2 = 1, x1 = 0.5, x2 = 0.5
164+
)
165+
} else {
166+
keep <- is.finite(transformed$x)
167+
args <- list(
168+
colours = alpha(data$fill, data$alpha)[keep],
169+
stops = rescale(transformed$x)[keep],
170+
x1 = 0, x2 = 1, y1 = 0.5, y2 = 0.5
171+
)
172+
}
173+
aes$fill <- inject(linearGradient(!!!args))
174+
aes$alpha <- NA
141175
}
142-
aes <- as.list(aes)
143176

144177
# Instead of removing NA values from the data and plotting a single
145178
# polygon, we want to "stop" plotting the polygon whenever we're

tests/testthat/_snaps/geom-ribbon.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
Problem while converting geom to grob.
1818
i Error occurred in the 1st layer.
1919
Caused by error in `draw_group()`:
20-
! Aesthetics can not vary along a ribbon.
20+
! Aesthetics can not vary along a ribbon: linewidth.
2121

2222
---
2323

tests/testthat/test-geom-ribbon.R

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ test_that("geom_ribbon() checks the aesthetics", {
77
geom_ribbon(aes(y = year, xmin = level - 5, xmax = level + 5), orientation = "x")
88
expect_snapshot_error(ggplotGrob(p))
99
p <- ggplot(huron) +
10-
geom_ribbon(aes(year, ymin = level - 5, ymax = level + 5, fill = year))
10+
geom_ribbon(aes(year, ymin = level - 5, ymax = level + 5, linewidth = year))
1111
expect_snapshot_error(ggplotGrob(p))
1212

1313
expect_snapshot_error(geom_ribbon(aes(year, ymin = level - 5, ymax = level + 5), outline.type = "test"))
@@ -74,3 +74,19 @@ test_that("outline.type option works", {
7474
expect_s3_class(g_area_default$children[[1]]$children[[2]], "polyline")
7575
expect_equal(g_area_default$children[[1]]$children[[2]]$id, rep(1L, each = 4))
7676
})
77+
78+
test_that("ribbons can have gradients", {
79+
skip_if_not(
80+
check_device("gradients", action = "test"),
81+
"graphics device does not support gradients."
82+
)
83+
84+
df <- data.frame(x = 1:2, ymin = c(-1:-2), ymax = 1:2)
85+
p <- ggplot(df, aes(x, ymin = ymin, ymax = ymax, fill = x)) +
86+
geom_ribbon(outline.type = "full") +
87+
scale_fill_gradientn(colours = c("red", "blue"))
88+
fill <- layer_grob(p)[[1]]$children[[1]]$gp$fill
89+
90+
expect_s3_class(fill, "GridLinearGradient")
91+
expect_equal(fill$colours, alpha(c("red", "blue"), NA))
92+
})

0 commit comments

Comments
 (0)