7

For a kind of mathematical illustration, consider the case where several distinguishable hatchings, all black and white, and precise direction has meaning, are required.

I give a simple example of a hatching pattern in the MWE.

1 Is there any general way to simply write a macro to generate a hatching pattern of this sort but with a single numerical variable that controls direction?

To clarify all my attempts have failed to make a macro, call it MCR, such that:

pattern=MCR x ,pattern color=... where x=2 or 1 or 0 for horizontal, or 0.5 for vertical, or 0.25 for diagonal, and approximately in between.

OR

So far I have used also a macro to import a graphic at a node to use it as a pattern, by scaling it or stretching it, and often using the [clip] setting of \draw and order of commanded execution to get the desired effect. A short macro makes repeated use easy.

2 So, alternatively, is it possible to construct a hatching graphic, one north west to south east, in addition to the given one south west to north east, and then output to a file, and use inputgraphics at a node to rotate the image and use it as pattern? The solution to 1 is preferred over this, if 1 is possible.

\documentclass{standalone}\usepackage{pgfplots,tikz}\usetikzlibrary{patterns}
\def\dotshading{figuretonedots}
\def\toning #1 #2 #3 {\node at (#1) {\includegraphics[width=#3\textwidth]{\csname #2\endcsname}};}
\begin{document}\begin{tikzpicture}%
\pgfdeclarepatternformonly{ltrtoning}%
   {\pgfqpoint{-1pt}{-1pt}}{\pgfqpoint{10pt}{10pt}}%
   {\pgfqpoint{9pt}{9pt}}{
   \pgfsetlinewidth{0.32pt}
   \pgfpathmoveto{\pgfqpoint{0pt}{0pt}}
   \pgfpathlineto{\pgfqpoint{9.1pt}{9.1pt}}
   \pgfusepath{stroke}}%
   %\toning 6.78,6.78 dotshading 1.1 %removing this removes toning
   \draw[draw=none,pattern=ltrtoning,pattern color=black!64]rectangle(13.4,13.4);
\end{tikzpicture}\end{document}
5
  • 1
    Have you seen this one tex.stackexchange.com/questions/54358/… ?
    – percusse
    Commented Sep 30, 2014 at 22:53
  • My question was imprecisely worded, I think. I have clarified it. It's more of a question of how to define hatching direction with a single input, keeping all else the same. (I think I learned how to write 45 degree hatchings from P.G.'s examples in that link originally.) Commented Sep 30, 2014 at 23:24
  • 1
    It isn't possible to use an arbitrary angles for the hatch pattern, only 45 and 90 degrees. This is discussed somewhere else on this site but I cannot find the link at the moment. It is possible (although with some overhead in performance) to use a path picture and manually draw the lines. Commented Oct 1, 2014 at 6:40
  • 1
    I should qualify my previous comment by stating that it isn't possible at the moment. I just looked at the PDF specification and it is possible, it just isn't currently implemented in PGF. Commented Oct 1, 2014 at 7:00
  • PGF doesn't support it but your answer is perfect! I was thinking of how to do just this, but had no idea at all how to code the PGF internals. What I was doing before was exporting a pattern to an image, rotated, and clipping with the resulting tone the path I wanted to fill with a rotated "pattern" tone, and it got tedious. Commented Oct 1, 2014 at 11:34

1 Answer 1

17
+50

Currently, PGF does not support adding a transformation matrix to pattern, so a general rotation isn't possible.

One could (with some computational overhead) use path pictures.

