---
title: "ggfun: Utilities and Extensions for ggplot2"
author: "Guangchuang Yu"
date: "`r Sys.Date()`"
vignette: >
  %\VignetteIndexEntry{ggfun: Utilities and Extensions for ggplot2}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
---

```{r}
#| label: setup
#| include: false
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 6,
  fig.height = 4,
  message = FALSE,
  warning = FALSE
)

library(grid)
library(ggplot2)
library(ggfun)

source_root <- normalizePath(file.path(".."), winslash = "/", mustWork = FALSE)
if (file.exists(file.path(source_root, "DESCRIPTION")) &&
    requireNamespace("pkgload", quietly = TRUE)) {
  pkgload::load_all(source_root, quiet = TRUE)
}

theme_set(theme_bw())
```

`ggfun` collects small but useful extensions for `ggplot2`. The package focuses
on tools that make it easier to style plots, add custom geometric layers, work
with legends, and inspect or modify existing plot objects.

This vignette groups the functions by the type of task they solve.

## Theme Elements

### Rounded rectangles

`element_roundrect()` works like `element_rect()`, but draws rounded rectangle
backgrounds. It can be used in theme elements such as facet strips, legends,
panel backgrounds, and plot backgrounds.

```{r}
#| label: rounded-strip
p <- ggplot(mpg, aes(displ, cty)) +
  geom_point(alpha = 0.7) +
  facet_grid(cols = vars(cyl))

p + theme(
  strip.background = element_roundrect(
    fill = "grey35",
    color = NA,
    r = 0.15
  ),
  strip.text = element_text(color = "white")
)
```

```{r}
#| label: rounded-legend
ggplot(mtcars, aes(mpg, disp, color = factor(cyl), size = wt)) +
  geom_point(alpha = 0.8) +
  theme(
    legend.background = element_roundrect(
      fill = "grey97",
      color = "grey55",
      linetype = 2,
      r = 0.08
    )
  )
```

### Alternating grid bands

`element_blinds()` and `theme_blinds()` draw alternating bands on major grid
lines. This is useful when categorical axes need more visual separation.

```{r}
#| label: theme-blinds
df_blinds <- data.frame(
  group = rep(LETTERS[1:6], each = 3),
  sample = rep(c("A", "B", "C"), 6),
  value = c(2, 4, 3, 5, 3, 4, 4, 6, 5, 6, 5, 7, 3, 4, 6, 5, 7, 6)
)

ggplot(df_blinds, aes(sample, group)) +
  geom_point(aes(size = value, color = value)) +
  scale_color_viridis_c() +
  theme_blinds(axis = "y", colour = c("grey95", "white")) +
  theme(panel.grid.major.x = element_blank())
```

### Minimal plot backgrounds

Several small theme helpers remove common plot decorations.

```{r}
#| label: theme-helpers
ggplot(mtcars, aes(mpg, disp)) +
  geom_point(aes(color = factor(cyl)), size = 2) +
  theme_noxaxis()
```

```{r}
#| label: theme-transparent
ggplot(mtcars, aes(mpg, disp)) +
  geom_point(aes(color = factor(cyl)), size = 2) +
  theme_transparent()
```

## Geometric Layers

### Histograms based on `hist()`

`geom_hist()` uses `hist()` to compute break midpoints and counts. This makes it
useful when the goal is to reproduce base R histogram calculations inside a
`ggplot2` layer.

```{r}
#| label: geom-hist
set.seed(42)
df_hist <- data.frame(
  value = c(rnorm(120), rnorm(120, mean = 2)),
  group = rep(c("A", "B"), each = 120)
)

ggplot(df_hist, aes(value, fill = group)) +
  geom_hist(breaks = 16, position = "identity", alpha = 0.55, color = "white")
```

### X-splines

`geom_xspline()` draws an X-spline through control points. Positive `shape`
values produce smoother curves, while negative values pull the curve toward the
control points.

```{r}
#| label: geom-xspline
set.seed(123)
df_line <- data.frame(
  x = rep(1:10, 2),
  y = c(cumsum(rnorm(10)), cumsum(rnorm(10, sd = 0.7)) + 2),
  series = rep(c("A", "B"), each = 10)
)

ggplot(df_line, aes(x, y, color = series, group = series)) +
  geom_point(size = 2) +
  geom_xspline(linewidth = 1, shape = 1)
```

### Continuous-color segments

`geom_segment_c()` draws line segments whose colors transition from `col0` to
`col1`.

