# Linking the columns widths of two tikz matrices

I have two figures containing a tikzfigure with a matrix. Both matrices are related, i.e. have the same columns, and I would like tikz to assign them the same column widths, just as if I had all rows in one matrix. An automatic solution would be ideal, without me having to provide manual spacing or having to mark one matrix as the template, i.e. tikz should collect the spacing from all matrices and then apply them (probably requiring recompilations).

Is that possible?

The following code demonstrates the issue

\documentclass{minimal}
\usepackage{tikz}
\begin{document}
Currently I have two matrices, this:

\begin{tikzpicture}
\matrix{
\node{first matrix with long and}; & \node{short column}; \\
\node{second}; & \node{row}; \\
};
\end{tikzpicture}

and this:

\begin{tikzpicture}
\matrix{
\node{second matrix}; & \node{with another column long}; \\
};
\end{tikzpicture}

I want that is that both matrices look as if they were cut out of this combined matrix:

\begin{tikzpicture}
\matrix{
\node{first matrix with long and}; & \node{short column}; \\
\node{second}; & \node{row}; \\
\node{second matrix}; & \node{with another column long}; \\
};
\end{tikzpicture}
\end{document}


In the example, the text in between is simple, but in the real example, the two matrices could be at totally different positions, e.g. two different floating figures.

-
Does this need to be an automatic solution, or could you provide the widths manually? If it needs to be automatic, is one matrix going to be the "master" that determines all column widths, or do the column widths have to adjust to the content in both matrices? – Jake May 30 '12 at 14:13
Can the intervening text be part of the tikzpicture, or do there need to be two separate tikzpicture environments? – Andrew Stacey May 30 '12 at 19:07
@AndrewStacey There need to be two environments, as the figures can be in completely different figure environments (question amended). – Joachim Breitner May 31 '12 at 7:32

I think I have a solution for you. It works by measuring the widths of the columns in a pgf matrix and saving them to the .aux file. Then on a subsequent run (or later that run for subsequent matrices), it inserts an extra row at the start which consists of paths of the correct widths. There's a bit of trickery there to ensure that this extra row doesn't actually do anything, though I don't guarantee that that works - I tested it with setting row styles and with setting row separation. But my tests were not extensive.

Here's the code:

