5

I need to evenly distribute two (or more) nodes along a given distance, cf. the MWE below:

enter image description here

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning, calc}
\begin{document}
\begin{tikzpicture}[
    text node/.style={rectangle, draw}
]
    \small

    \begin{scope}
        \node (aa) at (0,0) {};
        \node (ab) at (14,0) {};
        \node (c) [text node, right=of aa] {This is a rather long node with text};
        \node (d) [text node, right=of c] {Short};  
    \end{scope}

    \begin{scope}[shift={(0,-1)}]
        \node (ba) at (0,0) {};
        \node (bb) at (14,0) {};
        \node (c2) [text node] at ($(ba)!0.33!(bb)$) {This is a rather long node with text};
        \node (d2) [text node] at ($(ba)!0.66!(bb)$) {Short};   
    \end{scope}

    \foreach \n in {aa, ab, c, d, ba, bb, c2, d2}
        \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}
\end{document}

When simply placing the nodes next to each other, they obviously clutter at the right of the distance (first case). So I tried to compute their placement along the distance, but this approach fails due to the different sizes of the nodes (second case). How can I compute the positions of the nodes so that the distance between them and to the sides is equal?

5

This answer proceeds in stages. If you just want the final code, scroll to the bottom.


Proof-of-concept, maybe. Note that nodes always have dimensions, even if they are empty. So your picture is misleading in the sense that you do not show the borders of the nodes between which the other nodes are placed. However, their dimensions must be accounted for if the red marks are intended as the relevant points, since those are not on the borders of the nodes.

