# Coil path decoration without straight segment

When a path is decorated in TikZ, a straight line segment might be added at the end, depending on the length of the path. This is undesirable in some cases, and a very nice versatile resolution for the snake decoration has appeared in this question.

I would like get the same behavior for a coil decoration. That is, I would like TikZ to adjust the wavelength so that a straight line segment is avoided at the end of a coiled path. The option to start the path going in either direction would also be desirable.

My motivation is to avoid ugliness like the one in the gluon lines here.

-

Edit 2: Adapted code using \makeatletter and \makeatother to avoid the bad practice of midifying a package's file.

Edit: Changed code to make the coils end at the middle of the line.

If I understand correctly, you can get the result you are looking for by changing the predefined coil decoration. Although, the solution I propose is not prefect. The problem is that you need to change the segment length value a little bit to avoid too much (or too little) space at the end of the coil. Here are some examples, the blue coils correspond to the modified decoration:

\documentclass{article}

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

\makeatletter

% gluon decoration (based on the original coil decoration)

\pgfdeclaredecoration{gluon}{coil}
{
\state{coil}[switch if less than=%
0.5\pgfdecorationsegmentlength+%>
\pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude+%
\pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
width=+\pgfdecorationsegmentlength]
{
\pgfpathcurveto
{\pgfpoint@oncoil{0    }{ 0.555}{1}}
{\pgfpoint@oncoil{0.445}{ 1    }{2}}
{\pgfpoint@oncoil{1    }{ 1    }{3}}
\pgfpathcurveto
{\pgfpoint@oncoil{1.555}{ 1    }{4}}
{\pgfpoint@oncoil{2    }{ 0.555}{5}}
{\pgfpoint@oncoil{2    }{ 0    }{6}}
\pgfpathcurveto
{\pgfpoint@oncoil{2    }{-0.555}{7}}
{\pgfpoint@oncoil{1.555}{-1    }{8}}
{\pgfpoint@oncoil{1    }{-1    }{9}}
\pgfpathcurveto
{\pgfpoint@oncoil{0.445}{-1    }{10}}
{\pgfpoint@oncoil{0    }{-0.555}{11}}
{\pgfpoint@oncoil{0    }{ 0    }{12}}
}
\state{last}[next state=final]
{
\pgfpathcurveto
{\pgfpoint@oncoil{0    }{ 0.555}{1}}
{\pgfpoint@oncoil{0.445}{ 1    }{2}}
{\pgfpoint@oncoil{1    }{ 1    }{3}}
\pgfpathcurveto
{\pgfpoint@oncoil{1.555}{ 1    }{4}}
{\pgfpoint@oncoil{2    }{ 0.555}{5}}
{\pgfpoint@oncoil{2    }{ 0    }{6}}
}
\state{final}{}
}

\def\pgfpoint@oncoil#1#2#3{%
\pgf@x=#1\pgfdecorationsegmentamplitude%
\pgf@x=\pgfdecorationsegmentaspect\pgf@x%
\pgf@y=#2\pgfdecorationsegmentamplitude%
\pgf@xa=0.083333333333\pgfdecorationsegmentlength%
}

\makeatother

