[Tex/LaTex] Define decoration with cloud shapes to resemble smoke

tikz-pgf

I want to create a decoration that would decorate a path with gray transparent clouds with random shifts, random number of puffs and scaling size. The idea is that it should look like smoke coming up.

I tried to define my own decoration, tried using shape backgrounds and markings, but nothing really worked. In the end, I managed to draw the clouds in a loop, but of course it is not the most elegant solutions. So this is the code of my solution and attempts, and the expected result:

\documentclass[tikz,border=5]{standalone}

\usetikzlibrary{decorations.pathmorphing, decorations.markings, decorations.shapes}
\usetikzlibrary{shapes,calc}

\makeatletter
\newdimen\cloud@sep
\cloud@sep=0.2cm\relax

\pgfdeclaredecoration{smoke}{initial}{
  \state{initial}[width=\cloud@sep]
  {
    \pgfsetfillopacity{0.3}
    \def\tikz@fillcolor{mygray}
    \tikz@mode@filltrue
    \pgfnode{cloud}{center}{}{}{}
    \pgfmathsetlength\cloud@sep{0.2cm + rand*0.2cm}
    \global\cloud@sep=\cloud@sep
  }
  \state{final}
  {
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \foreach \i [evaluate={\j=\i-1;}] in {1,2,...,20} {%
    \node[shape=cloud, cloud puffs=9+5*rnd, fill=gray, opacity=0.4, %
    minimum width=4+\j, minimum height=3+0.5*\j] at %
    ($(60:1) + 0.09*(\i,0) + 0.1*(0,\i) + 0.25*(rand,0) + 0.1*(0,rand)$) {}; }
\end{tikzpicture}

%% the code that does not work
\begin{tikzpicture}
  \path[decorate,decoration={smoke, shape start width=1.5mm, shape end
    width=2.5mm, shape start height=2mm, shape end height=3mm},
  decoration={shape scaled}, fill=gray] (60:1.5) -- (50:3);
\end{tikzpicture}

\begin{tikzpicture}
  \path[decorate,decoration={shape backgrounds, shape=cloud, shape
    width=4mm+rand, shape height=3mm+rand}, cloud puffs=11+3*rand, fill=gray,
  opacity=0.3] (60:1.5) -- (50:3);
\end{tikzpicture}
  \end{document}

Smoke

Is it possible to define/specify such a decoration? Note that, it would be nice to have a decoration where each cloud is in a different transparency group, that is, one could see their overlapping.

Best Answer

First Version

Here is a possible solution via a markings decoration (the gray clouds are on a line and the orange clouds are on a circle):

enter image description here

\documentclass[tikz,border=5]{standalone}

\usetikzlibrary{decorations.markings,shapes,calc}

\tikzset{
  random clouds/.style={
    decoration={markings,mark=between positions 0 and 1 step 5mm with {
        \pgfmathsetmacro\myh{5mm+ rnd*1cm}
        \pgfmathsetmacro\myw{\myh + 5mm + rnd*1cm}
        \node[shape=cloud, cloud puffs={10+int(5*rnd)}, fill=#1, opacity=0.4,
        minimum width=\myw,minimum height=\myh]
        at (rand * 1cm,rand * 1cm) {};
      }},
  },
}
\begin{document}
\begin{tikzpicture}
  \path[decorate,random clouds=gray]
  (0,0) -- (10,10);

  \path[decorate,random clouds=red]
  (5,5) circle (3cm);
\end{tikzpicture}
\end{document}

Second Version (with fade out fading)

enter image description here

\documentclass[tikz,border=5]{standalone}

\usetikzlibrary{decorations.markings,shapes,calc}

\tikzset{
  random clouds/.style={
    decoration={markings,mark=between positions 0 and 1 step 5mm with {
        \pgfmathsetmacro\myh{15mm + rnd*1cm}
        \pgfmathsetmacro\myw{\myh + 5mm + rnd*1cm}
        \pgfkeysgetvalue{/pgf/decoration/mark info/distance from start}{\currdist}
        \pgfmathsetmacro\myop{1-\currdist/\pgfdecoratedpathlength}
        \node[shape=cloud, cloud puffs={10+int(5*rnd)}, fill=#1, opacity=\myop,
        minimum width=\myw,minimum height=\myh]
        at (rand * 1cm,rand * 1cm) {};
      }},
  },
}
\begin{document}
\begin{tikzpicture}
  \path[decorate,random clouds=orange]
  (0,0) -- (10,10);

\end{tikzpicture}
\end{document}

Third Version (with keys to control size and color)

In this version, clouds sizes are not random.

enter image description here

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations.markings,shapes,calc}

\makeatletter
\tikzset{
  random clouds/.is family,
  random clouds,
  color/.store in=\randomclouds@c,
  start width/.store in=\randomclouds@startw,
  start height/.store in=\randomclouds@starth,
  end width/.store in=\randomclouds@endw,
  end height/.store in=\randomclouds@endh,
  color=gray,
  start width=22mm,start height=12mm,
  end width=14mm,end height=8mm
}
\tikzset{
  random clouds decoration/.style={
    decoration={markings,mark=between positions 0 and 1 step 5mm with {
        \tikzset{random clouds,#1}
        \pgfkeysgetvalue{/pgf/decoration/mark info/distance from start}{\currdist}
        \pgfmathsetmacro\myop{1-\currdist/\pgfdecoratedpathlength}
        \pgfmathsetmacro\myw{\randomclouds@endw+\myop*(\randomclouds@startw-\randomclouds@endw)}
        \pgfmathsetmacro\myh{\randomclouds@endh+\myop*(\randomclouds@starth-\randomclouds@endh)}
        \node[shape=cloud, cloud puffs={10+int(5*rnd)},fill=\randomclouds@c, opacity=\myop,
        minimum width=\myw,minimum height=\myh]
        at (rand * 1cm,rand * 1cm) {};
      }},
  },
}
\makeatother

\pgfmathsetseed{\pdfuniformdeviate 1000000}

\begin{document}
\begin{tikzpicture}
  \path[decorate,random clouds decoration]
  (0,0) -- (10,10);

  \path[decorate,random clouds decoration={
    color=orange,
    start width=12mm,end width=5mm,
    start height=22mm,end height=10mm,
  }]
  (5,0) -- (15,10);

\end{tikzpicture}
\end{document}