R v4.1.0 - {grid} graphics new feature - patterns

Introduciton

As of R 4.1.0 there is built-in support for patterns in grid graphics. There are 3 types of pattern:

  1. grid::linearGradient() for linear colour gradients
  2. grid::radialGradient() for radial colour gradients
  3. grid::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:

library(grid)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Ensure that images are rendered using a device which understands patterns
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
knitr::opts_chunk$set(dev.args = list(png = list(type = "cairo")))

Define the core element of the pattern

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect <- rectGrob(
  width  = unit(10, 'mm'), 
  height = unit(10, 'mm'),
  gp     = gpar(
    fill = 'lightblue', 
    col  = 'blue'
  )
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(small_rect)

Create a pattern by wrapping the core element in grid::pattern()

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 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.5, 
  height = 0.5,
  gp = gpar(fill = small_rect_pattern)
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)

Patterns can have masks

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 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(4, 'mm'),
  gp = gpar(fill = 'black')
)

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

Pattern - Patterns can have patterns

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define an inner pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inner_pattern_element <- circleGrob(
  r  = unit(5, 'mm'), 
  gp     = gpar(
    fill = 'pink', 
    col  = 'red'
  )
)

inner_pattern <- grid::pattern(
  inner_pattern_element,
  width  = unit(12, 'mm'),
  height = unit(12, 'mm'), 
  extend = 'repeat'
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define an outer pattern which is filled with the inner pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
outer_pattern_element <- polygonGrob(
  x = unit(0.5, 'npc') + unit(c(0, 20, 20) - 10, 'mm'),
  y = unit(0.5, 'npc') + unit(c(0,  0, 20) - 10, 'mm'),
  gp = gpar(fill = inner_pattern, col = 'blue')
)

outer_pattern <- grid::pattern(
  outer_pattern_element,
  width  = unit(30, 'mm'),
  height = unit(30, 'mm'), 
  extend = 'reflect'
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Fill a 'rect' with the outer pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
large_rect <- rectGrob(
  width  = 0.5, 
  height = 0.5,
  gp = gpar(fill = outer_pattern)
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)

Pattern - Patterns can contain multiple objects

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_rect <- rectGrob(
  width  = unit(10, 'mm'), 
  height = unit(10, 'mm'),
  gp     = gpar(
    fill = 'lightblue', 
    col  = 'blue'
  )
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define a small circle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
small_circle <- circleGrob(
  x = unit(0.5, 'npc') + unit(3, 'mm'),
  y = unit(0.5, 'npc') + unit(3, 'mm'),
  r  = unit(4, 'mm'),
  gp     = gpar(
    fill = 'pink', 
    col  = 'red'
  )
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Creat a compound pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compound_pattern <- pattern(
  grobTree(small_rect, small_circle),
  width  = unit(15, 'mm'),
  height = unit(15, 'mm'),
  extend = 'repeat'
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Fill a 'rect' with the compound pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
large_rect <- rectGrob(
  width  = 0.5, 
  height = 0.5,
  gp = gpar(fill = compound_pattern)
)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Display the rectangle
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
grid.newpage()
grid.draw(large_rect)