R/FlowFieldBackgrounds.R

Defines functions next_y next_x palette_sampler create

Documented in create next_x next_y palette_sampler

#' @author Theo Jongerius, \email{theo.j.jongerius@@gmail.com}

## you need these 2 packages first:
library(ambient)
library(ggplot2)

#' Create
#'
#' Creates a plot of a flow field using a color palette
#'
#' @param xdim specifies the width of the image
#' @param ydim specifies the height of the image
#' @param palette a list of colors to choose from. built-in palettes include: sky, clouds, ground, neon, retro, glow, seafoam, and pixiedust
#' @param maxLength the maximum possible length of an individual line segment. Shorter values will create shorter lines
#' @param iterations how many times the function will run, increasing the length of each line
#' @param numPoints how many lines will be created by the function. For a densely populated image, use large numbers i.e. > 1000. For a sparsely populated image, use small numbers
#' @param curliness the multiplier for the sin and cos functions in each iteration; determines how wavy the resulting image will be
#'
#' @return A ggplot with the resulting generated image.
#'
#' @examples
#' create(xdim = 1080, ydim = 1920, palette = ground, maxLength = 20, iterations = 100, numPoints = 1000, curliness = 6.28)
#' create(palette = neon, maxLength = 1, iterations = 200, numPoints = 500, curliness = 1)
#'
#' After running this function, if you like what you see, I recommended using the ggsave() function to save the image.
#' For a non-pixelated image, it is best to take set width = (your xdim multiplied by 10), height = (your ydim multiplied by 10), dpi = 900, and units = "px"
#'
#' @export
create <- function(xdim = 1920, ydim = 1080, palette = sky, maxLength = 100, iterations = 250, numPoints = 750, curliness = 6.28) { #, backgroundColor = "grey69") {
  # grab the background color from the palette, separate it from the rest of the palette
  colors <- palette_sampler(palette)
  backgroundColor <- colors[1]
  colorsLeft <- colors[-1]
  color <- sample(colorsLeft, numPoints, replace=TRUE)
  # initialize the image with theme_void, no legend, and a filled-in background
  image <- ggplot() + theme_void() +
    theme(legend.position = "none") +
    geom_rect(mapping = aes(xmin=0, ymin=0, xmax=xdim, ymax=ydim, fill=TRUE), fill = backgroundColor) +
    xlim(0, xdim) + ylim(0, ydim)
  # generate the perlin noise
  noise <- noise_perlin(c(ydim, xdim))
  # generate starting x values
  x1 <- round(runif(numPoints, 0, xdim), digits = 0)
  # generate starting y values
  y1 <- round(runif(numPoints, 0, ydim), digits = 0)
  # while loop to grab the next point in a line
  n <- 0
  while (n < iterations) {
    # call the next_x and next_y functions
    x2 <- next_x(x1, numPoints, noise, y1, maxLength, curliness)
    y2 <- next_y(y1, numPoints, noise, x1, maxLength, curliness)
    # round the newly-generated points to the nearest whole number
    x2 <- round(x2, digits = 0)
    y2 <- round(y2, digits = 0)
    df <- data.frame(x1, y1, x2, y2, color)
    image <- image + geom_segment(data = df, aes(x = x1, y = y1, xend = x2, yend = y2), color = color)
    # if a point exceeds the dimensions of the image, this for loop transfers the point to the opposite side of the image
    for (i in 1:numPoints) {
      if (x2[i] <= 0) {
        x2[i] = x2[i] + xdim
      }
      if (y2[i] <= 0) {
        y2[i] = y2[i] + ydim
      }
      if (x2[i] > xdim) {
        x2[i] = x2[i] - xdim
      }
      if (y2[i] > ydim) {
        y2[i] = y2[i] - ydim
      }
    }
    # set the newly generated segment endpoints to the segment startpoints
    x1 <- x2
    y1 <- y2
    # iterate n
    n = n+1
  }
  return(image)
}