However, the following shows one (admittedly very bad) way of achieving rotated patterns by hacking the system layer to include a transformation matrix for patterns and exploiting the fact that "mutable" patterns (which aren't actually mutable) are created just before they are applied depending on value of the variables supplied in the pattern definition.

\documentclass[tikz, border=5]{standalone}
\usetikzlibrary{patterns}
\makeatletter

\def\pgfsys@patternmatrix{1.0 0.0 0.0 1.0 0.0 0.0}

\def\pgfsys@declarepattern#1#2#3#4#5#6#7#8#9{%
  % Start building the pattern dictionary:
  \pgf@xa=#2\relax%
  \pgf@ya=#3\relax% 
  \pgf@xb=#4\relax%
  \pgf@yb=#5\relax%
  \pgf@xc=#6\relax%
  \pgf@yc=#7\relax%
  \pgf@sys@bp@correct\pgf@xa%
  \pgf@sys@bp@correct\pgf@ya%
  \pgf@sys@bp@correct\pgf@xb%
  \pgf@sys@bp@correct\pgf@yb%
  \pgf@sys@bp@correct\pgf@xc%
  \pgf@sys@bp@correct\pgf@yc%
  % Now create the pattern object:
  \immediate\pdfobj stream
  attr
  {
    /Type /Pattern
    /PatternType 1
    /PaintType \ifnum#9=0 2 \else 1 \fi
    /TilingType 1
    /BBox [\pgf@sys@tonumber\pgf@xa\space\pgf@sys@tonumber\pgf@ya\space\pgf@sys@tonumber\pgf@xb\space\pgf@sys@tonumber\pgf@yb]
    /XStep \pgf@sys@tonumber\pgf@xc\space
    /YStep \pgf@sys@tonumber\pgf@yc\space
    /Matrix [\pgfsys@patternmatrix]
    /Resources << >> %<<
  }
  {#8}% 
  \pgfutil@addpdfresource@patterns{/pgfpat#1\space \the\pdflastobj\space 0 R}%
}

\def\pgf@sp{ }%
\def\pgftransformextractmatrix#1#2{%
\begingroup%
\pgftransformreset%
#2%
\xdef\pgf@tmp{\pgf@pt@aa\pgf@sp\pgf@pt@ab\pgf@sp\pgf@pt@ba\pgf@sp\pgf@pt@bb\pgf@sp\pgf@sys@tonumber\pgf@pt@x\pgf@sp\pgf@sys@tonumber\pgf@pt@y}%
\endgroup%
\let#1=\pgf@tmp}



\pgfdeclarepatternformonly[\patternangle]{rotated hatch}%
  {\pgfqpoint{-.1pt}{-1.pt}}{\pgfqpoint{5pt}{5pt}}
  {\pgfqpoint{5pt}{5pt}}
  {
    \pgfsetlinewidth{.5pt}
    \pgfpathmoveto{\pgfqpoint{-.1pt}{-.1pt}}
    \pgfpathlineto{\pgfqpoint{5pt}{5pt}}
     \pgfpathmoveto{\pgfqpoint{5pt}{-.1pt}}
    \pgfpathlineto{\pgfqpoint{-.1pt}{5pt}}
    \pgfusepath{stroke}
  }

\tikzset{%
  pattern angle/.code={%
    \pgfmathparse{#1}\let\patternangle=\pgfmathresult
    \pgftransformextractmatrix\pgfsys@patternmatrix{\pgftransformrotate{\patternangle}}%
  },
  pattern angle=0
}
\begin{document}

\begin{tikzpicture}[x=3cm,y=3cm];

\foreach \i [count=\j from 0] in {0,7,...,105}{
   \draw [pattern=rotated hatch, pattern angle=\i] 
     ({mod(\j,4)}, {floor(\j/4)}) rectangle ++(0.75,0.75)
     node [above] {$\i^\circ$};
}
\end{tikzpicture}

\end{document}

enter image description here

Or...

Here is an incomplete implementation of mutable patterns with transformations. It looks a bit different as I tried to translate the way the new arrows.meta library creates arrows to a new way of defining patterns:

\pgfdeclarepattern{name=hatch,
  type=uncolored,
  parameters={\hatchsize, \hatchangle, \hatchlinewidth},
  bottom left={x=-.1pt, y=-.1pt}, 
  top right={x=\hatchsize+.1pt, y=\hatchsize+.1pt},
  tile size={width=\hatchsize, height=\hatchsize},
  transformation={rotate=\hatchangle},
  code={
    \pgfsetlinewidth{\hatchlinewidth}
    \pgfpathmoveto{\pgfpoint{-.1pt}{-.1pt}}
    \pgfpathlineto{\pgfpoint{\hatchsize+.1pt}{\hatchsize+.1pt}}
    \pgfpathmoveto{\pgfpoint{-.1pt}{\hatchsize+.1pt}}
    \pgfpathlineto{\pgfpoint{\hatchsize+.1pt}{-.1pt}}
    \pgfusepath{stroke}
  }}

The parameters can be macros or dimensions and so on, but if dimensions or counts are used they must be prefixed with \the. It is possible (I haven't tried it) that keys could be included using \pgfkeysvalueof{mykey}.

It hacks both the system layer and the basic layer so you have been warned...

\documentclass[tikz, border=5]{standalone}

\usetikzlibrary{patterns}

\makeatletter

% Alternate system layer pattern definition.
% Takes 15(!) arguments
\def\pgfsys@declarepattern@alt#1#2#3#4#5#6#7{%
  % Start building the pattern dictionary:
  \pgf@xa=#2\relax%
  \pgf@ya=#3\relax% 
  \pgf@xb=#4\relax%
  \pgf@yb=#5\relax%
  \pgf@xc=#6\relax%
  \pgf@yc=#7\relax%
  \pgf@sys@bp@correct\pgf@xa%
  \pgf@sys@bp@correct\pgf@ya%
  \pgf@sys@bp@correct\pgf@xb%
  \pgf@sys@bp@correct\pgf@yb%
  \pgf@sys@bp@correct\pgf@xc%
  \pgf@sys@bp@correct\pgf@yc%
  \pgfsys@@declarepattern@alt{#1}}

\def\pgfsys@@declarepattern@alt#1#2#3#4#5#6#7#8#9{%
   \pgfutil@tempdima=#6\relax%
   \pgfutil@tempdimb=#7\relax%
   \pgf@sys@bp@correct\pgf@xa%
   \pgf@sys@bp@correct\pgf@ya%
   % Now create the pattern object:
   \immediate\pdfobj stream
   attr
   {
     /Type /Pattern
     /PatternType 1
     /PaintType \ifnum#9=0 2 \else 1 \fi
     /TilingType 1
     /BBox [\pgf@sys@tonumber\pgf@xa\space\pgf@sys@tonumber\pgf@ya\space\pgf@sys@tonumber\pgf@xb\space\pgf@sys@tonumber\pgf@yb]
     /XStep \pgf@sys@tonumber\pgf@xc\space
     /YStep \pgf@sys@tonumber\pgf@yc\space
     /Matrix [#2\space#3\space#4\space#5\space\pgf@sys@tonumber\pgfutil@tempdima\space\pgf@sys@tonumber\pgfutil@tempdimb]
     /Resources << >> %<<
   }
   {#8}% 
   \pgfutil@addpdfresource@patterns{/pgfpat#1\space \the\pdflastobj\space 0 R}%
 }

% Pattern keys
\pgfkeys{/pgf/patterns/.cd,
  name/.code=\edef\pgf@pat@name{#1},
  type/.is choice,
  type/uncolored/.code=\def\pgf@pat@type{0},
  type/colored/.code=\def\pgf@pat@type{1},
  parameters/.store in=\pgf@pat@parameters,
  bottom left/.store in=\pgf@pat@bottomleft,
  top right/.store in=\pgf@pat@topright,
  tile size/.store in=\pgf@pat@tilesize,
  transformation/.store in=\pgf@pat@transformation,
  code/.store in=\pgf@pat@code,
  name=,
  type=uncolored,
  parameters=,
  bottom left=,
  top right=,
  transformation=,
  code=,
  points/.style={/pgf/patterns/points/.cd, #1},
  transformations/.style={/pgf/patterns/transformations/.cd,#1},
  /pgf/patterns/points/.cd,
    x/.store in=\pgf@pat@x,
    y/.store in=\pgf@pat@y,
    width/.store in=\pgf@pat@x,
    height/.store in=\pgf@path@y,
  /pgf/patterns/transformations/.cd,
    rotate/.code=\pgftransformrotate{#1},
    xscale/.code=\pgftransformxscale{#1},
    yscale/.code=\pgftransformyscale{#1},
    % Plus others... 
}

% Points can be specified using PGF commands
% or x and y keys
\def\pgf@pat@processpoint#1{%
  \def\pgf@marshal{\pgfutil@in@=}%
  \expandafter\pgf@marshal\expandafter{#1}%
  \ifpgfutil@in@%
    \pgfkeys{/pgf/patterns/points/.expanded=#1}%
    \pgf@process{\pgfpoint{\pgf@pat@x}{\pgf@pat@y}}%
  \else%
    \pgf@process{#1}%
  \fi%
}

% Transformations can be specified using PGF commands
% or keys (currently only rotate, xscale and yscale)
\def\pgf@pat@processtransformations#1{%
  \def\pgf@marshal{\pgfutil@in@=}%
  \expandafter\pgf@marshal\expandafter{#1}%
  \ifpgfutil@in@%
    \pgfkeys{/pgf/patterns/transformations/.expanded=#1}%
  \else%
    #1%
  \fi%
}

% New pattern definition command
%
% #1 is a list of keys.
\def\pgfdeclarepattern#1{%
  \begingroup%
    \def\pgf@pat@opts{#1}%
    \pgfkeys{/pgf/patterns/.cd,#1}%
    \pgfutil@ifundefined{pgf@pattern@name\pgf@pat@name}{%
      \ifx\pgf@pat@parameters\pgfutil@empty%
        \expandafter\global\expandafter\let\csname pgf@pattern@name@\pgf@pat@name @parameters\endcsname=\pgfutil@empty%
        \pgf@declarepattern%    
      \else%
        \expandafter\global\expandafter\let\csname pgf@pattern@name@\pgf@pat@name @parameters\endcsname=\pgf@pat@parameters
        \expandafter\global\expandafter\let\csname pgf@pattern@name@\pgf@pat@name\endcsname=\pgf@pat@opts%
      \fi%
    }{%
       \pgferror{Pattern `\pgf@pat@type' already defined}%
    }%
  \endgroup%
}


\def\pgf@declarepattern{%
   \pgfsysprotocol@getcurrentprotocol\pgf@pattern@temp%
   {%
     \pgfinterruptpath%
       \pgfpicturetrue%
       \pgf@relevantforpicturesizefalse%
       \pgftransformreset%
       \pgfsysprotocol@setcurrentprotocol\pgfutil@empty%
       \pgfsysprotocol@bufferedtrue%
       \pgfsys@beginscope%
       \pgfsetarrows{-}%
       \pgf@pat@code%
       \pgfsys@endscope%
       \pgfsysprotocol@getcurrentprotocol\pgf@pattern@code%
       \global\let\pgf@pattern@code=\pgf@pattern@code%
     \endpgfinterruptpath%
     \pgf@pat@processpoint{\pgf@pat@bottomleft}%
     \pgf@xa=\pgf@x%
     \pgf@ya=\pgf@y%
     \pgf@pat@processpoint{\pgf@pat@topright}%
     \pgf@xb=\pgf@x%
     \pgf@yb=\pgf@y%
     \pgf@pat@processpoint{\pgf@pat@tilesize}%
     \pgf@xc=\pgf@x%
     \pgf@yc=\pgf@y%
     \begingroup%
       \pgftransformreset%
       \pgf@pat@processtransformations\pgf@pat@transformation%
       \pgfgettransformentries\aa\ab\ba\bb\shiftx\shifty%
       \global\edef\pgf@pattern@matrix{{\aa}{\ab}{\ba}{\bb}{\shiftx}{\shifty}}%
     \endgroup% 
     % Now, build a name for the pattern
     \pgfutil@tempcnta=\pgf@pattern@number%
     \advance\pgfutil@tempcnta by1\relax%
     \xdef\pgf@pattern@number{\the\pgfutil@tempcnta}%
     \expandafter\xdef\csname pgf@pattern@name@\pgf@pat@name\endcsname{\the\pgfutil@tempcnta}%
     \expandafter\xdef\csname pgf@pattern@type@\pgf@pat@name\endcsname{\pgf@pat@type}%
     \xdef\pgf@marshal{\noexpand\pgfsys@declarepattern@alt%
       {\csname pgf@pattern@name@\pgf@pat@name\endcsname}
       {\the\pgf@xa}{\the\pgf@ya}{\the\pgf@xb}{\the\pgf@yb}{\the\pgf@xc}{\the\pgf@yc}\pgf@pattern@matrix{\pgf@pattern@code}{\pgf@pat@type}}%
   }%
   \pgf@marshal%
   \pgfsysprotocol@setcurrentprotocol\pgf@pattern@temp%   
 }

\def\pgfsetfillpattern#1#2{%
  \pgfutil@ifundefined{pgf@pattern@name@#1}%
  {%
    \pgferror{Undefined pattern `#1'}%
  }%
  {%
     % Patterns from library won't have pgf@pattern@name@#1@parameters
     \pgfutil@ifundefined{pgf@pattern@name@#1@parameters}{%
        \pgf@set@fillpattern{#1}{#2}%
     }{%
     \expandafter\ifx\csname pgf@pattern@name@#1@parameters\endcsname\pgfutil@empty%
       \pgf@set@fillpattern{#1}{#2}%
     \else
       \edef\pgf@pat@currentparameters{\csname pgf@pattern@name@#1@parameters\endcsname}%
       \edef\pgf@pat@mutablename{#1@\pgf@pat@currentparameters}%
       \pgfutil@ifundefined{pgf@pattern@name@\pgf@pat@mutablename}%
       {%
         \expandafter\expandafter\expandafter\pgfdeclarepattern\expandafter\expandafter\expandafter{\csname pgf@pattern@name@#1\endcsname,
           name=\pgf@pat@mutablename,parameters=}%
       }%
       {}%
       \expandafter\pgf@set@fillpattern\expandafter{\pgf@pat@mutablename}{#2}%
     \fi%
    }%
  }%
}


 \def\pgf@set@fillpattern#1#2{%
    % Pattern types are 0 (uncolored) or 1 (colored)
    \ifcase\csname pgf@pattern@type@#1\endcsname\relax%
       \pgfutil@colorlet{pgf@tempcolor}{#2}%
       \pgfutil@ifundefined{applycolormixins}{}{\applycolormixins{pgf@tempcolor}}%
       \pgfutil@extractcolorspec{pgf@tempcolor}{\pgf@tempcolor}%
       \expandafter\pgfutil@convertcolorspec\pgf@tempcolor{rgb}{\pgf@rgbcolor}%
       \expandafter\pgf@set@fill@patternuncolored\pgf@rgbcolor\relax{#1}%
    \or
     \pgfsys@setpatterncolored{\csname pgf@pattern@name@#1\endcsname}%
    \else
    \fi
 }


\def\tikzdeclarepattern#1{%
   \begingroup%
     \pgfkeys{/pgf/patterns/code/.code={\def\pgf@pat@code{%
       \let\tikz@transform=\relax\tikz@installcommands##1}}}
     \pgfdeclarepattern{#1,type=colored}%
   \endgroup%
 }
\makeatother


\pgfdeclarepattern{name=hatch,
  type=uncolored,
  parameters={\hatchsize, \hatchangle, \hatchlinewidth},
  bottom left={x=-.1pt, y=-.1pt}, % or \pgfqpoint{-.1pt}{-.1pt} will also work
  top right={x=\hatchsize+.1pt, y=\hatchsize+.1pt},
  tile size={width=\hatchsize, height=\hatchsize},
  transformation={rotate=\hatchangle},
  code={
    \pgfsetlinewidth{\hatchlinewidth}
    \pgfpathmoveto{\pgfpoint{-.1pt}{-.1pt}}
    \pgfpathlineto{\pgfpoint{\hatchsize+.1pt}{\hatchsize+.1pt}}
    \pgfpathmoveto{\pgfpoint{-.1pt}{\hatchsize+.1pt}}
    \pgfpathlineto{\pgfpoint{\hatchsize+.1pt}{-.1pt}}
    \pgfusepath{stroke}
  }}

\tikzset{%
  hatch size/.store in=\hatchsize,
  hatch angle/.store in=\hatchangle,
  hatch line width/.store in=\hatchlinewidth,
  hatch size=5pt,
  hatch angle=0pt,
  hatch line width=.5pt,
}

\begin{document}
\begin{tikzpicture}
\foreach \r in {1,...,4}
  \draw [pattern=hatch, pattern color=red] 
    (\r*3,0) rectangle ++(2,2);

\foreach \r in {1,...,4}
  \draw [pattern=hatch, pattern color=green, hatch size=2pt] 
    (\r*3,3) rectangle ++(2,2);

\foreach \r in {1,...,4}
  \draw [pattern=hatch, pattern color=blue, hatch size=10pt, hatch angle=21] 
    (\r*3,6) rectangle ++(2,2);

\foreach \r in {1,...,4}
  \draw [pattern=hatch, pattern color=orange, hatch line width=2pt]
    (\r*3,9) rectangle ++(2,2);
\end{tikzpicture}
\end{document}

enter image description here

In addition, it opens up the possibility of patterns being specified using TikZ (the code for \tikzdeclarepattern is included above):

\tikzdeclarepattern{name=flower,
    type=uncolored,
    bottom left={x=-.1pt, y=-.1pt}, 
    top right={x=10.1pt, y=10.1pt},
    tile size={width=10pt, height=10pt},
    code={
      \tikzset{x=1pt,y=1pt}
      \path [draw=green] (5,2.5) -- (5, 7.5);
      \foreach \i in {0,60,...,300}
        \path [fill=pink, shift={(5,7.5)}, rotate=-\i]
          (0,0) .. controls ++(120:4) and ++(60:4) .. (0,0);
      \path [fill=red] (5,7.5) circle [radius=1];
      \foreach \i in {-45,45}
        \path [fill=green, shift={(5,2.5)}, rotate=-\i]
          (0,0) .. controls ++(120:4) and ++(60:4) .. (0,0);
    }}

Which is then used in the usual way:

\tikz\draw [pattern=flower] circle [radius=1];

and produces:

enter image description here

7
  • This is a very nice answer. I am going to add a bounty when able. My question only question: how would I change the color if required? E.g., black |-> black!64 ? pattern color=black!64 no longer works for some reason. Commented Oct 1, 2014 at 12:02
  • 1
    @GuidoJorg The short answer appears to be "a bug". Since I wrote the "mutable" patterns stuff that makes it my fault. Can't quite work out where the problem is though currently. Commented Oct 1, 2014 at 15:51
  • I hope you can eventually figure out where the bug is regarding color selection not working. I've put up a bounty for the answer, since the solution I think is nice and compiles quickly. Will award it tomorrow :) Commented Oct 3, 2014 at 2:05
  • 1
    @GuidoJorg and Mark Wibrow, I agree, a beautiful answer. By adding the command \pgfsys@color@rgb@stroke{0.64}{0.64}{0.64} immediately before the command \pgfusepath{stroke} you can adjust the color of the hatching. Varying the rgb values gives you any chosen color, but I haven't looked into a more flexible mode of adjusting colors.
    – corporal
    Commented Oct 3, 2014 at 3:28
  • 1
    I should also have mentioned that the command \pgfsys@color@rgb@stroke{0.64}{0.64}{0.64} can be used within a tikzpicture environment to adjust the hatching color for that particular diagram.
    – corporal
    Commented Oct 3, 2014 at 6:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .