## ----setup, include=FALSE-----------------------------------------------------
knitr::opts_chunk$set(
  echo = TRUE,
  message = FALSE,
  warning = FALSE,
  fig.width = 8,
  fig.height = 5
)

## ----libraries----------------------------------------------------------------
library(xkcd)
library(dplyr)
library(palmerpenguins)

# Drop rows with missing values for cleaner plots
penguins <- na.omit(penguins)

## ----theme_xkcd---------------------------------------------------------------
set.seed(123456)
ggplot(penguins, aes(flipper_length_mm, body_mass_g, colour = species)) +
  geom_point(size = 2, alpha = 0.7) +
  labs(
    title = "Flipper length vs body mass",
    x = "Flipper length mm",
    y = "Body mass g",
    colour = "Species"
  ) +
  theme_xkcd()

## ----xkcdaxis-----------------------------------------------------------------
xrange <- range(penguins$bill_length_mm)
yrange <- range(penguins$bill_depth_mm)

set.seed(7)
ggplot() +
  geom_point(
    aes(bill_length_mm, bill_depth_mm, colour = species),
    data = penguins, size = 2, alpha = 0.8
  ) +
  xkcdaxis(xrange, yrange) +
  labs(
    x = "Bill length mm",
    y = "Bill depth mm",
    colour = "Species",
    title = "Bill dimensions by species"
  )

## ----geom_xkcdpath_segment----------------------------------------------------
# Gentoo penguins — add an arrow-like segment pointing at the cluster
xrange <- range(penguins$flipper_length_mm)
yrange <- range(penguins$body_mass_g)

arrow_df <- data.frame(
  x    = 228, y    = 4200,
  xend = 220, yend = 5300
)

set.seed(99)
ggplot() +
  geom_point(
    aes(flipper_length_mm, body_mass_g, colour = species),
    data = penguins, size = 2, alpha = 0.7
  ) +
  geom_xkcdpath(
    mapping = aes(x = x, y = y, xend = xend, yend = yend),
    data    = arrow_df,
    linewidth = 1, xjitteramount = 1, yjitteramount = 60,
    mask = TRUE
  ) +
  annotate("text", x = 230, y = 4100,
           label = "Big Gentoos!", family = "xkcd", size = 5) +
  xkcdaxis(xrange, yrange) +
  labs(x = "Flipper length mm", y = "Body mass g", colour = "Species")

## ----geom_xkcdpath_circle-----------------------------------------------------
xrange <- c(160, 240)
yrange <- c(2500, 6500)
ratioxy <- diff(xrange) / diff(yrange)

# diameter is in x-axis units; ratioxy corrects for the different x/y scales
# so the circle appears round on screen
circle_df <- data.frame(x = 200, y = 4000, diameter = 20)

set.seed(5)
ggplot() +
  geom_point(
    aes(flipper_length_mm, body_mass_g, colour = species),
    data = penguins, size = 2, alpha = 0.7
  ) +
  geom_xkcdpath(
    aes(x = x, y = y, diameter = diameter),
    data = circle_df, linewidth = 1.2, colour = "firebrick",
    ratioxy = ratioxy, mask = FALSE
  ) +
  annotate("text", x = 200, y = 3600,
           label = "Overlap zone", family = "xkcd", size = 4, colour = "firebrick") +
  xkcdaxis(xrange, yrange) +
  labs(x = "Flipper length mm", y = "Body mass g", colour = "Species",
       title = "A fuzzy circle highlights the overlap zone")

## ----xkcdrect-----------------------------------------------------------------
# Average body mass per species as a bar chart using fuzzy rectangles
avg_mass <- penguins |>
  group_by(species) |>
  summarise(mean_mass = mean(body_mass_g), .groups = "drop") |>
  mutate(
    xmin = as.numeric(species) - 0.35,
    xmax = as.numeric(species) + 0.35,
    ymin = 0,
    ymax = mean_mass
  )

xrange <- c(0.5, 3.5)
yrange <- c(0, max(avg_mass$mean_mass) + 300)

set.seed(11)
ggplot() +
  xkcdrect(
    aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
    data = avg_mass,
    fill      = c("#f28e2b", "#4e79a7", "#59a14f"),
    colour    = "black",
    linewidth = 1
  ) +
  annotate("text",
           x     = 1:3,
           y     = avg_mass$mean_mass + 150,
           label = levels(penguins$species),
           family = "xkcd", size = 5) +
  xkcdaxis(xrange, yrange) +
  scale_x_continuous(breaks = 1:3, labels = levels(penguins$species)) +
  labs(
    x = "Species",
    y = "Mean body mass g",
    title = "Average penguin weight"
  )

## ----xkcdman_basic------------------------------------------------------------
xrange <- range(penguins$flipper_length_mm)
# Expand y upward to give room for figures above the data
yrange <- c(min(penguins$body_mass_g) - 200, max(penguins$body_mass_g) + 1200)
ratioxy <- diff(xrange) / diff(yrange)

# scale ≈ 10% of yrange so figures are clearly visible
scale_val <- diff(yrange) * 0.10

dataman <- data.frame(
  x             = c(178, 228),
  y             = c(max(penguins$body_mass_g) + 500,
                    min(penguins$body_mass_g) + 1500),
  scale         = scale_val,
  ratioxy       = ratioxy,
  angleofspine  = -pi / 2,
  anglerighthumerus = c(-pi / 6, -pi / 6),
  anglelefthumerus  = c(-pi / 2 - pi / 6, -pi / 2 - pi / 6),
  anglerightradius  = c(pi / 5, -pi / 5),
  angleleftradius   = c(pi / 5, -pi / 5),
  anglerightleg = 3 * pi / 2 - pi / 12,
  angleleftleg  = 3 * pi / 2 + pi / 12,
  angleofneck   = -pi / 2
)

mapping <- aes(
  x = x, y = y, scale = scale, ratioxy = ratioxy,
  angleofspine      = angleofspine,
  anglerighthumerus = anglerighthumerus,
  anglelefthumerus  = anglelefthumerus,
  anglerightradius  = anglerightradius,
  angleleftradius   = angleleftradius,
  anglerightleg     = anglerightleg,
  angleleftleg      = angleleftleg,
  angleofneck       = angleofneck
)

set.seed(22)
ggplot() +
  geom_point(
    aes(flipper_length_mm, body_mass_g, colour = species),
    data = penguins, size = 2, alpha = 0.7
  ) +
  xkcdaxis(xrange, yrange) +
  xkcdman(mapping, dataman) +
  annotate("text", x = 174, y = max(penguins$body_mass_g) + 1050,
           label = "Small\nones!", family = "xkcd", size = 4) +
  annotate("text", x = 234, y = max(penguins$body_mass_g) - 1050,
           label = "Big\nones!", family = "xkcd", size = 4) +
  labs(x = "Flipper length mm", y = "Body mass g", colour = "Species",
       title = "Two researchers discuss the data")

## ----xkcdman_per_island-------------------------------------------------------
island_means <- penguins |>
  group_by(island) |>
  summarise(
    mx = mean(flipper_length_mm),
    my = mean(body_mass_g),
    .groups = "drop"
  )

xrange <- range(penguins$flipper_length_mm)
yrange <- c(min(penguins$body_mass_g) - 200, max(penguins$body_mass_g) + 1400)
ratioxy <- diff(xrange) / diff(yrange)
scale_val <- diff(yrange) * 0.10

set.seed(33)
dataman <- data.frame(
  x             = island_means$mx,
  y             = island_means$my + 800,
  scale         = scale_val,
  ratioxy       = ratioxy,
  angleofspine  = -pi / 2,
  anglerighthumerus = runif(3, -pi / 6 - pi / 10, -pi / 6 + pi / 10),
  anglelefthumerus  = runif(3, -pi / 2 - pi / 6 - pi / 10, -pi / 2 - pi / 6 + pi / 10),
  anglerightradius  = runif(3, pi / 5 - pi / 10, pi / 5 + pi / 10),
  angleleftradius   = runif(3, pi / 5 - pi / 10, pi / 5 + pi / 10),
  anglerightleg = 3 * pi / 2 - pi / 12,
  angleleftleg  = 3 * pi / 2 + pi / 12,
  angleofneck   = -pi / 2
)

