25

I like to draw a double line such that the gap between the two lines is transparent. My purpose is to draw railway tracks:

 \documentclass[tikz]{standalone}

 \tikzstyle{track}=[
   postaction={draw=gray,densely dashed,line width=14pt},
   postaction={draw=black,double distance=8pt,line width=2pt},
   postaction={draw=gray,densely dashed,line width=8pt},]

 \begin{document}

 \begin{tikzpicture}

 \draw[track] (-5,27) to[out=  0,in=180] ( 5,33) 
                      to[out=  0,in= 90] ( 7,25) 
                      to[out=270,in=  0] (-5,27);

 \draw[track] (-4,10) to (0,10) to[out=  0,in=270] (4,16);
 \draw[track] (12,10) to (8,10) to[out=180,in=270] (4,16) to (4,20);

 \draw[track] (-4,0) to (12,0);

 \draw[track] (0,0) to[out=  0,in=270] (3,2) 
                    to[out= 90,in=270] (1,4)
                    to[out= 90,in=180] (4,6) 
                    to[out=  0,in=120] (8,5) 
                    to[out=300,in=  0] (4,0);
 \end{tikzpicture}

 \end{document}

The second postaction draws a double line, but the gap between the two lines is not transparent. Therefore, I made the third postaction. But I expect that the tracks will look much more realistic (in particular at the switches) if the gap of the double lines is transparent. Any ideas?

user61383
  • 353
  • 1
    Good question, and a really clever way to draw the sleepers! – Jake Aug 26 '14 at 20:17
  • 1
    double distance (or simply double) draws the path twice---one with the base color, and one with the inbetween color. If you specify double=transparent, so that the inbetween color is transparent, you will see that instead of getting a white fill, it is all solid black. If you want a different solution than your own, double distance might not be the appropriate command. A different approach could be to instead draw two black lines---one shifted to the left and one shifted to the right. – eiterorm Aug 26 '14 at 20:26
  • I tried the solution of https://tex.stackexchange.com/questions/102975/draw-additional-parallel-paths-in-tikz without success: The decorations in the postactions were not drawn along the path. – user61383 Aug 26 '14 at 20:39
  • The first try of http://tex.stackexchange.com/questions/193243/how-to-improve-on-this-twolines-decoration/193668#193668 seems to be quite close, but I could not make it working. The other solutions there use knots or double which have the same non-transparent issue. – user61383 Aug 26 '14 at 20:44

3 Answers3

21

This can be done (admittedly with some effort) using fadings and decorations. The following is I guess more proof-of-concept than anything else but might prove useful:

\documentclass[tikz, border=5]{standalone}
\usetikzlibrary{decorations,fit,fadings}

% Layers
\pgfdeclarelayer{sleeper}
\pgfdeclarelayer{rail}
\pgfsetlayers{sleeper,rail,main}

\pgfdeclaredecoration{tracks}{final}{%
\state{final}{%
  \pgftransformreset% <- I think is possibly vital.
  %
  % Get bounding box of decorated path as a node.
  % Must do it this way using basic layer.
  \pgftransformshift{\pgfpointanchor{current path bounding box}{south west}}%
  \pgfcoordinate{@1}\pgfpointorigin%
  \pgftransformshift{\pgfpointanchor{current path bounding box}{north east}}%
  \pgfcoordinate{@2}\pgfpointorigin%
  \node [fit=(@1)(@2), inner sep=\railsep+2*\railwidth] (@@) {};
  %
  % Create a fading for the track.
  \pgfinterruptpicture%
    \begin{tikzfadingfrompicture}[name=tracks]
    \path[draw=transparent!0, line width=\railsep+2*\railwidth, 
     postaction={draw=transparent!100, line width=\railsep}] 
      \pgfextra{\pgfsetpath\pgfdecoratedpath};
    \useasboundingbox (@@.south west) (@@.north east);
  \end{tikzfadingfrompicture}%
  \endpgfinterruptpicture
  %
  % Draw sleepers.
  \ifx\sleeperlayer\emptylayer\else\pgfonlayer{\sleeperlayer}\fi%
  \draw [draw=\sleepercolor,line width=\sleeperlength, dash pattern=on \sleeperwidth off \sleepersep, every sleeper/.try]
    \pgfextra{\pgfsetpath\pgfdecoratedpath};
  \ifx\sleeperlayer\emptylayer\else\endpgfonlayer\fi%
  %
  % Draw the track
  \ifx\raillayer\emptylayer\else\pgfonlayer{\raillayer}\fi%
  \fill [path fading=tracks, fit fading=false, 
    fading transform={shift=(@@.center)}, fill=\railcolor] 
   (@@.south west) rectangle (@@.north east);
   \ifx\raillayer\emptylayer\else\endpgfonlayer\fi%
}
}
\def\emptylayer{}
\tikzset{%
  track/.style={
    decoration=tracks, decorate
  },
  decorations/.cd,
    rail sep/.store in=\railsep,
    rail width/.store in=\railwidth,
    rail color/.store in=\railcolor,
    rail layer/.store in=\raillayer,
    sleeper sep/.store in=\sleepersep,
    sleeper width/.store in=\sleeperwidth,
    sleeper length/.store in=\sleeperlength,
    sleeper color/.store in=\sleepercolor,
    sleeper layer/.store in=\sleeperlayer,
    rail sep=4pt,
    rail width=1pt,
    rail color=black,
    rail layer=rail,
    sleeper sep=6pt,
    sleeper width=1pt,
    sleeper length=10pt,
    sleeper color=gray,
    sleeper layer=sleeper,
}
\begin{document}   
\begin{tikzpicture}
\draw [track] (-2,5) to (0,5) to[out=  0,in=270] (2,8);
\draw [track] (6,5) to (4,5) to[out=180,in=270] (2,8) to (2,10);
\end{tikzpicture}   
\end{document} 

