#' @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"
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.