mapping <- aes(
  x = x, y = y, scale = scale, ratioxy = ratioxy,
  angleofspine      = angleofspine,
  anglerighthumerus = anglerighthumerus,
  anglelefthumerus  = anglelefthumerus,
  anglerightradius  = anglerightradius,
  angleleftradius   = angleleftradius,
  anglerightleg     = anglerightleg,
  angleleftleg      = angleleftleg,
  angleofneck       = angleofneck
)

set.seed(33)
ggplot() +
  geom_point(
    aes(flipper_length_mm, body_mass_g, colour = island),
    data = penguins, size = 2, alpha = 0.7
  ) +
  xkcdaxis(xrange, yrange) +
  xkcdman(mapping, dataman) +
  annotate("text",
           x = island_means$mx,
           y = island_means$my + 1350,
           label = island_means$island,
           family = "xkcd", size = 4) +
  labs(x = "Flipper length mm", y = "Body mass g", colour = "Island",
       title = "One researcher per island",caption = "Trogersen and Dream Island overlap!!")

## ----full_example-------------------------------------------------------------
# Yearly penguin count as fuzzy bars + a stick figure + annotation arrow
counts <- penguins |>
  group_by(year, species) |>
  summarise(n = n(), .groups = "drop") |>
  group_by(year) |>
  summarise(total = sum(n), .groups = "drop") |>
  mutate(
    xmin = year - 0.35,
    xmax = year + 0.35,
    ymin = 0,
    ymax = total
  )

xrange <- c(2006.5, 2009.5)
# Expand y to give the figure room above the tallest bar
yrange <- c(0, max(counts$total) + 60)
ratioxy <- diff(xrange) / diff(yrange)
scale_val <- diff(yrange) * 0.12   # ~12% of y range = clearly visible

# Figure stands above the 2009 bar (tallest), pointing left
dataman <- data.frame(
  x             = 2009,
  y             = min(counts$total) - 30,
  scale         = scale_val,
  ratioxy       = ratioxy,
  angleofspine  = -pi / 2,
  anglerighthumerus = -pi / 6,
  anglelefthumerus  = -pi / 2 - pi / 6,
  anglerightradius  = pi / 5,
  angleleftradius   = pi / 5,
  anglerightleg = 3 * pi / 2 - pi / 12,
  angleleftleg  = 3 * pi / 2 + pi / 12,
  angleofneck   = -pi / 2
)

man_mapping <- aes(
  x = x, y = y, scale = scale, ratioxy = ratioxy,
  angleofspine      = angleofspine,
  anglerighthumerus = anglerighthumerus,
  anglelefthumerus  = anglelefthumerus,
  anglerightradius  = anglerightradius,
  angleleftradius   = angleleftradius,
  anglerightleg     = anglerightleg,
  angleleftleg      = angleleftleg,
  angleofneck       = angleofneck
)

# Arrow from annotation label to 2009 bar top
arrow_df <- data.frame(
  x = 2007.8, y = max(counts$total) + 48,
  xend = 2008.6, yend = max(counts$total) + 10
)

set.seed(55)
ggplot() +
  xkcdrect(
    aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
    data = counts,
    fill = "#aecbfa", colour = "black", linewidth = 1
  ) +
  geom_xkcdpath(
    aes(x = x, y = y, xend = xend, yend = yend),
    data = arrow_df,
    linewidth = 1, xjitteramount = 0.03, yjitteramount = 3, mask = TRUE
  ) +
  xkcdman(man_mapping, dataman,color="white") +
  xkcdaxis(xrange, yrange) +
  annotate("text", x = 2007.5, y = max(counts$total) + 48,
           label = "More penguins\nevery year!", family = "xkcd", size = 4) +
  annotate("text", x = counts$year, y = counts$total + 8,
           label = counts$total, family = "xkcd", size = 5) +
  scale_x_continuous(breaks = c(2007, 2008, 2009)) +
  labs(x = "Year", y = "Penguins observed",
       title = "Palmer penguins surveyed per year")

