Draw additional parallel paths in TikZ

How can parallel lines elegantly be added to an existing path? I am looking for a solution or hint to, how a general style can be made, that works with any path.

Example path:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\draw (0,0) -- (1,1) -| (2,2) -- (3,2) -- (3,1);
\end{tikzpicture}
\end{document}


Same path, with customizable extra lines:

Here I have drawn two extra lines, just to show, what I mean. Ideally a solution should only add one extra line, but somehow be expandable to any number of lines.

The question: How to build parallel paths between nodes?, where my solution is less than elegant, has inspired me to ask this question.

-
I'm not sure if the "parallel path" is well defined. Intuitively I can see what you mean, but, what would be its mathematical definition? For example, lets say that the parallel path at distance X is the path such that, at every point, the minimal distance of that point to the original path is X. This sound a reasonable definition, but would produce rounded corners instead of angles... –  JLDiaz Mar 17 '13 at 23:45
@JLDiaz: I see your point. Let us say our original path is closed and not self intersecting (a 'loop' of sort). Then you can take each line segment and shift it parallel outward(or inward). Extend(or shorten) the line segments, to make the new path. I am sure that similar ideas can be applied to the general path. –  Hans-Peter E. Kristiansen Mar 17 '13 at 23:55
Does the path consist of straight line segments only? –  g.kov Mar 18 '13 at 1:28
@g.kov: I must admit, that I do not have an actual application for this ...yet. I am sure that a solution that works for straight line segments will be useful for me and others. –  Hans-Peter E. Kristiansen Mar 18 '13 at 1:42
What (I think) is required here, is what vector graphics applications call "outsetting" and "insetting" (usually with closed paths). The shifting/shortening approach is (very) broadly correct, but AFAIK you also need to take into account the angle between consecutive segments. It's ages since I looked at this, but I think a segment should be lengthened by -h*cot(A/2) (A is the angle between segments, h is the outset distance). –  Mark Wibrow Mar 18 '13 at 10:13

It turns out some of the work is already done, but an important macro \pgfdecoratedangletonextinputsegment is undocumented (and incorrectly defined - possibly why it is undocumented). Here's a not-very-well tested solution for straight lines only with no mid-path moveto commands. I think dealing with curveto and closepath segments will also prove extra-tricky.

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{decorations}

\def\pgfdecoratedcontourdistance{0pt}

\pgfkeys{/pgf/decoration/contour distance/.code={%
\pgfmathparse{#1}%
\let\pgfdecoratedcontourdistance=\pgfmathresult}%
}

\pgfdeclaredecoration{contour lineto}{start}
{
\state{start}[next state=draw, width=0pt]{
\pgfpathmoveto{\pgfpoint{0pt}{\pgfdecoratedcontourdistance}}%
}
\state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{
\pgfmathparse{-\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
\let\shorten=\pgfmathresult%
\pgfpathlineto{\pgfpoint{\pgfdecoratedinputsegmentlength+\shorten}{\pgfdecoratedcontourdistance}}%
}
\state{final}{
\pgfpathlineto{\pgfpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}%
}
}

\begin{document}

\begin{tikzpicture}

\draw [
postaction={
decoration={contour lineto, contour distance=-5pt},
draw=red, dotted, decorate},
postaction={
decoration={contour lineto, contour distance=15pt},
draw=blue, dashed, decorate},
postaction={
decoration={contour lineto, contour distance=10pt},
draw=green, decorate}
]
(0, 0) -- (3, 1) -- (4, 4) -- (6, 4) --
(8,-1) -- (2,-2) -- (5, 2) -- (6, 0);

\end{tikzpicture}

\end{document}


-
This looks like it is exactly, what I imagined. Does your comment about an incorrectly defined macro means, that there is anything wrong with this code? When time permits, I will try to understand the low level part of your code. –  Hans-Peter E. Kristiansen Mar 18 '13 at 16:43
Check out closed path, e.g. (0,0)--(6,0)--(3,4.5)--cycle; It might be a good idea to add edge constrains as the lines that intersect the path at the first and the last point. –  g.kov Mar 19 '13 at 6:03
@Hans-PeterE.Kristiansen: in the low-level decorations code the macro is (for some reason) defined as B-A instead of A+180-B where A and B are the angle from the horizontal of consecutive segments. The calculation in the cot function given above takes this into account. @k.gov: As it says in the answer "dealing with ... closepath segments will prove extra-tricky". By "extra-tricky", I mean probably impossible to do with out ugly hacking. –  Mark Wibrow Mar 19 '13 at 6:55
I’d love your input on this answer which uses almost all of your decoration code. –  Qrrbrbirlbel Sep 16 '13 at 22:03