2

In a TikZ matrix, how can I underline multicolumns automatically as follows (esp. when height of one header cell is larger than others):

enter image description here

MWE from this question:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}


\def\totcvtext{$\begin{array}{c}{{\rm tot}\ {\rm comm}}\\[-.8ex]{\rm vol (words)}\end{array}$}

\makeatletter
\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
  \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}
\begin{tikzpicture}
\matrix (mat) [matrix of nodes] {
L2A &|[multicol=2]| L2B     && |[multicol=2]| L2C & \\
L3A &   L3B &   L3C         &  \totcvtext & L3E \\
10  &   20  &   30          &   40  & 50 \\
};
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-2.north west) -- (mat-2-3.north east);
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-4.north west) -- (mat-2-5.north east);
\end{tikzpicture}
\end{document}

Note that height of other cells containing numbers should not be changed.

Kadir
  • 1,537
  • 1
  • 11
  • 27
  • To clarify: do you mean that you want the line to show just by typing |[multicol=2]|? You don't want to have to type \draw[thick,black] ([shift={(1.1,1.1)}]mat.south west) -- ([shift={(2.8,1.1)}]mat.south west); each time, right? – John Wickerson May 08 '13 at 12:11
  • 1
    Here's a way to make it slightly more automatic: if you use \draw[thick,black] (mat-2-2.north west) -- (mat-2-4.north west); then you avoid your magic numbers. – John Wickerson May 08 '13 at 12:28
  • Dear @JohnWickerson, using (mat-2-2.north west) -- (mat-2-4.north west); is good. However, there will be problem when there are two multicolumns side by side, the two lines should not be combined, there must be a little space. So again I have to use magic numbers to shift lines. If it is possible to underline multicolumn via using only |[multicol=2]| or additionally with a letter for indicating underline operation, it will be great!!! – Kadir May 08 '13 at 15:57
  • If you don't want to combine line, use \draw[thick,black,shorten >=...,shorten <=...] (mat-2-2.north west)--(mat-2-4.north west); where ... is some value (3pt, 1mm, ...). Your line will start this distance after mat-2-2.north west and finish this distance earlier than mat-2-2.north west. – Ignasi May 08 '13 at 16:41

1 Answers1

2

Note:

  • you can refer to the cell in the ith row and jth column of the matrix mat as mat-i-j, and
  • as Ignasi pointed out above, you can shorten the lines a little, to avoid problems with consecutive merged cells.

Result:

enter image description here

Modified code:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}

\makeatletter
\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
  \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}
\begin{tikzpicture}
\matrix (mat) [matrix of nodes] {
L2A &|[multicol=2]| L2B     && |[multicol=2]| L2C & \\
L3A &   L3B &   L3C         &   L3D & L3E \\
10  &   20  &   30          &   40  & 50 \\
};
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-2.north west) -- (mat-2-3.north east);
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-4.north west) -- (mat-2-5.north east);
\end{tikzpicture}
\end{document}

Update

The OP has pointed out that this solution does not work well when the cells in the matrix have different heights. The problem is that although the cells in a TikZ matrix are horizontally and vertically aligned, they are not stretched into a uniform tessellating grid. That is, the south west point of one cell doesn't necessary coincide with the north west point of the cell immediately below it. The picture below clarifies this situation by colouring each cell in red.

enter image description here

One way to fix this situation is to specify, manually, the height of each row. This can be done for just the second row using the key row 2/.style={text height=2ex, text depth=1ex}. The result is as follows.

enter image description here

Complete code:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}

\makeatletter
\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
  \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}

\def\totcvtext{\begin{tabular}{c}tot comm \\[-0.8ex] vol(words)\end{tabular}}

\begin{tikzpicture}
\matrix (mat) [matrix of nodes, nodes={fill=red}, row 2/.style={text height=2ex, text depth=1ex}
] {
L2A &|[multicol=2]| L2B     && |[multicol=2]| L2C & \\
L3A &   L3B &   L3C         & \totcvtext & L3E \\
10  &   20  &   30          &   40       & 50 \\
};
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-2.north west) -- (mat-2-3.north east);
\draw[thick,black,shorten >=0.5mm,shorten <=0.5mm] (mat-2-4.north west) -- (mat-2-5.north east);
\end{tikzpicture}
\end{document}
David Carlisle
  • 757,742