R v4.1.0 - {grid} graphics new feature - patterns (part 2)

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

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)