enter image description here

Mark Wibrow
  • 70,437
  • Remark: If you wanted to add the background, you can, there are z-layers in TikZ, it's a bit complicated (you need to scope everything) but it works. – yo' Aug 27 '14 at 07:54
  • This solves my problem. Additionally, I will split the decoration into two such that I can draw all sleepers at a layer in a first step and then draw all rails on another layer--like tracks are build in practice. – user61383 Aug 27 '14 at 07:55
  • 1
    @user61383 you don't necessarily have to split the decoration as the the decoration can use different layers. See the updated answer – Mark Wibrow Aug 27 '14 at 08:53
  • Why do you think the reset is vital? I tried commenting it out and couldn't tell any difference. I was trying to figure out how to draw outlined tracks in one step. I assumed commenting it out would break everything, but wanted to know what it did. However, I couldn't tell the difference. (I also couldn't figure out how to combine my steps: https://tex.stackexchange.com/a/418299/ :(. At least, I think I could combine them by changing the decoration code more radically, but I was trying to do it without that & just using e.g. preaction/postaction. Obviously a dumb idea!) – cfr Mar 03 '18 at 21:52
  • This used to work but stopped (I suspect after an update of tikz). The track lines show up at the wrong places. The MWE in the original post works. – GrB Feb 20 '22 at 18:45
13

I think this is the best suggestion I have:

\documentclass[tikz]{standalone}
\usetikzlibrary{decorations.pathmorphing}

\tikzstyle{track}=[
    postaction={draw=gray,densely dashed,line width=14pt},
    postaction={draw,decorate,decoration={curveto,raise=4pt},line width=2pt},
    postaction={draw,decorate,decoration={curveto,raise=-4pt},line width=2pt}]

\begin{document}

\begin{tikzpicture}

    \path[track] (-5,27)    to[out=  0,in=180] ( 5,33) 
                            to[out=  0,in= 90] ( 7,25) 
                            to[out=270,in=  0] (-5,27);

    \path[track] (-4,10)    to ( 0,10) to[out=  0,in=270] (4,16);
    \path[track] (12,10)    to ( 8,10) to[out=180,in=270] (4,16) to (4,20);

    \path[track] (-4, 0)    to (12, 0);

    \path[track] ( 0, 0)    to[out=  0,in=270] (3,2) 
                            to[out= 90,in=270] (1,4)
                            to[out= 90,in=180] (4,6) 
                            to[out=  0,in=120] (8,5) 
                            to[out=300,in=  0] (4,0);
\end{tikzpicture}

\end{document}

As you can see, this creates a problem at the endpoints, where the rails meet at the center line. By defining a custom decoration (somewhat beyond my experience), you might be able to also make the curveto line start at a raised point. If not, a crude workaround would be to erase the endpoints by drawing a white rectangle on top of them.

output example

eiterorm
  • 1,813
  • 2
  • 15
  • 29
3

Here is another approach that uses blend group.

\documentclass[border=9,tikz]{standalone}
\begin{document}
\tikz{
    \path[save path=\cloud](0,0)..controls(5,5)..(10,0);
    \path[save path=\hill](0,5)..controls(0,0)and(10,0)..(10,5);
    \begin{scope}[blend group=darken]
        \begin{scope}[dash pattern=on2off4,line width=12,darkgray]
            \draw[use path=\cloud];
            \draw[use path=\hill];
        \end{scope}
        \begin{scope}[blend group=normal]
            \draw[use path=\cloud][line width=8];
            \draw[use path=\cloud][line width=6,white];
        \end{scope}
        \begin{scope}[blend group=normal]
            \draw[use path=\hill][line width=8];
            \draw[use path=\hill][line width=6,white];
        \end{scope}
    \end{scope}
}
\end{document}

Symbol 1
  • 36,855