12

Working with path decorations based on this solution provided by marmot I am searching for a possibility to change the decorations amplitude along the graph.

Having this plot

enter image description here

applying the mentioned decoration gives

enter image description here

which is exactly what the decoration is supposed to do.

In fact the required curve should look like this one:

enter image description here

The last output has been created by manually searching the correct positions to manipulate the amplitude which is a "trial and error" method. Changing the dimensions of the tikzpicture will then give a false result, fx

enter image description here

Now the basic idea is to provide a separate path (which can be made visible during development) to control the decorations amplitude along the original (blue) curve. In this case the control path (red) would be quite simple:

enter image description here

The control path could be interpreted as a factor to the decorations amplitude that can be set via decoration={amplitude=}.

Assuming this method would be quite handy I'm a bit stunned it is not available in TikZ - or have I overseen it? And if it's not: how can I get the y-value of the control curve within the \state{step} portion of the decorations definition?

The MWE producing all the above graphs (even if not nicely coded in terms of efficiency and structural beauty):

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,decorations.pathmorphing}

\newcounter{randymark}
\newcommand{\amplitudesetter}{}
\pgfdeclaredecoration{mark random y steps}{start}
{%
  \state{start}[width=+0pt,next state=step,persistent precomputation={\pgfdecoratepathhascornerstrue\setcounter{randymark}{0}}]
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{0pt}{0pt}}
    }%
  \state{step}[auto end on length=1.5\pgfdecorationsegmentlength,
               auto corner on length=1.5\pgfdecorationsegmentlength,
               width=+\pgfdecorationsegmentlength]
    {\stepcounter{randymark}\amplitudesetter
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{\pgfdecorationsegmentlength}{rand*\pgfdecorationsegmentamplitude}}
    }%
  \state{final}
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpointdecoratedpathlast}%
    }%
}%

\begin{document}
\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]% original curve
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \draw[blue!80!black,thick] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]% original curve
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \draw[black] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \path[decorate] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \draw[blue!80!black,thick] plot[variable=\x,samples at={1,...,\arabic{randymark}},smooth] (randymark\x);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]% original curve
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \renewcommand{\amplitudesetter}{%
    \pgfdecorationsegmentamplitude=0.75mm
    \ifnum\value{randymark}<48\pgfdecorationsegmentamplitude=0.7mm\fi%
    \ifnum\value{randymark}<46\pgfdecorationsegmentamplitude=0.6mm\fi%
    \ifnum\value{randymark}<44\pgfdecorationsegmentamplitude=0.5mm\fi%
    \ifnum\value{randymark}<42\pgfdecorationsegmentamplitude=0.4mm\fi%
    \ifnum\value{randymark}<40\pgfdecorationsegmentamplitude=0.3mm\fi%
    \ifnum\value{randymark}<38\pgfdecorationsegmentamplitude=0.2mm\fi%
    \ifnum\value{randymark}<36\pgfdecorationsegmentamplitude=0.1mm\fi%
    \ifnum\value{randymark}<34\pgfdecorationsegmentamplitude=0mm\fi%
    \ifnum\value{randymark}<8\pgfdecorationsegmentamplitude=0.75mm\fi%
    }
  \draw[black] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \path[decorate] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \draw[blue!80!black,thick] plot[variable=\x,samples at={1,...,\arabic{randymark}},smooth] (randymark\x);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=2.5mm,y=2.5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]% original curve
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \renewcommand{\amplitudesetter}{%
    \pgfdecorationsegmentamplitude=0.75mm
    \ifnum\value{randymark}<48\pgfdecorationsegmentamplitude=0.7mm\fi%
    \ifnum\value{randymark}<46\pgfdecorationsegmentamplitude=0.6mm\fi%
    \ifnum\value{randymark}<44\pgfdecorationsegmentamplitude=0.5mm\fi%
    \ifnum\value{randymark}<42\pgfdecorationsegmentamplitude=0.4mm\fi%
    \ifnum\value{randymark}<40\pgfdecorationsegmentamplitude=0.3mm\fi%
    \ifnum\value{randymark}<38\pgfdecorationsegmentamplitude=0.2mm\fi%
    \ifnum\value{randymark}<36\pgfdecorationsegmentamplitude=0.1mm\fi%
    \ifnum\value{randymark}<34\pgfdecorationsegmentamplitude=0mm\fi%
    \ifnum\value{randymark}<8\pgfdecorationsegmentamplitude=0.75mm\fi%
    }
  \draw[black] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \path[decorate] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \draw[blue!80!black,thick] plot[variable=\x,samples at={1,...,\arabic{randymark}},smooth] (randymark\x);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \draw [red,thick,name=amplitudecontrol] (0,1) -- (2,1) -- (2,0) -- (7,0) -- (12,1) -- (25,1);
  \draw[blue!80!black,thick] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
\end{tikzpicture}

\end{document}
13

Let me start by saying that I really like that question and am truly impressed by what you have achieved. Here is a proposal to address the scalability. Define a function that governs the amplitude,

varyingamp(x) = whatever you like

where x is the fraction of the decorated path (in order to ensure scalability). (Such a function has been used already here in order to have variable varying line widths. I would not at all be surprised if similar things had been used before.) This is the MWE.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,decorations.pathmorphing}

