├── .gitignore ├── README.md ├── images ├── rstudio_01.png └── rstudio_02.png ├── rstudiopostcard.Rproj └── source ├── rstudio_01.R ├── rstudio_02.R └── stepping_stone.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .Rhistory 2 | .RData 3 | .Rproj.user 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # RStudio postcard 3 | 4 |    5 | 6 | -------------------------------------------------------------------------------- /images/rstudio_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djnavarro/rstudiopostcard/6e866a21179c1f64b7d57c44a5140dfd189df1d2/images/rstudio_01.png -------------------------------------------------------------------------------- /images/rstudio_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djnavarro/rstudiopostcard/6e866a21179c1f64b7d57c44a5140dfd189df1d2/images/rstudio_02.png -------------------------------------------------------------------------------- /rstudiopostcard.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /source/rstudio_01.R: -------------------------------------------------------------------------------- 1 | 2 | library(Rcpp) 3 | library(tidyverse) 4 | library(ambient) 5 | library(flametree) 6 | library(ggforce) 7 | library(paletteer) 8 | library(here) 9 | 10 | sourceCpp(here("source", "stepping_stone.cpp")) 11 | output <- here("images", "rstudio_01.png") 12 | 13 | 14 | # generate stepping stone background -------------------------------------- 15 | 16 | cat("generating image...\n") 17 | 18 | # parameters 19 | seed_ss <- 339 20 | shades <- 1000 21 | grains_wide <- 500 22 | grains_high <- 750 23 | 24 | palette <- paletteer_c( 25 | palette = "ggthemes::Classic Blue", 26 | n = shades 27 | ) 28 | 29 | # seed for RNG 30 | set.seed(seed_ss) 31 | 32 | # create long grid for the raster with appropriate aspect ratio 33 | ar <- grains_high / grains_wide 34 | raster <- long_grid( 35 | x = seq(0, 1, length.out = grains_wide), 36 | y = seq(0, ar, length.out = grains_high) 37 | ) 38 | 39 | # initialise raster using worley noise 40 | raster$base <- fracture( 41 | noise = gen_worley, 42 | fractal = fbm, 43 | octaves = 5, 44 | frequency = 3, 45 | value = "distance2", 46 | seed = seed_ss, 47 | x = raster$x, 48 | y = raster$y 49 | ) 50 | 51 | # convert base raster image to integer index 52 | raster$base <- as.integer(ceiling(normalise(raster$base) * shades)) 53 | 54 | # convert to matrix and run stepping stone automaton 55 | ss <- matrix(raster$base, grains_wide, grains_high) 56 | ss <- t(timestep(ss, 200)) 57 | 58 | # read colours off the ss matrix 59 | raster$shade <- palette[ss] 60 | 61 | 62 | 63 | 64 | 65 | # generate flametree ------------------------------------------------------ 66 | 67 | seed_ft <- 1 68 | set.seed(seed_ft) 69 | 70 | # create the tree 71 | ftree <- flametree_grow( 72 | time = 12, 73 | seed = seed_ft, 74 | angle = c(-2:4) * 10, 75 | scale = c(.6, .8, .9) 76 | ) 77 | 78 | # compute aspect ratio of generated tree 79 | ar2 <- with(ftree, (max(coord_y) - min(coord_y))/(max(coord_x) - min(coord_x))) 80 | 81 | # scale the tree image to fit the postcard 82 | ftree <- ftree %>% 83 | mutate( 84 | coord_x = normalise(coord_x, to = range(raster$x)), 85 | coord_y = normalise(coord_y, to = range(raster$y)) 86 | ) %>% 87 | mutate( 88 | coord_x = coord_x * min(1, ar/ar2), 89 | coord_y = coord_y * min(1, ar2/ar) 90 | ) %>% 91 | mutate( 92 | coord_x = .045 + coord_x * .9, 93 | coord_y = coord_y * .9 94 | ) 95 | 96 | 97 | # "leaf" coordinates are at terminal locations (id_step = 2) 98 | # on the terminal branches (id_leaf == TRUE) in the tree 99 | vleaf <- ftree %>% 100 | filter(id_leaf == TRUE, id_step == 2) 101 | 102 | 103 | 104 | 105 | # generate raindrops ------------------------------------------------------ 106 | 107 | seed_rd <- 1 108 | set.seed(seed_rd) 109 | 110 | # rain 111 | rain <- expand_grid( 112 | x = seq(.01, .99, length.out = 50), 113 | y = seq(.01 * ar, .99 * ar, length.out = round(50 * ar)) 114 | ) %>% 115 | mutate(droplet = runif(n()) < .1) %>% 116 | group_by(x) %>% 117 | mutate(droplet = droplet | lag(droplet, default = FALSE) | lag(droplet, 2, default = FALSE)) %>% 118 | ungroup() 119 | 120 | rain1 <- rain %>% 121 | filter(droplet == TRUE) 122 | 123 | rain2 <- rain %>% 124 | group_by(x) %>% 125 | filter(droplet, !lag(droplet) | y == min(y)) %>% 126 | ungroup() 127 | 128 | 129 | 130 | # render the image -------------------------------------------------------- 131 | 132 | cat("rendering image...\n") 133 | 134 | pic <- ggplot( 135 | data = raster, 136 | mapping = aes(x, y, fill = shade) 137 | ) + 138 | 139 | # background is the raster object 140 | geom_raster() + 141 | 142 | # underlay the rain sparkle 143 | geom_point( 144 | data = rain2, 145 | mapping = aes(x, y), 146 | size = 1, 147 | alpha = 1, 148 | color = "white", 149 | show.legend = FALSE, 150 | inherit.aes = FALSE 151 | ) + 152 | 153 | # add the rain drops 154 | geom_point( 155 | data = rain1, 156 | mapping = aes(x, y), 157 | size = .5, 158 | alpha = 1, 159 | color = "black", 160 | show.legend = FALSE, 161 | inherit.aes = FALSE 162 | ) + 163 | 164 | # tree trunk is drawn using geom_bezier from ggforce 165 | geom_bezier( 166 | data = ftree, 167 | mapping = aes( 168 | x = coord_x, 169 | y = coord_y, 170 | group = id_path, 171 | size = .3 + seg_wid * 3 172 | ), 173 | lineend = "round", 174 | show.legend = FALSE, 175 | inherit.aes = FALSE 176 | ) + 177 | 178 | # overlay the leaves 179 | geom_point( 180 | data = vleaf, 181 | mapping = aes( 182 | x = coord_x, 183 | y = coord_y 184 | ), 185 | size = 1, 186 | color = "white", 187 | show.legend = FALSE, 188 | inherit.aes = FALSE 189 | ) + 190 | 191 | # plot settings 192 | scale_fill_identity() + 193 | scale_size_identity() + 194 | coord_equal() + 195 | scale_x_continuous(expand = c(0, 0)) + 196 | scale_y_continuous(expand = c(0, 0)) + 197 | theme_void() + 198 | NULL 199 | 200 | # export image 201 | ggsave( 202 | filename = output, 203 | plot = pic, 204 | width = grains_wide / 150, 205 | height = grains_high / 150, 206 | dpi = 2400 207 | ) 208 | 209 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /source/rstudio_02.R: -------------------------------------------------------------------------------- 1 | 2 | library(Rcpp) 3 | library(tidyverse) 4 | library(ambient) 5 | library(flametree) 6 | library(voronoise) 7 | library(paletteer) 8 | library(here) 9 | 10 | sourceCpp(here("source", "stepping_stone.cpp")) 11 | output <- here("images", "rstudio_02.png") 12 | 13 | 14 | # generate stepping stone background -------------------------------------- 15 | 16 | cat("generating image...\n") 17 | 18 | # parameters 19 | seed_ss <- 340 20 | shades <- 1000 21 | grains_wide <- 500 22 | grains_high <- 750 23 | 24 | palette <- paletteer_c( 25 | palette = "viridis::magma", 26 | n = shades 27 | ) 28 | 29 | # seed for RNG 30 | set.seed(seed_ss) 31 | 32 | # create long grid for the raster with appropriate aspect ratio 33 | ar <- grains_high / grains_wide 34 | raster <- long_grid( 35 | x = seq(0, 1, length.out = grains_wide), 36 | y = seq(0, ar, length.out = grains_high) 37 | ) 38 | 39 | # initialise raster using worley noise 40 | raster$base <- fracture( 41 | noise = gen_worley, 42 | fractal = fbm, 43 | octaves = 5, 44 | frequency = 3, 45 | value = "distance2", 46 | seed = seed_ss, 47 | x = raster$x, 48 | y = raster$y 49 | ) 50 | 51 | # convert base raster image to integer index 52 | raster$base <- as.integer(ceiling(normalise(raster$base) * shades)) 53 | 54 | # convert to matrix and run stepping stone automaton 55 | ss <- matrix(raster$base, grains_wide, grains_high) 56 | ss <- t(timestep(ss, 200)) 57 | 58 | # read colours off the ss matrix 59 | raster$shade <- palette[ss] 60 | 61 | 62 | 63 | # generate flametree ------------------------------------------------------ 64 | 65 | seed_ft <- 1 66 | set.seed(seed_ft) 67 | 68 | # the "flametree" itself 69 | ftree <- flametree_grow( 70 | time = 10, 71 | seed = seed_ft, 72 | angle = c(-2:4) * 10, 73 | scale = c(.6, .8, .9) 74 | ) 75 | 76 | # compute aspect ratio of generated tree 77 | ar2 <- with(ftree, (max(coord_y) - min(coord_y))/(max(coord_x) - min(coord_x))) 78 | 79 | # scale the tree image to fit the postcard 80 | ftree <- ftree %>% 81 | mutate( 82 | coord_x = normalise(coord_x, to = range(raster$x)), 83 | coord_y = normalise(coord_y, to = range(raster$y)) 84 | ) %>% 85 | mutate( 86 | coord_x = coord_x * min(1, ar/ar2), 87 | coord_y = coord_y * min(1, ar2/ar) 88 | ) %>% 89 | mutate( 90 | coord_x = .075 + coord_x * .85, 91 | coord_y = coord_y * 1.05 92 | ) 93 | 94 | # "leaf" coordinates are at terminal locations (id_step = 2) 95 | # on the terminal branches (id_leaf == TRUE) in the tree 96 | vleaf <- ftree %>% 97 | filter(id_leaf == TRUE, id_step == 2) %>% 98 | sample_frac(.7) 99 | 100 | 101 | # render the image -------------------------------------------------------- 102 | 103 | cat("rendering image...\n") 104 | 105 | pic <- ggplot( 106 | data = raster, 107 | mapping = aes(x, y, fill = shade) 108 | ) + 109 | 110 | # the raster object forms the background 111 | geom_raster() + 112 | 113 | # tree trunk is drawn using geom_bezier from the 114 | # ggforce package (loaded by voronoise) 115 | geom_bezier( 116 | data = ftree, 117 | mapping = aes( 118 | x = coord_x, 119 | y = coord_y, 120 | group = id_path, 121 | size = .2 + seg_wid * 3 122 | ), 123 | lineend = "round", 124 | colour = "white", 125 | alpha = .5, 126 | show.legend = FALSE, 127 | inherit.aes = FALSE 128 | ) + 129 | 130 | # leaves generated using the voronoise package (in this instance 131 | # it's more or less identical to geom_voronoi_tile) 132 | geom_voronoise( 133 | data = vleaf, 134 | mapping = aes( 135 | x = coord_x, 136 | y = coord_y 137 | ), 138 | expand = -.0005, 139 | radius = 0, 140 | max.radius = .02, 141 | size = 2, 142 | alpha = 1, 143 | fill = "white", 144 | show.legend = FALSE, 145 | inherit.aes = FALSE 146 | ) + 147 | 148 | # add the marginal distributions 149 | geom_rug( 150 | data = vleaf, 151 | mapping = aes( 152 | x = coord_x, 153 | y = coord_y 154 | ), 155 | length = unit(3, "mm"), 156 | sides = "bl", 157 | size = .1, 158 | alpha = .4, 159 | color = "white", 160 | show.legend = FALSE, 161 | inherit.aes = FALSE 162 | ) + 163 | 164 | # bunch of settings... 165 | scale_fill_identity() + 166 | scale_size_identity() + 167 | coord_equal() + 168 | scale_x_continuous(expand = c(0, 0)) + 169 | scale_y_continuous(expand = c(0, 0)) + 170 | theme_void() + 171 | NULL 172 | 173 | # export image 174 | ggsave( 175 | filename = output, 176 | plot = pic, 177 | width = grains_wide / 150, 178 | height = grains_high / 150, 179 | dpi = 2400 180 | ) 181 | -------------------------------------------------------------------------------- /source/stepping_stone.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace Rcpp; 3 | 4 | // [[Rcpp::export]] 5 | IntegerMatrix timestep(IntegerMatrix x, int iter) { 6 | 7 | // size of the matrix 8 | int nrow = x.nrow(); 9 | int ncol = x.ncol(); 10 | 11 | // indexing and weighting variables 12 | double wt; 13 | int rind; 14 | int cind; 15 | int rstp; 16 | int cstp; 17 | 18 | for(int i = 0; i < iter; i++) { 19 | for(int r = 0; r < nrow; r++) { 20 | for(int c = 0; c < ncol; c++) { 21 | 22 | // sample from binomial distributions 23 | rstp = R::rbinom(4, 0.5); 24 | cstp = R::rbinom(4, 0.5); 25 | 26 | // find corresponding indices 27 | rind = r + rstp - 2; 28 | cind = c + cstp - 2; 29 | 30 | // truncate at the boundaries 31 | if(rind < 0) {rind = 0;} 32 | if(cind < 0) {cind = 0;} 33 | if(rind >= nrow) {rind = nrow - 1;} 34 | if(cind >= ncol) {cind = ncol - 1;} 35 | 36 | // replace 37 | x(r,c) = x(rind, cind); 38 | } 39 | } 40 | } 41 | 42 | return x; 43 | } --------------------------------------------------------------------------------