PGF treats every matrix cell as a TeX box, these have a height (above the baseline) and a depth (below the baseline).
Similar as with
\raisebox{1em}{Foo}\par
Bar
The second paragraph with only Bar doesn't get moved up to be the normal vertical space below Foo.
Similar things are happening with your cells. The baseline (y = 0) will be part of your matrix row one way or the other.
My advice? Don't have y = 0 outside of your cells' bounding box.
In your example, you could just add shift=(down:1) to all cells so that the original y = 0 is part of the cells bounding box again.
(I'm using (down:1) and not yshift=-1cm because your \grid macro also uses the xy coordinate system and not the canvas one.
\documentclass[tikz,border=1cm]{standalone}
\newcommand{\grid}{%
\foreach \x in {1,2}
\foreach \y in {1,2}
\draw (\x,\y) rectangle ++(1,1);%
}
\begin{document}
\begin{tikzpicture}
\matrix[cells={shift=(down:1)}]{
\grid & \grid \\
\grid & \grid \\
};
\end{tikzpicture}
\end{document}
If you're feeling adventurous:
Here is a fix that applies a few patches so that maybe half the cases work again.
Firstly, with \pgf@matrix@init@row I initialize every row to have a height of −32000 pt (similar initialization with 16000pt are done all across PGF/TikZ even though TeX would allow a bit more). Originally, they both had 0pt.
Secondly, I'll patch \pgf@matrix@finish@line which now checks whether the minimal y value of the row is above 0pt, meaning you have a “negativ depth” (similar to Foo from the example). This depth is subtracted from the row seperation between the current and the next row.
Lastly, the macro \pgf@end@matrix which does a lot of work after collecting all cells to actual place the matrix node and manage some things needs another patch so that after the last row the same problem doesn't appear again.
For some reason, only half the vertical space needs to be removed.
Caveats:
- Don't use empty rows.
- This doesn't deal with “negative heights”, meaning when y = 0 is above the bounding box.
- Don't trust this answer to not introduce other problems down the line.
Code
\documentclass[tikz,border=1cm]{standalone}
\makeatletter
\def\pgf@matrix@init@row{%
\relax%
\global\advance\pgfmatrixcurrentrow by 1\relax%
\global\pgfmatrixcurrentcolumn=0\relax%
\expandafter\gdef\csname pgf@matrix@maxy\the\pgfmatrixcurrentrow\endcsname{-16000pt}% ← almost mindimen
\expandafter\gdef\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname{16000pt}% ← almost maxdimen
}%
\usepackage{etoolbox}
\preto{\pgf@matrix@finish@line}{% row has negative depth, remove that from column seperation
\ifdim\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname>0pt
\pgf@matrix@addtolength\pgf@y{-\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname}%
\fi}
\patchcmd{\pgf@end@matrix}{\egroup\egroup}{%
\egroup % last row has negative depth? remove that (why .5?)
\ifdim\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname>0pt
\vskip-.5\dimexpr\csname pgf@matrix@miny\the\pgfmatrixcurrentrow\endcsname\relax
\fi
\egroup}{}{}
\makeatother
\newcommand{\grid}{%
\foreach \x in {1,2}
\foreach \y in {1,2}
\draw (\x,\y) rectangle ++(1,1);%
}
\begin{document}
\tikz
\matrix[draw=red, cells=black]{
\grid & \grid \\
\grid & \grid \\
% & \\ % empty lines are bad!
\grid \\
};
\end{document}
Output

\foreach \y in {1,2}to\foreach \y in {0,1}. – Jasper Habicht Nov 28 '22 at 21:11(1,1)upwards, so you leave a space between y = 0 and y = 1. – Jasper Habicht Nov 28 '22 at 21:15\tikz[baseline]{\grid}, so the baseline is taken into account somehow here. – Jasper Habicht Nov 28 '22 at 21:31\path (0,0);before the first\foreachmacro inside the defintion of\grid. If you want no gaps but consistent code, change\foreach \y in {1,2}to\foreach \y in {0,1}and\foreach \x in {1,2}to\foreach \x in {0,1}. – Jasper Habicht Nov 28 '22 at 21:49\foreachloops? – Evan Aad Nov 28 '22 at 22:20