Best way to draw matchstick pictures

EDIT At the end there is a better solution for the shadows problem.

Here is an idea which combines several hacks.

  1. For the match shape, use the cylinder node shape, with yellow body and red top. A drop shadow should be nice too, but for some reason it makes the cylinder to appear translucent...

  2. You can make that node shape to be used as a straight line in a path, if you draw the node midway, sloped, and give it as minimum height the distance among the two ends of the intended path. In fact, you can give only a 80% of that distance, to create a gap at the ends (kind of shorten > and shorten <)

  3. I think that the easiest way to specify the coordinates of a match is to give the cartesian coordinates of the beginning of the match, and then the polar coordinates of the other end with respect to its beginning. That is, for example: \match{3,2}{60:3} represents a match which starts at (3,2), and points in an angle of 60 and has a length of 3. This ensures that all matches are drawn with the same length, even if they are not perpendicular or in a grid.

I combined these ideas in the following code:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{shapes.geometric,shadows,calc}

\tikzset{
  match/.style = {
    cylinder,
    shape aspect = 1.5,
    cylinder uses custom fill,
    cylinder body fill=yellow,
    cylinder end fill = red,
    inner sep = 0pt,
    minimum width = 2mm,
    draw = black!20!yellow,
    sloped, midway,
    drop shadow={
     shadow yshift=-.7mm, shadow xshift=.5mm, opacity=.4},
   }
}

\def\match#1#2{
\path let
    \p1=(#2),
    \n1={0.8*veclen(\p1)}
    in (#1) -- +(#2)
    node[match,minimum height=\n1] {\phantom{x}}; % phantom is required, without it the end of the cylinder is not drawn (?)
}

\begin{tikzpicture}
\foreach \a in {0,30,...,359} { 
  \match{0,0}{\a:3}
} 
\begin{scope}[xshift=4cm,yshift=-3cm]
  \foreach \i in {0,3,...,9} {
    \foreach \j in {0,3} {
      \match{\i,\j}{0:3}
      \match{\i,\j}{90:3}
    }
  }
  \foreach \i in {0,3,...,9} {
      \match{\i,6}{0:3}
  }
\end{scope}
\end{tikzpicture}
\end{document}

Result

Another example :-)

\begin{tikzpicture}
 \foreach \a in {0,135,270} { \match{0,3}{\a:3} }
 \foreach \a in {0,-60,-120} { \match{0,0}{\a:3} }
 \foreach \a in {180,210,270} { \match{6,3}{\a:3} }
 \foreach \a in {150,180,-60,-120} { \match{6,0}{\a:3} }
\end{tikzpicture}

Cow

And this is the result if you remove the drop shadow from match style:

Without shadows

Update: fix for the shadows

In this question I learned a trick (credit to Andrew Stacey) to make the shadow to appear properly behind the matches. This is the final code, with a new example:

\documentclass{article}
\usepackage[margin=5mm,a4paper]{geometry}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric,shadows,calc}

