2

How could I create an environment that wraps a tikzcd (basically a tikz matrix) inside \vcenter{...} while keeping the syntax intact?

What I tried: According to How do I wrap a macro definition in an environment? I need to use environ or NewDocumentComment{+b} (I prefer the later since it seems to be "native" now). But then the environment turns into a macro and I have troubles with & (see Problem with defining shortcuts for TikZ matrices).

/!\ Note that I don't want to change & into \&! So I tried to adapt https://tex.stackexchange.com/a/611535/116348 to environments, but not sure how to do.

MWE:

\documentclass{article}
\usepackage{amsmath}
\usepackage{etoolbox}
\usepackage{tikz}
\usepackage{tikz-cd}
\usetikzlibrary{shapes,shapes.geometric,shapes.misc,positioning}

\NewDocumentEnvironment{vtikzcd}{O{}+b}{ \ensuremath{% \vcenter{% \hbox{ \begin{tikzcd}[#1] #2 \end{tikzcd} }% }% }% }

\begin{document} % Ok \begin{tikzcd} A & B\ C & D \end{tikzcd}

% Fails: \begin{vtikzcd} A & B\ C & D \end{vtikzcd}

\end{document}

I tried to define:

\newenvironment{vtikzcd}{
  \begingroup% To avoid ampersand issues https://tex.stackexchange.com/a/611535/116348
  \NewDocumentEnvironment{tmpZX}{O{}+b}{%
    \endgroup%
    \ensuremath{%
      \vcenter{%
        \hbox{%
          \begin{tikzcd}[##1]%
            ##2%
          \end{tikzcd}%
        }%
      }%
    }%
  }{}%
  \catcode`&=13
  \begin{tmpZX}%
}{\end{tmpZX}}

but it does not compile.

EDIT Instead of using \vcenter, I managed to center the diagram appropriately (including one line diagrams) by editing instead the style, using something like:

baseline={([yshift=-.27em]current bounding box.center)},1-row diagram/.style={%
      /tikz/baseline={([yshift=-.27em]current bounding box.center)}%
    }]

I'm not sure exactly which value I should pick for yshift tought. That said, I'm still interested by this question in case I want later to wrap my environment with any command.

tobiasBora
  • 8,684
  • Wrapping tikz-cd is in general a bad idea because of all the \catcode things (not only &). I'd say the baseline approach is more natural and flexible (in the sense that you may replace .27em by something else, like .5ex). – Symbol 1 Oct 07 '21 at 08:45
  • 1
    One more thing, check out axis_height in the tikz-cd manual. – Symbol 1 Oct 07 '21 at 08:48
  • @Symbol1 Thanks a lot for axis_height, baseline={([yshift=-axis_height]current bounding box.center)} (same for 1-row diagram) works great. See also https://tex.stackexchange.com/questions/618054/fake-vcenter-in-tikz-using-baseline/618057#618057. – tobiasBora Oct 07 '21 at 09:46

1 Answers1

2

When & is used in TikZ matrices (which tikzcd environments is a particular case of), its category code is changed to active (13). But this is not possible when the code is passed as the argument to another command.

So your

\NewDocumentEnvironment{vtikzcd}{O{}+b}{% <--- this is necessary
  \ensuremath{% <--- this is not necessary
    \vcenter{% <--- this is not necessary
      \hbox{% <--- this is necessary
        \begin{tikzcd}[#1]
          #2
        \end{tikzcd}% <--- this is necessary
      }% <--- this is not necessary
    }% <--- this is not necessary
  }% <--- this is necessary
}
{}

doesn't work (I added the necessary % and {} that you missed), because the environment's content is grabbed as the argument to a command.

So you should not use the b argument type, if you don't want to add ampersand replacement=\& and type in \& inside vtikzcd.

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}

\newsavebox{\vtikzcdbox}

\NewDocumentEnvironment{vtikzcd}{O{}}{% \begin{lrbox}{\vtikzcdbox} $\vcenter\bgroup\hbox\bgroup \begin{tikzcd}[#1] }{% \end{tikzcd}% \egroup\egroup$ \end{lrbox} \usebox{\vtikzcdbox}% }

\begin{document} % no vcenter X\begin{tikzcd} A & B\ C & D \end{tikzcd}X

% vcenter X\begin{vtikzcd} A & B\ C & D \end{vtikzcd}X

\end{document}

enter image description here

If you want to pursue the b way, you can replace all & with \&.

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz-cd}

\ExplSyntaxOn

\NewDocumentEnvironment{vtikzcd}{O{}+b} { \ensuremath{\vcenter{\hbox{ \begin{tikzcd}[ampersand~replacement=&amp;,#1] \tl_set:Nn \l_tmpa_tl { #2 } \tl_replace_all:Nnn \l_tmpa_tl { & } { &amp; } \tl_use:N \l_tmpa_tl \end{tikzcd} }}} } {}

\ExplSyntaxOff

\begin{document} % no vcenter X\begin{tikzcd} A & B\ C & D \end{tikzcd}X

% vcenter X\begin{vtikzcd} A & B\ C & D \end{vtikzcd}X

\end{document}

Don't overuse the b argument type. It's a possibility, not the best one in all cases.

Symbol 1
  • 36,855
egreg
  • 1,121,712
  • Thanks you so much! Btw, why do you need the lrbox? I tried without and it seems to work fairly well. Can I say that a general recipe to wrap macro around environment is to replace { by \bgroup? – tobiasBora Oct 07 '21 at 08:56
  • @tobiasBora That removes the need for \ensuremath. – egreg Oct 07 '21 at 08:56