12

Using TikZ, I want to create a nested rectangle split, with rounded corners, that looks like this:

desired result http://www.liacs.nl/~schraage/tikzsplit2.png

The question that most closely resembles this problem is Nested rectangles , however the proposed solution with two separate nodes fitted together in a virtual node seems rather complex and does not have the advantages of a regular splitted node such as margin handling, rounded corners, or nodepart definitions.

The closest thing I could come up with is this:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\begin{document}
\begin{tikzpicture}
\node (A) [rectangle split,rectangle split parts=2,draw,text centered,rounded corners]{
    \textit{marriage}, 1850
    \nodepart{second}
        \begin{tabular}{c|c}
            John & Mary \\
            Smith & Jones\\
        \end{tabular}
    };
\end{tikzpicture}
\end{document}

But the tabular environment does not stretch to the borders of the node: partial solution

What I want is something like this:

\node (A) [rectangle split,rectangle split parts=2,draw,text centered,rounded corners]{
    \textit{marriage}, 1850
    \nodepart{second}[rectangle split,rectangle split parts=2,rectangle split horizontal]
        John\\Smith
        \nodepart{second}
            Mary\\Jones
    };

or any other convenient nesting syntax.

Other solutions (e.g., a matrix, or a straightforward fit) are also appreciated.

9

Nesting TikZ pictures/nodes is always a tricky business. I try to avoid this.

A simply fix for your example (tabular lines do not touch the shape’s border) could be to set the inner ysep to zero. Unfortunately, this cannot be done for the second node part separately. You’ll end up with a very tight node.

I propose two solutions:

  1. A simple one that follows Table Frames beyond simple lines but uses a big \Strut;
  2. A complex TikZ nodes-filled solution

Simple

If you use this often, you might want to create a macro to which you only feed the tree text parts.

Code

\documentclass[tikz,convert=false]{standalone}
\newcommand*{\ml}[2][c]{\tabular[t]{@{}#1@{}} #2 \endtabular}
\makeatletter
\newcommand*{\Strut}[1][1em]{\vrule\@width\z@\@height#1\@depth\z@\relax}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[rounded corners, draw, inner sep=+0pt] {%
  \begin{tabular}{c|c}
    \multicolumn{2}{c}{\textit{marriage}, 1850\Strut} \\\hline
    \ml{\Strut John\\Smith} & \ml{\Strut Mary\\Jones}
  \end{tabular}};
\end{tikzpicture}
\end{document}

Output

enter image description here

Complicated

This solution provides a three parts key that takes three arguments:

  1. the text for the first top node,
  2. the text for the lower left node, and
  3. the text for the lower right node.

Every argument can be preceded with a set of optional argument in brackets [ … ] (as done in the example).

The fit library is used to fit a node around those three nodes (this is the one we actually want to draw).

Re-arranging the order how the nodes are placed or making certain changed at the default options of the nodes one can achieve slightly different alignment.

Code

\documentclass[tikz,convert=false]{standalone}
\usetikzlibrary{fit}
\tikzset{
  three parts left node/.style={
    every three parts node,
    name=qrr@tikz@tp@l,
    at=(qrr@tikz@tp@t.south),
    anchor=north east,
    outer sep=+0pt},
  three parts right node/.style={
    every three parts node,
    name=qrr@tikz@tp@r,
    at=(qrr@tikz@tp@t.south),
    anchor=north west,
    outer sep=+0pt},
  three parts top node/.style={
    every three parts node,
    name=qrr@tikz@tp@t,
    outer sep=+0pt},
  three parts node/.style={
    inner sep=-.5\pgflinewidth,
    minimum size=+0pt,
    fit=(qrr@tikz@tp@l)(qrr@tikz@tp@r)(qrr@tikz@tp@t)},
  every three parts node/.style={align=center},
  three parts node after/.style={
    insert path={
      ([xshift=\pgflinewidth]   qrr@tikz@tp@t.south west) edge[three parts node after edge 1/.try] ([xshift=-\pgflinewidth]qrr@tikz@tp@t.south east)
      ([yshift=-.5\pgflinewidth]qrr@tikz@tp@l.north east) edge[three parts node after edge 2/.try] ([yshift=\pgflinewidth] qrr@tikz@tp@r.south west)}},
}
\makeatletter
\tikzset{
  three parts/.code args={#1#2#3}{%
    \pgfutil@ifnextchar[%
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts top node}}
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts top node}[]}#1\egroup\pgf@stop
    \pgfutil@ifnextchar[%
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts left node}}
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts left node}[]}#2\egroup\pgf@stop
    \pgfutil@ifnextchar[%
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts right node}}
      {\expandafter\tikz@scan@next@command\qrr@tikz@split@nodeOpt{three parts right node}[]}#3\egroup\pgf@stop
    \tikz@scan@next@command node[three parts node/.try]{}[three parts node after]\pgf@stop
  }
}
\def\qrr@tikz@split@nodeOpt#1[#2]{node[#2,#1]\bgroup}
\makeatother
\begin{document}
\begin{tikzpicture}[three parts node/.append style={draw,rounded corners}]
\path [three parts={[blue!50!red]\textit{marriage}, 1850}{[blue]John\\Smith}{[red]Mary\\Jones}];
\end{tikzpicture}
\end{document}

Output

enter image description here

  • Awesome. The second solution is close to what I need as well. I am noticing though that if content of the top node is not as wide as the whole box, then the horizontal line does not go from edge to edge. Put only marriage in it to see. Similarly, if content in left or right node is only one line not two (but the other one is two), then vertical line is also shorter and does not go to the end. – Mitar Jul 28 at 15:33

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.