\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\makeatletter
\pgfkeys{%
  /tikz/on layer/.code={
    \pgfonlayer{#1}\begingroup
    \aftergroup\endpgfonlayer
    \aftergroup\endgroup
  },
  /tikz/node on layer/.code={
    \pgfonlayer{#1}\begingroup
    \expandafter\def\expandafter\tikz@node@finish\expandafter{\expandafter\endgroup\expandafter\endpgfonlayer\tikz@node@finish}%
  },
}
\makeatother

\tikzset{
  match/.style = {
    cylinder,
    shape aspect = 1.5,
    cylinder uses custom fill,
    cylinder body fill=yellow,
    cylinder end fill = red,
    inner sep = 0pt,
    minimum width = 2mm,
    draw = black!20!yellow,
    sloped, midway,
    drop shadow = {shadow yshift=-.7mm, shadow xshift=1mm, opacity=.8, on layer=back},
  }
}
\def\match#1#2{
\path let
    \p1=(#2),
    \n1={0.8*veclen(\p1)}
    in (#1) -- +(#2)
    node[match,minimum height=\n1] {\phantom{x}};
}

\begin{document}
\begin{tikzpicture}
  \foreach \a in {0,60,...,359} { \match{0,0}{\a:3}; \match{\a:3}{\a+120:3} }
  \foreach \a in {0,120,...,359} { \match{\a:3}{\a-60:3} }
  \foreach \a in {60,180,...,359} { \match{\a:3}{\a+60:3} }
\end{tikzpicture}
\end{document}

Result:

Proper shadows

Update: hand-made matchstick figures

Sorry! I cannot stop tweaking! If you introduce a little random rotation in the match definition:

\def\match#1#2{
\path let
    \p1=(#2),
    \n1={0.8*veclen(\p1)}
    in (#1) -- +(#2)
    node[rotate=-5+10*rnd, match, minimum height=\n1] {\phantom{x}};
}

You get more interesting (IMHO) results:

Hand made


Here's an alternative way using decorations. Match sticks are drawn on each input segment, so multiple matches can be drawn with one path. The decoration definition is a bit messy but unavoidably so in order to get the shadow opacity right.

EDITS 1 & 2: The decoration definition has been changed to make things a bit cleaner/customisable:

\documentclass[tikz,border=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{fadings}
\usetikzlibrary{decorations}

\pgfdeclaredecoration{match stick}{draw}{
    \state{draw}[width=\pgfdecoratedinputsegmentlength]{
        \tikzset{x=\pgfdecoratedinputsegmentlength/10, y=\pgfdecorationsegmentamplitude/2}
        \begin{scope}[transparency group, match shadow/.try]
            \path [fill=match shadow, match stick path/.try];
            \path [fill=match shadow, match head path/.try];
        \end{scope}
        \path [match stick/.try, match stick path/.try];
        \path [match head/.try, match head path/.try];
    }
}

\tikzset{
    match/.style={
        decoration={match stick, amplitude=2pt}, decorate
    },
    % The match stick is scaled so that
    % 1 x-unit = \pgfdecoratedinputsegmentlength/10
    % 1 y-unit = \pgfdecorationsegmentamplitude/2
    match stick path/.style={
        insert path={(1.375,-1) rectangle (8.375,1)}
    },
    match head path/.style={
        insert path={(8.375,0) ellipse [x radius=0.625, y radius=1.25]}
    },
    match shadow color/.code=\colorlet{match shadow}{#1},
    match shadow color=gray,
    match shadow/.style={
        transform canvas={shift=(330:1pt)},
        opacity=0.5,
    },
    match stick/.style={
        fill=yellow
    },
    match head/.style={
        fill=red
    }
}
\begin{document}
\tikz\path [match] 
    % tail
    (135:1) -- (0,0) 
    % body
    (0,0) -- (1,0) -- (2,0) -- (2,-1) --
    (1,-1) -- (0,-1) 
    (0,0) -- (0,-1)
    % back legs
    (0,-1) -- ++(250:1) (0,-1) -- ++(290:1)
    % front legs
    (2,-1) -- ++(250:1) (2,-1) -- ++(290:1)
    % nose
    (2,0)  -- ++(340:1)
    (2,-1) -- ++(45:1);

\tikz\foreach \p in {(0:0),(60:1),(60:2), (0:1),(0:2), (30:sqrt 3)}
    \path [match, shift={\p}] (0,0) -- (60:1) -- (0:1) (0,0) -- (0:1);

\end{document}

enter image description here

enter image description here


With PSTricks. Just 4 fun!

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{multido}
\def\ms(#1)#2{%
    \rput{#2}(#1){%
        \psline[linewidth=6pt,linecolor=brown](3,0)
        \psellipse*[linecolor=red](3,0)(6pt,4pt)}
        \ignorespaces
}

\SpecialCoor
\begin{document}    
\multido{\i=0+30}{12}{%
\begin{pspicture}(-4,-4)(4,4)
    \ms(1;\i){\i}
\end{pspicture}}
\end{document}

enter image description here

Tags:

Tikz Pgf