2

It is known that tikzexternalize and tikz-cd don't play well with each other: Are the TikZ libraries cd and external incompatible with one another?. Now, I have a tikzcd whose entries are tikzpictures. Can I somehow externalize the pictures in the tikz-cd?

\documentclass{scrartcl}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,tikz-cd,xstring}
\newcommand{\FilteredBall}[1][abcfghxy]{
    \begin{tikzpicture}[every node/.append style={inner sep=1pt}, baseline={(0,-0.2)}]%(current bounding box.center)]
        %\fill[white] (0,0) circle (2);
        \begin{scope}[gray]
            \draw[dashed] (2,0) arc (0:180:2 and 0.6);
            \draw (0,0) circle (2) ;
            \draw (-2,0) arc (180:360:2 and 0.6);
        \end{scope}
        \begin{scope}[opacity=.5]
            \IfSubStr{#1}{f}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:.6 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:1.2 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{g}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:.6 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:-1.3 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{h}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-1.2 and 2) arc (-90:90:1.2 and 2);
                    \shade[ball color=green!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
        \end{scope}
        \begin{scope}[thick]
            \IfSubStr{#1}{a}{\draw[red] (0,2) arc (90:-90:-1.3 and 2) node[below left, pos=.9] {$a$};}{}
            \IfSubStr{#1}{b}{\draw[blue] (0,2) arc (90:-90:1.2 and 2) node[below right, pos=.9] {$b$};}{}
            \IfSubStr{#1}{c}{
                \IfSubStr{#1}{h}{
                    \draw[Aqua, densely dotted] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }{
                    \draw[Aqua] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }
            }{}
        \end{scope}
        \IfSubStr{#1}{x}{\fill (0,2) circle (0.15);}{}
        \IfSubStr{#1}{y}{\fill (0,-2) circle (0.15);}{}
        \path
        (0,2) arc (90:-90:2) node[text=red, pos=.4, above right]                   {\IfSubStr{#1}{f}{$f$}{\phantom{{$f$}}}}
        (0,2) arc (90:-90:-2 and 2) node[text=blue!50!black, pos=.6,, below left]  {\IfSubStr{#1}{g}{$g$}{\phantom{{$g$}}}}
        (-.4, .65) node[text=green!50!black]                                        {\IfSubStr{#1}{h}{$h$}{\phantom{{$h$}}}}
        (0,2) node[above]                                                          {\IfSubStr{#1}{x}{$x$}{\phantom{{$x$}}}}
        (0,-2) node[below]                                                         {\IfSubStr{#1}{y}{$y$}{\phantom{{$y$}}}};
    \end{tikzpicture}
}
\begin{document}
    \begin{equation}
        \tikzset{every node/.append style={outer sep=2pt}, x=2mm, y=2mm}
        \begin{tikzcd}[row sep=tiny, column sep=tiny]
            \FilteredBall[xya] \rar["\subseteq", phantom] &
            \FilteredBall[xyabh] \rar["\subseteq", phantom] &
            \FilteredBall[xyabchg] \rar["\subseteq", phantom] &
            \FilteredBall[xyabcfgh]
            \\
            \FilteredBall[xya] \rar["\subseteq", phantom] &
            \FilteredBall[xyab] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
            \FilteredBall[xyabcg] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
            \FilteredBall[xyabcfg] \uar["\subseteq", phantom, sloped]  &
            \\
            \FilteredBall[xy] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
            \FilteredBall[xyb] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
            \FilteredBall[xybc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
            \FilteredBall[xybcf] \uar["\subseteq", phantom, sloped] \\
            \FilteredBall[xy] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
            \FilteredBall[xy] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]&
            \FilteredBall[xyc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
            \FilteredBall[xyc] \uar["\subseteq", phantom, sloped].
        \end{tikzcd}
    \end{equation}
\end{document}

Edit The following code, based on Qrrbrbirlbel's answer, tries to mix pictures with math. The red horizontal arrow is obviously wrong, and should be the same as the black one:

\documentclass{scrartcl}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,tikz-cd,xstring}
\usetikzlibrary{fit, external}
%\tikzexternalize
\makeatletter
\pgfkeys{% https://tex.stackexchange.com/a/665688/16595
    /utils/temp/.initial/.expand once=\tikzcd,
    /utils/temp/.prefix=\ifx\path\tikz@command@path % we're inside a tikzpicture
    \let\tikzpicture\scope
    \let\endtikzpicture\endscope
    \else % we're not inside a tikzpicture, disable externalizing
    \tikzexternaldisable
    \fi,/utils/temp/.get=\tikzcd}
\makeatother
\tikzset{
    cd vcenter/.style={baseline={([yshift=-axis_height]\tikzcdmatrixname)}},
    matrix bb node/.style={
        fit=(current bounding box), inner sep=0pt,%inner sep=+.3333em, 
        outer sep=+0pt, shape=rectangle, draw,%path only,
        name=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn},
    add bb node/.style={execute at end cell={\node[draw,matrix bb node]{};}}}
\tikzcdset{
    no matrix of nodes/.style={
        /tikz/add bb node,
        /tikz/matrix of nodes/.code=,
        /tikz/matrix of math nodes/.code=}}
\newcommand{\FilteredBall}[1][abcfghxy]{
%   \tikzset{}
    %\fill[white] (0,0) circle (2);
    \begin{scope}[every node/.append style={inner sep=2pt}]
        \begin{scope}[gray]
            \draw[dashed] (2,0) arc (0:180:2 and 0.6);
            \draw (0,0) circle (2) ;
            \draw (-2,0) arc (180:360:2 and 0.6);
        \end{scope}
        \begin{scope}[opacity=.5]
            \IfSubStr{#1}{f}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:.6 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:1.2 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{g}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:.6 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:-1.3 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{h}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-1.2 and 2) arc (-90:90:1.2 and 2);
                    \shade[ball color=green!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
        \end{scope}
        \begin{scope}[thick]
            \IfSubStr{#1}{a}{\draw[red] (0,2) arc (90:-90:-1.3 and 2) node[below left, pos=.9] {$a$};}{}
            \IfSubStr{#1}{b}{\draw[blue] (0,2) arc (90:-90:1.2 and 2) node[below right, pos=.9] {$b$};}{}
            \IfSubStr{#1}{c}{
                \IfSubStr{#1}{h}{
                    \draw[Aqua, densely dotted] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }{
                    \draw[Aqua] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }
            }{}
        \end{scope}
        \IfSubStr{#1}{x}{\fill (0,2) circle (0.15);}{}
        \IfSubStr{#1}{y}{\fill (0,-2) circle (0.15);}{}
        \path
        (0,2) arc (90:-90:2) node[text=red, pos=.4, above right]                   {\IfSubStr{#1}{f}{$f$}{\phantom{{$f$}}}}
        (0,2) arc (90:-90:-2 and 2) node[text=blue!50!black, pos=.6,, below left]  {\IfSubStr{#1}{g}{$g$}{\phantom{{$g$}}}}
        (-.4, .65) node[text=green!50!black]                                        {\IfSubStr{#1}{h}{$h$}{\phantom{{$h$}}}}
        (0,2) node[above]                                                          {\IfSubStr{#1}{x}{$x$}{\phantom{{$x$}}}}
        (0,-2) node[below]                                                         {\IfSubStr{#1}{y}{$y$}{\phantom{{$y$}}}};
    \end{scope}
}
\begin{document}
    \begin{equation}
        \begin{tikzpicture}[x=2mm, y=2mm, cd vcenter]
            \begin{tikzcd}[row sep=tiny, column sep=tiny, no matrix of nodes]
                \FilteredBall[xya] \rar["\subseteq", phantom] &
                \FilteredBall[xyabh] \rar["\subseteq", phantom] &
                \FilteredBall[xyabchg] \rar["\subseteq", phantom] &
                \FilteredBall[xyabcfgh]
                \\
                \FilteredBall[xya] \rar["\subseteq", phantom] &
                \FilteredBall[xyab] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
                \FilteredBall[xyabcg] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
                \FilteredBall[xyabcfg] \uar["\subseteq", phantom, sloped]  &
                \\
                \FilteredBall[xy] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
                \FilteredBall[xyb] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
                \FilteredBall[xybc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
                \FilteredBall[xybcf] \uar["\subseteq", phantom, sloped] \\
                \node[name=A]{$0_{0_{0_0}}$}; \rar[red] \uar &
                \node[name=B]{$0^{0^{0^0}}$}; \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]&
%               \FilteredBall[xy] \draw (current bounding box.base) node (B){} circle(2pt); \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]&
                \FilteredBall[xyc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
                \FilteredBall[xyc] \uar["\subseteq", phantom, sloped].
                \ar[from=A, to=B]
            \end{tikzcd}
        \end{tikzpicture}
    \end{equation}
\end{document}
Bubaya
  • 2,279
  • Why are you having TikZ pictures inside of a CD? Since each cell in a matrix doesn't need to have a node but could also have a normal TikZ diagram you could probably remove the inner TikZ picture. But yes, maybe you could by disabling the externalization at the start of the CD. Can you show us your diagram? – Qrrbrbirlbel Mar 19 '23 at 23:37
  • @Qrrbrbirlbel It's not true that the tikzpicture can be simply omitted. Go and try removing the tikzpicture from \FilteredSphere. So, why the downvote? – Bubaya Mar 20 '23 at 09:15
  • 1
    Noone downvoted. I didn't say it could be simply omitted. I said that it is probably possible to remove it. Take a look at how TikZ describes the usage of matrices. In its most simple form it has just some \paths in it. TikZ-CD uses matrix of (math) nodes which still can detect when a cell doesn't start with normal node text, though it can be finnicky. Since you only put normal diagrams in your cells, you can just not use matrix of (math) nodes. – Qrrbrbirlbel Mar 20 '23 at 10:35

1 Answers1

3

Since you don't actually use the matrix of (math) nodes that TikZ-CD install I'd disable it and then you can use your \FilteredBall in each cell without having to use nested tikzpictures. Using my answer to how to resolve incompatibility of tikz-cd and external you can put the tikzcd environment inside a tikzpicture that can get externalized.

I've added the key /tikz/cd vcenter following my answer to Problem with a TikZcd between curly braces so that the matrix is vertically centered.

A node with the fit library is created at the end of each cell that has the approriate names so that TikZ-CD draws the “arrows” with \subseteq. This node is fitted around the cell's current bounding box, encapuslating everything that's in the cell that hasn't been drawn with option overlay.

Since all your cells have the same size because you phantomize every node, this is all that is needed for the arrows to be drawn orthogonally. If the cells don't have the same size, this is going to be a bit more complicated.

Adjust the inner xsep/inner ysep of that node or row sep/column sep to get the correct spacing between your balls.

Update

A normal node inside this TikZ-CD is a bit less usable now since we've disables the matrix of (math) nodes. Though, they aren't easy to work around when using not-only nodes inside a cell which is still possible but whatever is in your cell needs to start with \path or some variations of it. A command that allows an optional argment and starts with \begin{scope} doesn't work here. (Though, in some cases it helps to just have an empty \path; at the start.)

Here we're using a normal \node but with the key matrix node so that it applies the correct naming scheme that is expected by TikZ-CD to work with its \arrow command.

TikZ-CD sets up its own asymmetrical rectangle shape that has a cheaty center anchor that is a fixed height of axis_height above the baseline and acts very similar to a mid anchor when placing a node. I believe this to be desirable in your case and wouldn't change it. The true power of the asymmetrical rectangle shape comes from connecting the nodes where horizontal lines point towards that fake center anchor which is now at the same height where normal math-mode arrows would be (and also where the tip of {} braces are).

In the case of a cell only having a normal node (that also should be used as such by TikZ-CD) you're going to have to use the no bb node key to temporarly disable it. Well it will be turned into an unnamed coordinate but that's almost the same. Truly disabling it will need a bit more work.

Speaking of the bounding box node. I've added a special fit key adjusting the one of Case 2 of my own answer that installs a asymmetrical rectangle in the cell that encapsulates everything but is symmetrical around the center of the cell horizontally and has its own center anchor in the center of the cell – including in the y direction which is why it's also takes axis_height in account. This is needed so that vertical arrows are truly vertical.

Code

\documentclass{scrartcl}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,tikz-cd,xstring}
\usetikzlibrary{fit, external}
%\tikzexternalize
\makeatletter
\tikzcdset{
  fit/.code={% tikz-cd uses an asymmetrical rectangle …
    \pgf@process{\pgfpointanchor{\tikz@pp@name{#1}}{east}}\pgf@xa=2\pgf@x
    \pgf@process{\pgfpointanchor{\tikz@pp@name{#1}}{west}}\pgf@x=-2\pgf@x
    \ifdim\pgf@xa>\pgf@x \pgf@x=\pgf@xa \fi
    \pgfkeysalso{/tikz/text width/.expanded=+\the\pgf@x}%
    \pgf@process{\pgfpointdiff{\pgfpointanchor{\tikz@pp@name{#1}}{south}}
                              {\pgfpointanchor{\tikz@pp@name{#1}}{north}}}%
    \pgf@ya=.5\pgf@y \pgf@yb=\pgf@ya
    \pgfmathaddtolength\pgf@ya{axis_height}%
    \pgfmathaddtolength\pgf@yb{-axis_height}%
    \pgfkeysalso{/tikz/text height/.expanded=+\the\pgf@ya,
                 /tikz/text depth/.expanded=+\the\pgf@yb}},
  /utils/temp/.initial/.expand once=\tikzcd,
  /utils/temp/.prefix=% https://tex.stackexchange.com/a/665688/16595
    \ifx\path\tikz@command@path
      \let\tikzpicture\scope \let\endtikzpicture\endscope
    \else \tikzexternaldisable \fi,/utils/temp/.get=\tikzcd}
\makeatother
\tikzset{
  matrix node/.style={
    name=\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn},
  cd vcenter/.style={baseline={([yshift=-axis_height]\tikzcdmatrixname)}},
  matrix bb node/.style={
    commutative diagrams/fit=current bounding box, inner sep=0pt,
    outer sep=+0pt, shape=asymmetrical rectangle, path only,  matrix node},
  add bb node/.style={execute at end cell={\node[matrix bb node]{};}},
  no bb node/.style={matrix bb node/.style={shape=coordinate}}}
\tikzcdset{
  no matrix of nodes/.style={
    /tikz/add bb node,
    /tikz/matrix of nodes/.code=,
    /tikz/matrix of math nodes/.code=}}
\newcommand{\FilteredBall}[1][abcfghxy]{
%   \tikzset{}
    %\fill[white] (0,0) circle (2);
    \begin{scope}[every node/.append style={inner sep=2pt}]
        \begin{scope}[gray]
            \draw[dashed] (2,0) arc (0:180:2 and 0.6);
            \draw (0,0) circle (2) ;
            \draw (-2,0) arc (180:360:2 and 0.6);
        \end{scope}
        \begin{scope}[opacity=.5]
            \IfSubStr{#1}{f}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:.6 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:2) arc (-90:90:1.2 and 2);
                    \shade[ball color=red!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{g}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:.6 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
                \begin{scope}
                    \clip (0,2) arc (90:-90:-2 and 2) arc (-90:90:-1.3 and 2);
                    \shade[ball color=blue!60] (0,0) circle[radius=2];
                \end{scope}
            }{}
            \IfSubStr{#1}{h}{
                \begin{scope}
                    \clip (0,2) arc (90:-90:-1.2 and 2) arc (-90:90:1.2 and 2);
                    \shade[ball color=green!70] (0,0) circle[radius=2];
                \end{scope}
            }{}
        \end{scope}
        \begin{scope}[thick]
            \IfSubStr{#1}{a}{\draw[red] (0,2) arc (90:-90:-1.3 and 2) node[below left, pos=.9] {$a$};}{}
            \IfSubStr{#1}{b}{\draw[blue] (0,2) arc (90:-90:1.2 and 2) node[below right, pos=.9] {$b$};}{}
            \IfSubStr{#1}{c}{
                \IfSubStr{#1}{h}{
                    \draw[Aqua, densely dotted] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }{
                    \draw[Aqua] (0,2) arc (90:270:-0.6 and 2) node[left=-2pt, midway] {$c$};
                }
            }{}
        \end{scope}
        \IfSubStr{#1}{x}{\fill (0,2) circle (0.15);}{}
        \IfSubStr{#1}{y}{\fill (0,-2) circle (0.15);}{}
        \path
        (0,2) arc (90:-90:2) node[text=red, pos=.4, above right]                   {\IfSubStr{#1}{f}{$f$}{\phantom{{$f$}}}}
        (0,2) arc (90:-90:-2 and 2) node[text=blue!50!black, pos=.6,, below left]  {\IfSubStr{#1}{g}{$g$}{\phantom{{$g$}}}}
        (-.4, .65) node[text=green!50!black]                                        {\IfSubStr{#1}{h}{$h$}{\phantom{{$h$}}}}
        (0,2) node[above]                                                          {\IfSubStr{#1}{x}{$x$}{\phantom{{$x$}}}}
        (0,-2) node[below]                                                         {\IfSubStr{#1}{y}{$y$}{\phantom{{$y$}}}};
    \end{scope}
}
\begin{document}
\begin{equation}
\begin{tikzpicture}[x=2mm, y=2mm, cd vcenter]
\begin{tikzcd}[row sep=tiny, column sep=tiny, no matrix of nodes]
\FilteredBall[xya] \rar["\subseteq", phantom] &
\FilteredBall[xyabh] \rar["\subseteq", phantom] &
\FilteredBall[xyabchg] \rar["\subseteq", phantom] &
\FilteredBall[xyabcfgh]
\\
\FilteredBall[xya] \rar["\subseteq", phantom] &
\FilteredBall[xyab] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
\FilteredBall[xyabcg] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]  &
\FilteredBall[xyabcfg] \uar["\subseteq", phantom, sloped]  &
\\
\FilteredBall[xy] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
\FilteredBall[xyb] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
\FilteredBall[xybc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
\FilteredBall[xybcf] \uar["\subseteq", phantom, sloped] \\
\tikzset{no bb node} \node[matrix node]{$0_{0_{0_0}}$}; \rar[red] \uar &
\tikzset{no bb node} \node[matrix node]{$0^{0^{0^0}}$}; \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]&
%&               \FilteredBall[xy] \draw (current bounding box.base) node (B){} circle(2pt); \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped]&
\FilteredBall[xyc] \rar["\subseteq", phantom] \uar["\subseteq", phantom, sloped] &
\FilteredBall[xyc] \uar["\subseteq", phantom, sloped].
\end{tikzcd}
\end{tikzpicture}
\end{equation}
\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • Wow, that's a lot of magic, thanks! I have a few questions: is it possible to move the fit node to the matrix code? But that's only for curiosity. And how does the /utils/temp/...-stuff work? I couldn't find anything in the tikz manual. – Bubaya Mar 20 '23 at 11:43
  • And how does the fit node actually work? Why does it only fit the cell? – Bubaya Mar 20 '23 at 11:52
  • @Bubaya I've added the key add bb node which automatically adds this fitted node to each non-empty cell. This works very well in your diagram because each cell has the same size. The /utils/temp stuff is directly taken from my other answer. I'm just using PGFKeys and a scratch key to add stuff before a macro without using another package for it. – Qrrbrbirlbel Mar 20 '23 at 14:07
  • I am sorry for bringing this up again, but what would you do if I wanted to mix math and pictures? Say, replace the ball in the bottom left by \emptyset? I guess I have to create a node for that cell then, but getting its baseline where I want it to is hard. – Bubaya Mar 22 '23 at 13:56
  • @Bubaya Yeah, the matrix of (math) nodes contains some magic about detecting whether a cell's content is actually just text or not. But that magic won't detect \FilteredBall, especially not when it accepts an optional argument. It only detects it when a cell starts with \path (or \draw or …). We could make \FilteredBall have only mandatory arguments and add an empty \path; at the start but I couldn't get it to work a few days ago. – Qrrbrbirlbel Mar 22 '23 at 14:03
  • @Bubaya Yes, just using \node{$\emptyset$}; might be the easiest approach. The cells are aligned by x = 0 vertically and y = 0 horizontally. Where do you want its baseline to be? – Qrrbrbirlbel Mar 22 '23 at 14:04
  • See my edit in the question. This shows a problem with the baseline that I wasn't able to fix so far. The black arrow is done by giving explicit names to the nodes, the red one by using \arrow – Bubaya Mar 22 '23 at 15:43
  • @Bubaya Two things: TikZ-CD sets up its asymmetrical rectangle shape for all cells (even the nodes in your \FilteredBall uses it, though we no effect on the outcome). They have a fixed center that acts very similar to the mid anchor when placed. Since the nodes are placed at the origin when no at is given this makes it so their (fake) center anchor is at the same height. Which I believe to be something that is a good idea. – Qrrbrbirlbel Mar 22 '23 at 20:14
  • However, the diagram still added the matrix bb node taking the name matrix-row-column which then gets used by the \ar by default. Since that is an explicit rectangle (fit doesn't work good with that, see explanation of case 2 this screws up the arrows which is now connecting the fit nodes. But we can disable that node explicitly. I think the fit node has to be an asymmetrical rectangle for horizontal connections and needs to be adjusted a bit for vertical arrows. – Qrrbrbirlbel Mar 22 '23 at 20:16
  • @Bubaya I've updated my answer. – Qrrbrbirlbel Mar 22 '23 at 20:43
  • Wow. I wouldn't have expected that my additional demand makes the solution so much more complicated. Thank you very much for addressing also that! – Bubaya Mar 23 '23 at 08:46