[Tex/LaTex] Custom decoration along curved paths

tikz-pgf

The problem that I started out with were those straight lines at the end of paths decorated with snake or coil, which I wanted to get rid of. I found a very nice solution here for the wavy line:
https://tex.stackexchange.com/a/29645/36900

The problem that I am facing now, however, is when I apply this decoration to curved paths:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\usetikzlibrary{decorations.markings}

\pgfdeclaredecoration{complete sines}{initial}
{
    \state{initial}[
        width=+0pt,
        next state=sine,
        persistent precomputation={\pgfmathsetmacro\matchinglength{
            \pgfdecoratedinputsegmentlength / round(\pgfdecoratedinputsegmentlength/\pgfdecorationsegmentlength)}
            \setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
        }] {}
    \state{sine}[width=\pgfdecorationsegmentlength]{
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-\pgfdecorationsegmentamplitude}}
        \pgfpathsine{\pgfpoint{0.25\pgfdecorationsegmentlength}{-\pgfdecorationsegmentamplitude}}
        \pgfpathcosine{\pgfpoint{0.25\pgfdecorationsegmentlength}{\pgfdecorationsegmentamplitude}}
}
    \state{final}{}
}

\begin{document}

\begin{tikzpicture}[line width=1. pt]

        \draw[decorate, decoration={snake}] 
        (3,-3) arc (180:-90:1);
        \draw[line width=0.3pt]
        (3,-3) arc (180:-90:1);

        \draw[decorate,decoration={complete sines}] 
        (4,1) arc (180:-90:1);
        \draw[line width=0.3pt]
        (4,1) arc (180:-90:1);

      \end{tikzpicture}

\end{document}

enter image description here

One clearly sees how these "decoration segments" are simply rotated and put behind one another resulting in a finite displacement with respect to the initial path, due the finite width of the pattern that is repeated.

I was wondering if there is the possibility to decorate a path "continuously". If I could specify the xy-displacement (x showing tangential to the initial path) of each point along the initial path like this

{0.}{\amplitude * sin(2*pi/\wavelength * \x)}

where \wavelength could be computed at the beginning as in the example above, and \x denotes the actual position along the initial path.

Superimposing an oscillation along the y-coordinate should also allow me to produce coils.

I guess one could define a decoration with segments of width 1pt but I thought that there must be a simpler solution.

Best Answer

Not sure it's exactly what is required and it certainly has the risk of being quite slow. But the approach taken here is to essentially plot the sine function along the path.

There is a high possibility of some inaccuracies/ugliness at the very end of the path, due to the low accuracy of TeX. Some ugliness will also occur along paths with acute angles.

\documentclass[border=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations}

\tikzset{/pgf/decoration/.cd,
    number of sines/.initial=10,
    angle step/.initial=20,
}
\newdimen\tmpdimen
\pgfdeclaredecoration{complete sines}{initial}
{
    \state{initial}[
        width=+0pt,
        next state=move,
        persistent precomputation={
            \pgfmathparse{\pgfkeysvalueof{/pgf/decoration/angle step}}%
            \let\anglestep=\pgfmathresult%
            \let\currentangle=\pgfmathresult%
            \pgfmathsetlengthmacro{\pointsperanglestep}%
                {(\pgfdecoratedremainingdistance/\pgfkeysvalueof{/pgf/decoration/number of sines})/360*\anglestep}%
        }] {}
    \state{move}[width=+\pointsperanglestep, next state=draw]{
        \pgfpathmoveto{\pgfpointorigin}
    }
    \state{draw}[width=+\pointsperanglestep, switch if less than=1.25*\pointsperanglestep to final, % <- bit of a hack
        persistent postcomputation={
        \pgfmathparse{mod(\currentangle+\anglestep, 360)}%
        \let\currentangle=\pgfmathresult%
    }]{%
        \pgfmathsin{+\currentangle}%
        \tmpdimen=\pgfdecorationsegmentamplitude%
        \tmpdimen=\pgfmathresult\tmpdimen%
        \divide\tmpdimen by2\relax%
        \pgfpathlineto{\pgfqpoint{0pt}{\tmpdimen}}%
    }
    \state{final}{
        \ifdim\pgfdecoratedremainingdistance>0pt\relax
            \pgfpathlineto{\pgfpointdecoratedpathlast}
        \fi
   }
}

\begin{document}

\begin{tikzpicture}[
    sines/.style={
        very thick,
        line join=round, 
        draw=black, 
        decorate, 
        decoration={complete sines, number of sines=15, amplitude=5pt}
    }
]

  \draw [red, postaction={sines}] 
      (0,0) .. controls +(-1,1) and +(1,-1) .. (2,2);

  \draw [red, postaction={sines}] 
        (1,3) circle [radius=1];

\end{tikzpicture}

\end{document}

enter image description here