Skip to contents

ggpatchy adds a pattern aesthetic to ggplot2 geoms. Patterns are rendered via native grid graphics — no ImageMagick or external raster dependencies are required.

Bar and column charts

geom_col_pattern() and geom_bar_pattern() are drop-in replacements for geom_col() and geom_bar(). Map pattern to a discrete variable with scale_pattern_manual() or scale_pattern_discrete().

df <- data.frame(
  category = c("A", "B", "C", "D"),
  value    = c(3.2, 5.1, 4.0, 2.7)
)

ggplot(df, aes(category, value, fill = category, pattern = category)) +
  geom_col_pattern() +
  scale_pattern_manual(values = c(
    A = "hatch", B = "crosshatch", C = "dots", D = "vertical"
  )) +
  scale_fill_brewer(palette = "Pastel1") +
  theme_minimal() +
  labs(title = "geom_col_pattern()")

geom_bar_pattern() uses stat = "count":

ggplot(mpg, aes(class, fill = class, pattern = class)) +
  geom_bar_pattern() +
  scale_pattern_discrete() +
  scale_fill_brewer(palette = "Set3") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  labs(title = "geom_bar_pattern() with scale_pattern_discrete()")

Polygons

geom_polygon_pattern() clips patterns to the exact polygon boundary, not just the bounding box. This means concave shapes like crescents or L-shapes are handled correctly.

theta <- seq(0, 2 * pi, length.out = 7)[-7]
hex <- data.frame(x = cos(theta), y = sin(theta))

ggplot(hex, aes(x, y)) +
  geom_polygon_pattern(
    pattern        = "hatch",
    fill           = "lightyellow",
    pattern_colour = "goldenrod",
    pattern_angle  = 30,
    pattern_spacing = 4
  ) +
  coord_fixed() +
  theme_void() +
  labs(title = "geom_polygon_pattern() on a hexagon")

Ribbon charts

geom_ribbon_pattern() adds patterns to confidence bands or any region defined by ymin and ymax.

x  <- seq(0, 2 * pi, length.out = 80)
df <- data.frame(x = x, ymin = sin(x) - 0.3, ymax = sin(x) + 0.3)

ggplot(df, aes(x, ymin = ymin, ymax = ymax)) +
  geom_ribbon_pattern(
    pattern        = "hatch",
    fill           = "lightblue",
    pattern_colour = "steelblue"
  ) +
  theme_minimal() +
  labs(title = "geom_ribbon_pattern() — confidence band")

Multiple groups with different patterns:

df2 <- data.frame(
  x       = c(x, x),
  ymin    = c(sin(x) - 0.2, cos(x) + 0.5),
  ymax    = c(sin(x) + 0.2, cos(x) + 1.0),
  group   = rep(c("sin", "cos"), each = length(x)),
  pattern = rep(c("hatch", "crosshatch"), each = length(x)),
  fill    = rep(c("lightblue", "lightyellow"), each = length(x))
)

ggplot(df2, aes(x, ymin = ymin, ymax = ymax,
                group = group, fill = fill, pattern = pattern)) +
  geom_ribbon_pattern(alpha = 0.8) +
  scale_pattern_identity(guide = "legend") +
  scale_fill_identity() +
  theme_minimal() +
  labs(title = "geom_ribbon_pattern() — two groups")

Area charts

geom_area_pattern() mirrors geom_area(): it sets ymin = 0 automatically and defaults to position = "stack".

df3 <- data.frame(
  x       = rep(1:8, 2),
  y       = c(1, 3, 2, 4, 3, 2, 4, 3,
              2, 1, 3, 2, 1, 3, 2, 1),
  group   = rep(c("Group A", "Group B"), each = 8),
  pattern = rep(c("hatch", "crosshatch"), each = 8)
)

ggplot(df3, aes(x, y, fill = group, pattern = pattern)) +
  geom_area_pattern(position = "stack", alpha = 0.85) +
  scale_pattern_identity(guide = "legend") +
  scale_fill_brewer(palette = "Pastel1") +
  theme_minimal() +
  labs(title = "geom_area_pattern() — stacked")

Violin charts

geom_violin_pattern() adds patterns to violin plots. Use it as a drop-in replacement for geom_violin().

ggplot(mpg, aes(class, hwy, fill = class, pattern = class)) +
  geom_violin_pattern(pattern_colour = "grey30") +
  scale_pattern_discrete() +
  scale_fill_brewer(palette = "Pastel1") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  labs(title = "geom_violin_pattern()")

Density plots

geom_density_pattern() extends geom_density() for pattern fills on smooth density estimates.

ggplot(mpg, aes(hwy, fill = drv, pattern = drv)) +
  geom_density_pattern(alpha = 0.7, pattern_colour = "grey20") +
  scale_pattern_manual(values = c("4" = "hatch", "f" = "crosshatch", "r" = "dots")) +
  scale_fill_brewer(palette = "Pastel2") +
  theme_minimal() +
  labs(title = "geom_density_pattern()")

Rectangles and tiles

geom_rect_pattern() and geom_tile_pattern() add pattern overlays to rectangle geoms. geom_tile_pattern() is convenient for heatmap-style layouts where cells are specified by centre coordinates.

blocks <- data.frame(
  xmin    = c(0, 1, 2, 0, 1, 2),
  xmax    = c(1, 2, 3, 1, 2, 3),
  ymin    = c(0, 0, 0, 1, 1, 1),
  ymax    = c(1, 1, 1, 2, 2, 2),
  pattern = c("hatch", "crosshatch", "dots", "vertical", "horizontal", "weave")
)

ggplot(blocks, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax,
                   fill = pattern, pattern = pattern)) +
  geom_rect_pattern(pattern_colour = "grey30") +
  scale_pattern_identity() +
  scale_fill_brewer(palette = "Pastel1") +
  coord_fixed() +
  theme_void() +
  labs(title = "geom_rect_pattern() — all six line/dot patterns")

Choropleth maps

geom_sf_pattern() works with sf geometry columns. It uses pattern to add a second encoding channel alongside fill colour, which is useful for accessibility (grayscale printing, colour-vision deficiency). See the Mapping with Patterns vignette for full examples with the NC counties and US states datasets.

Pattern reference

Built-in patterns:

Name Description Respects pattern_angle?
none No pattern (fill only) —
hatch Diagonal lines Yes
crosshatch Crossed diagonal lines Yes
horizontal Horizontal lines No (fixed)
vertical Vertical lines No (fixed)
dots Regular dot grid No (fixed)
weave Woven over/under line texture No (fixed)

pattern_spacing is in millimetres: pattern_spacing = 5 places pattern elements 5 mm apart on every shape at every size. Visual density is uniform across all shapes in the same plot. Custom patterns can be registered with register_pattern().

Known limitations

stat = "identity" with geom_violin_pattern() requires a violinwidth column. The default stat ("ydensity") computes violinwidth automatically from your raw data. If you pass stat = "identity" with pre-summarised density data, your data frame must include a violinwidth column (values in [0, 1] giving the normalised half-width at each y level). If the column is absent, geom_violin_pattern() emits an informative message and skips that group rather than crashing.

orientation = "y" (horizontal) is not supported for pattern overlays. geom_violin_pattern(), geom_ribbon_pattern(), and geom_area_pattern() all skip the pattern when orientation = "y" or flipped_aes = TRUE. The base shape still renders correctly. Use coord_flip() on a vertical geom as a workaround.

Non-Cartesian coordinates: Patterns are drawn in screen (npc) space after coord$transform(). Under coord_polar or coord_sf the pattern lines remain straight in screen pixels rather than following the coordinate curvature.