2

The \arrow functionality in tikz-cd is placed in a given cell, and uses the position of that cell, although execution of the code itself (if I'm understanding correctly) is deferred until after the matrix component has finished plotting.

I have several functions that I'd like to achieve the same with. At the moment, they're macros that I call within the 'execute at end picture' key for tikzcd, and I pass the relevant cell parameters as arguments. For example, \slice{col}{title} puts a dashed line between columns col and col+1 with a title above the dash. I'd like to be able to put the `slice` command in a cell of the matrix instead of in the key, and to be able to use the column of the cell it's placed in to determine the first parameter (reducing the number of arguments, making a big difference to usability). I'm getting very jumbled with how to get things to evaluate at the right times, and would appreciate some help

I've been trying to work through some of the code for the tikz-cd package, but I get lost very quickly with all the @ symbols, \expandafters and \noexpands, although I have understood that I probably want to attach my commands to \tikzcd@savedpaths, and have found this answer quite illuminating.

As a partial step, to get my head around this, I decided to try and write a new macro using the existing \arrow command which, instead of a d syntax for indicating a downward step, uses a numerical step, and wants to calculate the name of the target cell. If I just use the name of the target cell as the node text, it appears correctly, but is I use it as the argument for \arrow, it (presumably) gets evaluated after the matrix is drawn so the current row and column evaluate as the last row and column, and all placement is done relative to that. To be clear, I do not want a solution that just looks at the number in the argument and calls \arrow, repeating d that number of times in the argument.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{cd,fit,calc}

\newcommand{\slice}[2]{
    \xdef\Loop{}
    \foreach \n in  {1,...,\the\pgfmatrixcurrentrow} {\xdef\Loop{\Loop(m-\n-#1)} }
    \node (slice-#1) [fit=\Loop, inner sep=0pt] {}; %a node that contains column #1
    \xdef\Loop{}
    \foreach \n in  {1,...,\the\pgfmatrixcurrentrow} {\xdef\Loop{\Loop(m-\n-\the\numexpr#1+1\relax)} }
    \node (slice2-#1) [fit=\Loop, inner sep=0pt] {}; %a node that contains column #1+1
%now draw a line between the two nodes
    \draw[slice] ($1/2*(slice-#1.north east |- m.north)+1/2*(slice2-#1.north west |- m.north)$) to node[pos=0,inner sep=4pt,anchor=south,color=black] {#2} ($1/2*(slice-#1.south east |- m.south)+1/2*(slice2-#1.south west |- m.south)+(0,-3pt)$);
}

\newcommand{\ctrl}[1]{
    %implement essentially an arrow command but take a numerical parameter for the number of rows below (positive) or above (negative)
    %many ways I could do this, but using \pgfmatrixcurrentrow illustrates one of the issues in wider implementation
    \arrow[dash,thick]{\the\numexpr#1+\pgfmatrixcurrentrow\relax-\the\pgfmatrixcurrentcolumn}
    %actually calculates target relative to bottom right of matrix (i.e. \pgfmatrixcurrentrow not evaluated at right time).
}
\newcommand{\slicer}[1]{
%somehow have to make this command execute after matrix. Either through key `execute at end picture' or by adding to macro \tikzcd@savedpaths?
%need this \pgfmatrixcurrentcolumn to evaluate immediately, but values inside the slice macro must be evaluated when run (to give total number of rows/cols)

    %\slice{\the\pgfmatrixcurrentcolumn}{#1}
    %currently gives Undefined control sequence error
}

\tikzcdset{nodes in empty cells,every matrix/.append style={name=m}} %fix name of matrix as m
\tikzset{
    every node/.style={
        anchor=center,minimum size=0pt,inner sep=0pt,outer sep=0pt,thick
    },
    slice/.style={thick,red,dash pattern=on 5pt off 3pt},
    operator/.style={draw,fill=white,minimum size=1.5em}
}

\begin{document}
\begin{center}
\begin{tikzcd}[execute at end picture={\slice{1}{title}}]
|[operator]| A & |[operator]| B \\
|[operator]| C \arrow[u,dash,thick] & |[operator]| D
\end{tikzcd}
\end{center}
The syntax that I'd like to use to achieve this:
\begin{center}
\begin{tikzcd}
|[operator]| A \slicer{title} & |[operator]| B \\
|[operator]| C \ctrl{-1} & |[operator]| D
\end{tikzcd}
\end{center}
\end{document} 

The output is: enter image description here

4
  • Could you please explain why you want these commands to be inside the commutative diagram. It is almost trivial to achieve these things by drawing them after you're done with the diagram.
    – user121799
    Aug 16, 2018 at 15:27
  • @marmot Sure, this is the way I've always done it. But I'm trying to package up a bunch of these commands to make what I do much more automatic, and thinking about other future users of such a package, it would be better to minimise the complexity of the argument sequence. If they can be implicitly supplied by position in the matrix, I think it's better to put the effort on the side of the package than on the side of the user.
    – DaftWullie
    Aug 16, 2018 at 15:33
  • @marmot After all, that is the purpose of the commutative diagram package: we could make a matrix and write in all the arrows by hand afterwards, painstakingly calculating all the cell locations. But by not having to specify the start, and making the displacement relative, it's much nicer.
    – DaftWullie
    Aug 16, 2018 at 15:46
  • 1
    If you look into tikzlibrarycd.code.tex, you'll see that an arrow essentially adds something to a list that will be executed at the very end. \def\tikzcd@ar@new[#1]{% new syntax \pgfutil@g@addto@macro\tikzcd@savedpaths{% \path[/tikz/commutative diagrams/.cd,every arrow,#1]% (\tikzcd@ar@start\tikzcd@startanchor) to (\tikzcd@ar@target\tikzcd@endanchor); }} Maybe you can use that to add your commands.
    – user121799
    Aug 17, 2018 at 4:39

1 Answer 1

0

As @marmot suggested, the way to make the slicer work is to add the command to the \tikzcd@savedpaths macro. One then just has to get a bunch of \expandafter commands to make sure that you have evaluated the current column before adding it to the macro.

\makeatletter
\newcommand{\slicer}[1]{
%somehow have to make this command execute after matrix. Either through key `execute at end picture' or by adding to macro \tikzcd@savedpaths?
%need this \pgfmatrixcurrentcolumn to evaluate immediately, but values inside the slice macro must be evaluated when run (to give total number of rows/cols)
\edef\col{\the\pgfmatrixcurrentcolumn}
\expandafter\pgfutil@g@addto@macro\expandafter\tikzcd@savedpaths\expandafter{%
    \expandafter\slice\expandafter{\col}{#1}
}
}
\makeatother

Incidentally, this way of doing this gives you lots of possibilities for implementing ideas such as multi-row and/or multicolumns in a tikz matrix.

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.