TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question
    
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 at 7:40
up vote 15 down vote accepted

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.

enter image description here

\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 http://tex.stackexchange.com/a/281102/586 when writing that code. In other words, I used pretty much exactly the same, so thanks cfr.)

Original answer

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}

enter image description here

share|improve this answer
    
(+1), from mine comment to your answer: only 3 min! :-) – Zarko Jan 22 at 7:48
    
@Zarko Sorry, I didn't see your comment before I answered. – Torbjørn T. Jan 22 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 at 7:51
    
Thank you so much! That's ultra awesome. – Anthony Mottaz Jan 22 at 21:44

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}

enter image description here

share|improve this answer

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.

enter image description here

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.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.