Plop

Galton Board Setup

library(chipmunkcore)
library(ggplot2)
set.seed(1)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize a simulation space
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cm <- Chipmunk$new(time_step = 0.005, gravity = cpv(0, -30))

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Add floor with bucket
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cm$add_static_segment(-70, 0,  70,  0, elasticity = 0.9)
cm$add_static_segment(-20, 0, -30, 20)
cm$add_static_segment( 20, 0,  30, 20)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Fetch all the segments. Use for plotting
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
segments_df <- cm$get_static_segments()

ggplot() +
  geom_segment(data = segments_df, aes(x = x1, y = y1, xend = x2, yend = y2)) +
  coord_fixed() +
  theme_void() +
  theme(legend.position = 'none')

Add some bodies to fall through the board

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Add some bodies. Currently only circular bodies supported
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Nballs <- 100
for (i in seq_len(Nballs)) {
  cm$add_circle(
    x          = runif(1,  -20,  20),
    y          = runif(1,  0, 20),
    radius     = runif(1, 1, 3),
    friction   = 0.01,
    elasticity = 0.9
  )
}


cm$add_circle(
  x        = -70,
  y        = 10,
  radius   = 10,
  mass     = 200,
  vx       = 15,
  vy       = 70,
  friction = 0.01
)

# print(cm$get_circles())

Animate!

  • Advance the state of the simulation
  • get the positions of all the bodies
  • Plot everything.
  • Repeat
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clear out the target directory for all the frames
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
unlink(list.files("vignettes/png", "*.png", full.names = TRUE))


steps <- 600; advance <-  6
# steps <-  50; advance <- 40
# steps <-  5; advance <- 400
# steps <- 150; advance <- 13

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# (1) advance the simulation (2) plot the bodies. (3) Repeat.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for (i in seq_len(steps)) {

  if (i  %% 10 == 0) message(i)

  cm$advance(advance)

  bodies <- cm$get_circles()
  large  <- bodies[Nballs + 1,]
  bodies <- bodies[seq_len(Nballs),]


  p <- ggplot() +
    geom_point(data = bodies, aes(x, y, size = I(2.845*r)), colour = 'black') +
    geom_point(data = large , aes(x, y), size = 30 , colour = '#002366') +
    geom_segment(data = segments_df, aes(x = x1, y = y1, xend = x2, yend = y2), colour = 'lightblue') +
    coord_fixed(xlim = c(-70, 70), ylim = c(0, 100)) +
    theme_void() +
    theme(legend.position = 'none')


  outfile <- sprintf("vignettes/png/%04i.png", i)
  ggsave(outfile, p, width = 7, height = 7)
}


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ffmpeg - create mp4 from PNG files
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
system("ffmpeg -y -framerate 30 -pattern_type glob -i 'vignettes/png/*.png' -c:v libx264 -pix_fmt yuv420p -s 800x800 vignettes/anim/plop.mp4")