16

I was wondering if there is a way to set the height of rectangle split parts. I had no luck finding something in the pgfmanual. (Using \phantom seems just like a quickfix.)

Here is an example to illustrate my problem.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\tikzstyle{data}=[rectangle split,rectangle split parts=2,draw,text centered]

\begin{document}
\begin{tikzpicture}[node distance=2cm]

    \node [data]                (A)    {data \nodepart{second} \phantom{null}};
    \node [data, right of=A]    (B)    {data};
    \node [data, right of=B]    (C)    {data \nodepart{second} null};

\end{tikzpicture}
\end{document}

Output

Result of the example.

5
  • 4
    Welcome to TeX.sx! Does rectangle split empty part height (page 452 in the manual) do what you want? Dec 26, 2012 at 22:26
  • 1
    As new user without image posting privileges simply include the image as normal and remove the ! in front of it to turn it into a link. A moderator or another user with edit privileges can then reinsert the ! to turn it into an image again. Dec 26, 2012 at 22:36
  • @TorbjørnT. Nearly, now I can set the height of empty parts, but I'd like to have the same height for every (in my case) second part, with or without content. [Sidenote: in my version of the manual it is page 509]
    – rtzll
    Dec 26, 2012 at 22:43
  • 1
    The reply by @Alain Matthes to a different question might be useful for you as well: tex.stackexchange.com/questions/54252/… Dec 26, 2012 at 22:59
  • 2
    @PeterJansson I was hoping one could avoid a macro, which needs to be put everytime in there, when i want the specific format. Instead an option would be nicer, once defined in the preamble. But I think I'm going to use Alain Matthes solution, so thank you.
    – rtzll
    Dec 26, 2012 at 23:25

2 Answers 2

12

The following solution is a compromise between \nodepart{second} \hphantom{null} (or an extra macro) and the condition that even not-empty parts have an specified width.

The style data+ takes one mandatory argument and sets the dimension of empty parts to the dimension of its argument.

In your example you would need data+=null. Of course, if the actual content of the second part of the last node changes, you would have to change the argument given to data+ (though this could be a macro too).

Code

