Skip to contents

local_pattern() registers a pattern (see register_pattern()) until the current function (or test, see .local_envir) exits and then restores the previous registration (if any). local_prefer_pattern() sets pattern preferences (see prefer_pattern()) until the current function (or test) exits and then restores the previous preferences (if any) for those bare pattern names. Both require the suggested {withr} package.

Usage

local_pattern(
  name,
  fn,
  kind = c("geometry", "array"),
  defaults = list(),
  unprefixed = character(),
  env = caller_env(),
  prefer = FALSE,
  .local_envir = parent.frame()
)

local_prefer_pattern(pattern, must_exist = NA, .local_envir = parent.frame())

Arguments

name

Pattern name. Must not contain ":".

fn

Pattern function. Its required signature depends on kind:

geometry

function(params, boundary_df, aspect_ratio, legend) returning a grid grob that lies within the boundary (e.g. clipped to it). params is a list of (prefixed) pattern parameters e.g. params$pattern_fill, boundary_df is a data frame with x, y, and id columns describing the boundary polygon(s) in “npc” coordinates, aspect_ratio is the (best guess) aspect ratio of the viewport, and legend is a logical value of whether the pattern is being drawn in a legend key (e.g. by {ggpattern}; see the legend argument of patternGrob()).

array

function(width, height, params, legend) returning a 3D array of RGBA values (all values in the range [0, 1]) which {gridpattern} will mask to the boundary. width and height are the dimensions (in pixels) of the boundary's bounding box and params and legend are as for geometry patterns.

See the “Developing Patterns” vignette vignette("developing-patterns", package = "gridpattern") for more details and examples.

kind

Either "geometry" or "array".

defaults

A named list of pattern-specific default parameter values e.g. list(pattern_type = "running", pattern_spacing = 0.1). Names are used as-is and must be pattern_ prefixed unless listed in unprefixed. Values may be constants or functions of the form function(params, gp) where params is the list of pattern parameters filled in so far and gp is the grob's grid::gpar() object e.g. list(pattern_amplitude = function(params, gp) 0.5 * params$pattern_spacing) or list(pattern_size = function(params, gp) gp$fontsize %||% 12). These replace the generic defaults for parameters that weren't supplied. The remaining generic defaults are filled in first and then these in the order given so a function default may use any generic parameter and any earlier pattern-specific default. With {ggpattern} (>= 1.4.0) these also apply to any pattern_ aesthetic the user doesn't map or set ({ggpattern} passes unset aesthetics as NaN). {ggpattern} 1.3.1 and earlier supply their own defaults for most pattern_ aesthetics so with them these mainly apply with grid.pattern() / patternGrob(). Unprefixed {ggplot2} aesthetics (see unprefixed) such as fill may be set by the geom's own defaults so their defaults may not apply with {ggpattern}.

unprefixed

A character vector of parameter names without the pattern_ prefix that the pattern reads so that defaults may set them. The usual case is a plain {ggplot2} aesthetic that {ggpattern} passes to pattern functions alongside the pattern_ aesthetics e.g. defaults = list(fill = "black"), unprefixed = "fill" lets a pattern that reads params$fill under {ggpattern} also work with grid.pattern(). Since such patterns are rare any other unprefixed name (e.g. fill or spacing instead of pattern_fill or pattern_spacing) is an error of class "gridpattern_error_unprefixed_defaults".

env

The environment used to determine the “owner” of the pattern. If writing a function that registers patterns on behalf of its caller add an env = rlang::caller_env() argument to that function and pass it on.

prefer

If TRUE also prefer the registered pattern (see prefer_pattern()) until .local_envir exits.

.local_envir

The environment whose exit should undo the registration (or preferences).

pattern

The qualified names "owner::name" of the preferred patterns (see prefer_pattern()).

must_exist

What to do if a preferred pattern isn't (yet) registered. If TRUE throw an error, if NA throw a warning, and if FALSE do nothing. In all cases the preference is set (unless there is an error) and takes effect once the pattern is registered e.g. once its package is loaded. If FALSE the existence of the patterns isn't checked at all (in particular their packages aren't loaded), which is useful in an .Rprofile or to prefer a user pattern before it is registered. Otherwise the packages of the preferred patterns are loaded (if installed). must_exist likewise controls what happens if the user has more than one pattern with that name (only possible via the legacy ggpattern_geometry_funcs / ggpattern_array_funcs options, see register_pattern()) since a preference can't choose between them (an error is thrown once the pattern is used).

Value

local_pattern() invisibly returns the previous registration (a list) or NULL if there wasn't one. local_prefer_pattern() invisibly returns a character vector of the qualified names of the previous preferences (character(0) if there weren't any).

Details

Like register_pattern() the owner of the pattern registered by local_pattern() is determined by env. In particular, since {testthat} evaluates a package's tests in an environment that inherits from the package namespace, local_pattern("name", fn) in a package's tests registers "pkg::name" (use env = globalenv() to register "R_GlobalEnv::name" instead). In package tests prefer referring to the pattern by its qualified name "pkg::name" (which doesn't depend on preferences nor on which other packages are loaded) and only use prefer = TRUE when bare names must resolve to your pattern (e.g. to test code that uses bare names or to deliberately shadow a builtin pattern).

See also

register_pattern() to (permanently) register patterns and prefer_pattern() to (permanently) set preferences.

Examples

create_pattern_polygon <- function(params, boundary_df, aspect_ratio, legend = FALSE) {
  grid::polygonGrob(boundary_df$x, boundary_df$y, boundary_df$id,
                    gp = grid::gpar(fill = params$pattern_fill))
}
x_hex <- 0.5 + 0.5 * cos(seq(2 * pi / 4, by = 2 * pi / 6, length.out = 6))
y_hex <- 0.5 + 0.5 * sin(seq(2 * pi / 4, by = 2 * pi / 6, length.out = 6))
draw_polygon <- function() {
  local_pattern("polygon", create_pattern_polygon)
  grid::grid.newpage()
  grid.pattern("polygon", x_hex, y_hex, fill = "red")
}
# skip if there is already a "polygon" pattern (which could conflict with ours)
if (requireNamespace("withr", quietly = TRUE) && !has_registered_pattern("polygon")) {
  draw_polygon()
}

print(has_pattern("polygon"))
#> [1] FALSE

# temporarily prefer the user's "fill" pattern over the builtin "fill" pattern
draw_fill <- function() {
  local_pattern("fill", create_pattern_polygon, env = globalenv(), prefer = TRUE)
  grid::grid.newpage()
  grid.pattern("fill", x_hex, y_hex, fill = "blue")
}
# skip if the user already has a "fill" pattern
# (one set by the legacy `ggpattern_geometry_funcs` option would conflict with ours)
if (
  requireNamespace("withr", quietly = TRUE) &&
    !has_registered_pattern("R_GlobalEnv::fill")
) {
  draw_fill()
}