\newcounter{randymark}
%\newcommand{\amplitudesetter}{}
\pgfdeclaredecoration{mark random y steps}{start}
{%
  \state{start}[width=+0pt,next state=step,persistent precomputation={
  \pgfdecoratepathhascornerstrue\setcounter{randymark}{0}}]
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{0pt}{0pt}}
    }%
  \state{step}[auto end on length=1.5\pgfdecorationsegmentlength,
               auto corner on length=1.5\pgfdecorationsegmentlength,
               width=+\pgfdecorationsegmentlength]
    {\stepcounter{randymark}%\amplitudesetter
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{\pgfdecorationsegmentlength}{rand*\pgfdecorationsegmentamplitude}}
    }%
  \state{final}
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpointdecoratedpathlast}%
    }%
}%

\pgfdeclaredecoration{mark varying random y steps}{start}
{%
  \state{start}[width=+0pt,next state=step,persistent precomputation={
  \pgfdecoratepathhascornerstrue\setcounter{randymark}{0}}]
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{0pt}{0pt}}
    }%
  \state{step}[auto end on length=1.5\pgfdecorationsegmentlength,
               auto corner on length=1.5\pgfdecorationsegmentlength,
               width=+\pgfdecorationsegmentlength]
    {\stepcounter{randymark}
     \pgfmathsetmacro{\myfraction}{\the\pgfdecorationsegmentlength*\value{randymark}/\pgfdecoratedpathlength}
     \pgfmathsetmacro{\myamplitude}{varyingamp(\myfraction)}
     %\typeout{\myfraction,\myamplitude}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpoint{\pgfdecorationsegmentlength}{rand*\myamplitude*\pgfdecorationsegmentamplitude}}
    }%
  \state{final}
    {\stepcounter{randymark}
     \pgfcoordinate{randymark\arabic{randymark}}{\pgfpointdecoratedpathlast}%
    }%
}%



\begin{document}
\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark random y steps,segment length=1.5mm,amplitude=0.75mm}]% original curve
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \draw[blue!80!black,thick] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=5mm,y=5mm,decoration={mark varying random y steps,segment
length=1.5mm,amplitude=0.75mm},declare function={
varyingamp(\x)=ifthenelse(\x<0.08,1,ifthenelse(\x<0.28,0,ifthenelse(\x<0.48,5*(\x-0.28),1)));}]% 
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \draw[red,thick] plot[variable=\x,domain=0:25,samples=101] ({\x},{varyingamp(\x/25)});
  \pgfmathsetseed{2}
  \draw[black] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \path[decorate] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \draw[blue!80!black,thick] plot[variable=\x,samples at={1,...,\arabic{randymark}},smooth] (randymark\x);
\end{tikzpicture}

\vspace{2ex}

\begin{tikzpicture}[x=2.5mm,y=2.5mm,decoration={mark varying random y
steps,segment length=0.75mm,amplitude=0.75mm},declare function={
varyingamp(\x)=ifthenelse(\x<0.08,1,ifthenelse(\x<0.28,0,ifthenelse(\x<0.48,5*(\x-0.28),1)));}]
  \draw[style=help lines] (0,-4) grid[step=5mm] (25,1);
  \pgfmathsetseed{2}
  \draw[black] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \path[decorate] (0,0) -- (2,0) to [out=0,in=180](4,-3.5) to [out=0,in=225](6,-1.75) to [out=45,in=180](11,0) -- (24.25,0);
  \draw[blue!80!black,thick] plot[variable=\x,samples at={1,...,\arabic{randymark}},smooth] (randymark\x);
\end{tikzpicture}
\end{document}

enter image description here

The function is shown in red in the second plot. The third plot shows scalability. (Of course, you also need to rescale the segment lengths. Notice also that this decoration has discrete steps, so if you have a strongly varying function but only a few steps, the function may not be fully "appreciated" since it only gets evaluated at a few points.)

  • 1
    +1 for this amazing answer, and I wish I could +1 another time for the first sentence only... – user156344 Mar 17 at 16:03
  • @marmot you know you're driving me crazy, don't you? I did not even get a basic function implemented yet and you, well, uh. Ok, I'm digging through this one. Btw: \x holds the fraction of \pgfdecoratedpathlength, right? Even if your approach is more elegant than mine it's not the red curve that controls the amplitude... Therefore I would like to keep the question still open if you don't mind... – AndiW Mar 17 at 16:35
  • 1
    @AndiW Sorry for driving you crazy. ;-) The function drawn in red does determine the amplitude, i.e. the random deviation is rand*varyingamp(s)*\pgfdecorationsegmentamplitude, where s denotes the fraction of the path we are at. Of course, s is not the x value of the curve at that point, which would not be independent of the coordinate system. You could of course define your own function that returns the path length s (or fraction) a a function of x, which would work in this specific case because here it is single-valued but in general it is not. (I'm just back from cycling.... ) – user121799 Mar 17 at 18:37
  • 1
    @AndiW Yes. What you are saying is correct, but is not contradicting with any of my statements, is it? – user121799 Mar 17 at 20:21
  • 1
    @marmot well - actually I sometimes forget that LaTeX is no low-level-end-user tool. Given the fact that it's a function controling the amplitude and not a graph (which would be something an end-user would expect) the answer is great. Having quit physics 25 yrs ago I have to change my approach I guess... :-) +1! – AndiW Mar 17 at 21:04

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.