logos/logo_functions.R

# functions for creating logos etc.

# get the coordinates and links to tile the logo 'repeats' times.
# x_start and y_start give the position of the first node
logo_shape <- function (x_start = 0, y_range = c(0, 1)) {

  # coordinates and links of the base shape
  coords <- data.frame(x = c(0, 0, 1, 1, 2, 2, 3),
                       y = c(0, 1, 0.5, 1.5, 0, 1, 0.5))

  links <- rbind(c(1, 3),
                 c(2, 3),
                 c(3, 6),
                 c(4, 6),
                 c(5, 7),
                 c(6, 7))

  scale <- abs(diff(y_range)) / 1.6
  coords$x <- coords$x * scale + x_start
  coords$y <- coords$y * scale + y_range[1]

  list(coords = coords, links =links)

}

plot_logo <- function (background = c('white', 'purple', 'light', 'lighter'),
                       pointsize = 4.5,
                       add = FALSE,
                       edge_width = 1,
                       ...) {

  background <- match.arg(background)

  data <- logo_shape(...)

  bg_col <- switch (background,
                    white = 'white',
                    light = greta:::greta_col('light'),
                    lighter = greta:::greta_col('lighter'),
                    purple = greta:::greta_col('main'))

  link_col <- switch (background,
                      white = greta:::greta_col('light'),
                      light = greta:::greta_col('dark'),
                      lighter = greta:::greta_col('dark'),
                      purple = greta:::greta_col('dark'))

  node_col <- switch (background,
                      white = greta:::greta_col('dark'),
                      light = greta:::greta_col('dark'),
                      lighter = greta:::greta_col('dark'),
                      purple = greta:::greta_col('dark'))

  if (!add) {

    old_mar <- par()$mar
    old_xpd <- par()$xpd
    old_bg <- par()$bg

    on.exit( par(mar = old_mar, xpd = old_xpd, bg = old_bg) )

    par(mar = rep(2, 4),
        xpd = NA,
        bg = bg_col)

    plot.new()
    plot.window(xlim = range(data$coords$x),
                ylim = range(data$coords$y),
                asp = 1)


  }

  # loop though from right to left, plotting points and edges to ensure
  # gaps on either side distances

  x_loc <- sort(unique(data$coords$x),
                decreasing = TRUE)

  for (loc in x_loc) {

    # find relevant nodes and edges
    idx_nodes <- which(data$coords$x %in% loc)
    idx_edges <- which(data$links[, 2] %in% idx_nodes)
    nodes <- data$coords[idx_nodes, , drop = FALSE]
    edges <- data$links[idx_edges, , drop = FALSE]

    # plot nodes with fat edge
    points(y ~ x,
           data = nodes,
           pch = 21,
           bg = node_col,
           col = bg_col,
           cex = pointsize - 1.5,
           lwd = pointsize * 4 * edge_width)

    # plot lines
    for (i in seq_len(nrow(edges))) {
      link <- edges[i, ]
      lines(x = data$coords$x[link],
            y = data$coords$y[link],
            lwd = pointsize * 2.3 * edge_width,
            col = link_col)
    }

    # plot nodes with thin edge
    points(y ~ x,
           data = nodes,
           pch = 21,
           bg = node_col,
           col = bg_col,
           cex = pointsize - 1.5,
           lwd = 0)

  }

}

# greta logo generation
# Muli fontface from: https://fonts.google.com/specimen/Muli

# plot a purple banner, with greta in white
# 'width' gives the width:height ratio
# 'margin' gives the proportion of the vertical height to use a border on each side
# the text is scaled to never exceed that border
#' @importFrom graphics par plot.new plot.window strheight strwidth text
banner <- function (background = c('purple', 'white', 'light', 'lighter'),
                    transparent_bg = FALSE,
                    width = 8, margin = 0.2,
                    font = c('Muli', 'sans'),
                    add_logo = TRUE, ...) {

  font <- match.arg(font)
  background <- match.arg(background)

  # warn if the banner isn't height-filled
  min_width <- 3.184175 + 2 * margin * (1 - 3.184175)
  if (width < min_width) {
    warning ('with a margin of ',
             margin,
             ' the minimum width to ensure the banner is height-filled is ',
             min_width)
  }

  bg_col <- switch (background,
                    white = 'white',
                    light = greta:::greta_col('light'),
                    lighter = greta:::greta_col('lighter'),
                    purple = greta:::greta_col('main'))

  text_col <- switch (background,
                      white = greta:::greta_col('dark'),
                      light = 'white',
                      lighter = greta:::greta_col('dark'),
                      purple = 'white')

  # cache the old graphics options
  old_bg <- par('bg')
  old_mar <- par('mar')
  old_family <- par('family')

  # switch to a purple background, no margins and Muli typeface
  par(bg = ifelse(transparent_bg, NA, bg_col),
      mar = rep(0, 4),
      family = font)

  # set up the device, to have the correct width
  plot.new()
  plot.window(xlim = c(0, width),
              ylim = c(0, 1),
              asp = 1)

  # scale the font, so that 'greta' fills the area (excluding self-imposed
  # margins) either vertically or horizontally
  max_height <- (1 - 2 * margin) / strheight('greta')
  max_width <- (width - 2 * margin) / strwidth('greta')
  fontsize <- min(max_height, max_width)

  # find the final string dimensions
  string_height <- fontsize * strheight('greta')
  string_width <- fontsize * strwidth('greta')

  # how far to indent 'greta'
  xpos <- margin

  # 'g' should be aligned to the left of the box
  text(x = xpos,
       y = 0.5,
       label = 'greta',
       col = text_col,
       cex = fontsize,
       pos = 4,
       offset = 0)

  if (add_logo) {

    plot_logo(background = background,
              add = TRUE,
              x_start = string_width + xpos * 3,
              y_range = 0.55 + string_height * 0.5 * c(-1, 1),
              ...)

  }

  par(bg = old_bg,
      mar = old_mar,
      family = old_family)

  invisible(NULL)

}

# same dimensions as banner, but with no text
blank_banner <- function (width = 8, margin = 0.2) {

  # cache the old graphics options
  old_bg <- par('bg')
  old_mar <- par('mar')

  # switch to a purple background with no margins
  par(bg = greta:::greta_col(),
      mar = rep(0, 4))

  # set up the device, to have the correct width
  plot.new()
  plot.window(xlim = c(0, width),
              ylim = c(0, 1),
              asp = 1)

  par(bg = old_bg,
      mar = old_mar)

  invisible(NULL)

}

# make and save an image of a triangular tesselation GMRF pattern in greta purple
tesselation_image <- function (ncol = 10, nrow = 10,
                               max_edge = 0.08,
                               jitter = 0.1,
                               thickness = 1,
                               line_col = greta:::greta_col('light'),
                               ramp_cols = NULL) {

  if (is.null(ramp_cols)) {
    cols <- c(greta:::greta_col('lighter'),
              greta:::greta_col('light'))
    ramp_cols <- colorRampPalette(cols)(2000)[-(1:1000)]
  }

  require (INLA)
  require (raster)
  require (greta)
  require (fields)

  # grid sizes for sampling the GRF and for the final image
  ncol_sim <- round(ncol / 10)
  nrow_sim <- round(nrow / 10)
  ratio <- ncol / nrow
  grid <- list(x = seq(0, 1, length.out = ncol_sim),
               y = seq(0, 1, length.out = nrow_sim))

  obj <- Exp.image.cov(grid = grid, theta = 0.1, setup = TRUE)

  r <- raster(sim.rf(obj))

  image <- raster(nrow = nrow, ncol = ncol)
  extent(image) <- c(0, ratio, 0, 1)
  extent(r) <- extent(image)


  pts <- expand.grid(seq(0, ratio, length.out = 10),
                     seq(0, 1, length.out = 10))
  pts <- pts + cbind(rnorm(100, 0, jitter),
                     rnorm(100, 0, jitter))

  # make an inla mesh
  sp <- as(extent(image), 'SpatialPolygons')
  mesh <- inla.mesh.2d(loc = pts,
                       boundary = inla.sp2segment(sp),
                       max.edge = max_edge,
                       offset = 0)

  # sample GRF at nodes
  z <- extract(r, mesh$loc[, 1:2])

  # get projection to raster
  image_coords<- xyFromCell(image, 1:ncell(image))
  A <- inla.spde.make.A(mesh, loc = image_coords)

  # instead of linear interpolation, average the three node values
  A2 <- A
  A2@x[A2@x > 0] <- 1/3
  image[] <- (A2 %*% z)[, 1]

  pm <- par("mar")
  on.exit(par(mar = pm))

  par(mar = rep(0, 4))
  image(image,
        col = ramp_cols,
        asp = 1,
        axes = FALSE,
        xlab = '',
        ylab = '')
  plot(mesh,
       add = TRUE,
       edge.color = line_col,
       lwd = thickness,
       draw.segments = FALSE)
  points(mesh$loc,
         pch = 16,
         cex = 0.5 * sqrt(thickness),
         col = line_col)
}
goldingn/greta documentation built on May 24, 2021, 11 a.m.