9

I am trying to draw a "table" in tikz, and more generally, to work it out so that it is easy to write a lot of them.

The matrix library makes it a lot easier to place aligned nodes. The next step would be to be able to draw vertical and horizontal lines.

I have tried several approaches (some of them "just to be sure"):

  • using nodes anchors as described for example in the answer to this question (does not work if nodes have variable height or width, see the blue and red lines below)
  • using nodes for the columns or rows, using the fit library as described in answers to this question (better than the previous, see the orange line, but still some problems if the row (resp column) is not as wide (resp high) as the matrix [see the green line].

It is worth noting that horizontal lines can be drawn with \hline (but it lacks the customizability of tikz paths).

So the question: how to draw this kind of lines in a consistent way?

Edit (more information after the first answer)

I understand that it is possible to force the anchors to be aligned by specifying the height, depth and width of each cell. However, isn't that exactly what pgf did when drawing the matrix?

So the point of the question would be (if at all possible) to access this information (the position of the intersections of the bounding box of the matrix, and the limits of the areas between rows and columns), as computed by pgf, after the matrix is drawn.

MWE:

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{matrix,fit}

\begin{document}

\begin{tikzpicture}%[every node/.style={draw=black!30}]
  \node[%
  matrix of nodes,%
  every node/.append style={%
    inner xsep=5pt,
    inner ysep=5pt,
    outer sep=0pt
  },
  row sep=0pt,
  column sep=0pt
  ] (M) {
    {} & 1 & 2 \\
    1 & 1 & 2 \\
    2 & 2 & 4 \\
    3 & 3 & 6 \\
    1000000 & 1000000 & 2000000 \\
  };
  \draw[red] (M-1-2.north west) -- (M-5-1.south east);
  \draw[blue] (M-1-1.north east) -- (M-5-1.south east);

  \node[fit=(M-1-3) (M-5-3),inner sep=0pt] (C3) {};
  \draw[orange!80!black] (C3.north west) -- (C3.south west);

  \node[fit=(M-2-1) (M-2-3),inner sep=0pt] (R2) {};
  \draw[green!50!black] (R2.north west) -- (R2.north east);
\end{tikzpicture}

\end{document}

Output:

enter image description here

Output with cell borders:

enter image description here

Related questions:

2 Answers 2

13

Because all nodes are horizontally centered in the columns you can load the calc library and use

\draw[blue]({$(M-1-1)!.5!(M-1-2)$} |- M.north) -- ({$(M-1-1)!.5!(M-1-2)$} |- M.south);

to draw the blue line between the first and the second column.

To get the green line you have to use all nodes of the second row inside the fit option:

\node[fit=(M-2-1) (M-2-2) (M-2-3),inner sep=0pt] (R2) {};
\draw[green!50!black] (R2.north -| M.west) -- (R2.north -| M.east);

enter image description here

\documentclass[tikz,margin=10pt]{standalone}
\usetikzlibrary{matrix,fit,calc}

\begin{document}
\begin{tikzpicture}%
  \node[%
  matrix of nodes,%
  inner xsep=0pt,% <- code added
  every node/.append style={%
    draw=lightgray,
    inner xsep=5pt,
    inner ysep=5pt,
    outer sep=0pt,
  },
  row sep=0pt,
  column sep=0pt
  ] (M) {
    {}& 1 & 20 \\
    {}& 1 & {} \\
    2 & 2 & 4 \\
    3 & 3 & 6 \\
    1000000 & 1000000 & 2000000 \\
  };
% horizontal lines
  \draw[blue]({$(M-1-1)!.5!(M-1-2)$} |- M.north) -- ({$(M-1-1)!.5!(M-1-2)$} |- M.south);
  \draw[orange!80!black]({$(M-1-2)!.5!(M-1-3)$} |- M.north) --({$(M-1-2)!.5!(M-1-3)$} |- M.south);
% vertical lines
    \node[fit=(M-2-1) (M-2-2) (M-2-3),inner sep=0pt] (R2) {};
    \draw[green!50!black] (R2.north -| M.west) -- (R2.north -| M.east);
\end{tikzpicture}
\end{document}

It is possible to define macros:

\documentclass[tikz,margin=10pt]{standalone}
\usetikzlibrary{matrix,fit,calc}

% \mvline[<style>]{<matrix name>}{<row number on the right hand side of the line>}
\newcommand\mvline[3][]{%
  \pgfmathtruncatemacro\hc{#3-1}
  \draw[#1]({$(#2-1-#3)!.5!(#2-1-\hc)$} |- #2.north) -- ({$(#2-1-#3)!.5!(#2-1-\hc)$} |- #2.south);
}
% \mhline[<style>]{<matrix name>}{<column number below of the line>}{<number of columns in a row>}
\newcommand\mhline[4][]{%
  \node[fit=(#2-#3-1),inner sep=0pt,outer sep=0pt](R){};
  \foreach \i in {1,...,#4}\node[fit=(R) (#2-#3-\i),inner sep=0pt,outer sep=0pt](R){};
  \draw[#1] (R.north -| #2.west) -- (R.north -| #2.east);
}
\begin{document}
\begin{tikzpicture}%
  \node[%
  matrix of nodes,%
  inner xsep=0pt,% <- code added
  nodes in empty cells,% <- code added, nodes also in empty cells
  every node/.append style={%
    %draw=lightgray,
    inner xsep=5pt,
    inner ysep=5pt,
    outer sep=0pt,
  },
  row sep=0pt,
  column sep=0pt
  ] (M) {
      & 1 & 20 \\
      &   & \huge T  \\
    2 & 2 & 4 \\
    3 & 3 & 6 \\
    1000000 & 1000000 & 2000000 \\
  };
% border of the table
    \draw[purple](M.south west) rectangle (M.north east);
% horizontal lines
    \mvline[blue]{M}{2}
    \mvline[orange]{M}{3}
% vertical lines
    \foreach \r in {2,...,5} {\mhline[green!50!black]{M}{\r}{3}}
\end{tikzpicture}
\end{document}

enter image description here

0
3

You can force all nodes in the matrix to have the same size using text height, text width, and text depth. Then adjust column sep and row sep so that nodes are more compact.

MWE

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{matrix,fit}
\usepackage{calc}

\begin{document}

\begin{tikzpicture}%[every node/.style={draw=black!30}]
  \node[%
  matrix of nodes,%
  every node/.append style={%
    inner xsep=5pt,
    inner ysep=5pt, 
    draw=lightgray, % just to show node borders
    text height=\heightof{0},
    text width=\widthof{2000000},
    align=center
  },
  row sep=-\pgflinewidth,
  column sep=-.5\pgflinewidth,
  ] (M) {
    {} & 1 & 2 \\
    1 & 1 & 2 \\
    2 & 2 & 4 \\
    3 & 3 & 6 \\
    1000000 & 1000000 & 2000000 \\
  };
  \draw[red] (M-1-2.north west) -- (M-5-1.south east);
  \draw[blue] (M-1-1.north east) -- (M-5-1.south east);

  % \node[fit=(M-1-3) (M-5-3),inner sep=0pt] (C3) {};
  \draw[orange!80!black] (M-1-3.north west) -- (M-5-3.south west);

  % \node[fit=(M-2-1) (M-2-3),inner sep=0pt] (R2) {};
  \draw[green!50!black] (M-2-1.north west) -- (M-2-3.north east);
\end{tikzpicture}

\end{document}

Output

enter image description here

5
  • Thanks! Row sep and column sep have to remain at 0pt, otherwise the correct anchors for the lines will get really messy. Adjusting width, height and depth to match the max of the values shouldn't change the "compactness" of the matrix anyway. While this solution works, it will quickly become a lot more work for more complicated matrices (requiring one option per row, for height and depth, and one per column, for width), with the need to adjust the values whenever there is a change in the matrix.
    – T. Verron
    Oct 3, 2014 at 16:58
  • 1
    Overall, this approach looks much like repeating the work done by matrix: aligning the nodes somewhat requires to figure out the width, height and depth of each one. That's why I hope that there is a way to do this without resorting to this kind of manual adjustments. If it turns out that there is no other answer, of course, I will accept this. Better get the work done with dirty and repetitive code than not at all.
    – T. Verron
    Oct 3, 2014 at 17:01
  • @T.Verron: According to my reading of the PGF manual (v3.0, Ch.103), the alignment of matrix nodes is done internally via the \halign command, a TeX primitive that is also used in creating ordinary tables in LaTeX. So I'm not sure if there'll be internal anchors that you can access to help draw the lines...
    – Herr K.
    Oct 3, 2014 at 21:34
  • @T.Verron: Regarding row/column sep, if only set to 0pt, you'll see that, when drawn, the adjoining borders are thicker than the non-adjoining ones. This is clear from the 2nd pic in your question: see the vertical border of the first two cells in the bottom row and the horizontal borders of the adjacent cells in each column. Setting the col/row sep to -\pgflinewidth would correct this issue.
    – Herr K.
    Oct 3, 2014 at 21:42
  • Ah indeed. I misunderstood your first paragraph, I thought that you meant that would be an adjustment that would be required everytime after changing the height, depth or width.
    – T. Verron
    Oct 4, 2014 at 6:23

You must log in to answer this question.

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