\begin{document}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (2,0) {B};
\path (a) edge[decorate,decoration={coil, amplitude=4pt,
segment length=5pt}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (2,0) {B};
\path (a) edge[color=blue,decorate,decoration={gluon, amplitude=4pt,
segment length=5.25pt}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (2,0) {B};
\path (a) edge[decorate,decoration={coil, amplitude=4pt,
segment length=5pt, aspect=0}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (2,0) {B};
\path (a) edge[color=blue,decorate,decoration={gluon, amplitude=4pt,
segment length=5.2pt, aspect=0}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (3.5,0) {B};
\path (a) edge[decorate,decoration={coil, amplitude=4pt,
segment length=5pt}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (3.5,0) {B};
\path (a) edge[color=blue,decorate,decoration={gluon, amplitude=4pt,
segment length=5.25pt}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (3.5,0) {B};
\path (a) edge[decorate,decoration={coil, amplitude=4pt,
segment length=5pt, aspect=0}] (b);
\end{tikzpicture}

\begin{tikzpicture}
\node (a) at (0,0) {A};
\node (b) at (3.5,0) {B};
\path (a) edge[color=blue,decorate,decoration={gluon, amplitude=4pt,
segment length=4.9pt, aspect=0}] (b);
\end{tikzpicture}

\end{document}
-
That looks really good, but I guess AndyS would prefer the coils to end at the middle of the line. –  Jake Aug 4 '12 at 10:20
Also possible, I will update my answer. But still one would need to play with the value of segment length. –  Gustavo Aug 4 '12 at 10:24
Note that you can also tweak the segment length value in the normal coil decoration, and for a right value you will see no straight line at the end. The advantage of the previous code is that such line is never drawn. But I guess this may not solve the OP question, as it does not calculate/modify segment lenght automatically. –  Gustavo Aug 4 '12 at 13:36
@Gustavo This is really good, albeit not entirely satisfactory because of the segment length hackery. Nevertheless, I think it's worth the bounty. Thank you! –  AndyS Aug 6 '12 at 4:46

Here is a version of Gustavo's code that calculates the segment length automatically. It can both shorten and lengthen the segment length. In order to only do one of these, remove the other and the > 0.5 check from the persistent precomputation.

\pgfpoint@oncoil is commented out because it is the same as the original from pgf.

The only real change is adding the initial state with the persistent precomputation.

\documentclass{article}

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

\makeatletter
\pgfdeclaredecoration{gluon}{initial}
{
\state{initial}[
width=+0pt,
next state=coil,
persistent precomputation={
\pgfmathsetmacro\matchinglength{
(ceil(\pgfdecoratedinputsegmentlength / \pgfdecorationsegmentlength) - \pgfdecoratedinputsegmentlength / \pgfdecorationsegmentlength) > 0.5
? (\pgfdecoratedinputsegmentlength - 2 * \pgfdecorationsegmentaspect * \pgfdecorationsegmentamplitude) / (floor(\pgfdecoratedinputsegmentlength / \pgfdecorationsegmentlength) + 0.499)
: (\pgfdecoratedinputsegmentlength - 2 * \pgfdecorationsegmentaspect * \pgfdecorationsegmentamplitude) / (ceil(\pgfdecoratedinputsegmentlength / \pgfdecorationsegmentlength) + 0.499)
}
\setlength{\pgfdecorationsegmentlength}{\matchinglength pt}
},
]{}
\state{coil}[
switch if less than=%
0.5\pgfdecorationsegmentlength%
+\pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude%
+\pgfdecorationsegmentaspect\pgfdecorationsegmentamplitude to last,
width=+\pgfdecorationsegmentlength,
]
{
\pgfpathcurveto
{\pgfpoint@oncoil{0    }{ 0.555}{ 1}}
{\pgfpoint@oncoil{0.445}{ 1    }{ 2}}
{\pgfpoint@oncoil{1    }{ 1    }{ 3}}
\pgfpathcurveto
{\pgfpoint@oncoil{1.555}{ 1    }{ 4}}
{\pgfpoint@oncoil{2    }{ 0.555}{ 5}}
{\pgfpoint@oncoil{2    }{ 0    }{ 6}}
\pgfpathcurveto
{\pgfpoint@oncoil{2    }{-0.555}{ 7}}
{\pgfpoint@oncoil{1.555}{-1    }{ 8}}
{\pgfpoint@oncoil{1    }{-1    }{ 9}}
\pgfpathcurveto
{\pgfpoint@oncoil{0.445}{-1    }{10}}
{\pgfpoint@oncoil{0    }{-0.555}{11}}
{\pgfpoint@oncoil{0    }{ 0    }{12}}
}
\state{last}[next state=final]
{
\pgfpathcurveto
{\pgfpoint@oncoil{0    }{ 0.555}{1}}
{\pgfpoint@oncoil{0.445}{ 1    }{2}}
{\pgfpoint@oncoil{1    }{ 1    }{3}}
\pgfpathcurveto
{\pgfpoint@oncoil{1.555}{ 1    }{4}}
{\pgfpoint@oncoil{2    }{ 0.555}{5}}
{\pgfpoint@oncoil{2    }{ 0    }{6}}
}
\state{final}{}
}

% same as in pgf, for reference
%\def\pgfpoint@oncoil#1#2#3{%
%  \pgf@x=#1\pgfdecorationsegmentamplitude%
%  \pgf@x=\pgfdecorationsegmentaspect\pgf@x%
%  \pgf@y=#2\pgfdecorationsegmentamplitude%
%  \pgf@xa=0.083333333333\pgfdecorationsegmentlength%