\documentclass{article}
%\url{http://tex.stackexchange.com/q/57871/86}
\thispagestyle{empty}
\usepackage{tikz}

\makeatletter
\pgfkeys{/tikz/save cell widths code/.code={
\edef\lc@widths{\noexpand\noexpand\noexpand\expandafter\gdef\noexpand\noexpand\noexpand\csname lc@widths@#1@cols\noexpand\noexpand\noexpand\endcsname{\the\pgf@matrix@numberofcolumns}}
\bgroup
\foreach \lc@k in {1,...,\pgf@matrix@numberofcolumns} {
\edef\lc@temp{\noexpand\noexpand\noexpand\expandafter\gdef\noexpand\noexpand\noexpand\csname lc@widths@#1@maxx\lc@k\noexpand\noexpand\noexpand\endcsname{\csname pgf@matrix@maxx\lc@k\endcsname}\noexpand\noexpand\noexpand\expandafter\gdef\noexpand\noexpand\noexpand\csname lc@widths@#1@minx\lc@k\noexpand\noexpand\noexpand\endcsname{\csname pgf@matrix@minx\lc@k\endcsname}}
\expandafter\expandafter\expandafter\gdef\expandafter\expandafter\expandafter\lc@widths\expandafter\expandafter\expandafter{\expandafter\lc@widths\lc@temp}
}
\immediate\write\pgfutil@auxout{\lc@widths}
\edef\lc@temp{\lc@widths}
\lc@temp
\egroup
}
}

\tikzset{save cell widths/.style={%
hack end matrix=#1,
append after command={[save cell widths code=#1]},%
},
hack end matrix/.code={%
\let\lc@matrix=\pgf@matrix@cont
\def\pgf@matrix@cont{\lc@matrix\lc@use{#1}}%
}
}

\def\lc@use#1{%
\@ifundefined{lc@widths@#1@cols}{}{%
\def\lc@line{}%
\expandafter\let\expandafter\lc@cols\csname lc@widths@#1@cols\endcsname
\xdef\lc@line{\noexpand\path (\csname lc@widths@#1@minx1\endcsname,0pt) (\csname lc@widths@#1@maxx1\endcsname,0pt);}
\foreach \lc@k in {2,...,\lc@cols} {
\expandafter\gdef\expandafter\lc@line\expandafter{\lc@line\pgfmatrixnextcell}
\edef\lc@temp{\noexpand\path (\csname lc@widths@#1@minx\lc@k\endcsname,0pt) (\csname lc@widths@#1@maxx\lc@k\endcsname,0pt);}
\expandafter\expandafter\expandafter\gdef\expandafter\expandafter\expandafter\lc@line\expandafter\expandafter\expandafter{\expandafter\lc@line\lc@temp}

}
\pgfmathparse{-\pgfmatrixrowsep}
\edef\lc@temp{\noexpand\pgfmatrixendrow[\pgfmathresult pt]}
\expandafter\expandafter\expandafter\gdef\expandafter\expandafter\expandafter\lc@line\expandafter\expandafter\expandafter{\expandafter\lc@line\lc@temp}
\lc@line
}
}

\makeatother

\begin{document}
Currently I have two matrices, this:

\begin{tikzpicture}
\matrix[save cell widths=m]{
\node{first matrix with long and}; & \node{short column}; \\
\node{second}; & \node{row}; \\
};
\end{tikzpicture}

and this:

\begin{tikzpicture}
\matrix[save cell widths=m]{
\node{second matrix}; & \node{with another column long}; \\
};
\end{tikzpicture}

I want that is that both matrices look as if they were cut out of this combined matrix:

\begin{tikzpicture}
\matrix[save cell widths=m]{
\node{first matrix with long and}; & \node{short column}; \\
\node{second}; & \node{row}; \\
\node{second matrix}; & \node{with another column long}; \\
};
\end{tikzpicture}

\begin{tikzpicture}
\matrix[nodes={anchor=base},draw,save cell widths=mm,row 2/.style={every node/.style={anchor=base,green}},row sep=1cm] (m) at (0,0) {
\node {a}; & \node {b}; & \node {cde}; \\
\node {a}; & \node {b}; & \node{c}; \\
};
\matrix[nodes={anchor=base},draw,save cell widths=mm] (ma) at (0,-3) {
\node {abc}; & \node {b}; & \node{c}; \\
\node {a}; & \node {b}; & \node{c}; \\
};
\end{tikzpicture}

\begin{tikzpicture}
\matrix[draw,save cell widths=mmm,row sep=1cm] (m) at (0,0) { \node[right] {abcdefgh}; & \node{x}; \\ };
\matrix[draw,save cell widths=mmm] (ma) at (0,-3) { \node[left] {abc}; & \node{x}; \\ };
\end{tikzpicture}
\end{document}


Here's the result:

Edit 2012-06-01 Following on from the comments, instead of centring the added stuff, it now uses the maxx and minx directly thus allowing for cell contents to be shifted relative to the origin (see last example in code).

-
The Tex-Foo is beyond my abilities to assess, but the result looks perfect. Great Work! – Joachim Breitner Jun 1 '12 at 7:07
That said, there is a bit room for improvement: The line you insert in \lc@use to set the column width is centered, so the origins of the nodes in the different matrices don’t line up any more. The solution is probably to just store and use \csname pgf@matrix@maxx\lc@k\endcsname and ...minn separately. – Joachim Breitner Jun 1 '12 at 7:12
@JoachimBreitner Storing the values isn't enough. The entries are positioned by an \halign and it is that which needs to know the distances. The \pgf@matrix@maxx... macros are so that PGF can work out where the nodes ended up but aren't used for actually positioning them in the first place. – Andrew Stacey Jun 1 '12 at 7:14
@JoachimBreitner Can you give an example of the code that you'd like to use it with? In my image above it looks to me as though the origins of the nodes line up. – Andrew Stacey Jun 1 '12 at 7:16
@JoachimBreitner Ah, I see. And that demonstrates what you meant in your comment about using the maxx and minx directly. That makes sense - see edited answer. – Andrew Stacey Jun 1 '12 at 8:58
show 1 more comment