# Automatically find which nodes are closest, to aid drawing lines within a TikZ matrix

This question is a follow up of Adding coloured dashed lines inside matrix with external braces.

UPDATE AT BOTTOM OF POST

I have a method for drawing lines in between rows and columns of a TikZ matrix that I would like to make more automated. Here is an example matrix:

Let's say I want to draw a vertical line between columns 1 and 2. My current method works like this:

• I manually figure out that I want this vertical line to be aligned between the east edge of node (3,1) and the west edge of (1,2), as these are respectively the eastmost and westmost edges of the 1st and 2nd columns.
• I find the midpoint between these two coordinates using \path (m-3-1.east)--(m-1-2.west) coordinate [midway] (X);
• And I draw the required vertical line using \draw [dashed] (X |- m.north) -- (X |- m.south);

This works perfectly. Here is the output:

To make this process simpler, I've made a command called \mymatrixdashedlineV which takes the following arguments \mymatrixdashedlineV[*optional line styles*]{*name of matrix*}{*column after which the vertical line comes*}{*the row number of the eastmost edge in preceding column*}{*the row number of westmost edge in following column*}. So for the example above, the required command is \mymatrixdashedlineV{m}{1}{3}{1}.

My question is, is it possible to improve this procedure? Rather than work it out by eye, can the computer work out in which row the eastmost edge occurs, and in which row the westmost edge occurs. Therefore all I would need to write is \mymatrixdashedlineV{m}{1}. It just happens that in this case it is obvious which are the eastmost and westmost edges. But often it is not obvious by eye, and this method requires constant upkeep if elements of the matrix are changed.

I have also implemented a similar method for drawing horizontal lines. So in this case, I want to specify a row (after which the horizontal line comes), but let the computer work out which are the southmost and northmost edges in the columns to align the horizontal line with.

Here is the example matrix annotated with the location of all the east, west, north and south edges. And a horizontal line using \mymatrixdashedlineH[blue]{m}{2}{3}{2}

Some thoughts

• N̶e̶e̶d̶ ̶a̶ ̶w̶a̶y̶ ̶t̶o̶ ̶a̶u̶t̶o̶m̶a̶t̶i̶c̶a̶l̶l̶y̶ ̶r̶e̶t̶r̶i̶e̶v̶e̶ ̶t̶h̶e̶ ̶s̶i̶z̶e̶ ̶o̶f̶ ̶t̶h̶e̶ ̶m̶a̶t̶r̶i̶x̶ ̶̶(̶m̶)̶. This can be achieved using Extract the tikzpicture matrix dimension and \the\pgfmatrixcurrentrow and \the\pgfmatrixcurrentcolumn.
• I̶t̶ ̶d̶o̶e̶s̶n̶'̶t̶ ̶l̶i̶k̶e̶ ̶i̶t̶ ̶i̶f̶ ̶a̶ ̶c̶e̶l̶l̶ ̶i̶s̶ ̶e̶m̶p̶t̶y̶.̶ ̶H̶e̶n̶c̶e̶ ̶i̶n̶ ̶t̶h̶i̶s̶ ̶e̶x̶a̶m̶p̶l̶e̶ ̶I̶'̶v̶e̶ ̶h̶a̶d̶ ̶t̶o̶ ̶p̶u̶t̶ ̶̶\̶n̶u̶l̶l̶̶ ̶i̶n̶ ̶a̶n̶ ̶e̶m̶p̶t̶y̶ ̶c̶e̶l̶l̶. Solved this by using the option nodes in empty cells. Updated my MWE.

MWE

\documentclass[12pt,a4paper]{article}
\usepackage{mathtools}