\documentclass{article}
\usepackage{tikz,calc}
\usetikzlibrary{shapes}
\tikzset{
    data/.style={
        draw,
        rectangle split,
        rectangle split parts=2,
        text centered,
    },
    data+/.style={
        data,
        rectangle split every empty part={},% resets empty-part macro (explanation below)
        rectangle split empty part width=\widthof{#1},
        rectangle split empty part height=\heightof{#1},
        rectangle split empty part depth=\depthof{#1},
    },
}
\newcommand{\data}{data \nodepart{second} \phantom{null}}
\begin{document}
\begin{tikzpicture}[node distance=2cm]
    \node [data+={null}]        (A)    {data};
    \node [data, right of=A]    (B)    {\data};
    \node [data, right of=B]    (C)    {data \nodepart{second} null};
\end{tikzpicture}
\end{document}

Output

enter image description here

Excursus: -1ex

The definition of the data+ style was previously

data+/.style={
    data,
    rectangle split empty part width=\widthof{#1}-1ex,
    rectangle split empty part height=\heightof{#1},
    rectangle split empty part depth=\depthof{#1},
},

The OP rightly asks

[W]hy do you have to subtract 1ex from the width?

PGF/TikZ has one special macro that is inserted for an empty node-part if /pgf/rectangle split ignore empty parts is false(which seems to be the default contrary to the PGF manual), otherwise we wouldn’t even get a box.
The name of this macro is \pgf@lib@sh@rs@every@emptypart (for short: empty-part macro).

One would expect that PGF stores away the lengths given to rectangle split empty part <width|height|depth> keys and does not build this macro with the latest dimensions until it is needed. But this is not the case.
Every time rectangle split empty part <width|height|depth> is used a rule of the given width, height or depth is created and added  to the empty-part macro (all other length parameters of that rule are 0pt, meaning they are invisible (e.g. \strut)).

The problem arises that rules (even invisible ones) are getting stacked horizontally. Rules with a height or depth do not get stacked vertically so they don’t accumulate extra height/depth, but rules with a width (horizontal rules) accumulate.

But wait! We only used those keys once! Yes, but no.
As a matter of fact they are used once before, namely during the initialization.
The file pgflibraryshapes.multipart.code.tex reads on lines 370, 378 and 386:

rectangle split empty part width=1ex,%  line 370
rectangle split empty part height=1ex,% line 378
rectangle split empty part depth=0ex,%  line 386

When we start we have a square box of 1ex × 1ex. If we add a vertical rule of 2ex we get a box of 1ex × 2ex, but if we add a horizontal rule of 2ex we get a box of 3ex × 1ex.

A picture worth than thousand words?

I constructed an example, where

  • the initial box is empty:

    \def\pgf@lib@sh@rs@every@emptypart{}% clean slate
    
  • and the rules have actually a “width” (orthogonal to its “length”), i.e. a horizontal rule has a height (actually a height and a depth so that the middle of the line lies on the baseline), and a vertical rule has a width.

Not-MWE

rs width is a short-cut for rectangular split empty parts width, the same holds for rs height and rs depth.

\node[rectangle split ignore empty parts=false]                                          (a) {a};
\node[right of=a,  rs width =  1ex]                                                      (b) {b};
\node[right of=b, rs height =  3ex]                                                      (c) {c};
\node[right of=c,  rs depth = .5ex]                                                      (d) {d};
\node[below of=a,   rs width = 1ex,  rs width = \widthof{null}]           [yshift=-.5cm] (e) {e};
\node[right of=e,  rs height = 1ex, rs height = 2ex, rs height = .5ex]                   (f) {f};
\node[right of=f,   rs depth = 1ex,  rs depth = 2ex,   rs depth = 3ex]                   (g) {g};
\node[right of=g,   rs width = 1ex,  rs depth = 2ex,  rs height = 1ex, rs width = 2.5ex] (h) {h};

Output

The rules are:

  • baseline: black
  • horizontal (width): red
  • vertical (height): green
  • vertical (depth): blue

enter image description here

Is there a better solution?

Two come to mind:

  1. instead of subtracting hard-coded 1ex we could subtract the current actual width

    rectangle split empty part width=\widthof{#1}-\widthof{\pgf@lib@sh@rs@every@emptypart}
    

    which would work quite okay as long as its content is not wider than #1.

  2. Overwriting the empty-part macro.

There is a undocumented key rectangle split every empty part that is not used once in PGF and TikZ. Its definition is

rectangle split every empty part/.store in=\pgf@lib@sh@rs@every@emptypart

We cannot do rectangle split every empty part={\phantom{null}} because TikZ switched to a \nullfont that swallows everything. (Have you ever tried to just write something in a tikzpicture environment?). This is the same reason, I used calc’s \<dimen>of macros, they escape this cleverly.

We could fill this empty part macro with rules (not again!) overwriting any existing content, but then we could just use our old solution without -1ex by just removing any content.

Code

\tikzset{
    data+/.style={
        data,
        rectangle split every empty part={},% resets empty-part macro
        rectangle split empty part width=\widthof{#1},
        rectangle split empty part height=\heightof{#1},
        rectangle split empty part depth=\depthof{#1},
    },
}
4
  • Great answer! Just out of curiosity: why do you have to subtract 1ex from the width?
    – rtzll
    Dec 31, 2012 at 0:24
  • 1
    @ChristianR. Please see the lengthy addition to my answer. Hopefully my explanation is helpful. Dec 31, 2012 at 1:43
  • 1
    @ChristianR. I have updated my solution with a better alternative by emptying the macro for the empty node-parts. Dec 31, 2012 at 2:44
  • I appreciate the effort, I hope this answer will get the attention it desrves. :-)
    – rtzll
    Dec 31, 2012 at 2:52
7

After trying out the suggestions in the comments to my question, I found it the easiest to stick with my first attempt using \phantom, so here how it lookes in code.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\tikzstyle{data}=[rectangle split,rectangle split parts=2,draw,text centered]
\newcommand{\data}{data \nodepart{second} \phantom{null}}

\begin{document}
\begin{tikzpicture}[node distance=2cm]
    \node [data]                (A)    {\data};
    \node [data, right of=A]    (B)    {\data};
    \node [data, right of=B]    (C)    {data \nodepart{second} null};
\end{tikzpicture}
\end{document}

Which results in:

Output

I needed data \nodepart{second} \phantom{null} a couple of times, therefore I thought it would be nice to define an own command for this, which might seem like a bit much in the example, but for the real application it was very useful. Have a look:

List

I post this for it may help someone with a similar problem.

You must log in to answer this question.

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