#' Palette Sampler
#'
#' Takes a color palette in the form of a list, selects a background color and separates it from the rest of the palette
#'
#' @param palette a list of colors; an example is shown below
#'
#' @return a list with the background color first and all other colors following
#'
#' @examples
#' greyscale <- c(
#' "black" = "#000000",
#' "white" = "#ffffff",
#' "gray" = "#808080",
#' "britishgray" = "#797979",
#' "charcoal" = "#36454f"
#' )
#'
#' @export
palette_sampler <- function(palette) {
  backgroundIndex <- sample(1:length(palette))
  backgroundColor <- palette[backgroundIndex]
  colors <- palette[-backgroundIndex]
  return(c(backgroundColor, colors))
}

#' grab the next x value
#'
#' generates the next points to be used to finish each line segment in the image
#'
#' uses perlin noise and a multiplier to generate a segment's endpoint
#'
#' @param x1 the starting x value
#' @param noise the perlin noise matrix generated by the create() function
#' @param y1 the y value associated with the starting x value
#' @param maxLength the largest possible length of a line segment
#' @param curliness the multiplier to determine the angle to direct the line segment
## these are the functions to grab further points
next_x <- function(x1, numPoints, noise, y1, maxLength, curliness) { # calculate next x value for each point
  x2 <- 1:numPoints
  for (i in 1:numPoints) {
    x2[i] <- x1[i] + (sample(1:maxLength, 1) * cos(noise[y1[i], x1[i]]*curliness))
  }
  return(x2)
}

#' grab the next y value
#'
#' generates the next points to be used to finish each line segment in the image
#'
#' uses perlin noise and a multiplier to generate a segment's endpoint
#'
#' @param y1 the starting y value
#' @param noise the perlin noise matrix generated by the create() function
#' @param x1 the x value associated with the starting y value
#' @param maxLength the largest possible length of a line segment
#' @param curliness the multiplier to determine the angle to direct the line segment
next_y <- function(y1, numPoints, noise, x1, maxLength, curliness) { # calculate next x value for each point
  y2 <- 1:numPoints
  for (i in 1:numPoints) {
    y2[i] <- y1[i] + (sample(1:maxLength, 1) * sin(noise[y1[i], x1[i]]*curliness))
  }
  return(y2)
}

## these are the palettes
sky <- c(
  'stpatblue' = '#173679',
  'cba' = '#4888C8',
  'maastricht' = '#0B1E38',
  'background' = '#7fc5dc'
)

clouds <- c(
  'lsb' = '#ADC5E0',
  'azureish' = '#DDE5F7',
  'white' = '#FCFEFF',
  'aliblu' = '#E8F1FF',
  'ceil' = '#8EACD3'
)

ground <- c(
  "Sienna" = "#693C26",
  "Rosy Brown" = "#8F6242",
  "Silver" = "#CEC8C2",
  "Black" = "#2F2622",
  "background" = "#cdcecc"
)

seafoam <- c(
  'metallicblue' = '#557b83',
  'turquoise' = '#39aea9',
  'springgreen' = '#a2d5ab',
  'sand' = '#e5efc1',
  'teal' = '#6edcd9',
  'mintgreen' = '#019267',
  'background' = '#e0ddaa'
)

neon <- c(
  "hotpink" = "#e92efb",
  "pink" = "#ff2079",
  "blue" = "#440bd4",
  "darkblue" = "#04005e",
  "background" = "#222222"
)

glow <- c(
  "aqua" = "#08f7fe",
  "turquoise" = "#09fbd3",
  "pink" = "#fe53bb",
  "yellow" = "#f5d300"
)

retro <- c(
  "pink" = "#fe1c80",
  "orange" = "#ff5f01",
  "burntorange" = "#ce0000",
  "background" = "#ffe3f1"
)

pixiedust <- c(
  "palegoldenrod" = "#f1d7b6",
  "rosy" = "#f39ca1",
  "darkslategray" = "#1c1b1d",
  "dimgray" = "#6b6568",
  "background" = "#d2a590"
)

tealorange <- c(
  "willpowerorange" = "#FD5901",
  "utorange" = "#F78104",
  "yelloworange" = "#FAAB36",
  "lightseagreen" = "#249EA0",
  "teal" = "#008083",
  "bangledeshgreen" = "#005F60"
)
Theo-Jongerius/FlowFieldBackgrounds documentation built on May 21, 2022, 2:29 a.m.