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 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?

share|improve this question
    
Related: TikZ: Make node height span several others –  Qrrbrbirlbel Dec 19 '13 at 17:46
add comment

5 Answers 5

up vote 18 down vote accepted

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.

tikz fit and shift

\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.

fitted and automatically shifted node

\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}
share|improve this answer
    
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
1  
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
1  
@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
show 3 more comments

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}      

enter image description here

share|improve this answer
    
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
show 1 more comment

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}
share|improve this answer
add comment

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}

enter image description here

share|improve this answer
    
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
add comment

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:

Result

share|improve this answer
    
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
add comment

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.