# Drawing Spirograph patterns

I like drawing Spirograph patterns (which is plotted by a point in a wheel moving along a ring). I use a programme from this site, in which I posted this Tutorial help file.

Is it possible to produce an external style (package) that can be implemented to produce such drawings.

The following routine written in the Math Processor programme produces the following pattern

spirograph = function (R, r, p, nRotations, color)
{
t = vectorin(0, 0.05, 2*pi*nRotations)
x = (R+r) * cos(t) + p * cos((R+r)*t/r)
y = (R+r) * sin(t) + p * sin((R+r)*t/r)
plot(x, y, color)
}
spirograph(60, -15, 10, 1, green)


The first line: is a command line to use a function named spirograph, to draw a curve with the variables written on the last line.

R: is the radius of the Ring.

r: is the radius of the Wheel.

p: is the distance of the drawing point from the centre of the Wheel.

nRotations: is the number of rotations that point P has to move to reach to the point from where it started.

color: is the name of the colour of the line that will be drawn.

t: is a symbol for the part after the equal sign.

vectorin: is a MathProcessor programme command.

x = and y =: are the mathematical parametric equation to draw the Spirograph.

plot: is a command to draw (plot) a curve, using the parameters specified in the previous equation (in the x,y coordinates), in the specified colour.

The last line: is the main parameters to draw the pattern with the function named Spirograph using a Ring of radius 60, a Wheel of radius 15, with a point away from the centre of the Wheel by a distance of 10; making the curve by moving point P one rotation cycle, in a green colour.

This implements your functions as a pic.

