tikz: Jigsaw Puzzle Boundary Shape

Based on a single side, one can easily draw jigsaw pieces. In the following example, the macro \piece can be used to draw a piece of the desired shape. For all four sides it can be controlled if the nose should face outward (-1) or inward (1) or a straight line (0).

\documentclass{standalone}

\usepackage{tikz}

\newcommand{\side}[1]{
(0.0,#1*0.00) .. controls (0.0,#1*0.00) and (0.4,#1*-0.04) .. 
(0.4,#1*0.04) .. controls (0.4,#1*0.11) and (0.2,#1*0.26) .. 
(0.5,#1*0.26) .. controls (0.8,#1*0.26) and (0.6,#1*0.11) .. 
(0.6,#1*0.04) .. controls (0.6,#1*-0.04) and (1.0,#1*0.00) .. 
(1.0,#1*0.00)
}

\newcommand{\piece}[4]{
    \draw 
    \side{#1}
    [rotate around={90:(0.5,0.5)}] \side{#2} 
    [rotate around={180:(0.5,0.5)}] \side{#3} 
    [rotate around={270:(0.5,0.5)}] \side{#4};
}

\begin{document}

\begin{tikzpicture}[scale=3]
\piece{1}{1}{1}{1}
\end{tikzpicture}

\begin{tikzpicture}[scale=3]
\piece{1}{-1}{1}{1}
\end{tikzpicture}


\begin{tikzpicture}[scale=3]
\piece{1}{-1}{-1}{1}
\end{tikzpicture}


\begin{tikzpicture}[scale=3]
\piece{1}{-1}{-1}{-1}
\end{tikzpicture}


\begin{tikzpicture}[scale=3]
\piece{-1}{-1}{-1}{-1}
\end{tikzpicture}


\begin{tikzpicture}[scale=3]
\piece{-1}{1}{-1}{1}
\end{tikzpicture}

\end{document}

enter image description here

These pieces can also be used to build a puzzle:

\documentclass{standalone}

\usepackage{tikz}

\newcommand{\side}[1]{
(0.5,0.5) -- 
(0.0,#1*0.00) .. controls (0.0,#1*0.00) and (0.4,#1*-0.04) .. 
(0.4,#1*0.04) .. controls (0.4,#1*0.11) and (0.2,#1*0.26) .. 
(0.5,#1*0.26) .. controls (0.8,#1*0.26) and (0.6,#1*0.11) .. 
(0.6,#1*0.04) .. controls (0.6,#1*-0.04) and (1.0,#1*0.00) .. 
(1.0,#1*0.00)
}

\newcommand{\piece}[5][white]{
    \fill[#1] 
    \side{#2}
    [rotate around={90:(0.5,0.5)}] \side{#3} 
    [rotate around={180:(0.5,0.5)}] \side{#4} 
    [rotate around={270:(0.5,0.5)}] \side{#5} 
    -- cycle;
}

\begin{document}

\begin{tikzpicture}

\begin{scope}
    \piece[red]{1}{1}{0}{0}
\end{scope}
\begin{scope}[xshift=1cm]
    \piece[blue]{1}{-1}{-1}{0}
\end{scope}
\begin{scope}[xshift=2cm]
    \piece[green]{1}{0}{1}{0}
\end{scope}

\begin{scope}[yshift=-1cm]
    \piece[green]{1}{-1}{0}{-1}
\end{scope}
\begin{scope}[xshift=1cm,yshift=-1cm]
    \piece[red]{1}{-1}{1}{-1}
\end{scope}
\begin{scope}[xshift=2cm,yshift=-1cm]
    \piece[blue]{-1}{0}{1}{-1}
\end{scope}

\begin{scope}[yshift=-2cm]
    \piece[blue]{0}{-1}{0}{-1}
\end{scope}
\begin{scope}[xshift=1cm,yshift=-2cm]
    \piece[green]{0}{-1}{1}{-1}
\end{scope}
\begin{scope}[xshift=2cm,yshift=-2cm]
    \piece[red]{0}{0}{1}{1}
\end{scope}

\end{tikzpicture}

\end{document}

enter image description here

Or to produce a random puzzle:

\documentclass{standalone}

\usepackage{tikz}
\pgfmathparse{int(random(1,120))}

\newcommand{\side}[1]{
(0.0,#1*0.00) .. controls (0.0,#1*0.00) and (0.4,#1*-0.04) .. 
(0.4,#1*0.04) .. controls (0.4,#1*0.11) and (0.2,#1*0.26) .. 
(0.5,#1*0.26) .. controls (0.8,#1*0.26) and (0.6,#1*0.11) .. 
(0.6,#1*0.04) .. controls (0.6,#1*-0.04) and (1.0,#1*0.00) .. 
(1.0,#1*0.00)
}

\newcommand{\piece}[2]{
    \draw[ultra thick] \side{#1} [rotate around={90:(0.5,0.5)}] \side{#2};
}

\pgfmathdeclarerandomlist{inout}{{-1}{1}}

\begin{document}

\begin{tikzpicture}[scale=5]

    \def\xmax{10}
    \def\ymax{10}


    \foreach \x in {0,...,\xmax}{
        \foreach \y in {0,...,\ymax}{

            \ifnum\y=0
                \def\bottom{0}
            \else
                \pgfmathrandomitem{\bottom}{inout}%
            \fi

            \ifnum\x=\xmax
                \def\right{0}
            \else
                \pgfmathrandomitem{\right}{inout}%
            \fi

            \begin{scope}[xshift=\x cm, yshift=\y cm]
                \piece{\bottom}{\right}
            \end{scope}
        }
    }

    \draw (0,0) -- (0,\ymax+1) -- (\xmax+1,\ymax+1);

\end{tikzpicture}

\end{document}

enter image description here

With background image

\documentclass{standalone}

\usepackage{tikz}
\pgfmathparse{int(random(1,120))}

\newcommand{\side}[1]{
(0.0,#1*0.00) .. controls (0.0,#1*0.00) and (0.4,#1*-0.04) .. 
(0.4,#1*0.04) .. controls (0.4,#1*0.11) and (0.2,#1*0.26) .. 
(0.5,#1*0.26) .. controls (0.8,#1*0.26) and (0.6,#1*0.11) .. 
(0.6,#1*0.04) .. controls (0.6,#1*-0.04) and (1.0,#1*0.00) .. 
(1.0,#1*0.00)
}

\newcommand{\piece}[2]{
    \draw[thick] \side{#1} [rotate around={90:(0.5,0.5)}] \side{#2};
}

\pgfmathdeclarerandomlist{inout}{{-1}{1}}

\begin{document}

\begin{tikzpicture}

    \node at (5.5,4) {\includegraphics[width=11cm,height=8cm]{example-image-duck}};

    \def\xmax{10}
    \def\ymax{7}


    \foreach \x in {0,...,\xmax}{
        \foreach \y in {0,...,\ymax}{

            \ifnum\y=0
                \def\bottom{0}
            \else
                \pgfmathrandomitem{\bottom}{inout}%
            \fi

            \ifnum\x=\xmax
                \def\right{0}
            \else
                \pgfmathrandomitem{\right}{inout}%
            \fi

            \begin{scope}[xshift=\x cm, yshift=\y cm]
                \piece{\bottom}{\right}
            \end{scope}
        }
    }

    \draw (0,0) -- (0,\ymax+1) -- (\xmax+1,\ymax+1);

\end{tikzpicture}

\end{document}

enter image description here


And a version with pics. That may make it a bit easier to build a full puzzle.

\documentclass[tikz,border=3.14mm]{standalone}
\begin{document}
\tikzset{pics/.cd,
  jigsaw/.style={
    code={
\fill[#1] (-2,-0.35) to[out=90,in=135] (-1.5,-0.45) arc(-135:135:0.6 and
{0.45*sqrt(2)}) to[out=-135,in=-90] (-2,0.35) |- (-0.35,2)
to[out=0,in=-45] (-0.45,2.5) arc(225:-45:{0.45*sqrt(2)} and 0.6)
to[out=-135,in=180] (0.35,2) -| (2,0.35) 
to[out=-90,in=225] (2.5,0.45) arc(135:-135:0.6 and {0.45*sqrt(2)})
to[out=135,in=90] (2,-0.35) |- (0.35,-2)
to[out=180,in=-135] (0.45,-1.5) arc(-45:225:{0.45*sqrt(2)} and 0.6) 
to[out=-45,in=0] (-0.35,-2) -| cycle;
}}}
\begin{tikzpicture}
\draw (-2,-2) pic{jigsaw=blue} (2,-2) pic{jigsaw=red}
(-2,2) pic[rotate=90]{jigsaw=purple} (2,2) pic[rotate=90]{jigsaw=green!60!black};
\end{tikzpicture}
\end{document}

enter image description here

UPDATE: With multipurpose jigsaw pics: the syntax is

pic{multipurpose jigsaw=fill <color> and <left>/<top>/<right>/<bottom>}

where <color> determines the fill color and the other arguments specify the lug: -1 means that lug goes in, 1 that the lug goes out and 0 means no lug. (Also tried to improve on the colors. ;-)

\documentclass[x11names, svgnames, dvipsnames,tikz,border=3.14mm]{standalone}
\begin{document}
\tikzset{pics/.cd,
  jigsaw/.style={
    code={
\fill[#1] (-2,-0.35) to[out=90,in=135] (-1.5,-0.45) arc(-135:135:0.6 and
{0.45*sqrt(2)}) to[out=-135,in=-90] (-2,0.35) |- (-0.35,2)
to[out=0,in=-45] (-0.45,2.5) arc(225:-45:{0.45*sqrt(2)} and 0.6)
to[out=-135,in=180] (0.35,2) -| (2,0.35) 
to[out=-90,in=225] (2.5,0.45) arc(135:-135:0.6 and {0.45*sqrt(2)})
to[out=135,in=90] (2,-0.35) |- (0.35,-2)
to[out=180,in=-135] (0.45,-1.5) arc(-45:225:{0.45*sqrt(2)} and 0.6) 
to[out=-45,in=0] (-0.35,-2) -| cycle;
}},
multipurpose jigsaw/.style args={fill #1 and #2/#3/#4/#5}{
    code={%
\fill[#1] (-2,-0.35) to[out=90,in={90+#2*45}] ({-2+0.5*#2},-0.45) 
arc({-135-(#2-1)*45}:{(#2-1)*180+135+(#2-1)*45}:0.6 and
{0.45*sqrt(2)}) to[out=-90-#2*45,in=-90] (-2,0.35) |- (-0.35,2)
to[out=0,in={0+#3*45}] (-0.45,2-0.5*#3) arc(180-#3*45:{(#3+1)*180+#3*45}:{0.45*sqrt(2)} and 0.6)
to[out=-180-#3*45,in=180] (0.35,2) -| (2,0.35) 
to[out=-90,in=270+#4*45] (2-#4*0.5,0.45) 
arc(90-#4*45:{(#4+1)*180-90+#4*45}:0.6 and {0.45*sqrt(2)})
to[out=90-#4*45,in=90] (2,-0.35) |- (0.35,-2)
to[out=180,in=-180+#5*45] (0.45,-2+#5*0.5) arc(-#5*45:{(#5-1)*180+180+#5*45}:{0.45*sqrt(2)} and 0.6) 
to[out=-#5*45,in=0] (-0.35,-2) -| cycle;
}}}
% order : left/top/right/bottom and -1 is out, 1 is in, 0 none
\begin{tikzpicture}
\draw (-4,-4) pic{multipurpose jigsaw=fill FireBrick and 0/-1/1/0}
(0,-4) pic{multipurpose jigsaw=fill Blue and -1/-1/1/0}
(4,-4) pic{multipurpose jigsaw=fill ForestGreen and -1/1/0/0}
(-4,0) pic{multipurpose jigsaw=fill ForestGreen and 0/1/-1/1}
(0,0) pic{multipurpose jigsaw=fill FireBrick and 1/-1/1/1}
(4,0) pic{multipurpose jigsaw=fill Blue and -1/1/0/-1}
(-4,4) pic{multipurpose jigsaw=fill Blue and 0/0/1/-1}
(0,4) pic{multipurpose jigsaw=fill ForestGreen and -1/0/1/1}
(4,4) pic{multipurpose jigsaw=fill FireBrick and -1/0/0/-1};
\end{tikzpicture}
\end{document}

enter image description here


An option using [out=Angle,in=Angle,out looseness=Value,in looseness=Value] and coordinate calculations to draw rotated shape; then other tricks to control sizes, angles ,positions and colors.

RESULT:

enter image description here

MWE:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,calc,fit,shapes}

\begin{document}
    \begin{tikzpicture}
    \def\JigzawPiece(#1)[#2]#3#4{
    \begin{scope}[shift={(#1)},rotate=#4,transform shape]
    \coordinate (a) at (0:#2);\coordinate (b) at (90:#2);\coordinate (c) at (180:#2);\coordinate (d) at (270:#2);
    \foreach \nodA/\nodB in {a/b,b/c,c/d,d/a}{
    \coordinate (\nodA\nodB1) at ($ (\nodA)!{0.4}!(\nodB) $);
    \coordinate (\nodA\nodB2) at ($ (\nodA)!{0.6}!(\nodB) $); 
    \coordinate (\nodA\nodB3) at ($ (\nodA)!{0.5}!(\nodB) $);
    \coordinate (\nodA\nodB4) at ($(\nodA\nodB3)!0.5*#2 cm!90:(\nodA)$);
    \coordinate (\nodA\nodB5) at ($(\nodA\nodB3)!-0.5*#2 cm!90:(\nodA)$);
    }
    \draw[fill=#3]
        (a) -- (ab1) 
            to [out=133,in=-45,out looseness=1,in looseness=2] (ab5) 
            to [out=135,in=-45,out looseness=2,in looseness=1] (ab2) --
        (b) -- (bc1) 
            to [out=-135,in=45,out looseness=1,in looseness=2] (bc4) 
            to [out=-135,in=45,out looseness=2,in looseness=1] (bc2) --
        (c) -- (cd1) 
            to [out=-45,in=135,out looseness=1,in looseness=2] (cd4) 
            to [out=-45,in=135,out looseness=2,in looseness=1] (cd2) --
        (d) -- (da1) 
            to [out=45,in=-135,out looseness=1,in looseness=2] (da5) 
            to [out=45,in=-135,out looseness=2,in looseness=1] (da2) --(a);
    \end{scope}
    }

    \foreach \x in {0,1,...,7}{
        \foreach \y in {0,1,...,5}{
        \pgfmathparse{0.9*rnd+0.3}
             \definecolor{Rcolor}{rgb}{\pgfmathresult,\pgfmathresult,\pgfmathresult} % from https://tex.stackexchange.com/a/37279/154390
            \JigzawPiece(\x,\y)[0.5]{blue!50!Rcolor}{0}
            \JigzawPiece(\x+0.5,\y+0.5)[0.5]{blue!30!Rcolor}{0}
        }
    }
    \foreach \ang [count=\j from 0] in {0,90,180,270} {
        \JigzawPiece(0.5+\j*2,7)[1]{red}{\ang}
    }

    \foreach \ang [count=\j from 0] in {-45,45,45,-45,-45} {
        \JigzawPiece(0.5+\j*1.4142,9)[1]{red}{\ang}
    }
    \end{tikzpicture}

\end{document}