7

I've been trying to use this great answer https://tex.stackexchange.com/a/21167/116348 to provide multicolumn in a tikzcd environment. However I have two issues:

  • the alignment is not correct (I expect the multicolumn cell to be horizontally centered while it's too much on the left)
  • connecting arrows gives bad result (it seems to point to the place where the box would have been without the mutlicol)
  • it is not made for multirow, or both multicol and multirow.

enter image description here

Any idea how to improve it?

MWE

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{cd}
\usetikzlibrary{backgrounds,positioning}
\usetikzlibrary{shapes,shapes.geometric,shapes.misc}
\usepackage{xparse}

\usetikzlibrary{matrix}

\makeatletter
%%% https://tex.stackexchange.com/a/21167/116348
\newdimen\multi@col@width
\newdimen\multi@col@margin
\newcount\multi@col@count
\multi@col@width=0pt

\tikzset{
  multicol/.code={%
    \global\multi@col@count=#1\relax
    \global\let\orig@pgfmatrixendcode=\pgfmatrixendcode
    \global\let\orig@pgfmatrixemptycode=\pgfmatrixemptycode
    \def\pgfmatrixendcode##1{\orig@pgfmatrixendcode%
      ##1%
      \pgfutil@tempdima=\pgf@picmaxx
      \global\multi@col@margin=\pgf@picminx
      \advance\pgfutil@tempdima by -\pgf@picminx
      \divide\pgfutil@tempdima by #1\relax
      \global\multi@col@width=\pgfutil@tempdima
      \pgf@picmaxx=.5\multi@col@width
      \pgf@picminx=-.5\multi@col@width
      \global\pgf@picmaxx=\pgf@picmaxx
      \global\pgf@picminx=\pgf@picminx
      %% Proposed fix does not help (it actually breaks the initial example)
      % \gdef\multi@adjust@position{%
      % \setbox\pgf@matrix@cell=\hbox\bgroup
      % \hfil\hskip-1.5\multi@col@margin
      % \hfil\hskip-.5\multi@col@width
      \gdef\multi@adjust@position{%
        \setbox\pgf@matrix@cell=\hbox\bgroup
        \hfil\hskip-\multi@col@margin
        \hfil\hskip-.5\multi@col@width
        \box\pgf@matrix@cell
        \egroup
      }%
      \gdef\multi@temp{\aftergroup\multi@adjust@position}%
      \aftergroup\multi@temp
    }
    \gdef\pgfmatrixemptycode{%
      \orig@pgfmatrixemptycode
      \global\advance\multi@col@count by -1\relax
      \global\pgf@picmaxx=.5\multi@col@width
      \global\pgf@picminx=-.5\multi@col@width
      \ifnum\multi@col@count=1\relax
      \global\let\pgfmatrixemptycode=\orig@pgfmatrixemptycode
      \fi
    }
  }
}
\makeatother

\begin{document}

\tikzstyle{Z}=[fill=green]

Works but poorly aligned:

\begin{tikzcd}
  |[Z]| \beta & |[Z,multicol=2]|\alpha+\beta+\delta+2\pi &&|[Z]| x\\
  |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta
\end{tikzcd}\\


Arrows are not exactly the expected ones:

\begin{tikzcd}
  |[Z]| \beta \ar[r] & |[Z,multicol=2]|\alpha+\beta+\delta+2\pi \ar[rr] &&|[Z]| x\\
  |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta
\end{tikzcd}


\begin{tikzpicture}
  \matrix[matrix of nodes,nodes={draw}] {
    A &|[multicol=3]| A long entry spanning three columns &&&  A \\
    A B &|[multicol=2]| A shorter entry &&  A & D E F \\
    A B & A B & A B C D E F & A B\\
  };
\end{tikzpicture}

\end{document}

EDIT

To clarify what I want, here is a first draft:

enter image description here

If we want a more rigorous definition, I'd try to write it in term of an optimisation program: try to find the smaller figure such that:

  • all normal (not multicol) node's center anchor are aligned on a grid pattern
  • multicolumns nodes's center are vertically aligned to be on the same line as their neighbor nodes (similar for multirow)
  • every right side of a node should be away by at least column sep (this value might change on per-column basis) from the left side of it's right neighbor and we place two separation coordinate on this segment, away by columnsep (similar for bottom/row sep)
  • all couples of column separation coordinates for a given column should be vertically aligned (similar for rows)
  • all nodes's centers should be midway between their closest left and right separation coordinate (similar for rows)

Hopefully this is constraint enough ^^

Actually I'm considering here only multicol or multirow nodes, but I guess we could define similar rules for nodes that share both multiple rows and lines (maybe by considering them as multiple stacked multicolumns rows, and adding additional constraints to align the top node with the bottom nodes).

2
  • 3
    Rather than only showing what you attempted, a draft of what you expect to get would be useful.
    – egreg
    Commented Feb 24, 2023 at 14:14
  • @egreg I tried to provide pictures of what I'd like to have, with a more formal statement describing the constraints
    – tobiasBora
    Commented Feb 24, 2023 at 18:11

2 Answers 2

6
+100

Updated solution:

Here is a macro \multi that can be used in a tikzcd. It can be used for cells that span multiple rows and/or columns. The usage is \multi[<name>]{<span>}{<content>}. Note the option nodes in empty cells must be used.

<name> is optional (default=A) and is only needed if you have more than one multi-cell with arrows in your diagram. Options can be added here to affect the cell such as color or alignment (must be after the name).

<span> is a string of r, l, d, u characters indicating the multi-cell position. For example, in the diagram below, the 3-3 cell is the multi-cell with span option dr. You would get the same result by placing the multi-cell in position 4-4 with span option ul.

Arrows to multi-cells can be added to the diagram using from and to with the named multi-cell.

enter image description here

Here is the code:

\documentclass{article}

\usepackage{tikz-cd}

\newcommand{\multi}[3][A]{\arrow[#2, phantom, "#3"{name=#1, inner sep=1ex}]}

\begin{document}

\begin{tikzcd}[nodes in empty cells]
a_{11}\arrow[dr] & \multi{r}{\mbox{wide multi cell}} & & a_{14}\\
\multi[B, align=center]{d}{tall\\multi\\cell} & a_{22}\arrow[r]\arrow[d] & a_{23}\arrow[r] & a_{24}\\
 & a_{32}\arrow[d] & \multi[C, align=center]{dr}{tall and wide\\multi cell\\rows and columns} &\\
a_{41} & a_{42} & &
\arrow[from=1-1, to=A]
\arrow[from=A, to=1-4]
\arrow[from=1-1, to=B]
\arrow[from=B, to=4-1]
\arrow[from=2-2, to=C]
\end{tikzcd}

\end{document}

Note that column sep and row sep may still need to be set manually. If the multi-cell is too tall or too wide it will overlap adjacent cells. Also, the macro does not require that the cells covered by the multi-cell are empty. This could be useful if you want to place a cell at an intersection of filled cells.

Old solution:

Here is an alternate approach that is much simpler. Place the content of the multicell on a phantom arrow. Give it a name and then add arrows to and from the named content at the end.

To simplify this, I created a 2-argument style called multi that can be used in an arrow. Sample usage:

X \arrow[rrr, multi={<contents>}{<name>}] & & & Y

This will place <contents> centered between the X cell and the Y cell. If you use A for the name, you can then use \arrow[from=1-1, to=A].

Note that column sep will not automatically adjust.

enter image description here

\documentclass{article}

\usepackage{tikz-cd}
\tikzcdset{multi/.style 2 args={phantom, "#1"{name=#2, inner sep=1ex}}}

\begin{document}

\begin{tikzcd}
\beta\arrow[rrr, multi={\sqrt{\alpha+\beta}}{A}] & & & x\\
\beta\arrow[r] & \beta+2\arrow[r] & \beta\arrow[r] & \beta
\arrow[from=1-1, to=A]
\arrow[from=A, to=1-4]
\end{tikzcd}

\vspace{1cm}
\begin{tikzcd}[column sep=11mm]
\beta\arrow[rrr, multi={\alpha+\beta+\delta+\theta+2\pi}{B}] & & & x\\
\beta\arrow[r] & \beta+2\arrow[r] & \beta\arrow[r] & \beta
\arrow[from=1-1, to=B]
\arrow[from=B, to=1-4]
\end{tikzcd}

\end{document}
3
  • Thanks for the trick, but unfortunately it does not work if the text is too big: in that case it will overlap with the content around while I'd like it to be adapted automatically.
    – tobiasBora
    Commented Feb 24, 2023 at 20:38
  • @tobiasBora: It's not an automatic solution, but it's easy to adjust column sep as in the second example.
    – Sandy G
    Commented Feb 24, 2023 at 20:54
  • Yeah, it's true that it can be useful especially given its simplicity. But I'm still interested by an automatic solution.
    – tobiasBora
    Commented Feb 24, 2023 at 21:34
2

A way to tackle this could be to use \tikzcd@savedpaths to store and only later execute paths. So, the idea is essentially to first draw the matrix without the multi-column cell and then, after the matrix has been layed out, place it where it belongs.

The following is just a prelimilary approach. It comes with another, maybe not so nice syntax, but then again, syntax questions might be solvable in a second step:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{cd, calc}

\makeatletter
\newcommand{\spancell}[5][]{
    \pgfutil@g@addto@macro\tikzcd@savedpaths{%
        \node[#1] at ($(#2)!0.5!(#3)$) (#5) {$#4$};
    }
}
\makeatother

\tikzcdset{
    nodes in empty cells,
    every matrix/.append style={
        name=m
    },
    /tikz/Z/.style={
        fill=green
    }
}

\begin{document}

\begin{tikzcd}
  |[Z]| \beta & \spancell[Z]{m-1-2}{m-1-3}{\alpha+\beta+\delta+2\pi}{spancell-1} \ar[from=m-1-1,to=spancell-1] \ar[from=spancell-1,rr] & &|[Z]| x \\
  |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta \ar[r] & |[Z]| \beta
\end{tikzcd}

\end{document}

enter image description here

As for the stretching of the two relevant columns if the node is relatively wide, an idea could be to retrieve the width of this node, divide it by two and set the minimum width of the two columns to this width.

3
  • 1
    Re your last paragraph. My node-families library can already be used to measure the widest node in each column and retrieve those values on the next run but contributing the extra column sep onto the columns seems extra complicated because we can't change it after the second row. Either we need to adjust the cells' sizes accordingly or need to do this at the start of the matrix (another thing to write into the aux file). Commented Feb 25, 2023 at 11:18
  • @Qrrbrbirlbel True, you are right. It is not possible to change the width of the columns once the first row of nodes as been placed. So, essentially, in order to have an automatic solution as for the column widths, the whole thing would require at least two runs: first compute the widths of all the nodes, then layout the whole table (or matrix) and add things such as arrows. Commented Feb 25, 2023 at 17:07
  • Thanks, but I guess we would need to tweak node-families to differentiate betweer multinodes and normal nodes. By the way, once we compute the width of the column, how do you apply it?
    – tobiasBora
    Commented Feb 25, 2023 at 21:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .