This is largely adapted from Danielle Navarro’s “Art from Code” lessons, that go into a lot more detail: https://art-from-code.netlify.app/


Setup a grid of points, or any set of x and y coordinates

smol_grid <- long_grid(x = 1:20, y = 1:20)

Generate gradient noise for every point on the grid. It represents the vectors that point along the highest gradients in a noise field.

The gradient noise isn’t actually needed in the final plot but it helps in illustrating what is happening.

smol_slope <- gradient_noise(
  generator = gen_simplex,
  seed = 1,
  frequency = .1,
  x = smol_grid$x,
  y = smol_grid$y
)

Generate curl noise vectors for every point on the grid, these are perpendicular to the gradient noise vectors and follow the lines of zero gradient in the field.

smol_curl <- curl_noise(
  generator = gen_simplex,
  seed = 1,
  frequency = .1,
  x = smol_grid$x,
  y = smol_grid$y
)

Combine grid, and noise vectors into a single data frame

smol_field <- smol_grid %>%
  mutate(
    z = gen_simplex(x, y, seed = 1, frequency = .1),
    slope_x = smol_slope$x,
    slope_y = smol_slope$y,
    curl_x = smol_curl$x,
    curl_y = smol_curl$y,
  )

Plot it with a color map for the “terrain” (the noise field) and arrows for the gradient and curl vectors.

ggplot(smol_field) +
  geom_contour_filled( aes(x, y, z = z), show.legend = FALSE, bins = 20) +
  geom_segment(
    mapping = aes(
      x = x, 
      y = y, 
      xend = x + slope_x * 2, 
      yend = y + slope_y * 2
    ), 
    colour = "white", 
    arrow = arrow(length = unit(0.1, "cm"))
  ) +
  geom_segment(
    mapping = aes(
      x = x, 
      y = y, 
      xend = x + curl_x * 2, 
      yend = y + curl_y * 2
    ), 
    colour = "black", 
    arrow = arrow(length = unit(0.1, "cm"))
  ) +
  theme_minimal() +
  coord_equal()