103

How can I center a TikZ node exactly between two others?

Hypothetically,

\node (a) {a}
\node (c) [right of=c] {c}
\node (b) [between={a,c}] {b}

2 Answers2

146

My suggestion is to use the calc library (see the pgfmanual 13.5 Coordinate Calculations - version October 25, 2010).

An example:

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

\begin{document}
\scalebox{4}{
\begin{tikzpicture}[text height=2ex]
\node (a) {a};
\node (c) [right of=a] {c};
\node (b) at ($(a)!0.5!(c)$) {b};
\end{tikzpicture}
}
\end{document}

The result:

enter image description here

Notice that the syntax you used is wrong: each node should end with ; and the node c can not be positioned at its right.

Notice that without specifying the text height, the nodes are not vertically aligned:

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

\begin{document}
\scalebox{4}{
\begin{tikzpicture}
\node (a) {a};
\node (c) [right of=a] {c};
\node (b) at ($(a)!0.5!(c)$) {b};
\end{tikzpicture}
}
\end{document}

enter image description here

See as reference Problem with TikZ and vertical alignment of text inside nodes.

The calc library, however, is not the only way to proceed. In his comment percusse suggested another approach:

\documentclass{article}   
\usepackage{tikz}

\begin{document}
\scalebox{4}{
\begin{tikzpicture}[text height=2ex]
\node (a) {a};
\node (c) [right of=a] {c};
\path (a) -- (c) node[midway] (b) {b};
\end{tikzpicture}
}
\end{document}

and I see even one more (ok, is not so convenient, but I report it for the sake of completness). Suppose you are using the positioning library and nodes are placed on grid; then for sure the node distance is set in some way so one could go as follows:

\documentclass{article}   
\usepackage{tikz}
\usetikzlibrary{positioning}
\pgfmathtruncatemacro\distance{1}

\begin{document}
\scalebox{4}{
\begin{tikzpicture}[text height=2ex, on grid]
\node (a) {a};
\node (c) [right=\distance cm of a] {c};
\node (b) [right=0.5\distance cm of a]{b};
\end{tikzpicture}
}
\end{document}

Both approaches lead to the result shown in the first example picture.

  • The code was only hypothetical. Not meant to be working code(just enough to get the point across). Anyways, your method works. Thanks. – AbstractDissonance Sep 14 '12 at 07:51
  • 7
    You can also add \path (a) -- (c) node[midway] (b) {b}; to the list. – percusse Sep 15 '12 at 11:04
  • 1
    @percusse: absolutly right! Actually I see also another way besides your: I'll edit my answer in a minute. – Claudio Fiandrino Sep 16 '12 at 09:12
  • 1
    Also, you can align the nodes with respect to their baseline to align them vertically, with anchor=base. – Bordaigorl Oct 14 '13 at 12:46
  • 1
    @Bordaigorl: are you sure? I suspect \begin{tikzpicture} \node[anchor=base] (a) {a}; \node[anchor=base] (c) [right of=a] {c}; \node[anchor=base] (b) at ($(a)!0.5!(c)$) {b}; \end{tikzpicture} won't vertically align a, b and c. Anyway, feel free to edit the answer with your example. :) – Claudio Fiandrino Oct 14 '13 at 13:05
  • @ClaudioFiandrino: yes what you need is more than just setting the anchor, you also need to use it =) \node[anchor=base] (a) {at}; \node[anchor=base] (c) [base right=of a] {cg}; \node[anchor=base] (b) at ($(a.base)!0.5!(c.base)$) {b}; \draw (a.base) -- (c.base); would do. Similarly you can use a \path (a.base) -- (c.base) node[midway] (b) {b}. Shall I edit the answer? Post another? – Bordaigorl Oct 14 '13 at 15:18
  • @Bordaigorl: as you like more. No problems from my side, and if you want to post a new answer, you have already got a +1 :) – Claudio Fiandrino Oct 14 '13 at 16:01
  • @ClaudioFiandrino I added an answer because I also attempted at the definition of the between key. If you know better solutions, comments are welcome! – Bordaigorl Oct 14 '13 at 17:30
  • @Bordaigorl: excellent! Good idea the between key :) – Claudio Fiandrino Oct 14 '13 at 19:28
  • @ClaudioFiandrino You need to use .5*\distance in the last example. Otherwise you produce .51cm instead of .5*1cm. (Just use 2 as distance.) Instead of having three examples each with one solution, why don’t you add one example with all solutions, also showing the difference between them. (The one at the path places the new nodes between the borders of the other nodes (as long as you don’t specify an anchor or use a coordinate anyway), here between a.east and c.west, while calc’s syntax (again without anchors) places the new node between the centers.) – Qrrbrbirlbel Oct 15 '13 at 02:10
  • There is also the barycentric cs which you can use to place a node “between” more than two nodes (it works for two, too). calc’s not even needed. :) – Qrrbrbirlbel Oct 15 '13 at 02:17
  • @Qrrbrbirlbel: good point! As soon as possible I revise the answer :) – Claudio Fiandrino Oct 15 '13 at 19:27
  • You might want to add that for centering text in node b between text in nodes a and c, the anchors east and west are very helpful. – Chris Chudzicki Aug 02 '14 at 17:27
  • @mrc: Unfortunately that's not always true. Try adding draw option to the nodes in the example where they're not aligned. You will discover that they have different heights. Hence, under such an hypothesis, it is not guarantee that the positioning via .east and .west anchors really achieves vertical alignment. – Claudio Fiandrino Aug 03 '14 at 07:45
  • What I meant was in a case where node (a) has text {A} and node (c) has much wider text, e.g., {cccc}. In such a case you probably don't want the horizontal centering to be with respect to the center of A and the center of cccc, but rather with the right edge and left edge, respectively. I used this method yesterday and it worked very well (and I did vertical centering with \strut). – Chris Chudzicki Aug 03 '14 at 10:30
32

In addition to @Claudio's detailed answer here's a more concise solution which also aligns the nodes with respect to the text's baseline without requiring an explicit specification of the text height.

The following does not require calc and only requires positioning because of the use of base right which is included to be faithful to the example in the question. Otherwise, absolute positioning (i.e. with at) would work as expected.

\begin{tikzpicture}[anchor=base]
    \node (a) {a};
    \node (c) [base right=of a] {c};
    \path (a.base) -- node (b) {b} (c.base);
\end{tikzpicture}

Setting the anchor to base globally makes the code more concise but you can apply it to each node individually.

The third line creates the node (b) midway the line connecting the baselines of the other two nodes.

Also note that the key right of is deprecated in favour of right=of ... (see this answer).


Addendum: how to create a between key

Here's a way to actually provide the syntax proposed in the question.

\tikzset{
    between/.style args={#1 and #2}{
         at = ($(#1)!0.5!(#2)$)
    }
}

Then you can write something like \node[between=a and c] (b) {b};. Again, if you want the baseline alignment you have to write between=a.base and c.base or write an abbreviation as

\tikzset{
    between base/.style args={#1 and #2}{
        between=#1.base and #2.base
    }
}

so you can just write between base=a and c.

This however requires the calc library.

Bordaigorl
  • 15,135