Skip to contents

fill_scribble() builds a grid::pattern() fill value from several randomly wandering lines, each a random finite sum of sine harmonics (see the internal scribble_lines() helper) rather than a smooth noise field or a scattered discrete motif – giving a loose, hand-drawn scribble texture built from genuinely continuous strokes.

Usage

fill_scribble(
  color = "black",
  angle = 0,
  n_lines = 5L,
  n_harmonics = 3L,
  amplitude = 0.35,
  resolution = 200L,
  linewidth = 1,
  spacing = 0.25,
  aspect = NULL,
  seed = 1L,
  extend = "repeat"
)

Arguments

color

One or more line colours. A vector shorter than n_lines is recycled (in order, not randomly) across the wandering lines – a single colour (the default) colours every line the same, matching the original behaviour. Default "black".

angle

Line angle in degrees, measured counterclockwise from horizontal (matching fill_hatch()'s own angle convention). 0/90/180/270 (the old direction = "horizontal"/"vertical") tile exactly seamlessly; any other angle rotates the tile content as an approximation only – see "Known limitation" below. Default 0.

n_lines

Number of wandering lines per tile. Must be a positive integer. Default 5L.

n_harmonics

Number of sine harmonics summed per line. Must be a positive integer. Default 3L.

amplitude

Maximum total wiggle amplitude, as a "npc" fraction of the tile, split across n_harmonics (so more harmonics each contribute proportionally less). Must be a non-negative number. Default 0.35.

resolution

Number of points sampled along each line. Must be a positive integer of at least 2L. Default 200L.

linewidth

Line width. Must be a positive number. Default 1.

spacing

Baseline tile size, as a fraction of the target's bounding box. Must be a positive number. Default 0.25.

aspect

Width-to-height ratio of the target polygon's bounding box. Must be a positive number, or NULL (the default) to resolve it automatically from the real target's own bounding-box aspect ratio at draw() time – see the fill class. Passing a fixed number instead computes the pattern once, immediately, against that value only.

seed

Integer seed for the random harmonics. Default 1L.

extend

Passed to grid::pattern(). Default "repeat".

Value

A pattern object as returned by grid::pattern(), suitable for use as the fill argument to grid::gpar().

Details

A wandering open line poses a tiling problem none of the other fill_*() helpers have: fill_hatch()'s diagonal and fill_noise()'s raster both tile by construction (a straight corner-to-corner line, or an already-periodic field), and fill_stipple()/fill_scatter()/ fill_halftone() sidestep the problem entirely by keeping their scattered content margined well clear of the tile edge. A wandering line that's meant to look continuous can't stay clear of the edge – it has to run all the way to it, and pick up again at exactly the right place, in both position and slope, on the opposite edge, or the seam shows as a visible kink. fill_scribble() gets this for free by building each line as a random sum of sine harmonics at integer frequencies only: over one full period, such a sum always returns exactly to its starting value and slope (to floating-point precision), so consecutive tile copies join with no visible seam – confirmed visually with extend = "repeat" at small spacing, including repeated copies (unlike the polygon-in-a-genuinely-repeated-tile issue documented at fill_scatter(), open-line content showed no clipping or distortion in testing).

Known limitation – arbitrary angle is only approximate

Every other angled helper (fill_hatch()/fill_crosshatch()/ fill_stripe()) achieves an arbitrary angle by reshaping the tile itself (via hatch_tile_dims()) around content that's a plain corner-to-corner diagonal. That trick doesn't generalize here: reshaping the tile around a wandering line just anisotropically stretches its wiggle rather than rotating it, since the line's content isn't a bare diagonal the tile shape can reinterpret. A genuinely rotated wandering line would need the tile built as a rotated/sheared parallelogram with edge-matching worked out for a curve rather than a segment – no such technique exists in this package. At angle a multiple of 90 degrees, fill_scribble() instead swaps/reflects the along/across axes directly (no trigonometry involved), so tiling stays exactly seamless – this is what angle = 0/90 (the old direction = "horizontal"/"vertical") already relied on, now extended to 180/270 for free. Any other angle instead rotates the whole tile's content about its own centre, which is only an approximation: the underlying wandering line is periodic along its own local axis, not around an arbitrary rotated one, so a non-right-angle rotation can leave a visible seam where adjacent tile copies meet, and may leave small gaps near the tile corners that no line's wiggle reaches. Revisit if a real sketch needs a genuinely seamless arbitrary angle.

Examples

draw(shape_circle(fill = fill_scribble(n_lines = 4L, seed = 6602L)))


# angle = 90 runs the wandering lines top-to-bottom instead, tiling
# exactly as seamlessly as angle = 0
draw(shape_circle(
  fill = fill_scribble(n_lines = 4L, angle = 90, seed = 6602L)
))


# any other angle rotates the tile content -- an approximation only,
# so a seam can be visible at the tile edges
draw(shape_circle(
  fill = fill_scribble(n_lines = 4L, angle = 30, seed = 6602L)
))


# more harmonics and higher amplitude give a more agitated scribble
draw(shape_circle(
  fill = fill_scribble(
    n_lines = 6L,
    n_harmonics = 6L,
    amplitude = 0.6,
    seed = 6602L
  )
))


# a colour vector is recycled across the wandering lines
draw(shape_circle(
  fill = fill_scribble(
    color = c("steelblue", "tomato"),
    n_lines = 4L,
    seed = 6602L
  )
))