Turn tikz drawing into a macro

I have created my own method of drawing a randomly generated closed loop. Here is my code:

\documentclass[tikz]{standalone}

\usepackage{pgf}

\pgfmathsetseed{\number\pdfrandomseed}

\begin{document}
\begin{tikzpicture}

% Set random in/out angles for the points
\pgfmathsetmacro{\a}{random(45,135)}
\pgfmathsetmacro{\b}{random(300,190)}
\pgfmathsetmacro{\c}{random(290,380)}
\pgfmathsetmacro{\d}{random(340,430)}
\pgfmathsetmacro{\e}{random(20,110)}
\pgfmathsetmacro{\f}{random(110,200)}
\pgfmathsetmacro{\g}{random(160,250)}
\pgfmathsetmacro{\h}{random(200,290)}

% Set random location for points.
% These points are based on an ellipse with horizontal radius 8 and vertical radius 6
\coordinate (a) at (8+1.5*rand,1.5*rand);
\coordinate (b) at (6+1.5*rand,4+1.5*rand);
\coordinate (c) at (1.5*rand,6+1.5*rand);
\coordinate (d) at (-6+1.5*rand,4+1.5*rand);
\coordinate (e) at (-8+1.5*rand,1.5*rand);
\coordinate (f) at (-6+1.5*rand,-4+1.5*rand);
\coordinate (g) at (1.5*rand,-6+1.5*rand);
\coordinate (h) at (6+1.5*rand,-4+1.5*rand);

% Draw the randomly generated closed shape.
\draw (a) to[out=\a, in=\b] (b);
\draw (b) to[out=\b+180, in=\c] (c);
\draw (c) to[out=\c+180, in=\d] (d);
\draw (d) to[out=\d+180, in=\e] (e);
\draw (e) to[out=\e+180, in=\f] (f);
\draw (f) to[out=\f+180, in=\g] (g);
\draw (g) to[out=\g+180, in=\h] (h);
\draw (h) to[out=\h+180, in=\a+180] (a);

\end{tikzpicture}
\end{document}

My question is... how might I turn this into a tikz command/macro, so for instance I could type

\draw (1,2) \myrandomshape;

into a tikzpicture environment to generate one of these shapes centered at the point (1,2)?

Bonus: It would also be pretty neat to be able to add key values and nodes, such as

\draw[thick, fill=red] (1,2) \myrandomshape node{A};

to produce one of these shapes centered at the point (1,2) with thick lines, red colored fill, and the letter "A" printed in the middle.

Double Bonus: It would be doubly neat to be able to arrow arrow specifiers, such as

\draw[->] (1,2) \myrandomshape;
\draw[<-] (-3,0) \myrandomshape;

to produce one of these shapes centered at the point (1,2) with arrows oriented clockwise on the boundary, and another shape centered at the point (-3,0) with arrows oriented counter-clockwise on the boundary.

