2019-06-09 Defining custom TikZ fill patterns

Some time ago I had a need to define my own filling pattern in TikZ. It turned out to be quite simple, although I had to learn a few things. While the manual (as usual) does a pretty good job at teaching that, I felt that the explanation was a bit too short in one place, and hence this blog post.

First of all, I will deal with “form-only” patterns here. I guess “inherently colored patterns” are pretty similar.

The entry point here is the \pgfdeclarepatternformonly macro. It accepts five mandatory arguments, possibly preceded by an optional one. Let’s forget about the optional parameter for a moment and talk about the mandatory ones.

The first one is easy – the name of the pattern. I’m not sure whether it’s documented anywhere (probably yes), but it seems pretty obvious that using a backslash in the name is prohibited, and using other characters with atypical catcodes (like a tilde, a caret etc.) is risky business – I tend to stay with letters, digits and spaces.

The second and third parameters are the “bottom left” and “top right” points. The define what the manual calls the “bounding box” of the pattern “tile”. This has an effect of defining the part of the Cartesian plane where it makes sense to put the drawings on, since all you draw in the last parameter will be clipped to that rectangle. The fourth parameter is the “size” of the tile, which is one point, whose coordinates are the width and height of the tile. The last parameter (as mentioned) is the actual code that makes the drawing – we’ll get to this in a minute.

Now there is a legitimate question of why we specify the tile size twice. The manual explains that, though at first I wasn’t sure if I understood the explanation: the bounding box is related to the actual size of what is visible (as mentioned above, it is used for clipping), and the tile size is related to how dense on the plane the drawing will be put.

This means that while it is possible to specify the bounding box to be smaller than the tile size, it seems there is no point in doing so. (I don’t know of any performance penalty, but I doubt one exists.) However, specifying a bounding box smaller than the tile size makes perfect sense – an identical visual effect can be achieved without it, but it might be both more difficult to code and slower to draw.

Before we look at some examples, let me mention that the “code” (the last parameter) should be low-level PGF code which actually makes the drawing. (Low-level here means “using the so-called «basic layer» of PGF, described towards the end of the TikZ manual.) I will not spend time here for that – this part is well documented (and pretty boring).

So, let us now look as some examples. Assume that we want to fill the plane with small circles. Here is the first version, where the bounding box is a square equal in size to the tile and the circles’ diameters are equal to the tile’s side.

\documentclass[tikz]{standalone}

\usetikzlibrary{patterns}
\pgfdeclarepatternformonly
{circles1}                     % name
{\pgfqpoint{-6pt}{-6pt}}      % bottom left
{\pgfqpoint{6pt}{6pt}}        % top right
{\pgfqpoint{12pt}{12pt}}      % tile size
{                             % code
\pgfsetlinewidth{2pt}
\pgfpathcircle{\pgfpointorigin}{6pt}
\pgfusepathqstroke
}

\begin{document}
\begin{tikzpicture}
\fill[pattern=circles1] (0,0) rectangle (8,2);
\end{tikzpicture}
\end{document}


Here, nothing unexpected happens. Let us now shake things a bit. What would happen if we increased the tile size? The answer is easy: we would be able to actually observe the clipping in action.

\pgfdeclarepatternformonly
{circles2}                    % name
{\pgfqpoint{-6pt}{-6pt}}      % bottom left
{\pgfqpoint{6pt}{6pt}}        % top right
{\pgfqpoint{16pt}{16pt}}      % tile size
{                             % code
\pgfsetlinewidth{2pt}
\pgfpathcircle{\pgfpointorigin}{6pt}
\pgfusepathqstroke
}


If we increased the circles’ radii instead, we would see the circles clipped even more.

\pgfdeclarepatternformonly
{circles3}                    % name
{\pgfqpoint{-6pt}{-6pt}}      % bottom left
{\pgfqpoint{6pt}{6pt}}        % top right
{\pgfqpoint{12pt}{12pt}}      % tile size
{                             % code
\pgfsetlinewidth{2pt}
\pgfpathcircle{\pgfpointorigin}{7pt}
\pgfusepathqstroke
}


To obtain properly overlapping circles, we obviously need to increase the bounding box, too.

\usetikzlibrary{patterns}
\pgfdeclarepatternformonly
{circles4}                    % name
{\pgfqpoint{-7pt}{-7pt}}      % bottom left
{\pgfqpoint{7pt}{7pt}}        % top right
{\pgfqpoint{12pt}{12pt}}      % tile size
{                             % code
\pgfsetlinewidth{2pt}
\pgfpathcircle{\pgfpointorigin}{6pt}
\pgfusepathqstroke
}


Of course, we could play with the options more, but I hope that the interplay between the bounding box and tile size is obvious now.

Notice that this is not the whole story: there is the optional argument (“variables”), which allows for using variables. This, however, is documented more extensively, so there is no need to cover it here – just consult the manual to see a nice example (in fact, very similar to what we did here).