\documentclass[tikz,multi,border=10pt]{standalone}
\usetikzlibrary{positioning, calc}
\begin{document}
\begin{tikzpicture}[
  text node/.style={rectangle, draw, inner sep=2.5pt},
  spread two/.code n args=6{%
    \pgfmathsetmacro\firstwidth{width("#3")}%
    \pgfmathsetmacro\secondwidth{width("#5")}%
    \path ($(#6.west)-(#1.east)$) ++(-\firstwidth-\secondwidth-10 pt,0) coordinate (a);
    \node (#2) [text node, anchor=west] at ($(#1.east)+1/3*(a)$) {#3};
    \node (#4) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#5};
  },
  ]
  \small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two={aa}{c}{This is a rather long node with text}{d}{Short}{ab}}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}
\end{document}

proof-of-concept

Probably the \let syntax might offer a more convenient approach.


Update

It is possible to somewhat automate this. For example, we can get the value of inner xsep rather than needing to hard-code it. (Above, I used 2.5pt. The default is .3333em.)

\begin{tikzpicture}[
  text node/.style={rectangle, draw},
  spread two/.code n args=6{%
    \pgfmathsetmacro\firstwidth{width("#3")}% 145.58443 pt for testing below (default inner xsep is .3333em)
    \pgfmathsetmacro\secondwidth{width("#5")}% 22.1179 pt
    \path ($(#6.west)-(#1.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#2) [text node, anchor=west] at ($(#1.east)+1/3*(a)$) {#3};
    \node (#4) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#5};
  },
  ]
  \small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two={aa}{c}{This is a rather long node with text}{d}{Short}{ab}}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}

As before, this gives us

auto-find all dimensions

[It is a little different because this version uses the default inner xsep since I didn't tell it otherwise, but that's all.]

Here's a check that it does what we'd expect:

\begin{tikzpicture}[
  text node/.style={rectangle, draw},
  spread two/.code n args=6{%
    \pgfmathsetmacro\firstwidth{width("#3")}%
    \pgfmathsetmacro\secondwidth{width("#5")}%
    \path ($(#6.west)-(#1.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#2) [text node, anchor=west] at ($(#1.east)+1/3*(a)$) {#3};
    \node (#4) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#5};
  },
  ]
  \small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two={aa}{c}{This is a rather long node with text}{d}{Short}{ab}}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
  \node [red, inner sep=0pt] at (a) {a};
  \path ($(ab.west)-(aa.east)$) ++(-167.7pt,0) ++(-1.3333em,0) node [blue,inner sep=0pt] {a};
  \node [blue, inner sep=0pt] at ($(aa.east)+1/3*(a)$) {x};
  \node [blue, inner sep=0pt] at ($(ab.west)-1/3*(a)$) {x};
  \node [blue, inner sep=0pt, xshift=145.58443pt] at ($(aa.east)+1/3*(a)+(.6667em,0)$) {x};
  \node [blue, inner sep=0pt, xshift=145.58443pt] at ($(aa.east)+2/3*(a)+(.6667em,0)$) {x};
\end{tikzpicture}

which gives us

placement check

showing the blue marks where we'd expect.

However, the syntax leaves something to be desired. We might, therefore, change our definition of spread two to be a little more meaningful. For example,

\tikzset{%
  text node/.style={rectangle, draw},
  spread two/.code args={#1 and #2 with contents #3 and #4 between #5 and #6}{%
    \pgfmathsetmacro\firstwidth{width("#3")}%
    \pgfmathsetmacro\secondwidth{width("#4")}%
    \path ($(#6.west)-(#5.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#1) [text node, anchor=west] at ($(#5.east)+1/3*(a)$) {#3};
    \node (#2) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#4};
  },
}

Now we can write

\begin{tikzpicture}\small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two=c and d with contents {This is a rather long node with text} and {Short} between aa and ab}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}

to produce

more meaning

Complete code:

\documentclass[tikz,multi,border=10pt]{standalone}
\usetikzlibrary{positioning,calc}
\begin{document}
\begin{tikzpicture}[
  text node/.style={rectangle, draw},
  spread two/.code n args=6{%
    \pgfmathsetmacro\firstwidth{width("#3")}% 145.58443 pt for testing below (default inner xsep is .3333em)
    \pgfmathsetmacro\secondwidth{width("#5")}% 22.1179 pt
    \path ($(#6.west)-(#1.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#2) [text node, anchor=west] at ($(#1.east)+1/3*(a)$) {#3};
    \node (#4) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#5};
  },
  ]
  \small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two={aa}{c}{This is a rather long node with text}{d}{Short}{ab}}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}
\begin{tikzpicture}[
  text node/.style={rectangle, draw},
  spread two/.code n args=6{%
    \pgfmathsetmacro\firstwidth{width("#3")}%
    \pgfmathsetmacro\secondwidth{width("#5")}%
    \path ($(#6.west)-(#1.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#2) [text node, anchor=west] at ($(#1.east)+1/3*(a)$) {#3};
    \node (#4) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#5};
  },
  ]
  \small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two={aa}{c}{This is a rather long node with text}{d}{Short}{ab}}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
  \node [red, inner sep=0pt] at (a) {a};
  \path ($(ab.west)-(aa.east)$) ++(-167.7pt,0) ++(-1.3333em,0) node [blue,inner sep=0pt] {a};
  \node [blue, inner sep=0pt] at ($(aa.east)+1/3*(a)$) {x};
  \node [blue, inner sep=0pt] at ($(ab.west)-1/3*(a)$) {x};
  \node [blue, inner sep=0pt, xshift=145.58443pt] at ($(aa.east)+1/3*(a)+(.6667em,0)$) {x};
  \node [blue, inner sep=0pt, xshift=145.58443pt] at ($(aa.east)+2/3*(a)+(.6667em,0)$) {x};
\end{tikzpicture}
\tikzset{%
  text node/.style={rectangle, draw},
  spread two/.code args={#1 and #2 with contents #3 and #4 between #5 and #6}{%
    \pgfmathsetmacro\firstwidth{width("#3")}%
    \pgfmathsetmacro\secondwidth{width("#4")}%
    \path ($(#6.west)-(#5.east)$) ++(-\firstwidth-\secondwidth-4*\pgfkeysvalueof{/pgf/inner xsep},0) coordinate (a);
    \node (#1) [text node, anchor=west] at ($(#5.east)+1/3*(a)$) {#3};
    \node (#2) [text node, anchor=east] at ($(#6.west)-1/3*(a)$) {#4};
  },
}
\begin{tikzpicture}\small
  \node (aa) at (0,0) {};
  \node (ab) at (14,0) {};
  \tikzset{spread two=c and d with contents {This is a rather long node with text} and {Short} between aa and ab}
  \foreach \n in {aa, ab, c, d}
  \draw[red, shift=(\n.center)] plot[mark=x] coordinates{(0,0)};
\end{tikzpicture}
\end{document}
  • Why do you subtract 10 pt when calculating coordinate (a)? – Dux Feb 18 '16 at 10:13
  • @Dux The inner sep is 2.5pt and there are 4 lots of it. Hence 10pt. – cfr Feb 18 '16 at 20:27
  • is there a way to get this value from tikz keys to automate the command a bit? – Dux Feb 19 '16 at 9:34
  • @Dux Probably but I wasn't sure quite how to do it. To be honest, I assumed that somebody else would have answered by now. I just intended this answer as a beginning i.e. here's how you can get things in the right places manually. I thought somebody would take that (or use a different method) and provide a convenient macro or something ;). – cfr Feb 19 '16 at 13:19
  • However, you must be able to recover the value of inner sep from the keys somehow..., mustn't you? I mean, it must be there. At least that should be doable. I'll have a look. – cfr Feb 19 '16 at 13:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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