• Welcome to SE! You can declare your picture as "small picture" \pic and than use it as pic at (...) {<pic name>{}. For details see chapter 18: Pics: Small Pictures on Paths (pp 251) in TikZ manual. – Zarko Jan 22 '16 at 7:40

You can use a pic. The interface here isn't that good, but I think it works. To just add a black shape use e.g. \pic at (0,0) {randomblob};. As set up, randomblob has four key-value arguments, used as randomblob={<key>=<value>}. They are

• edgecolor - the color of the line, black by default.
• fillcolor - fill color. If not specified, no filling is done.
• label - the text of a node placed in the center of the shape.
• arrowtip - an arrow tip. If specified, 10 arrow tips are placed evenly along the path.

Other settings such as thick and fill opacity can be added to the pic, not randomblob.

\documentclass[tikz]{standalone}
\usetikzlibrary{decorations.markings,arrows.meta}
\pgfmathsetseed{\number\pdfrandomseed}

\tikzset{randomblob/.pic={
\tikzset{%
/randomblob/.cd,
edgecolor=black,
fillcolor=none,
arrowtip={},
label={},
#1
}
% Set random in/out angles for the points
\pgfmathsetmacro{\a}{random(45,135)}
\pgfmathsetmacro{\b}{random(300,190)}
\pgfmathsetmacro{\c}{random(290,380)}
\pgfmathsetmacro{\d}{random(340,430)}
\pgfmathsetmacro{\e}{random(20,110)}
\pgfmathsetmacro{\f}{random(110,200)}
\pgfmathsetmacro{\g}{random(160,250)}
\pgfmathsetmacro{\h}{random(200,290)}

% Set random location for points.
% These points are based on an ellipse with horizontal radius 8 and vertical radius 6
\coordinate (a) at (8+1.5*rand,1.5*rand);
\coordinate (b) at (6+1.5*rand,4+1.5*rand);
\coordinate (c) at (1.5*rand,6+1.5*rand);
\coordinate (d) at (-6+1.5*rand,4+1.5*rand);
\coordinate (e) at (-8+1.5*rand,1.5*rand);
\coordinate (f) at (-6+1.5*rand,-4+1.5*rand);
\coordinate (g) at (1.5*rand,-6+1.5*rand);
\coordinate (h) at (6+1.5*rand,-4+1.5*rand);

\draw [
decoration={
markings,
mark=between positions 0 and 0.9 step 0.1 with {\arrow{\blobarrow}}
},
postaction={decorate},
fill=\blobfillclr,draw=\blobedgeclr]
(a) to[out=\a,     in=\b]     (b)
to[out=\b+180, in=\c]     (c)
to[out=\c+180, in=\d]     (d)
to[out=\d+180, in=\e]     (e)
to[out=\e+180, in=\f]     (f)
to[out=\f+180, in=\g]     (g)
to[out=\g+180, in=\h]     (h)
to[out=\h+180, in=\a+180] (a);

\node (0,0) {\bloblabel};

},
/randomblob/.search also={/tikz},
/randomblob/.cd,
label/.store in=\bloblabel,
edgecolor/.store in=\blobedgeclr,
fillcolor/.store in=\blobfillclr,
arrowtip/.store in=\blobarrow
}

\begin{document}
\begin{tikzpicture}[x=0.3cm,y=0.3cm]

\pic at (0,0) {randomblob={label=$a+b$,arrowtip={Stealth}}};
\pic [fill opacity=0.2,very thick] at (5,-2) {randomblob={fillcolor=blue,edgecolor=red}};
\pic[thick,blue] at (-5,-5) {randomblob={label={What?},arrowtip=latex,edgecolor=brown}};

\end{tikzpicture}
\end{document}

(I looked rather hard at https://tex.stackexchange.com/a/281102/586 when writing that code. In other words, I used pretty much exactly the same, so thanks cfr.)

At the moment it doesn't fulfill your bonus points though, will have to get back to you on that. (Probably wont have time during the day, but maybe I can figure something out during the afternoon/evening.)

\documentclass[tikz]{standalone}
\pgfmathsetseed{\number\pdfrandomseed}
\tikzset{randomblob/.pic={
% Set random in/out angles for the points
\pgfmathsetmacro{\a}{random(45,135)}
\pgfmathsetmacro{\b}{random(300,190)}
\pgfmathsetmacro{\c}{random(290,380)}
\pgfmathsetmacro{\d}{random(340,430)}
\pgfmathsetmacro{\e}{random(20,110)}
\pgfmathsetmacro{\f}{random(110,200)}
\pgfmathsetmacro{\g}{random(160,250)}
\pgfmathsetmacro{\h}{random(200,290)}

% Set random location for points.
% These points are based on an ellipse with horizontal radius 8 and vertical radius 6
\coordinate (a) at (8+1.5*rand,1.5*rand);
\coordinate (b) at (6+1.5*rand,4+1.5*rand);
\coordinate (c) at (1.5*rand,6+1.5*rand);
\coordinate (d) at (-6+1.5*rand,4+1.5*rand);
\coordinate (e) at (-8+1.5*rand,1.5*rand);
\coordinate (f) at (-6+1.5*rand,-4+1.5*rand);
\coordinate (g) at (1.5*rand,-6+1.5*rand);
\coordinate (h) at (6+1.5*rand,-4+1.5*rand);

% Draw the randomly generated closed shape.
\draw (a) to[out=\a,     in=\b]     (b)
to[out=\b+180, in=\c]     (c)
to[out=\c+180, in=\d]     (d)
to[out=\d+180, in=\e]     (e)
to[out=\e+180, in=\f]     (f)
to[out=\f+180, in=\g]     (g)
to[out=\g+180, in=\h]     (h)
to[out=\h+180, in=\a+180] (a);
}}
\begin{document}
\begin{tikzpicture}

\pic at (0,0) {randomblob};
\pic[blue] at (2,2) {randomblob};
\pic[thick,red] at (-2,-2) {randomblob};
\end{tikzpicture}
\end{document}

• (+1), from mine comment to your answer: only 3 min! :-) – Zarko Jan 22 '16 at 7:48
• @Zarko Sorry, I didn't see your comment before I answered. – Torbjørn T. Jan 22 '16 at 7:48
• It's ok. You should start to write your answer before I wrote my comment ... and temporary I haven't time to write it. – Zarko Jan 22 '16 at 7:51

As others have mentioned the pic is a useful way to go. Note, that if the loop is drawn using individual paths then a fill is not possible. Conversely, if the loop was drawn as one path then it would not be possible to get arrows on the individual segments of the loop without redrawing individual segments, unless a decoration is used:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations.pathreplacing,math}
\tikzset{pics/random loop/.style={code={
\tikzmath{%
integer \a; coordinate \p;
\a1 = random(45,135);  \a2 = random(300,190);
\a3 = random(290,380); \a4 = random(340,430);
\a5 = random(20,110);  \a6 = random(110,200);
\a7 = random(160,250); \a8 = random(200,290);
\p1 = (8+1.5*rand,1.5*rand);  \p2 = (6+1.5*rand,4+1.5*rand);
\p3 = (1.5*rand,6+1.5*rand);  \p4 = (-6+1.5*rand,4+1.5*rand);
\p5 = (-8+1.5*rand,1.5*rand); \p6 = (-6+1.5*rand,-4+1.5*rand);
\p7 = (1.5*rand,-6+1.5*rand); \p8 = (6+1.5*rand,-4+1.5*rand);
}
\path [pic actions/.try, postaction={decoration={show path construction,
curveto code={\path [pic outline/.try]
(\tikzinputsegmentfirst) .. controls(\tikzinputsegmentsupporta)
and (\tikzinputsegmentsupportb) .. (\tikzinputsegmentlast);
}}, decorate}] (\p1)
\foreach \i [evaluate={\j=int(mod(\i, 8)+1);
\u = \a\i + (\i > 1) * 180; \v = \a\j + (\i == 8) * 180;}] in {1,...,8}{
to [out=\u, in=\v] (\p\j) } -- cycle;
}}}
\begin{document}
\begin{tikzpicture}
\foreach \c [count=\i] in {red, yellow, pink, green, orange, purple, blue}
\pic [fill=\c!50,
pic outline/.style={draw=\c!75!black, line width=0.25cm, ->}]
at (360/7*\i:16) {random loop};
\end{tikzpicture}
\end{document}