% CODE BELOW FROM https://tex.stackexchange.com/a/1070/128068
\usepackage{tikz}
\usetikzlibrary{matrix,decorations.pathreplacing,calc}
% Set various styles for the matrices and braces.
\pgfkeys{tikz/mymatrixenv/.style={decoration=brace,every left delimiter/.style={xshift=3pt},every right delimiter/.style={xshift=-3pt}}}
\pgfkeys{tikz/mymatrix/.style={matrix of math nodes,left delimiter=[,right delimiter={]},inner sep=2pt,column sep=1em,row sep=0.5em,nodes={inner sep=0pt}}}
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

% My custom command for drawing lines (dashed by default) within matrices

\newcommand*\mymatrixdashedlineH[5][]{
\pgfmathparse{int(#3+1)} \path (#2-#3-#4.south) -- (#2-\pgfmathresult-#5.north)
coordinate [midway] (Y); \draw [dashed,#1] (Y -| #2.west) -- (Y -| #2.east);}

\newcommand*\mymatrixdashedlineV[5][]{
\pgfmathparse{int(#3+1)} \path (#2-#4-#3.east) -- (#2-#5-\pgfmathresult.west)
coordinate [midway] (X); \draw [dashed,#1] (X |- #2.north) -- (X |- #2.south);}

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em,column sep=1em,nodes in empty cells] (m) { 1& AB+1234 & 1234 \\ 0 & 1 & \dfrac{A}{B} \\ \frac{x}{y} + 1234 & \dfrac{A}{B} & \\ }; Display the locations of node N,S,E,W \foreach \X in {1,...,\the\pgfmatrixcurrentrow}{ \foreach \Y in {1,...,\the\pgfmatrixcurrentcolumn}{ \draw [fill=blue] (m-\X-\Y.south) circle (1pt); \draw [fill=blue] (m-\X-\Y.north) circle (1pt); \draw [fill=red] (m-\X-\Y.east) circle (1pt); \draw [fill=red] (m-\X-\Y.west) circle (1pt); }} %% The current manual method for drawing horizontal lines %\path (m-2-3.south)--(m-3-2.north) coordinate [midway] (Y); %\draw [dashed] (Y -| m.west) -- (Y -| m.east); %% The current manual method for drawing vertical lines %\path (m-3-1.east)--(m-1-2.west) coordinate [midway] (X); %\draw [dashed] (X |- m.north) -- (X |- m.south); \mymatrixdashedlineV{m}{1}{3}{1} \mymatrixdashedlineH[blue]{m}{2}{3}{2} \end{tikzpicture}$$
\end{document}


UPDATE

Reading the PGF/TikZ manual, on page 307 it demonstrates how the column separation, which is controlled by the option column sep is between the eastmost edge to the westmost edge of neighboring columns. This simplifies my problem, in that we in theory only need to focus on one column? If we find the eastmost edge of all the nodes in the preceding column, then we can just add the column sep value divided by 2 to the x coordinate and draw a vertical line from there. TikZ seems to already be working out which node edge is eastmost to do the column separation anyway, so this must be possible. Can you automatically retrieve the value of column sep from the options that were passed to the matrix though?

Adding nodes=draw to the matrix options. And adding the line

\draw [red] (m-3-1.east)--++(0:1em)--(m-1-2.west);


Shows that as expected these two columns are 1em apart (as specified by my column sep value).

• You could use fits to fit all nodes of a column and draw the line in the between the fitting nodes. – user121799 Jul 25 '18 at 18:28

You could use fits to fit all nodes of a column and draw the line in the between the fitting nodes.

\documentclass[12pt,a4paper]{article}
\usepackage{mathtools}

% CODE BELOW FROM https://tex.stackexchange.com/a/1070/128068
\usepackage{tikz}
\usetikzlibrary{matrix,decorations.pathreplacing,calc,fit}
% Set various styles for the matrices and braces.
\pgfkeys{tikz/mymatrixenv/.style={decoration=brace,every left delimiter/.style={xshift=3pt},every right delimiter/.style={xshift=-3pt}}}
\pgfkeys{tikz/mymatrix/.style={matrix of math nodes,left delimiter=[,right delimiter={]},inner sep=2pt,column sep=1em,row sep=0.5em,nodes={inner sep=0pt}}}
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

% My custom command for drawing lines (dashed by default) within matrices

\newcommand*\mymatrixdashedlineH[5][]{
\pgfmathparse{int(#3+1)} \path (#2-#3-#4.south) -- (#2-\pgfmathresult-#5.north)
coordinate [midway] (Y); \draw [dashed,#1] (Y -| #2.west) -- (Y -| #2.east);}

\newcommand*\mymatrixdashedlineV[5][]{
\pgfmathparse{int(#3+1)} \path (#2-#4-#3.east) -- (#2-#5-\pgfmathresult.west)
coordinate [midway] (X); \draw [dashed,#1] (X |- #2.north) -- (X |- #2.south);}

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em,column sep=1em,nodes in empty cells] (m) { 1& AB+1234 & 1234 \\ 0 & 1 & \dfrac{A}{B} \\ \frac{x}{y} + 1234 & \dfrac{A}{B} & \\ }; Display the locations of node N,S,E,W \foreach \X in {1,...,\the\pgfmatrixcurrentrow}{ \foreach \Y in {1,...,\the\pgfmatrixcurrentcolumn}{ \draw [fill=blue] (m-\X-\Y.south) circle (1pt); \draw [fill=blue] (m-\X-\Y.north) circle (1pt); \draw [fill=red] (m-\X-\Y.east) circle (1pt); \draw [fill=red] (m-\X-\Y.west) circle (1pt); }} %% The current manual method for drawing horizontal lines %\path (m-2-3.south)--(m-3-2.north) coordinate [midway] (Y); %\draw [dashed] (Y -| m.west) -- (Y -| m.east); %% The current manual method for drawing vertical lines %\path (m-3-1.east)--(m-1-2.west) coordinate [midway] (X); %\draw [dashed] (X |- m.north) -- (X |- m.south); \node [fit=(m-1-1) (m-2-1) (m-3-1)] (fit-1) {}; \node [fit=(m-1-2) (m-2-2) (m-3-2)] (fit-2) {}; \path (fit-1.north east) -- (fit-2.north west) coordinate[midway] (X1) (fit-1.south east) -- (fit-2.south west) coordinate[midway] (X2); \draw[dashed] (X1 |- m.north) -- (X2 |- m.south) ; % \mymatrixdashedlineV{m}{1}{3}{1} % \mymatrixdashedlineH[blue]{m}{2}{3}{2} \end{tikzpicture}$$
\end{document}


• +1 very nice! Could you simplify and just use \draw (fit-1.east) -- (fit-2.west) coordinate[midway] (X1); \draw[dashed] (X1 |- m.north) -- (X1 |- m.south) ; ? Any reason to calculate two midway coordinates? – Milo Jul 25 '18 at 18:41
• Next question is how to perform the fit for a matrix of arbitrary size? \the\pgfmatrixcurrentrow should be handy for this. But not sure how to implement something. – Milo Jul 25 '18 at 18:45
• Worked it out using this hack! This post is from 2012, so I don't know if the feature request has since been added ? – Milo Jul 25 '18 at 18:59
• \newcommand*\mymatrixdashedlineV[3][]{ \pgfmathparse{int(#3+1)} \node [fit= \foreach \X in {1,...,\the\pgfmatrixcurrentrow}{(m-\X-#3)}] (fit-a) {}; \node [fit= \foreach \X in {1,...,\the\pgfmatrixcurrentrow}{(m-\X-\pgfmathresult)}] (fit-b) {}; \path (fit-a.east) -- (fit-b.west) coordinate[midway] (X); \draw[dashed,#1] (X |- #2.north) -- (X |- #2.south);} requires the hack from the linked post. e.g. Try \mymatrixdashedlineV{m}{1} – Milo Jul 25 '18 at 19:00
• @Milo Looks great, will give more feedback later, need to run now... – user121799 Jul 26 '18 at 4:41