\documentclass[tikz,border=3mm]{standalone}
\begin{document}
\begin{tikzpicture}[declare function={
spirox(\t,\R,\r,\p)=(\R+\r)*cos(\t)+\p*cos((\R+\r)*\t/\r);
spiroy(\t,\R,\r,\p)=(\R+\r)*sin(\t)+\p*sin((\R+\r)*\t/\r);},
pics/spiro/.style={code={
\tikzset{spiro/.cd,#1}
\def\pv##1{\pgfkeysvalueof{/tikz/spiro/##1}}
plot[variable=\t,domain=0:2*pi*\pv{nRotations},
samples=90*\pv{nRotations}+1,smooth cycle]
({spirox(\t,\pv{R},\pv{r},\pv{p})},{spiroy(\t,\pv{R},\pv{r},\pv{p})});
}},
spiro/.cd,R/.initial=6,r/.initial=-1.5,p/.initial=1,nRotations/.initial=1]
\draw pic[scale=0.5,blue]{spiro}
(5,0) pic[scale=0.5,red]{spiro={R=5,r=-1,p=0.5}}
(0,-6) pic[scale=0.5,blue,ultra thick,inner color=blue!10,outer color=blue]{spiro}
(5,-6) pic[scale=0.5,red,line width=1mm,fill=orange,rotate=15]{spiro={R=5,r=-1,p=0.5}};
\end{tikzpicture}
\end{document}


You can set the parameters with pgf keys, as illustrated. In principle one can also pass them as a comma separated list. Please let me now if that's needed. I also added now further examples showing why pics are (IMHO) so useful. You can add all sorts of things, fills, rotations, shadings and so on.

This is a slightly faster version using shadings.

\documentclass[tikz,border=3mm]{standalone}
\tikzset{pics/spiro/.style={code={
\tikzset{spiro/.cd,#1}
\def\pv##1{\pgfkeysvalueof{/tikz/spiro/##1}}
plot[variable=\t,domain=0:2*pi*\pv{nRotations},
samples=90*\pv{nRotations}+1,smooth cycle]
({(\pv{R}+\pv{r})*cos(\t)+\pv{p}*cos((\pv{R}+\pv{r})*\t/\pv{r})},
{(\pv{R}+\pv{r})*sin(\t)+\pv{p}*sin((\pv{R}+\pv{r})*\t/\pv{r})});
}},
spiro/.cd,R/.initial=6,r/.initial=-1.5,p/.initial=1,nRotations/.initial=1}
\begin{document}
\begin{tikzpicture}[]
\draw
(0,0) pic[scale=0.5,blue,ultra thick,rotate=45,
lower left=orange,lower right=yellow,upper left=red,
upper right=magenta]{spiro}
(5,0) pic[scale=0.5,red,line width=1mm,inner color=red!20,
outer color=red,rotate=18]{spiro={R=5,r=-1,p=0.9}};
\end{tikzpicture}
\end{document}


Or another example illustrating the transformability (inspired by the date to some extent).

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\tikzset{pics/spiro/.style={code={
\tikzset{spiro/.cd,#1}
\def\pv##1{\pgfkeysvalueof{/tikz/spiro/##1}}
plot[variable=\t,domain=0:2*pi*\pv{nRotations},
samples=90*\pv{nRotations}+1,smooth cycle]
({(\pv{R}+\pv{r})*cos(\t)+\pv{p}*cos((\pv{R}+\pv{r})*\t/\pv{r})},
{(\pv{R}+\pv{r})*sin(\t)+\pv{p}*sin((\pv{R}+\pv{r})*\t/\pv{r})});
}},
spiro/.cd,R/.initial=6,r/.initial=-1.5,p/.initial=1,nRotations/.initial=1}
\begin{document}
\tdplotsetmaincoords{70}{110}
\begin{tikzpicture}[tdplot_main_coords,line join=round]
\begin{scope}[canvas is xy plane at z=3]
\path[fill=blue] (-3,-3) rectangle (3,3);
\path (0,0) pic[scale=0.5,orange,line width=1mm,inner color=orange!40!black,
outer color=orange,rotate=18+90,transform shape]{spiro={R=5,r=-1,p=0.9}};
\end{scope}
\begin{scope}[canvas is xz plane at y=3]
\path[fill=blue!80!black] (-3,-3) rectangle (3,3);
\path (0,0) pic[scale=0.5,yellow,line width=1mm,inner color=yellow!40!black,
outer color=yellow,rotate=18,transform shape]{spiro={R=5,r=-1,p=0.9}};
\end{scope}
\begin{scope}[canvas is yz plane at x=3]
\path[fill=blue!60!black] (-3,-3) rectangle (3,3);
\path (0,0) pic[scale=0.5,red,line width=1mm,inner color=red!40!black,
outer color=red,rotate=18,transform shape]{spiro={R=5,r=-1,p=0.9}};
\end{scope}
\end{tikzpicture}
\end{document}


• Interesting ! I need to study the last pgfmanual Dec 24, 2019 at 13:51
• @Schrödinger's cat Thank you very much. Yes please. Can rotation (for repeating the pattern) and line width be controlled also.
– Hany
Dec 24, 2019 at 14:17
• @AlainMatthes This should be quick, it has only 1318 pages. ;-) Merry Christmas!
– user194703
Dec 24, 2019 at 17:16
• I know a big part ! Merry Christmas !! Dec 24, 2019 at 18:07
• @AlainMatthes I know, I was just kidding. I really enjoyed reading your contributions to, say, the mathematical engine.
– user194703
Dec 24, 2019 at 18:09

I would use a Metapost macro to do this.

Here included in a LuaLaTeX file:

\documentclass[border=2mm]{standalone}
\usepackage{luatex85,luamplib}
\mplibnumbersystem{double}
\everymplib{%
pi := 3.14159265358979323846; radian := 180/pi;
vardef cos primary x = cosd(x*radian) enddef;
vardef sin primary x = sind(x*radian) enddef;

vardef param_fcn (expr tmin, tmax, tstep)(text f_t)(text g_t) =
save t; t := tmin;
(f_t, g_t)
forever: hide(t := t+tstep) exitif t > tmax;
.. (f_t, g_t)
endfor
if t - tstep <> tmax: hide(t := tmax) .. (f_t, g_t) fi
enddef;

vardef spirograph(expr R, r, p, n, u) =
param_fcn(0, 2*pi*n, .05)
((R+r) * cos(t) + p * cos((R+r)*t/r)) ((R+r) * sin(t) + p * sin((R+r)*t/r))
scaled u
enddef;

beginfig(1);}
\everyendmplib{endfig;}

\begin{document}
\begin{mplibcode}
draw spirograph(60, -15, 10, 1, mm) withcolor green;
\end{mplibcode}

\begin{mplibcode}
path spir; spir = spirograph(60, -15, 10, 1, mm) rotated 60;
fill spir .. cycle withcolor red;
draw spir withcolor blue withpen pencircle scaled mm;
\end{mplibcode}

\end{document}


The additional parameter u is the unit scale.

• Thank you. I tried compiling with LuaLatex, but it gave me an error.
– Hany
Dec 25, 2019 at 7:40
• @Hany What kind of error? It works fine here on TeX Live 2019 (LuaTex 1.10.0). Dec 25, 2019 at 8:21
• This is a link to the screen shot of the lower part of the compilation error. i.stack.imgur.com/MMDVz.png
– Hany
Dec 25, 2019 at 11:36
• @ Franck Pastor This is a link to the screen shot of the upper part of the compilation error. i.stack.imgur.com/0dRrz.png
– Hany
Dec 25, 2019 at 11:46
• @Hany I've just made my coding independant of the metafun format, at the cost of adding a few definitions. I hope it works with your distribution now. Dec 25, 2019 at 19:30