2

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         |
+----------------------------+
  • 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 '17 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 '17 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 '17 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 '17 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 '17 at 13:30
1

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

  • A definite improvement. – Steven B. Segletes Mar 8 '17 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 '17 at 16:35
  • The fix is to add the option remember picture to both tikzpicture environments. – spraff Mar 9 '17 at 11:34
2

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}
  • \setbox0=\hbox{} also works, while \savebox doesn't. – John Kormylo Mar 8 '17 at 14:59
  • @JohnKormylo I could not get it to work with \hbox. – Steven B. Segletes Mar 8 '17 at 15:01
  • The [tikz] option to standalone puts every tikzpicture on a new page. – John Kormylo Mar 8 '17 at 15:17
  • @JohnKormylo Thanks. That explains a lot, and I have edited my answer to reflect that new information. – Steven B. Segletes Mar 8 '17 at 15:26
1

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}

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.