Skip to contents

WebmorphR has plotting functions to make it easier to create reproducible figures from your stimuli.

library(webmorphR)
library(webmorphR.stim) # for extra stimulus sets
library(tidyverse)
wm_opts(plot.maxwidth = 850)

Load stimuli

stimuli <- demo_stim()
plot(stimuli)

Borders and Background

You can add a border to each image using pad. Values less than 1 will set border width as a proportion of the image width, while values greater than or equal to 1 will set it as pixels. You can also change the border colour using fill.

pad(stimuli, 1, fill = "hotpink")

You can set different border sizes for the top, right, bottom and left sides (think “TRouBLe”). This can be useful for creating space for labels outside the image.

stimuli |>
  pad(60, 0, 0, 0, fill = "black") |>
  label(color = "white", size = 50)

You can set the border colour separately for each image.

pad(stimuli, 10, fill = c("#F9DEC9", "#BAEDDD"))

Rows and columns

Set nrow or ncol in the plot() function to control the number of rows and columns.

comp <- load_stim_composite() |> resize(0.5)

plot(comp, nrow = 2)

Set byrow = FALSE to order the images by column instead of the default. You can control the space between images with padding.

We’d set maxwidth globally at the top of the script using wm_opts(), but you can also set maxwidth or maxheight for a specific plot.

plot(comp, 
     ncol = 2, 
     byrow = FALSE, 
     padding = 0, 
     maxheight = 850*2)

Multipart figure

To make a multipart figure, you usually don’t want padding around the outside of a component image, so set external_pad = FALSE. Set byrow = FALSE to distribute images by column.

# make the centre columns
ind <- comp[c(1:2,4:7,9:10)] |>
  plot(ncol = 2, byrow = FALSE, padding = 60, external_pad = FALSE)

# add multi-ethnic composites to left and right on centre column
c(comp[3], ind, comp[8]) |>
  resize(height = 500) |> # make sure parts are the same height
  plot(nrow = 1)

Visualising Templates

Use the draw_tem() function to show the delineations. By default, points are translucent green circles and lines are translucent blue.

stimuli |>
  draw_tem()

You can change the default colours and translucency with the line.color, pt.color, line.alpha and pt.alpha arguments. Remove the image and set the background colour with bg.

stimuli |>
  resize(1000) |>
  draw_tem(line.color = "grey", line.size = 2, line.alpha = 0.2, 
           pt.color = "red", pt.size = 4, bg = "grey") |>
  crop_tem(50)

Labels

There are two labelling functions: mlabel() uses syntax like magick::image_annotate() to configure labels and gglabel() uses syntax like ggplot2::annotate() to configure labels. They have slightly different features, but which you use will probably be determined by whether you’re more familiar with magick or ggplot. The label() function defaults to mlabel() unless you use arguments that are only used in gglabel().

mlabel()

Label figure panels with the image name.

mlabel(stimuli)

Use the rename_stim() function to set new names.

load_stim_composite() |>
  rename_stim(pattern = "f_", replacement = "Female ") |>
  rename_stim(pattern = "m_", replacement = "Male ") |>
  label() |>
  plot(nrow = 2)

Alternatively, set text to a vector of labels to use. Labels can be positioned with the gravity and location arguments from the {magick} package. In the example below, the text is positioned at the northwest (upper-left) corner, 10 pixels to the right and 5 pixels down.

label(stimuli,
      text = c("Female", "Male"),
      gravity = "northwest",
      location = "+10+5",
      color = c("darkgreen", "darkmagenta"),
      size = 40, 
      style = c("normal", "italic")
)

You can customise the labels further, including vectors with a different value for each image. Vectors that are too long will be truncated and short vectors will be recycled.

directions <- c("north", "northeast", "east", "southeast", 
                "south", "southwest", "west", "northwest")

rep(stimuli, 4) |>
  label(
    text = paste0(" ", directions, " "),
    gravity = directions,
    color = "white",
    boxcolor = rainbow(4),
    strokecolor = "black",
    font = "Arial",
    weight = 800,
    style = "italic",
    kerning = 1.2
  ) |>
  plot(nrow = 2)

stimuli |>
  label(TRUE,
        color = "dodgerblue3",
        size = 40,
        weight = 700,
        gravity = "southwest",
        location = c("+10+100", "+10+110"),
        degrees = 45
  )

gglabel()

Label figure panels with the image name.

gglabel(stimuli)

Set label to a vector of labels to use. The default font size used by ggplot is pretty small, so increase size. I can’t help you with how to set that argument; I just use trial-and-error. Labels can be positioned with the x and y arguments. Unlike mlabel(), the origin is the lower left corner, so setting the label 5 pixel below the top requires you to calculate the image height and subtract 5. However, unlike ggplot, x and y values less than or equal to 1 are interpreted as percentages. Adjust label alignment with hjust and vjust.

label(stimuli,
      label = c("Female", "Male"),
      size = 15,
      x = 10,  y = height(stimuli) - 5,
      hjust = 0, vjust = 1,
      color = c("darkgreen", "darkmagenta"),
      fontface = c("plain", "italic")
)

You can customise the labels further, including vectors with a different value for each image. Vectors that are too long will be truncated and short vectors will be recycled.

The “label” geom lets you set fill and passing for the labels.

directions <- c("north", "northeast", "east", "southeast", 
                "south", "southwest", "west", "northwest")

rep(stimuli, 4) |>
  label(
    geom = "label",
    label = directions,
    size = 10,
    x = c(0.5, 1, 1, 1, 0.5, 0, 0, 0),
    y = c(1, 1, 0.5, 0, 0, 0, 0.5, 1),
    hjust = c(0.5, 1, 1, 1, 0.5, 0, 0, 0),
    vjust = c(1, 1, 0.5, 0, 0, 0, 0.5, 1),
    color = "white",
    fill = rainbow(4),
    family = "Arial",
    fontface = "bold.italic",
    label.padding = unit(3, "mm"),
    label.r = unit(0.5, "lines"), # round corners
    label.size = unit(1, "lines") # label border
  ) |>
  plot(nrow = 2)

You can set alpha and angle to create watermarked images.

stimuli |>
  label(
    label = "watermark",
    x = 0.5,
    y = 0.5,
    geom = "text",
    size = 20,
    color = "black",
    fontface = "bold",
    angle = -30,
    alpha = 0.25
  )

Other annotations

While the gglabel() function is mainly meant for “text” and “label” geoms, you can use it for other ggplot geoms.

# get eye points and calculate mean x and y coordinates for each image
intercepts <- get_point(stimuli, 0:1) |>
  group_by(image) |>
  summarise(x = mean(x),
            y = mean(y),
            .groups = "drop") |>
  # face coordinate y-axis is opposite to ggplot y-axis
  mutate(y = height(stimuli) - y)
stimuli |>
  label(
    geom = "rect",
    xmin = 0,
    xmax = intercepts$x,
    ymin = 0, 
    ymax = intercepts$y,
    fill = "grey"
  ) |>
  label(
    geom = "vline",
    xintercept = intercepts$x,
    color = "red", size = 2
  ) |>
  label(
    geom = "hline",
    yintercept = intercepts$y,
    color = "purple", size = 2
  )


This script took 0.3 minutes to render all the included images from scratch.