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).paramsis a list of (prefixed) pattern parameters e.g.params$pattern_fill,boundary_dfis a data frame withx,y, andidcolumns describing the boundary polygon(s) in “npc” coordinates,aspect_ratiois the (best guess) aspect ratio of the viewport, andlegendis a logical value of whether the pattern is being drawn in a legend key (e.g. by{ggpattern}; see thelegendargument ofpatternGrob()).- 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.widthandheightare the dimensions (in pixels) of the boundary's bounding box andparamsandlegendare 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 bepattern_prefixed unless listed inunprefixed. Values may be constants or functions of the formfunction(params, gp)whereparamsis the list of pattern parameters filled in so far andgpis the grob'sgrid::gpar()object e.g.list(pattern_amplitude = function(params, gp) 0.5 * params$pattern_spacing)orlist(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 anypattern_aesthetic the user doesn't map or set ({ggpattern}passes unset aesthetics asNaN).{ggpattern}1.3.1 and earlier supply their own defaults for mostpattern_aesthetics so with them these mainly apply withgrid.pattern()/patternGrob(). Unprefixed{ggplot2}aesthetics (seeunprefixed) such asfillmay 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 thatdefaultsmay set them. The usual case is a plain{ggplot2}aesthetic that{ggpattern}passes to pattern functions alongside thepattern_aesthetics e.g.defaults = list(fill = "black"), unprefixed = "fill"lets a pattern that readsparams$fillunder{ggpattern}also work withgrid.pattern(). Since such patterns are rare any other unprefixed name (e.g.fillorspacinginstead ofpattern_fillorpattern_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
TRUEalso prefer the registered pattern (seeprefer_pattern()) until.local_envirexits.- .local_envir
The environment whose exit should undo the registration (or preferences).
- pattern
The qualified names
"owner::name"of the preferred patterns (seeprefer_pattern()).- must_exist
What to do if a preferred pattern isn't (yet) registered. If
TRUEthrow an error, ifNAthrow a warning, and ifFALSEdo 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. IfFALSEthe existence of the patterns isn't checked at all (in particular their packages aren't loaded), which is useful in an.Rprofileor to prefer a user pattern before it is registered. Otherwise the packages of the preferred patterns are loaded (if installed).must_existlikewise controls what happens if the user has more than one pattern with that name (only possible via the legacyggpattern_geometry_funcs/ggpattern_array_funcsoptions, seeregister_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()
}