To develop a pattern for use with {ggpattern} (and any
other packages that use {gridpattern}) you will need
to:
gridpattern::register_pattern() to let
{gridpattern} know that a certain pattern name should be
delegated to your function.This vignette shows how to:
create_pattern() function of the correct
signature for a geometry-based pattern or array-based pattern{ggpattern} on where to find this user-defined
patternThere are only 2 high-level classes of pattern supported by
gridpattern - geometry-based and array-based.
Geometry-based patterns create a series of geometry
objects and trim them (using sf::st_intersection(),
gridGeometry::polyclipGrob(),
gridpattern::alphaMaskGrob(), etc) to be within the
boundary of the grob. For example, the ‘stripes’ pattern in
gridpattern is a series of equally spaced rectangular
polygons.
Array-based patterns are RGBA image arrays. Any
supplied image will be processed by gridpattern to ensure
it is masked to only apply to the area within the grob’s boundary.
{ggpattern} Aesthetics -
Descriptions and DefaultsAlthough custom {gridpattern} pattern parameters need
not limit itself to the set of aesthetics provided by
{ggpattern} doing so may make your pattern more useful for
others:
A default of “pattern (x)” means the aesthetic is unset by default
(internally NaN) so the pattern’s own default applies,
which for most patterns is {gridpattern}’s generic default
x (see the documentation of the corresponding
gridpattern::grid.pattern_<name>() function).
{ggpattern} aesthetic summary - click to open/close
| aesthetic | description | default | possible values |
|---|---|---|---|
pattern |
Name of the pattern to draw | ‘stripe’ | gridpattern::names_pattern |
pattern_alpha |
Alpha | pattern (NA) | value in range [0, 1] or NA |
pattern_angle |
Rotation angle (entire pattern) | pattern (30) | angle in degrees |
pattern_aspect_ratio |
Aspect ratio adjustment | plot / key aspect ratio | usual range [0.01, 10] |
pattern_colour |
Stroke colour | pattern (‘grey20’) | colour |
pattern_density |
Approx. fraction of area the pattern fills | pattern (0.2) | value in range [0, 1] |
pattern_filename |
Image filename/URL | pattern (’’) | Filename/URL |
pattern_fill |
Fill colour | pattern (‘grey80’) | colour (or grid pattern/gradient) |
pattern_fill2 |
Second fill colour | pattern (‘#4169E1’) | colour (or grid pattern/gradient) |
pattern_filter |
Image scaling filter | pattern (‘lanczos’) | magick::filter_types() |
pattern_frequency |
Frequency | pattern (0.1) | |
pattern_gravity |
Image placement | pattern (‘center’) | magick::gravity_types() |
pattern_grid |
Pattern grid type | pattern (‘square’) | ‘square’, ‘hex’, ‘hex_circle’ |
pattern_key_scale_factor |
Scale factor for pattern in legend | 1 | |
pattern_linetype |
Stroke linetype | pattern (1) | linetype |
pattern_option_1 - 5 |
Generic options for expansion | pattern (0) | |
pattern_orientation |
Orientation | pattern (‘vertical’) | ‘vertical’, ‘horizontal’, ‘radial’ |
pattern_phase |
Phase | pattern (0) | |
pattern_res |
Pattern resolution (pixels per inch) | ≈ device resolution | |
pattern_rot |
Rotation angle (shape within pattern) | pattern (0) | angle in degrees |
pattern_scale |
Scale | pattern (1) | Multiplier |
pattern_shape |
Plotting shape | pattern (1) | shapes |
pattern_linewidth |
Stroke linewidth | pattern (0.5) | linewidth |
pattern_size |
Fontsize (or legacy stroke linewidth) | pattern (0.5) | fontsize (or linewidth) |
pattern_spacing |
Spacing between repetitions of pattern | pattern (0.05) | value in pattern_units grid units |
pattern_subtype |
Generic control option | pattern (NA) | pattern-dependent |
pattern_type |
Generic control option | pattern (NA) | pattern-dependent |
pattern_units |
Pattern grid unit | pattern (‘snpc’) | grid::unit() unit i.e. ‘snpc’, ‘cm’, and ‘inches’ (not
‘npc’, ‘native’, or ‘null’) |
pattern_xoffset |
Shift pattern along x axis | pattern (0) | value in pattern_units grid units |
pattern_yoffset |
Shift pattern along y axis | pattern (0) | value in pattern_units grid units |
Note {ggpattern} may also pass other geom aesthetics of
possible interest such as fill. Also note that
{ggpattern} will only pass pattern aesthetics values of
length one but if the pattern is called directly by
gridpattern::patternGrob() then the pattern may be passed
pattern parameters of arbitrary length.
All geometry-based pattern creation functions must:
Have the exact function signature:
function(params, boundary_df, aspect_ratio, legend)
params - parameters for the pattern (the aesthetics)
e.g pattern_fillboundary_df - data.frame containing polygon information
i.e. The polygon_df format.aspect_ratio - the best guess that
{gridpattern} / {ggpattern} is able to make as
to the aspect ratio of the viewport in which this
pattern is being rendered.legend logical value to indicate whether or not this
function is being called to render a key legend or the in-place geom
fill.Return a grid grob object. This can be any valid grob including a
grid::grobTree(). The user should make sure it lies within
the boundary represented by boundary_df either by clipping
with functions like sf::st_intersection(),
gridGeometry::polyclipGrob(),
gridpattern::alphaMaskGrob(), etc. or using bounded grob
functions like gridpattern::patternGrob() or
grid::polygonGrob().
All array-based pattern creation functions must:
function(width, height, params, legend)
width,height - dimensions of the bounding box of the
geom areaparams - parameters from the geom (the aesthetics) e.g
pattern_filllegend logical value to indicate whether or not this
function is being called to render a key legend or the in-place geom
fill.gridpattern itself will mask this image so that it only
applies to the area within the grob’s boundary.polygon_df data.frame
formatThe polygon_df is a very simple data.frame format to
contain polygon values. This is used to pass the coordinates of the geom
boundary from the geom to the pattern generating function.
It contains only ‘x’ and ‘y’ columns for the coordinates, and an ‘id’ column used to signify which polygon the coordinates belong to.
The following polygon_df data.frame contains 2
polygons:
| x | y | id |
|---|---|---|
| 0 | 0 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 2 |
| 2 | 0 | 2 |
| 2 | 1 | 2 |
| 0 | 1 | 2 |
{gridpattern} pattern nameUse gridpattern::register_pattern() to associate a
pattern function with a pattern name. Use kind = "geometry"
(the default) for geometry-based patterns and
kind = "array" for array-based patterns.
gridpattern::register_pattern("your_pattern_name", your_geometry_pattern_function)
gridpattern::register_pattern("your_pattern_name", your_array_pattern_function, kind = "array").onLoad() function (ideally wrapped in
rlang::on_package_load()). See the “Registering patterns
from a package” section of {gridpattern}’s Developing
Patterns vignette
(vignette("developing-patterns", package = "gridpattern"))
for a template."owner::name" e.g. "pkg::name",
"gridpattern::stripe", or "R_GlobalEnv::name"
(for patterns registered by the user)."name" resolves to the pattern the user has
preferred with gridpattern::prefer_pattern() (if any) and
otherwise to the pattern of the only owner (the user,
{gridpattern}, or a package) with that name. If more than
one owner has a pattern with that name and no preference has been
declared there is a conflict and using the bare name throws an error.
Resolve it with either a qualified name or a preference e.g.
prefer_pattern("R_GlobalEnv::stripe") lets
pattern = "stripe" use a user’s custom “stripe” pattern
instead of the builtin one.
gridpattern::conflicting_patterns() lists the conflicting
pattern names.register_pattern()’s defaults argument may
set pattern-specific default parameter values
e.g. defaults = list(pattern_type = "running", pattern_spacing = 0.1).
Most {ggpattern} pattern aesthetics are unset by default
(internally NaN, see the table above) so these
pattern-specific defaults apply under {ggpattern} too (as
do defaults for any parameter {ggpattern} doesn’t know
about) and a pattern that declares no default for one of these gets
{gridpattern}’s generic default.gridpattern::registered_patterns() lists all the
available patterns, gridpattern::has_pattern() checks
whether a pattern name resolves to a pattern,
gridpattern::unregister_pattern() removes a registration,
and gridpattern::local_pattern() registers a pattern only
until the current function (or test) exits.ggpattern_geometry_funcs /
ggpattern_array_funcs global options (named lists of
pattern functions) are still supported but we no longer recommend them
since setting them may clobber patterns registered by other
packages.{ggplot2} aesthetics)All geometry-based pattern creation functions must:
function(params, boundary_df, aspect_ratio, legend)
params - parameters from the geom (the aesthetics) e.g
pattern_fillboundary_df - data.frame containing polygon information
i.e. The polygon_df format.aspect_ratio - the best guess that
{gridpattern} / {ggpattern} is able to make as
to the aspect ratio of the viewport in which this
pattern is being rendered.legend logical value to indicate whether or not this
function is being called to render a key legend or the in-place geom
fill.boundary_df (including grid structures like a
grid::grobTree()).For this example we’ll adapt the built-in ‘polygon_tiling’ pattern to
create a new ‘tiling3’ pattern that fills the polygon tiling with three
colors: the fill aesthetic, the pattern_fill
aesthetic, and their “average” color.
tiling3_pattern <- function(params, boundary_df, aspect_ratio, legend = FALSE) {
args <- as.list(params)
args <- args[grep("^pattern_", names(args))]
# hexagonal tiling using "regular_polygon" pattern
args$pattern <- "polygon_tiling"
# three-color tiling using `fill`, `pattern_fill` and their "average"
avg_col <- gridpattern::mean_col(params$fill, params$pattern_fill)
args$pattern_fill <- c(params$fill, avg_col, args$pattern_fill)
args$x <- boundary_df$x
args$y <- boundary_df$y
args$id <- boundary_df$id
args$prefix <- ""
do.call(gridpattern::patternGrob, args)
}We register the function with
gridpattern::register_pattern(). The name
corresponds to the pattern name used with the geom - in
this case we will be using pattern = 'tiling3'.
df <- data.frame(trt = c("a", "b", "c"), outcome = c(2.3, 1.9, 3.2))
ggplot(df, aes(trt, outcome)) +
geom_col_pattern(aes(fill = trt, pattern_type = trt),
pattern = 'tiling3',
pattern_angle = 45,
pattern_spacing = 0.15) +
scale_pattern_type_manual(values = c("hexagonal", "tetrakis_square", "rhombille")) +
theme(legend.key.size = unit(1.2, 'cm'))The {gridpattern} package contains a vignette on
developing {gridpattern} / {ggpattern}
patterns with a few more examples including an example of layering
patterns on top of each other. Use vignette("developing-patterns", package="gridpattern")
to see it.
Also the {gridpattern} source contains the full source
code for 21patterns: https://github.com/trevorld/gridpattern/tree/main/R
{ggpattern} Aesthetics - Descriptions and Defaultspolygon_df data.frame format{gridpattern} pattern
name{ggplot2}
aesthetics)