Introduction to the array-based ambient pattern

The ambient package offers a way of creating multiple different types of noise as a matrix of values.

The pattern created below will use ambient::noise_simple() to create a particular type of noise. This noise will be mapped to a colour gradient between two user-specified colours.

Create the ambient pattern function

All array-based pattern creation functions must:

  1. Have the exact function signature: function(width, height, params, legend)
  2. Return a 3D RGBA numeric array with dimensions [height, width, 4]

Since ambient only creates a 2D matrix of noise, we will use a colour ramp to promote each value in the matrix to create an RGBA value.

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#' Create an array of noise using the 'ambient' package
#'
#' @param width,height area dimensions
#' @param params aesthetic parameters passed from the geom e.g. 'pattern_fill', 
#'        'pattern_frequency' etc.
#' @param legend logical. If the request to create a pattern comes during 
#'        creation of the legend, then this is TRUE, otherwise FALSE
#'
#' @return an RGBA numeric array with dimensions [height, width, 4]
#'
#' @import ambient
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
create_pattern_ambient <- function(width, height, params, legend) {
  
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # The only 2 parameters needed are the 2 ends of the colour scale
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  colour1 <- as.character(params$pattern_fill )
  colour2 <- as.character(params$pattern_fill2)
  
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # Create a ramp function from these 2 colours
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ramp_func <- colorRamp(c(colour1, colour2), alpha = TRUE)
  
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # Create a noise matrix of the requested dimenions using 'ambient'.
  # The contents are normalised to all be in the range [0,1]
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  noise_matrix  <- ambient::noise_simplex(dim = c(height, width))
  noise_matrix  <- ambient::normalise(noise_matrix)
  
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # Use each value in the noise matrix to lookup a colour using the 
  # colour ramp function, then ensure the results are an RGBA array of the
  # correct dimensions.
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  colour_matrix <- ramp_func(noise_matrix)/255
  noise_array   <- array(colour_matrix, dim = c(height, width, 4))
  
  
  noise_array
}

Let {ggpattern} know that there’s an external pattern function it can use

A global option (ggpattern_array_funcs) is a named list which contains array creating functions to use outside of ggpattern.

The name used in this list corresponds to the pattern name used with the geom - in this case we will be using pattern = 'ambient2'.

options(ggpattern_array_funcs = list(ambient2 = create_pattern_ambient))

Use this ambient2 pattern

  • ambient noise takes two colours which will be passed in via the parameters:
    • pattern_fill will be mapped to the trt variable
    • pattern_fill2 will always be ‘white’
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create some data to plot
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
df <- data.frame(
  trt     = c("a", "b", "c"), 
  outcome = c(2.3, 1.9, 3.2)
)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create a ggplot using this pattern
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ggplot(df, aes(trt, outcome)) +
  geom_col_pattern(
    aes(pattern_fill = trt),
    pattern       = 'ambient', 
    pattern_fill2 = 'white',
    colour        = NA, 
    fill          = NA
  ) +
  theme_bw(15) +
  labs(
    title    = "ggpattern::geom_density_pattern()",
    subtitle = "pattern = 'ambient2'"
  ) +
  theme(legend.position = 'none') +
  coord_fixed(ratio = 1/2)

Mapping both colours simultaneously

ggplot(mtcars) +
  geom_density_pattern(
    aes(
      x = mpg, 
      pattern_fill  = as.factor(cyl), 
      pattern_fill2 = as.factor(cyl)
    ), 
    pattern = 'ambient2'
  ) +
  theme_bw(15) +
  theme(legend.key.size = unit(2, 'cm')) + 
  scale_pattern_fill_brewer (palette = 'Accent', direction =  1) + 
  scale_pattern_fill2_brewer(palette = 'Dark2' , direction =  1) + 
  labs(
    title    = "ggpattern::geom_density_pattern()",
    subtitle = "pattern = 'ambient2'"
  ) 

Future

  • A more complete version of this pattern generator would need the following:
    • making all the different ambient noise types available (perhaps through pattern_name)
    • being able to configure the frequence, phase and other parameters for each of the noise types.
  • There is simple plasma pattern in ggpattern already, but the ambient package has a lot more to offer.
  • This might work better as a standalone package? ggpatternambient ?