```{r}
#| label: geom-segment-c
df_segments <- data.frame(
  x = c(0, 1, 2, 3),
  xend = c(1, 2, 3, 3),
  y = c(0, 1, 0.5, 0),
  yend = c(1, 0.5, 1.5, 1.5),
  start = c(0.1, 0.3, 0.5, 0.7),
  end = c(0.4, 0.6, 0.9, 1.0)
)

ggplot(df_segments) +
  geom_segment_c(
    aes(x = x, y = y, xend = xend, yend = yend, col0 = start, col1 = end),
    linewidth = 2,
    lineend = "round"
  ) +
  scale_color_viridis_c(name = "value") +
  coord_equal()
```

### Shape-like point layers

`geom_scatter_rect()`, `geom_triangle()`, and `geom_cake()` are convenient when a
plot needs custom glyph-like markers.

```{r}
#| label: geom-scatter-rect
df_rect <- data.frame(
  x = rep(1:5, 2),
  y = rep(1:2, each = 5),
  value = c(1, 3, 2, 4, 5, 2, 4, 3, 5, 6)
)

ggplot(df_rect, aes(x, y)) +
  geom_scatter_rect(aes(fill = value), width = 0.85, height = 0.5, color = "white") +
  scale_y_continuous(breaks = 1:2, labels = c("control", "treated")) +
  scale_fill_viridis_c()
```

```{r}
#| label: geom-custom-glyphs
df_glyph <- data.frame(
  x = 1:4,
  y = c(1, 2, 1.4, 2.4),
  angle = c(0, 45, 90, 135),
  cake_size = c(0.18, 0.2, 0.16, 0.22)
)

ggplot(df_glyph, aes(x, y)) +
  geom_triangle(aes(angle = angle), fill = "tomato", size = 16, alpha = 0.8) +
  geom_cake(aes(y = y + 0.45, size = cake_size), colour.cake = "pink") +
  coord_equal(xlim = c(0.5, 4.5), ylim = c(0.5, 3.2)) +
  theme_noaxis()
```

## Facets and Plot Data

### Updating facet labels

`facet_set()` can relabel existing facets or add a simple facet strip to a plot.

```{r}
#| label: facet-set-labels
p_facet <- ggplot(mtcars, aes(disp, drat)) +
  geom_point() +
  facet_grid(cols = vars(am), rows = vars(cyl))

p_facet + facet_set(label = c(`0` = "Automatic", `1` = "Manual", `6` = "Six cylinders"))
```

```{r}
#| label: facet-set-single
ggplot(mtcars, aes(mpg, disp)) +
  geom_point() +
  facet_set("Motor Trend cars")
```

### Extracting mapped data

`get_aes_var()` returns the variable used by a mapping, and `get_plot_data()`
extracts variables from the data associated with a plot or layer.

```{r}
#| label: plot-data
p_data <- ggplot(mtcars, aes(mpg, disp, color = factor(cyl))) +
  geom_point()

get_aes_var(p_data$mapping, "x")
head(get_plot_data(p_data, var = c("x", "y")))
```

`xrange()`, `yrange()`, and `ggrange()` summarize the visible data range of a
plot.

```{r}
#| label: plot-range
xrange(p_data)
yrange(p_data)
ggrange(p_data, var = "x", region = "plot", type = "range")
```

## Legends and Fonts

`get_legend()` extracts the legend grob from a plot.

```{r}
#| label: get-legend
p_legend <- ggplot(mtcars, aes(mpg, disp, color = factor(cyl))) +
  geom_point(size = 2)

legend <- get_legend(p_legend)
class(legend)
```

`gglegend()` adds a manual legend by drawing an invisible helper layer.

```{r}
#| label: gglegend
p_base <- ggplot(mtcars, aes(mpg, disp)) +
  geom_point()

legend_data <- data.frame(
  colour = c("firebrick", "steelblue"),
  label = c("Group A", "Group B")
)

gglegend(aes(colour = label, label = label), legend_data, geom_text, p_base)
```

`set_font()` writes the requested family, face, size and colour into the plot
theme. Text layers such as `geom_text()` do not follow the theme, so the same
values are also attached to those layers -- except for aesthetics already mapped
with `aes()`, which are left alone so that their legend survives. It returns the
modified plot, which can still be extended with `+`.

```{r}
#| label: set-font
df_text <- data.frame(
  x = 1:5,
  y = c(2, 3, 2.5, 4, 3.5),
  label = LETTERS[1:5]
)

p_text <- ggplot(df_text, aes(x, y, label = label)) +
  geom_text(size = 5) +
  labs(title = "One call, every text element")

# font family, face and colour
set_font(p_text, family = "serif", fontface = "italic", color = "firebrick")

# size in points, and the result is still a plot that takes `+`
set_font(p_text, size = 8) + labs(caption = "set_font() returns a plot")
```

## Summary

The functions in `ggfun` are intentionally small and composable. They are most
useful when a plot is almost complete but needs a custom theme element, a
specialized layer, a modified facet strip, or access to the data and grobs that
`ggplot2` builds internally.
