7

I'm trying to come up with a PGF decoration that adds pseudo-random noise to the y coordinates of a path, while leaving x coordinates the same. The idea is that if I'm drawing a function y=f(x), this decoration should leave it a function, just make it noisier. The random steps decoration doesn't work because it might yield two different points with the same x coordinate, which is no longer a function.

I tried taking the same basic approach as random steps, but using polar coordinates to add the noise at angle 90-\pgfdecoratedangle. However, this fails for paths that are not horizontal lines. Here is a minimal non-working example:

\documentclass[varwidth,convert]{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}

\begin{document}

\pgfdeclaredecoration{jiggly}{step}
{
  \state{step}[width=+\pgfdecorationsegmentlength]
  {
    \pgfpathlineto{
      \pgfpointadd
      {\pgfpoint{\pgfdecorationsegmentlength}{0pt}}
      {\pgfpointpolar{90-\pgfdecoratedangle}
          {rand*\pgfdecorationsegmentamplitude}}
    }
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\pgfmathsetseed{1}
\begin{tikzpicture}[very thick, decoration={jiggly, amplitude=1cm}]
\draw[help lines] (0,0) grid (7,4);
\draw[cyan, decorate] (0,1) -- (3,1);
\draw[red, decorate] (4,0) -- (7,3);
\end{tikzpicture}

\end{document}

enter image description here

As you can see, the cyan line looks fine, but the red line has been morphed into something that is no longer a function. It even extends out beyond the rightmost point of the input path, which shouldn't be possible by adding noise just to the y direction. Clearly I'm missing something about how decorations work, and the question is what?

7

After some diagrams in a napkin and a little of trigonometry (which I can explain if you are interested), I arrived at: (see edit at the end)

\documentclass[varwidth,convert]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}

\begin{document}

\pgfdeclaredecoration{jiggly}{step}
{
  \state{step}[width=+\pgfdecorationsegmentlength]
  { \pgfmathsetmacro{\delta}{rand*\pgfdecorationsegmentamplitude}
    \pgfmathsetmacro{\deltax}{\delta*cos(90+\pgfdecoratedangle}
    \pgfmathsetmacro{\deltay}{\delta*sin(90+\pgfdecoratedangle}
    \pgfpathlineto{\pgfpoint{\pgfdecorationsegmentlength-\deltax}{\deltay}}
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\pgfmathsetseed{1}
\begin{tikzpicture}[very thick, decoration={jiggly, amplitude=1cm}]
\draw[help lines] (0,0) grid (7,4);
\draw[cyan, decorate] (0,1) -- (3,1);
\draw[red, decorate] (4,0) -- (7,3);
\end{tikzpicture}
\end{document}

Result:

Result

EDIT

After comparing my code to the OP's one, I noticed that in the end I was doing the same, only computing myself the "projections" of the random noise on the appropiate direction, instead of letting pgf polar transformation to do the same. So... why my code works and the OP's one doesnt?

The only reasonable explanation to me was that rand was being used as second argument for \pgfpointpolar and, perhaps, when pgf is translating polar coordinates to cartesian coordinates, and thus computing sines and cosines, this parameter was evaluated twice (one per each axis), giving different rand result in each evaluation.

So I tested this idea by mading a simple modification to OP's code, which ensures that rand is evaluated once:

\pgfdeclaredecoration{jiggly}{step}
{
  \state{step}[width=+\pgfdecorationsegmentlength]
  {\pgfmathsetmacro{\delta}{rand*\pgfdecorationsegmentamplitude}
    \pgfpathlineto{
      \pgfpointadd
      {\pgfpoint{\pgfdecorationsegmentlength}{0pt}}
      {\pgfpointpolar{90-\pgfdecoratedangle}
          {\delta}}
    }
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

And it worked too!

| improve this answer | |
  • That looks great, thanks. However, I would also love the explanation of what was going wrong... – user3188445 Oct 19 '15 at 9:15
  • @user3188445 Edited answer. Your approach was ok too. – JLDiaz Oct 19 '15 at 9:30
3

Another (simpler?) solution:

\documentclass[varwidth,convert]{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}

\begin{document}

\pgfdeclaredecoration{jiggly}{step}
{
  \state{step}[width=+\pgfdecorationsegmentlength]
  {
    \pgfmathsetmacro{\r}{rand*\pgfdecorationsegmentamplitude}
    \pgfpathlineto{\pgfpointadd{\pgfpointpolar{90-\pgfdecoratedangle}{\r}}{\pgfpoint{\pgfdecorationsegmentlength}{0pt}}}
  }
  \state{final}
  {
    \pgfpathlineto{\pgfpointdecoratedpathlast}
  }
}

\pgfmathsetseed{1}
\begin{tikzpicture}[very thick, decoration={jiggly, amplitude=1cm}]
\draw[help lines] (0,0) grid (7,4);
\draw[cyan, decorate] (0,1) -- (3,1);
\draw[red, decorate] (4,0) -- (7,3);
\end{tikzpicture}

\end{document}

With \pgfpointadd I add the noise relatively to the end point.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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