Drawing parallel paths with the nfold
library
For TikZ, nowadays the nfold
library by Jonathan can be used. It automatically deals with curves as well. The math isn't trivial for Bézier curves (see the appendix of the package's manual) but it works very well.
I'm using the same paths as in the old answer below but added two diagrams with a new curved path to it where you can see the shortcomings of this mathematical challange. The parallel curves aren't perfectly parallel but you notice this only with offset=±\pgflinewidth
. Interestingly, for just two lines with offset=±.5\pgflinewidth
it looks perfect.
Code
\documentclass[tikz]{standalone}
\usetikzlibrary{nfold}
\makeatletter
\tikzset{offset/.code=\tikz@addmode{\pgfgetpath\tikz@temp\pgfsetpath\pgfutil@empty\pgfoffsetpath\tikz@temp{#1}}}
\makeatother
\begin{document}
\tikz[nodes={circle, fill, inner sep=+1.5pt}]
\draw[preaction={rounded corners, offset=10pt, fill=gray}]
(-2, 3) node (A) {}
( 3, 4) node (B) {}
( 2,-1) node (C) {}
(-2,-4) node (D) {}
(-4, 0) node (E) {}
plot[sharp cycle, samples at={A, ..., E}] (\x);
\begin{tikzpicture}[line width=.25cm, opacity=.75]
\draw[help lines] (-.5, -.5) grid (7.5, 3.9);
\draw (0,2) rectangle (2,3);
\draw[offset=.5\pgflinewidth] (0,0) rectangle (2,1);
\draw[offset=-.5\pgflinewidth] (3,0) rectangle (4,3);
\path[xshift=5cm, offset=.5\pgflinewidth, draw, line join=round]
(0,0) -| (1,3) -- (2,1) -- (0,1) -- cycle;
\end{tikzpicture}
\tikz[line width=.2cm]
\draw[
postaction={draw=blue, offset=+ \pgflinewidth},
postaction={draw=red, fill=green!50!black, offset=+-\pgflinewidth},
bend left] (0,0) to ++(30:2) to ++ (-60:2) to ++(-150:2) to cycle;
\tikz[line width=.2cm]
\path[
postaction={draw=blue, offset=+ .5\pgflinewidth},
postaction={draw=red, fill=green!50!black, offset=+-.5\pgflinewidth},
bend left] (0,0) to ++(30:2) to ++ (-60:2) to ++(-150:2) to cycle;
\end{document}
Output (only curves)

Drawing parallel paths with a (decoration)
Just for fun (cough) with TikZ (and PGF decorations).
The biggest part of the decoration code has already been provided by Mark Wilbrow in his answer to Draw additional parallel paths in TikZ. His decoration contour lineto
works very fine but doesn’t deal with closed paths.
To work around this issue, the angle of the first segment (here (A) -- (B)
is saved in \pgf@decorate@firstsegmentangle
and later used if the input segment is a closepath
/-- cycle
.
If the contour line is drawn on the outer (longer) side of the polygon his code for the start
state
\pgfpathmoveto{\pgfpoint{0pt}{\pgfdecoratedcontourdistance}}%
would have been fine
but this point would lie on the outer side of a contour polygon that lies inside the original path. I don’t know a way to access the last segment’s angle to correct this in the very first state for the first segment. As a work-around the actual first segment of the contour line starts halfway between the first points (here, halfway between (A)
and (B)
).
This is sub-optimal. It is advised that the -- cycle
operator actually travels a certain distance (so that an angle can be calculated). A path like
\path (0,0) -| (1,1) -| (0,0) -- cycle;
cannot be decorated successfully.
If the option rounded corners
shall be used on the decorated contour line, the contour
option (and rounded corners
) has to be included in a pre- or postaction.
Code
\documentclass[tikz]{standalone}
\makeatletter
\usetikzlibrary{decorations,backgrounds}
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
decoration/contour distance/.code=%
\pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
\state{start}[
next state=draw,
width=0pt,
persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
\pgfpathmoveto{\pgfpointlineattime{.5}
{\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
{\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
}%
\state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
\ifpgf@decorate@is@closepath@%
\pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
-\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
\fi
\pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
-\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
\pgfpathlineto
{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
{\pgfdecoratedcontourdistance}}%
\ifpgf@decorate@is@closepath@%
\pgfpathclose
\fi
}%
\state{final}{}%
}
\makeatother
\tikzset{
contour/.style={
decoration={
name=contour lineto closed,
contour distance=#1
},
decorate}}
\begin{document}
\begin{tikzpicture}
\path
(-2, 3) coordinate (A)
( 3, 4) coordinate (B)
( 2,-1) coordinate (C)
(-2,-4) coordinate (D)
(-4, 0) coordinate (E);
\draw[preaction={contour=10pt, rounded corners, fill=gray}]
(A) -- (B) -- (C) -- (D) -- (E) -- cycle;
\foreach \coord in {A,...,E} \fill (\coord) circle [radius=2pt];
\end{tikzpicture}
\begin{tikzpicture}[gridded, line width=.25cm, opacity=.75]
\draw (0,2) rectangle (2,3);
\draw[contour=.5\pgflinewidth] (0,0) rectangle (2,1);
\draw[contour=-.5\pgflinewidth] (3,0) rectangle (4,3);
\path[xshift=5cm,contour=.5\pgflinewidth,draw,line join=round]
(0,0) -| (1,3) -- (2,1) -- (0,1) -- cycle;
\end{tikzpicture}
\end{document}
Output
