Splinter fish

Splinter fish

Exploring the updates of ggforce (0.2.0.9000)

What: Combining ggforce and hypoimg to create hamlet mosaics.

Recently, Thomas Lin Pedersen released an update of the great package ggforce. On his blog he presents a few cool examples and especially the tessellation stuff looks cool. It got me hooked, so I decided to look at the new features of ggforce and see what they could do to the poor unprepared hamlets…

library(tidyverse)
library(ggforce)
library(sf)

So, first let’s plot the hamlet outline:

hypoimg::hypo_outline %>%
  ggplot(aes(x = x,y = y))+
  coord_equal()+
  geom_shape()

Ok - this seems to work nicely.

Now we need some random points on our hamlet - the poor fish gets the measles. To do this we first create random points throughout the whole x and y range of the fish.

Then we are going to turn both the points and the outline into a spatial object and intersect them.

For convenience we wrap this up in a function:

create_inner_points <- function(n = 170, data){
# turn the outline into spatial object
spatial_outline <- data %>%
    as.matrix() %>%
    list(.) %>%
    st_polygon(.)

# create random points and turn them into spatial object
spatial_points <- tibble(x = runif(n,min = min(data$x),max = max(data$x)),
                y = runif(n,min = min(data$y),max = max(data$y))) %>%
  as.matrix() %>%
  st_multipoint()

# intersect the points with outline
spatial_intersection <- st_intersection(spatial_outline,spatial_points)

# export the intersected points
data_intersection <- spatial_intersection %>%
  as.matrix() %>%
  as_tibble() %>%
  setNames(.,nm = c('x','y')) %>%
  mutate(clr = sample(LETTERS[1:5],size = length(x),replace = TRUE))
  data_intersection
}

Let’s see:

create_inner_points(n = 100, data = hypoimg::hypo_outline) %>%
    ggplot(aes(x,y))+
    coord_equal()+
    geom_shape(data = hypoimg::hypo_outline,fill = rgb(0,0,0,.4),color='black') +
    geom_point()+
    theme_void()

I’m lazy and also Hadley Wickham doesn’t like me repeating myself - so let’s pack up the plotting in a function:

splinter_fish <- function(n = 170,
                          data,
                          direction = 1,
                          palette = 'Blues',
                          expand_inner = 0,
                          expand_outer = 5){
  # include the option to flip the x axis
  # (scale_x_reverse() does no work with the tessellation)
  inner_data <- data %>%
    mutate(x = direction * x)

  # call the function to create random points
  create_inner_points(n = n,inner_data) %>%
    ggplot(aes(x,y))+
    coord_equal()+
    # background shadow
    geom_shape(data = inner_data,fill = rgb(0,0,0,.4),color='black') +
    # the original outline
    geom_shape(data = inner_data, alpha = .2,expand = unit(expand_outer,'pt')) +
    # the fun part
    geom_voronoi_tile(aes(fill = x*y*direction,group = 1L),
                      colour = 'black', bound = inner_data,
                      expand = unit(expand_inner,'pt'))+
    # some colors
    scale_fill_distiller(palette = palette, guide = FALSE)+
    theme_void()
}

Well, Alt J said it best: “let’s tessellate”!

    splinter_fish(n = 120, data = hypoimg::hypo_outline)

The fun part of reef fish is that they come in many different colors and there are swarms of them:

# our swarm will contain 9 fish
n_plot <- 9

# initialize the swarm (every fish can choose where to look and
# what to wear: the collection includes all sequential and
# diverging palettes from ColorBrewer)
tibble(direction = sample(c(-1,1),size = n_plot, replace = TRUE),
       palette = sample(x = RColorBrewer::brewer.pal.info %>%
                          rownames() %>%
                          .[-c(10:17)],size = n_plot,replace = FALSE)) %>%
  # each fish is plotted
  purrr::pmap(splinter_fish,n = 120, data = hypoimg::hypo_outline,expand_inner = -1) %>%
  # we collect the plot in a tibble
  tibble(plot = .) %>%
  # then we turn the plots into grobs and
  # add an angle and a facet id
  mutate(grob = purrr::map(plot,ggplotGrob),
         angle = runif(n = n_plot, min = -180, max = 180),
         facet = 1:n_plot) %>%
  # finally we plot the
  ggplot(aes(x=x,y=x))+
  hypoimg::geom_hypo_grob(aes(grob=grob,x=.5,y=.5,
                     angle=angle),
                 width=.8)+
  facet_wrap(facet~.,ncol = 3,scales = 'free')+
  theme_void()+
  theme(strip.text = element_blank(),
        panel.spacing = unit(0,'pt'))


© 2021. All rights reserved. KH.