71

Is it possible to draw edges or paths in the background of nodes independently of whether they are constructed before or after the nodes are defined?

In the following example, the \draw instruction comes after the nodes are declared and so the path from (foo) to (baz) crosses the node (bar).

\documentclass{article}

\usepackage{tikz}

\begin{document}
  \begin{tikzpicture}
    \node [fill=gray!30] (foo) at (0,0) { foo };
    \node [fill=gray!30] (bar) at (2,0) { bar };
    \node [fill=gray!30] (baz) at (4,0) { baz };

    \draw (foo) -- (baz);
  \end{tikzpicture}
\end{document}

What I would like to achieve is that the path crosses (bar) in the background, so the node is not crossed visually. Is this possible without changing the order of the instructions (in particular, without moving the creation of (bar) below the \draw instruction)?

Here is how the above example looks like:

edge crossing the node in the foreground

And here is the desired result:

edge crossing the node in the background

1
  • 3
    Maybe you can just use \draw (foo) -- (bar) -- (baz);? Commented May 4, 2018 at 19:32

2 Answers 2

98

TikZ/PGF has a concept of layers. They are described in chapter 82 “Layered Graphics” in the (v2.10) manual and can be used to have later commands be drawn below the things before them. In your example, you could write

\documentclass{article}
\usepackage{tikz}

\pgfdeclarelayer{bg}    % declare background layer
\pgfsetlayers{bg,main}  % set the order of the layers (main is the standard layer)

\begin{document}
  \begin{tikzpicture}
    \node [fill=gray!30] (foo) at (0,0) { foo };
    \node [fill=gray!30] (bar) at (2,0) { bar };
    \node [fill=gray!30] (baz) at (4,0) { baz };

    \begin{pgfonlayer}{bg}    % select the background layer
        \draw (foo) -- (baz);
    \end{pgfonlayer}
  \end{tikzpicture}
\end{document}

Actually, the backgrounds library (see chapter 25 in v2.10) already defines a background layer and a key that has to be used on a scope to select the layer:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}

\begin{document}
  \begin{tikzpicture}
    \node [fill=gray!30] (foo) at (0,0) { foo };
    \node [fill=gray!30] (bar) at (2,0) { bar };
    \node [fill=gray!30] (baz) at (4,0) { baz };

    \begin{scope}[on background layer]
        \draw (foo) -- (baz);
    \end{scope}
  \end{tikzpicture}
\end{document}

(The two solutions compile to the same code (except that the backgrounds library calls the layer background).)

5
  • I've used layers before but I wonder if it's possible to achieve what I want without drawing the path in a scope on the background layer. Are there any TikZ keys to define the layer per path/node or anything similar? Commented May 14, 2011 at 2:11
  • @Jannis: None that I know.
    – Caramdir
    Commented May 14, 2011 at 2:22
  • 1
    I talked to Till and soon edges might be drawn in the background of nodes by default, at least inside the new \graphs{} syntax that will be part of the next release. Commented Jul 18, 2011 at 19:25
  • There are situations where the two solutions aren't equivalent. For example, in case the background picture needs to refer to the current bounding box node, only the method with the backgrounds library will give the expected output.
    – T. Verron
    Commented May 6, 2013 at 16:22
  • In this solution, filling does the job of covering the line on the background layer. If I need (bar) to have a transparent background, the part of the line underneath it is visible. Is there any workaround simpler than something like \draw (foo.east) -- (bar.west) (bar.east) -- (baz.west) ;?
    – And R
    Commented Apr 14, 2023 at 10:19
1

I've come a long way since the question was asked. I answer it simply as an example, to show that there are simple solutions to seemingly complex problems.

These two solutions show that simply placing the nodes on the same path is enough to solve the problem.

\documentclass[tikz, border=5mm]{standalone}

\begin{document}
  \begin{tikzpicture}[every node/.style={fill=gray!30}]
    \draw (0,0) node (foo){ foo }-- (4,0)node (baz) { baz }
    node (bar)at(2,0) { bar };

\begin{scope}[yshift=-1cm]
    \draw (0,0) node (foo){ foo }-- (4,0)node (baz) { baz }
    node[midway] (bar) { bar };
\end{scope}

  \end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .