Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
1  
Good question, and a really clever way to draw the sleepers! –  Jake Aug 26 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 at 20:26
    
I tried the solution of tex.stackexchange.com/questions/102975/… without success: The decorations in the postactions were not drawn along the path. –  user61383 Aug 26 at 20:39
    
The first try of tex.stackexchange.com/questions/193243/… 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 at 20:44

2 Answers 2

up vote 15 down vote accepted

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

share|improve this answer
    
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. –  tohecz Aug 27 at 7: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 at 7: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 at 8:53

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

share|improve this answer
    
You need to draw the sleepers in a first pass, then the rails in a second pass. –  Nicholas Aug 27 at 13:28

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.