Is there a way to blur a certain part of a plot in ggplot
?
For example, consider the following plot:
library(ggplot2)
library(magrittr)
p <-
mtcars %>%
ggplot(aes(x = disp, y = mpg)) +
geom_line() +
theme_minimal() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
p +
annotate("rect", xmin = 100, xmax = 200, ymin = 15, ymax = 30,
alpha = .1,fill = "red")
Created on 2021-07-22 by the reprex package (v2.0.0)
I want to apply a blur effect in p
over the area where there's currently a red rectangle.
A demonstration of what I'm looking for:
Is this even possible?
Reference: Similar question (and answer) in python.
You can use ggfx' masks with a geom_rect()
to determine a region of where to apply an effect. Example below:
library(ggplot2)
library(magrittr)
library(ggfx)
# Plot without line
p <-
mtcars %>%
ggplot(aes(x = disp, y = mpg)) +
theme_minimal() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
# Add mask as a reference
p +
as_reference(
geom_rect(
aes(xmin = 100, xmax = 200, ymin = 15, ymax = 30),
inherit.aes = FALSE
),
id = "draw_area" # <- set some name
) +
# Plot blurry part with mask
with_mask(
with_blur(geom_line(), sigma = 5),
mask = ch_alpha("draw_area")
) +
# Plot crisp part with inverted mask
with_mask(
geom_line(),
mask = ch_alpha("draw_area"),
invert = TRUE
)
Created on 2021-07-22 by the reprex package (v1.0.0)
If you plan on using this a lot, you can make a wrapping function like so:
# trbl: Top, Right, Bottom, Left
area_effect <- function(layer, effect = with_blur, ..., trbl) {
id <- rlang::hash(trbl)
ref <- as_reference(
annotate(
"rect", xmin = trbl[4], xmax = trbl[2], ymin = trbl[3], ymax = trbl[1]
),
id = id
)
mask1 <- with_mask(
effect(layer, ...),
mask = ch_alpha(id)
)
mask2 <- with_mask(
layer, mask = ch_alpha(id), invert = TRUE
)
list(
ref, mask1, mask2
)
}
p +
area_effect(geom_line(), trbl = c(30, 200, 15, 100), sigma = 5)
We can split the data into two: data for normal plotting, data for blurring:
library(ggplot2)
library(ggfx)
d1 <- mtcars
d1 <- d2 <- d1[ order(d1$disp), ]
ix1 <- range(which(d1$disp > 100 & d1$disp < 200)) + c(1, -1)
ix2 <- which(d1$disp < 100 | d1$disp > 200)
d1[ ix1[ 1 ]:ix1[ 2 ], "mpg" ] <- NA
d2[ ix2, "mpg" ] <- NA
ggplot() +
geom_line(aes(x = disp, y = mpg), d1) +
with_blur(
geom_line(aes(x = disp, y = mpg), d2),
sigma = unit(1, 'mm')
)
Here's my first attempt, using {ggfx}
library, but still not quite the desired output.
library(ggplot2)
library(magrittr)
library(ggfx)
p_all_blurry <-
mtcars %>%
ggplot(aes(x = disp, y = mpg)) +
with_blur(
geom_line(),
sigma = unit(1, 'mm')
)+
theme_minimal() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
p_all_blurry
Created on 2021-07-22 by the reprex package (v2.0.0)
How could I limit the blurry area to where the red rectangle (in the question) is?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With