# Creating a node fitting the horizontal width of two other nodes

I have the two PGF nodes foo and bar positioned in a row.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\end{tikzpicture}
\end{document}


How can I create a third node below those two nodes with the width of foo, bar and the node distance between them? Is there a way to do this with the fit library or is this the wrong approach?

-
–  Qrrbrbirlbel Dec 19 '13 at 17:46

You can use yshift together with fit and inner sep=0pt to get a node of the same height and width as the other nodes, but shifted vertically. Note that the placement of the node text is different than in a normal node, so I would suggest you use the label=center:<text> option to place the text instead. As Martin points out, you should also set the outer sep of the nodes you're fitting around to 0pt, as otherwise your new node will be too large by \pgflinewidth.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,fit}
\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={outer sep=0pt, draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\node [
mynode,
inner sep=0pt,
yshift=-1cm,
fit={(foo) (bar)},
label=center:foobar] {};
\end{tikzpicture}
\end{document}


Below is an approach to get the vertical spacing between the old nodes and the newly created one right. Using the calc library, you can shift the new node down by the height of the old nodes by using ($(foo.south) - (foo.north)$) You can't directly read the value of node distance, so I've appended code to store the value in a new key that can be read in a yshift.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,fit,calc}

\pgfkeys{
/tikz/node distance/.append code={
\pgfkeyssetvalue{/tikz/node distance value}{#1}
}
}

\begin{document}
\begin{tikzpicture}[
node distance=0.2cm,
mynode/.style={
draw,
outer sep=0pt
}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\node [
mynode,
inner sep=0pt,
shift=($(foo.south)-(foo.north)$),
yshift=-\pgfkeysvalueof{/tikz/node distance value},
fit={(foo) (bar)},
label=center:foobar] {};
\end{tikzpicture}
\end{document}


A different approach is using the let syntax to calculate the difference between bar.east and foo.west, and using that to set the minimum width of the new node:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc}
\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\path let
\p1=(foo.west),
\p2=(bar.east)
in node [
mynode,
below=of foo.south west,
anchor=north west,
minimum width=\x2-\x1-\pgflinewidth
] {foobar};
\end{tikzpicture}
\end{document}

-
The inner sep was what I didn't know about yet. If someone knew a way to calculate the yshift automatically I'd be interested to hear it. –  h0b0 Jul 19 '11 at 12:20
The outer sep of the first two nodes should also create issues. It places the anchors on the outside of the node border, not in the middle of it. This should cause the fitted node to be 2x.5\pgflinewidth wider. –  Martin Scharrer Jul 19 '11 at 12:35
@Martin: Good point, thanks for that! –  Jake Jul 19 '11 at 12:57
@h0b0: I assume you want the same gap between the old and the new node as you have between the two old ones? I've edited my answer to show one possible way of doing this. –  Jake Jul 19 '11 at 12:58
@Jake: I suffered from that dearly. The issue is also that you can't get the original outer sep of the referenced nodes afterwards. –  Martin Scharrer Jul 19 '11 at 12:59

Another one with a getdist=p1 and p2 syntax. It gets the left border of the first and right of the second. I'm not sure if this is simpler but slightly cleaner.

\documentclass[border=3mm]{standalone}
\usepackage{tikz}

\makeatletter
\tikzset{
getdist/.style args={#1 and #2}{
getdistc={#1}{#2},minimum width=\mylength-\pgflinewidth
},
getdistc/.code 2 args={
\pgfextra{
\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
\xdef\mylength{\the\pgf@x}
}
}
}
\makeatother

\begin{document}
\begin{tikzpicture}
\node[ultra thick][draw] (f) {foo};
\node[draw,ultra thin] at (1cm,0.5cm) (b) {bar};

\node[anchor=west,getdist=f and b,draw] at ([yshift=-6mm]f.west) {foobar};

\end{tikzpicture}
\end{document}


-
With .code 2 args (fine idea !), you don't need \pgfextra. –  Alain Matthes Jul 24 '12 at 6:58
@Altermundus Ah, that's from an earlier version where I did all of this inside a path. Indeed it's not needed. –  percusse Jul 24 '12 at 7:58

Update

Without fit and calc

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

\makeatletter
\tikzset{minimum dist/.style 2 args={%
insert path={%
\pgfextra{%
\path (#1);
\pgfgetlastxy{\xa}{\ya}
\path (#2);
\pgfgetlastxy{\xb}{\yb}
\pgfpointdiff{\pgfpoint{\xa}{\ya}}%
{\pgfpoint{\xb}{\yb}}%
\pgf@xa=\pgf@x}
},
minimum width=\pgf@xa}
}

\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\node [mynode,below= 1cm of foo.south west,
anchor=west,
minimum dist={foo.south west}{bar.north east}
] {foobar};
\end{tikzpicture}
\end{document}


Another variant with fit:

 \documentclass{article}
\usepackage{tikz}
\usetikzlibrary{positioning,fit,calc}
\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\node [mynode,below=1cm of foo.south west,inner sep=0pt,
anchor=west,
fit={($(foo.south west)+(.5*\pgflinewidth,0)$)
($(bar.north east)-(.5*\pgflinewidth,0)$)},
label=center:foobar] {};
\end{tikzpicture}
\end{document}


-
If you fit using the anchor nodes, why not move the anchor nodes instead of yshifting? –  krlmlr Jul 18 '12 at 17:24
What is your idea? What you want to say by "moving" the anchor nodes ? –  Alain Matthes Jul 18 '12 at 18:22
I mean adding a vertical shift to the nodes used for fit instead of yshift-ing the node. –  krlmlr Jul 18 '12 at 21:02
Ah! Finally I understand your request (perhaps , I'm not sure) below=1cm for example. Personally I don't use positioning and I prefer yshift-ing ! because it's more easy to scale the picture. –  Alain Matthes Jul 19 '12 at 7:47
Do you want to substitute the first solution with the two-argument variant from the linked question? –  krlmlr Jul 19 '12 at 18:04

Another solution, which does not involve the fit library, but computes instead the required width of the node:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{positioning,calc}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};
\path let \p1=($(foo.west)-(bar.east)$),
\n1 = {veclen(\p1)-0.4pt}      % 0.4pt is the width of the border line
in node[mynode, below=of foo.south west, anchor=north west,
minimum width=\n1] {foobar};
\end{tikzpicture}
\end{document}


Resulting in:

-
Nice. Is there a way to programmatically retrieve the value of the border line width (0.4pt)? –  krlmlr Jul 18 '12 at 22:38
Oh, I just noticed that Jake's answer also containes this solution, but even better, because he uses \pgflinewidth instead of the hardcoded value 0.4pt. Shoud I retire my answer? –  JLDiaz Jul 18 '12 at 23:01
Indeed, but your code is shorter. –  krlmlr Jul 18 '12 at 23:03

A variant using fit library but without outer sep=0pt:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning,fit}
\begin{document}
\begin{tikzpicture}[node distance=0.2cm,mynode/.style={rectangle,draw,line width=2pt}]
\node[mynode] (foo)                {foo};
\node[mynode] (bar) [right=of foo] {bar};

\node[fit=(foo)(bar),yshift=-1cm,%
line width=1pt, %
inner sep=-.5\pgflinewidth, % -1/2 of current line width
draw,label=center:foobar]{};
\end{tikzpicture}
\end{document}

-