Here's a Metapost alternative. I like the fact that in MP you can make a macro that returns a path that you can assign to a variable, and then pass it to other commands like draw, reverse, fill and apply transformations to it like shifted, rotated, etc.

prologues := 3;
outputtemplate := "%j%c.eps";

% Make a random ellipsoid with semiaxes a,b and r amount of randomness
vardef random_ellipse(expr a,b,r) =
save e; path e;
e = fullcircle xscaled a yscaled b;
for i=1 upto length e:
point i of e shifted (r*normaldeviate, r*normaldeviate) ..
endfor cycle
enddef;

% Draw n evenly-space arrows along path p
vardef draw_arrows_along(expr p, n) =
save a; a = arclength p / n;
for i=1 upto n:
drawarrow subpath (arctime (i-1)*a of p, arctime i*a of p) of p;
endfor
enddef;

beginfig(1);

randomseed := 3142;

path e[];

e1 = random_ellipse(144,89,13);
e2 = random_ellipse(144,89,13) shifted (200, 0);
e3 = random_ellipse(144,89,13) shifted (200, 150);
e4 = random_ellipse(144,89,13) shifted (  0, 150);

draw e1;

drawoptions(withcolor .67 red);

draw_arrows_along(e2,13);

drawoptions(withcolor .4 green);

draw_arrows_along(reverse e3, 32);

drawoptions();

fill e4 withcolor .8[blue,white];
draw e4 withcolor .67 blue;

label("A", center e1);
label("B", center e2);
label("C", center e3);
label("D", center e4);

endfig;
end.