3

The following TikZ code compiles and renders as expected:

\matrix[struct]
{
    \node[varname] (age) {age}; & \node[vartype] {int*}; \\
    \node[varname] {height}; & \node[vartype] {float}; \\
    \node[varname] {weight}; & \node[vartype] {float}; \\
};

enter image description here

The following does not:

\documentclass[tikz,border=10pt,a4paper]{standalone}

\usetikzlibrary{arrows,intersections,shapes,backgrounds,scopes,positioning,fit,matrix}

%% Language and font encodings
\usepackage[english]{babel}

\begin{document}

\tikzset{varname/.style={rectangle,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{vartype/.style={rectangle,text=red,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{struct/.style={draw,matrix of nodes,column 1/.style={anchor=base west},column 2/.style={anchor=base west}}}

\begin{tikzpicture}
\matrix[struct] (person)
{
    \node[varname] {name}; & \node[vartype] {const char*}; \\
    \node[varname] {attributes}; & 

    \matrix[struct] (foo) 
    {
        \node[varname] (age) {age}; & \node[vartype] {int*}; & \\
        \node[varname] {height}; & \node[vartype] {float}; & \\
        \node[varname] {weight}; & \node[vartype] {float}; & \\
    }; \\
    \node[varname] {next}; & TODO & \\
};
\end{tikzpicture}

\end{document}

Missing $ inserted.

Why not? How to I get a matrix-in-a-matrix?

The objective is to represent data types and structures hierarchically. If a data structure Primary contains another data structure Secondary, then I need to be able to include/invoke the code which draws Secondary inside the code which draws Primary. I need to be able to uniquely name nodes within any given structure, so that I can attach arbitrary decorations to them. ASCII art:

PRIMARY----------------------+
| basic_field:   int         |
| complex_field: SECONDARY--+|
|                | foo: int ||    ______
|                | bar: int<======|note|
|                | baz: int ||    ^^^^^^
|                -----------+|
| basic_field_2: int         |
+----------------------------+
7
  • 1
    People don't like to get source from external sources that might not be available later on. Place the code here instead. It is short enough
    – daleif
    Mar 8, 2017 at 12:35
  • 1
    BTW: several constructions does not like blank lines, so try removing that blank line before the inner matrix. Removing that blank line will give you the answer. BTW2: what even use a matrix for this?
    – daleif
    Mar 8, 2017 at 12:37
  • Edited, thanks for the advice. What would you use instead of a matrix? I tried with just nodes but had to write a lot of [left=of blah] and that was a lot of typing, requiring me to name all the nodes.
    – spraff
    Mar 8, 2017 at 12:57
  • 1
    Can you explain what you actually want to achieve in the end? Is it just about arranging some words in a grid-fashion, or do you also want to draw lines connecting the entries?
    – gernot
    Mar 8, 2017 at 13:10
  • I'm with gernot, explian better what exactly it is you want, then we'll see that we can come up with
    – daleif
    Mar 8, 2017 at 13:30

3 Answers 3

2

A variation on Segletes' solution:

Note: (foo) and (age) will not be defined in the final tikzpicture.

\documentclass[border=10pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{arrows,intersections,shapes,backgrounds,scopes,positioning,fit,matrix}

%% Language and font encodings
\usepackage[english]{babel}% Really?

\newsavebox{\tempbox}

\begin{document}

\tikzset{varname/.style={rectangle,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{vartype/.style={rectangle,text=red,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{struct/.style={draw,matrix of nodes,column 1/.style={anchor=base west},column 2/.style={anchor=base west}}}

\setbox\tempbox=\hbox{\begin{tikzpicture}[baseline=(age.text)]
\matrix[struct] (foo)
    {
        \node[varname] (age) {age}; & \node[vartype] {int*}; & \\
        \node[varname] {height}; & \node[vartype] {float}; & \\
        \node[varname] {weight}; & \node[vartype] {float}; & \\
    };
\end{tikzpicture}}%

\begin{tikzpicture}
\matrix[struct] (person)
{
    \node[varname] {name}; & \node[vartype] {const char*}; \\
    \node[varname] {attributes}; & \node{\usebox{\tempbox}}; \\
    \node[varname] {next}; & TODO  \\
};
\end{tikzpicture}

\end{document}

demo

3
  • A definite improvement. Mar 8, 2017 at 15:12
  • Thank you for this solution, although it doesn't allow me to decorate the nodes properly (see the ASCII art in my original question, and this followup question.)
    – spraff
    Mar 8, 2017 at 16:35
  • The fix is to add the option remember picture to both tikzpicture environments.
    – spraff
    Mar 9, 2017 at 11:34
3

This is a bit ugly, because I din't know that standalone builds each tikzpicture in its own page (hat tip to John K in comment). Thus, there exists a standalone border around the outside of the inner struct box. Because of it, I have to resort to \\[-10pt] to eliminate the vertical border around the inner box and \hspace{-10pt} to eliminate the horizontal border. I give an ADDENDUM below showing how that whole issue disappears when not using standalone.

Even if that is fixed, it is still a bit ugly, as I must build the inner box in a separate \vbox and save that, to be regurgitated within the outer box.

\documentclass[tikz,border=10pt,a4paper]{standalone}
\usetikzlibrary{arrows,intersections,shapes,backgrounds,scopes,positioning,fit,matrix}
%% Language and font encodings
\usepackage[english]{babel}
\usepackage{stackengine}
\newcommand\insertmatrix[1]{%
  \hspace{-10pt}%
  \belowbaseline[-\dimexpr\baselineskip+\ht\strutbox\relax]{#1}%
  \hspace{-10pt}%
}
\begin{document}

\tikzset{varname/.style={rectangle,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{vartype/.style={rectangle,text=red,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{struct/.style={draw,matrix of nodes,column 1/.style={anchor=base west},column 2/.style={anchor=base west}}}

\setbox0=\vbox{\begin{tikzpicture}
    \matrix[struct] (foo) 
    {
        \node[varname] (age) {age}; & \node[vartype] {int*}; & \\
        \node[varname] {height}; & \node[vartype] {float}; & \\
        \node[varname] {weight}; & \node[vartype] {float}; & \\
    };
\end{tikzpicture}}
\savestack\mycomplex{\box0}
\begin{tikzpicture}
\matrix[struct] (person)
{
    \node[varname] {name}; & \node[vartype] {const char*}; \\[-10pt]
    \node[varname] {attributes}; & \insertmatrix{\mycomplex} 
      \\[-10pt]
    \node[varname] {next}; & TODO  \\
    \node[varname] {afternext}; & MORE TODO  \\
};
\end{tikzpicture}

\end{document}

enter image description here


ADDENDUM

Thanks to John for pointing out the issue being with standalone, not tikz. If I perform the task in article class, all those 10pt corrections are not needed, and the \vbox can be put into an \hbox.

\documentclass[tikz,border=10pt,a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{arrows,intersections,shapes,backgrounds,scopes,positioning,fit,matrix}
%% Language and font encodings
\usepackage[english]{babel}
\usepackage{stackengine}
\newcommand\insertmatrix[1]{%
  \belowbaseline[-\ht\strutbox]{#1}%
}
\begin{document}

\tikzset{varname/.style={rectangle,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{vartype/.style={rectangle,text=red,thin,inner sep=0.3ex,font=\ttfamily,text height=1.5ex,text depth=0.35ex}}
\tikzset{struct/.style={draw,matrix of nodes,column 1/.style={anchor=base west},column 2/.style={anchor=base west}}}

\setbox0=\hbox{\begin{tikzpicture}
    \matrix[struct,ampersand replacement=\&] (foo) 
    {
        \node[varname] (age) {age}; \& \node[vartype] {int*}; \& \\
        \node[varname] {height}; \& \node[vartype] {float}; \& \\
        \node[varname] {weight}; \& \node[vartype] {float}; \& \\
    };
\end{tikzpicture}}
\savestack\mycomplex{\box0}
\begin{tikzpicture}
\matrix[struct] (person)
{
    \node[varname] {name}; & \node[vartype] {const char*}; \\
    \node[varname] {attributes}; & \insertmatrix{\mycomplex} 
      \\
    \node[varname] {next}; & TODO  \\
    \node[varname] {afternext}; & MORE TODO  \\
};
\end{tikzpicture}

\end{document}
4
  • \setbox0=\hbox{} also works, while \savebox doesn't. Mar 8, 2017 at 14:59
  • @JohnKormylo I could not get it to work with \hbox. Mar 8, 2017 at 15:01
  • The [tikz] option to standalone puts every tikzpicture on a new page. Mar 8, 2017 at 15:17
  • @JohnKormylo Thanks. That explains a lot, and I have edited my answer to reflect that new information. Mar 8, 2017 at 15:26
3

I suggest to use plain LaTeX for the grid-like arrangements and tikz for annotations. Instead of \fbox you can use fancier methods of drawing nice boxes.

enter image description here

\documentclass{article}
\usepackage{tikz}
\newcommand\vartype[1]{\textcolor{red}{\ttfamily#1}}
\newcommand\varname[1]{{\ttfamily#1}}
\newcommand\struct[1]%
  {\fbox
    {\begin{tabular}[t]{@{}ll@{}}
     #1
     \end{tabular}%
    }%
  }
\newcommand\tikznode[2]%
  {\tikz[remember picture,baseline=(#1.base)]\node[inner sep=0pt] (#1) {#2};}

\begin{document}
\struct
  {\varname{name}       & \vartype{const char*} \\
   \varname{attributes}
   & \struct
       {\varname{age}    & \vartype{int*}  \\
        \varname{height} & \tikznode{heighttype}{\vartype{floatp}} \\
        \varname{weight} & \vartype{float}
       } \\[7ex]
   \varname{next}       & TODO
  }
\begin{tikzpicture}[remember picture,overlay]
  \draw[stealth-,shorten <=2pt] (heighttype) -- +(1,1) node[right] {important!};
\end{tikzpicture}
\end{document}

You must log in to answer this question.

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