You can download the RMarkdown source code for this page here.

1 Rendering a simple solar eclipse

Generate 200.000 points on a circle with normal-distributed angle (al), so that a few more are on one side of the circle, and a normal-distributed positive offset from the radius (dr) to spread them out a bit.

n <- 200000

tibble(
  dr = rnorm(n = n, sd=0.04, mean = 0) %>% abs,
  al = rnorm(n = n, sd = 1.0, mean = 0)
  ) %>% mutate(
    r = 1 + dr,
    x = r * cos(al),
    y = r * sin(al)
) -> points

Set up a custom ggplot theme with no axes and a black background.

theme_void() %+replace% theme(
  legend.position="none",
  panel.background = element_rect(fill="black")
) -> th

Then render as a scatter plot in white on a black background with low opacity.

points %>% ggplot(aes(x=x, y=y)) +
  geom_point(alpha = 0.2, shape = ".", color = "white") +
  coord_equal() +
  xlim(-3, 3) +
  ylim(-2, 2) +
  th

2 Adding a better corona effect

Make two subsets of the points: p1 with \(n = 40.000\) and p2 with \(n = 20000\)

p1 will remain unchanged and be rendered just as before. p2 will be used for drawing outward pointing rays to give that typical look of the sun’s corona during an eclipse.

Add an identifier to each point in p2, like the row number. It doesn’t really matter what it is as long as it’s unique to each point. We need that to control which points will be connected into lines when rendering.

Also add new value \(c = 100\) to them. We’ll need that later.

points %>% slice_sample(n = 40000) -> p1
points %>% slice_sample(n = 20000) -> p2

p2 %>% mutate(
  id = row_number(),
  c = 100,
  d = abs(rnorm(nrow(p2), mean = 0, sd = 0.07))
) -> p2

Move the points from p2 all further outward in a loop. Recalculate their X and Y coordinates as well and decrease \(c\) on each loop. Then merge the moved copies and originals (p2) together with bind_rows() into a new tibble (rays).

rays <- p2
steps <- 10

for (i in 1:steps) {
  p2 %>% mutate(
    r = r + d,
    x = r * cos(al),
    y = r * sin(al),
    c = c - (100 / steps)
  ) -> p2
  
  p2 %>% bind_rows(rays) -> rays
}

Plot them again, but this time add a line graph (geom_path) between each set of points that have the same identifier \(i\) and set the opacity (alpha) to the value of \(c\).

Set the opacity of the p1 points to a value much higher than 100. We want them to be brighter than the rays. We also need to control how the opacity (alpha) is handled for this with scale_alpha_continuous.

This might take a little while to run …

ggplot() +
  geom_path(data = rays,
            aes(x = x, y = y, group = id, alpha = c),
            color ="white", linewidth = 2) +
  geom_point(data = p1,
             aes(x = x, y = y, alpha = 800),
             shape = ".",
             color = "white") +
  coord_equal() +
  xlim(-4, 4) +
  ylim(-4, 4) +
  scale_alpha_continuous(range = c(0, 0.2)) + 
  th