Introduciton
As of R 4.1.0 there is built-in support for patterns in grid
graphics. There
are 3 types of pattern:
grid::linearGradient()
for linear colour gradientsgrid::radialGradient()
for radial colour gradientsgrid::pattern()
repetition of any combination of graphic objects (grobs)
This quick post summarises a few things which can be done with patterns:
- creating a pattern
- a repeating element within a pattern can be masked (another new feature of
grid
in R v4.1.0) - a pattern can itself have a pattern
- a pattern can consist of any objects you can put in a
grid::grobTree()
Reading list:
- 14 July 2020 Catching up with R Graphics Gradients, Patterns, Clipping Paths, and Masks
- 15 July 2020 New Features in the R Graphics Engine Paul Murrell
library(grid)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Ensure that images are rendered using a device which understands patterns
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
knitr::opts_chunk$set(dev.args = list(png = list(type = "cairo")))
Patterns can have masks with an alpha channel
In this example, a simple square is the basis of the repeating pattern.
The square is then masked with a circle containing a radial gradient which is transparent at the edges - this has the effect of softening the edges of the square
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a mask
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mask <- circleGrob(
x = unit(0.5, 'npc') + unit(2.5, 'mm'),
y = unit(0.5, 'npc') + unit(2.5, 'mm'),
r = unit(8, 'mm'),
gp = gpar(fill = radialGradient(c('black', 'transparent')), col = 'transparent')
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small rectangle with some masking
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect_with_mask <- rectGrob(
width = unit(10, 'mm'),
height = unit(10, 'mm'),
gp = gpar(
fill = 'lightblue',
col = 'blue'
),
vp = viewport(mask = mask)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap the core element (small_rect_with_mask) inside a pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect_with_mask_pattern <- grid::pattern(
small_rect_with_mask,
width = unit(15, 'mm'),
height = unit(15, 'mm'),
extend = 'repeat'
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Use the pattern as a 'fill' on another shape
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
large_rect <- rectGrob(
width = 0.5,
height = 0.5,
gp = gpar(fill = small_rect_with_mask_pattern)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)
Patterns can be defined with concrete units
When patterns are defined using concrete/absolute units (such as ‘mm’), then their size is constant regardless of the viewport
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small rectangle with size in concrete/absolute units
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect <- rectGrob(
width = unit(10, 'mm'),
height = unit(10, 'mm'),
gp = gpar(
fill = 'lightblue',
col = 'blue'
)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap the core element (small_rect) inside a pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect_pattern <- grid::pattern(
small_rect,
width = unit(15, 'mm'),
height = unit(15, 'mm'),
extend = 'repeat'
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Use the pattern as a 'fill' on another shape
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
large_rect <- rectGrob(
width = 0.3,
height = 0.3,
gp = gpar(fill = small_rect_pattern),
vp = viewport(
x = unit(0.33, 'npc'),
y = unit(0.5 , 'npc')
)
)
large_rect2 <- rectGrob(
width = 0.3,
height = 0.6,
gp = gpar(fill = small_rect_pattern),
vp = viewport(
x = unit(0.67, 'npc'),
y = unit(0.5 , 'npc')
)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)
grid.draw(large_rect2)
Patterns can be defined with relative units
When patterns are defined using relatives units (such as ‘npc’), then their apparent size depends on the viewport they’re rendered into.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small rectangle with size in relative units 'npc'
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect <- rectGrob(
width = unit(0.1, 'npc'),
height = unit(0.1, 'npc'),
gp = gpar(
fill = 'lightblue',
col = 'blue'
)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap the core element (small_rect) inside a pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect_pattern <- grid::pattern(
small_rect,
width = unit(15, 'mm'),
height = unit(15, 'mm'),
extend = 'repeat'
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Use the pattern as a 'fill' on another shape
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
large_rect <- rectGrob(
width = 0.3,
height = 0.3,
gp = gpar(fill = small_rect_pattern),
vp = viewport(
x = unit(0.33, 'npc'),
y = unit(0.5 , 'npc')
)
)
large_rect2 <- rectGrob(
width = 0.3,
height = 0.6,
gp = gpar(fill = small_rect_pattern),
vp = viewport(
x = unit(0.67, 'npc'),
y = unit(0.5 , 'npc')
)
)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)
grid.draw(large_rect2)