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}
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}
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}
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}
And a version with pic
s. 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}
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}
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:
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}