Introducing 'frak' - a julia set fractal generator

frak

frak is a small R package to generate fractals - currently just Julia Sets.

I needed a fast way to generate test images for testing other software, with images that

  • are fast to produce
  • are interesting, not just white noise
  • 8-bits-per-pixel, so the value can fit in an R raw vector or a C uint8_t vector.
  • can be be panned, zoomed and changed with simple changes to the input variables

Installation

You can install from GitHub with:

# install.package('remotes')
remotes::install_github('coolbutuseless/frak')

What’s in the box?

  • julia(cx, cy, movex, movey, zoom, size, max_iter) generate a julia set, where cx and cy are the components of imageinary iteration variable c.

Returned array of raw values represents the iteration count at which a particular location veered towards infinity. These values are scaled to span the full range from allowable within a raw vector (i.e. the whole numbers from 0 to 255)

Example: Generate a julia set

library(frak)

fractal <- julia(cx = -0.7, cy = 0.27, zoom = 1, size = 400, max_iter = 255)
mode(fractal) <- 'integer'
plot(as.raster(fractal/255))

Example: colour the image with viridis

library(viridisLite)

# generate a fractal
fractal <- julia(cx = -0.74, cy = 0.17, zoom = 3, size = 400, max_iter = 100)
mode(fractal) <- 'integer'

# Grab a palette from viridis
pal <- rev(viridisLite::viridis(256))

# Recode the values with the palette. Lowest raw value is 0, so offset by 1.
fractal_raster   <- fractal
fractal_raster[] <- pal[fractal_raster + 1]

plot(as.raster(fractal_raster))

Example: git file rendered from multiple frames

Click to show/hide code to generate gif

library(foist)
library(displease)
library(frak)

dir.create('/img/frak/anim', showWarnings = FALSE)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Use {displease} to get a pleasing motion as the zoom is performed
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
N <- 100
zoom <- displease::seq_ease(1, 1000, n = N, type = 'exp-in')

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Loop: create an image dump it to file.
# Using {foist} here for fast image output
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for (i in seq(N)) {
  frac <- frak::julia(cx = -0.71, zoom = zoom[i], movex = 0.1)
  mode(frac) <- 'integer'
  frac <- frac/255
  
  filename <- sprintf("/img/frak/anim/%03i.png", i)
  
  foist::write_png(
    data                 = frac, 
    filename             = filename, 
    pal                  = foist::vir$magma, 
    convert_to_row_major = FALSE
  )
}

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create the animation using imagemagick and delete the source frames
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
system("convert /img/frak/anim/*.png /img/frak/anim.gif")
unlink("/img/frak/anim/", recursive = TRUE)

Benchmark

Creating a julia set in C is about 100x faster than using plain R (as in the Julia package on CRAN)

Click to show/hide bench::mark() code

suppressPackageStartupMessages({
  library(Julia)
  library(frak)
  library(ggplot2)
  library(dplyr)
  library(tidyr)
})

size <- 400

# Julia::JuliaImage does at most 50 iterations
res <- bench::mark(
  `Julia::JuliaImage()` = Julia::JuliaImage(imageN = size, centre = 0.0, L = 4, C = 1-1.6180339887),
  `frak::julia()`       = julia(size = size, max_iter = 50),
  check = FALSE
)
Warning: Some expressions had a GC in every iteration; so filtering is disabled.
expression min median itr/sec
Julia::JuliaImage() 802.68ms 802.68ms 1.24582
frak::julia() 7.57ms 7.92ms 115.48860

Acknowledgements

  • R Core for developing and maintaining such a wonderful language.
  • CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining the repository