Adding coloured dashed lines inside matrix with external braces

This is the kind of the matrix I'm trying to create:

Because I wanted labelled braces around the matrix, I've used the TikZ solution from this question. To get the dashed lines I've used the cheating dash style from this question, and drawn them taking inspiration from How to create vertical and horizontal dotted lines in a matrix?. My problem is this method requires a lot of manual adjustment.

Another method to draw lines within matrices comes from How to add dash lines in Matrix, where there is a solution which uses array and arydshln.

This draws them automatically, but I can't at the moment (a) change the colour, (b) add external braces to the matrix and (c) the ends of the dashed lines get cut off (this is precisely why I use cheating dash in the previous example). All these reasons suggest the TikZ method is the way to go. If only there was a better way to draw the lines automatically without having to the manually specify the start and end points?

MWE

\documentclass[12pt,a4paper]{article}

\usepackage{mathtools}
\usepackage{arydshln}

% 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. It might pay off to fiddle around with the values a little bit
\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}}}
\pgfkeys{tikz/mymatrixbrace/.style={decorate,thick}}
\newcommand\mymatrixbraceoffseth{0.5em}
\newcommand\mymatrixbraceoffsetv{0.2em}

\newcommand*\mymatrixbraceright[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-#3-1.south west)!(#1.south west)-(\mymatrixbraceoffseth,0)$)
-- node[left=2pt] {#4}
($(#1.north west)!(#1-#2-1.north west)!(#1.south west)-(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbraceleft[4][m]{
\draw[mymatrixbrace] ($(#1.north east)!(#1-#2-1.north east)!(#1.south east)+(\mymatrixbraceoffseth,0)$)
-- node[right=2pt] {#4}
($(#1.north east)!(#1-#3-1.south east)!(#1.south east)+(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbracetop[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-1-#2.north west)!(#1.north east)+(0,\mymatrixbraceoffsetv)$)
-- node[above=2pt] {#4}
($(#1.north west)!(#1-1-#3.north east)!(#1.north east)+(0,\mymatrixbraceoffsetv)$);
}
\newcommand*\mymatrixbracebottom[4][m]{
\draw[mymatrixbrace] ($(#1.south west)!(#1-1-#3.south east)!(#1.south east)-(0,\mymatrixbraceoffsetv)$)
-- node[below=2pt] {#4}
($(#1.south west)!(#1-1-#2.south west)!(#1.south east)-(0,\mymatrixbraceoffsetv)$);
}

% CHEATING DASH FROM https://tex.stackexchange.com/a/133357/128068
\tikzset{
cheating dash/.code args={on #1 off #2}{
% Use csname so catcode of @ doesn't have do be changed.
\pgfgetpath\currentpath%
\pgfprocessround{\currentpath}{\currentpath}%
\csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
\pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1}\let\rest=\pgfmathresult%
\pgfmathparse{#1+#2}\let\onoff=\pgfmathresult%
\pgfmathparse{max(floor(\rest/\onoff), 1)}\let\nfullonoff=\pgfmathresult%
\pgfmathparse{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2, #2)}\let\offexpand=\pgfmathresult%
\pgfsetdash{{#1}{\offexpand}}{0pt}}%
}
}

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em] (m) { \frac{x}{y} + C & m\omega^2 + U \\ 0 & 1 \\ 1 & \frac{x}{y} + Dx^2 \\ }; % Dashed lines \draw [cheating dash=on 2pt off 2pt,red] ([xshift=1ex,yshift=1ex]m-1-1.north east) --++(-90:2.4cm); \draw [cheating dash=on 2pt off 2pt,red] ([xshift=-0.3ex,yshift=-0.9ex]m-1-1.south west) --++(0:3.3cm); % Braces \mymatrixbraceright{1}{3}{A} \mymatrixbracetop{2}{2}{B} \end{tikzpicture}$$

\begingroup
\renewcommand*{\arraystretch}{1.75}
$$\mathbf{X} = \left[ \begin{array}{c;{2pt/2pt}c} \frac{x}{y} + C & m\omega^2 + U \\ \hdashline[2pt/2pt] 0 & 1 \\ 1 & \frac{x}{y} + Dx^2 \\ \end{array} \right]$$
\endgroup

\end{document}


ADDED - I should mention that for different matrices I play around the tikz/mymatrix/.style settings, like changing inner sep and row sep. If the placement of the dashed lines could be somehow tied into those settings that would be ideal. This highlights why my current method (specifying the lengths in centimeters) is not ideal.

In order to avoid using explicit distances, I'd use the following code.

\documentclass[12pt,a4paper]{article}
% 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. It might pay off to fiddle around with the values a little bit
\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}}}
\pgfkeys{tikz/mymatrixbrace/.style={decorate,thick}}
\newcommand\mymatrixbraceoffseth{0.5em}
\newcommand\mymatrixbraceoffsetv{0.2em}

\newcommand*\mymatrixbraceright[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-#3-1.south west)!(#1.south west)-(\mymatrixbraceoffseth,0)$)
-- node[left=2pt] {#4}
($(#1.north west)!(#1-#2-1.north west)!(#1.south west)-(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbraceleft[4][m]{
\draw[mymatrixbrace] ($(#1.north east)!(#1-#2-1.north east)!(#1.south east)+(\mymatrixbraceoffseth,0)$)
-- node[right=2pt] {#4}
($(#1.north east)!(#1-#3-1.south east)!(#1.south east)+(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbracetop[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-1-#2.north west)!(#1.north east)+(0,\mymatrixbraceoffsetv)$)
-- node[above=2pt] {#4}
($(#1.north west)!(#1-1-#3.north east)!(#1.north east)+(0,\mymatrixbraceoffsetv)$);
}
\newcommand*\mymatrixbracebottom[4][m]{
\draw[mymatrixbrace] ($(#1.south west)!(#1-1-#3.south east)!(#1.south east)-(0,\mymatrixbraceoffsetv)$)
-- node[below=2pt] {#4}
($(#1.south west)!(#1-1-#2.south west)!(#1.south east)-(0,\mymatrixbraceoffsetv)$);
}

% CHEATING DASH FROM https://tex.stackexchange.com/a/133357/128068
\tikzset{
cheating dash/.code args={on #1 off #2}{
% Use csname so catcode of @ doesn't have do be changed.
\pgfgetpath\currentpath%
\pgfprocessround{\currentpath}{\currentpath}%
\csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
\pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1}\let\rest=\pgfmathresult%
\pgfmathparse{#1+#2}\let\onoff=\pgfmathresult%
\pgfmathparse{max(floor(\rest/\onoff), 1)}\let\nfullonoff=\pgfmathresult%
\pgfmathparse{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2, #2)}\let\offexpand=\pgfmathresult%
\pgfsetdash{{#1}{\offexpand}}{0pt}}%
}
}

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em] (m) { \frac{x}{y} + C & m\omega^2 + U \\ 0 & 1 \\ 1 & \frac{x}{y} + Dx^2 \\ }; \path (m-1-1.east) -- (m-1-2.west) coordinate[midway] (X) (m-1-1.south) -- (m-2-1.north) coordinate[midway] (Y); % Dashed lines \draw [cheating dash=on 2pt off 2pt,red] (X |- m.north) -- (X |- m.south); \draw [cheating dash=on 2pt off 2pt,red] (Y -| m.west) -- (Y -| m.east); % Braces \mymatrixbraceright{1}{3}{A} \mymatrixbracetop{2}{2}{B} \end{tikzpicture}$$
\end{document}


EDIT: Using your own suggestion (with some minor modifications), the code can be simplified to:

\documentclass[12pt,a4paper]{article}
% 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. It might pay off to fiddle around with the values a little bit
\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}}}
\pgfkeys{tikz/mymatrixbrace/.style={decorate,thick}}
\newcommand\mymatrixbraceoffseth{0.5em}
\newcommand\mymatrixbraceoffsetv{0.2em}

\newcommand*\mymatrixbraceright[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-#3-1.south west)!(#1.south west)-(\mymatrixbraceoffseth,0)$)
-- node[left=2pt] {#4}
($(#1.north west)!(#1-#2-1.north west)!(#1.south west)-(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbraceleft[4][m]{
\draw[mymatrixbrace] ($(#1.north east)!(#1-#2-1.north east)!(#1.south east)+(\mymatrixbraceoffseth,0)$)
-- node[right=2pt] {#4}
($(#1.north east)!(#1-#3-1.south east)!(#1.south east)+(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbracetop[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-1-#2.north west)!(#1.north east)+(0,\mymatrixbraceoffsetv)$)
-- node[above=2pt] {#4}
($(#1.north west)!(#1-1-#3.north east)!(#1.north east)+(0,\mymatrixbraceoffsetv)$);
}
\newcommand*\mymatrixbracebottom[4][m]{
\draw[mymatrixbrace] ($(#1.south west)!(#1-1-#3.south east)!(#1.south east)-(0,\mymatrixbraceoffsetv)$)
-- node[below=2pt] {#4}
($(#1.south west)!(#1-1-#2.south west)!(#1.south east)-(0,\mymatrixbraceoffsetv)$);
}

% CHEATING DASH FROM https://tex.stackexchange.com/a/133357/128068
\tikzset{
cheating dash/.code args={on #1 off #2}{
% Use csname so catcode of @ doesn't have do be changed.
\pgfgetpath\currentpath%
\pgfprocessround{\currentpath}{\currentpath}%
\csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
\pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1}\let\rest=\pgfmathresult%
\pgfmathparse{#1+#2}\let\onoff=\pgfmathresult%
\pgfmathparse{max(floor(\rest/\onoff), 1)}\let\nfullonoff=\pgfmathresult%
\pgfmathparse{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2, #2)}\let\offexpand=\pgfmathresult%
\pgfsetdash{{#1}{\offexpand}}{0pt}}%
}
}

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

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

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em] (m) { \frac{x}{y} + C & m\omega^2 + U \\ 0 & 1 \\ 1 & \frac{x}{y} + Dx^2 \\ }; \path (m-1-1.east) -- (m-1-2.west) coordinate[midway] (X) (m-1-1.south) -- (m-2-1.north) coordinate[midway] (Y); % Dashed lines \mymatrixdashedlineV[red,dashed]{m}{1}{2} \mymatrixdashedlineH[red,dashed]{m}{1}{2} % Braces \mymatrixbraceright{1}{3}{A} \mymatrixbracetop{2}{2}{B} \end{tikzpicture}$$
\end{document}


EDIT: Just for fun: some TikZy versions of your nice proposals. (NOTE: I am not saying that this is a safe path since I am using \pgfextra. It is really just for fun.)

\documentclass[12pt,a4paper]{article}
% 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. It might pay off to fiddle around with the values a little bit
\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}}}
\pgfkeys{tikz/mymatrixbrace/.style={decorate,thick}}
\newcommand\mymatrixbraceoffseth{0.5em}
\newcommand\mymatrixbraceoffsetv{0.2em}

\newcommand*\mymatrixbraceright[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-#3-1.south west)!(#1.south west)-(\mymatrixbraceoffseth,0)$)
-- node[left=2pt] {#4}
($(#1.north west)!(#1-#2-1.north west)!(#1.south west)-(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbraceleft[4][m]{
\draw[mymatrixbrace] ($(#1.north east)!(#1-#2-1.north east)!(#1.south east)+(\mymatrixbraceoffseth,0)$)
-- node[right=2pt] {#4}
($(#1.north east)!(#1-#3-1.south east)!(#1.south east)+(\mymatrixbraceoffseth,0)$);
}
\newcommand*\mymatrixbracetop[4][m]{
\draw[mymatrixbrace] ($(#1.north west)!(#1-1-#2.north west)!(#1.north east)+(0,\mymatrixbraceoffsetv)$)
-- node[above=2pt] {#4}
($(#1.north west)!(#1-1-#3.north east)!(#1.north east)+(0,\mymatrixbraceoffsetv)$);
}
\newcommand*\mymatrixbracebottom[4][m]{
\draw[mymatrixbrace] ($(#1.south west)!(#1-1-#3.south east)!(#1.south east)-(0,\mymatrixbraceoffsetv)$)
-- node[below=2pt] {#4}
($(#1.south west)!(#1-1-#2.south west)!(#1.south east)-(0,\mymatrixbraceoffsetv)$);
}

% CHEATING DASH FROM https://tex.stackexchange.com/a/133357/128068
\tikzset{
cheating dash/.code args={on #1 off #2}{
% Use csname so catcode of @ doesn't have do be changed.
\pgfgetpath\currentpath%
\pgfprocessround{\currentpath}{\currentpath}%
\csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
\pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1}\let\rest=\pgfmathresult%
\pgfmathparse{#1+#2}\let\onoff=\pgfmathresult%
\pgfmathparse{max(floor(\rest/\onoff), 1)}\let\nfullonoff=\pgfmathresult%
\pgfmathparse{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2, #2)}\let\offexpand=\pgfmathresult%
\pgfsetdash{{#1}{\offexpand}}{0pt}}%
}
}

\tikzset{
vertical line/.style args={with style #1 right of column #2 in
row #3}{
append after command={
\pgfextra{ \pgfmathparse{int(#2+1)}
\path (\tikzlastnode-#3-#2.east) -- (\tikzlastnode-#3-\pgfmathresult.west) coordinate [midway] (MiloX);
\draw [#1] (MiloX |- \tikzlastnode.north) -- (MiloX |- \tikzlastnode.south);
}
}
},
horizontal line/.style args={with style #1 below row #2 in
column #3}{append after command={\pgfextra{ \pgfmathparse{int(#2+1)}
\path (\tikzlastnode-#2-#3.south) -- (\tikzlastnode-\pgfmathresult-#3.north) coordinate [midway] (MiloY);
\draw [#1] (MiloY -| \tikzlastnode.west) -- (MiloY -| \tikzlastnode.east);
}}
}
}

\begin{document}

$$\mathbf{X} = \begin{tikzpicture}[baseline=0cm,mymatrixenv] \matrix [mymatrix,inner sep=4pt,row sep=1em, vertical line={with style {dashed,red} right of column 1 in row 1}, horizontal line={with style {dashed,red} below row 2 in column 1}] (m) { \frac{x}{y} + C & m\omega^2 + U \\ 0 & 1 \\ 1 & \frac{x}{y} + Dx^2 \\ }; \path (m-1-1.east) -- (m-1-2.west) coordinate[midway] (X) (m-1-1.south) -- (m-2-1.north) coordinate[midway] (Y); % Braces \mymatrixbraceright{1}{3}{A} \mymatrixbracetop{2}{2}{B} \end{tikzpicture}$$
\end{document}

• @Milo It was as stupid typo, there should not be an m]. I personally would prefer to also pass the options to the line. Thanks! – user121799 Jul 24 '18 at 20:00
• I propose an update to the macro! On second thoughts, it's rather unnecessary to provide two column numbers (or row numbers) to indicate between where the dividing line should occur. But, crucially, it is important that you are able to specify between which two matrix elements the midpoint is found. In my example, for the vertical line it just happened that the ideal location was for it to be aligned between cell (1,1) and (1,2). But say cell (1,1) and (3,1) were swapped. Then this macro fails to work effectively! Should have realised this earlier. here is my proposal: – Milo Jul 24 '18 at 20:29
• \newcommand*\mymatrixdashedlineV[4][]{ \pgfmathparse{int(#3+1)} \path (#2-#4-#3.east) -- (#2-#4-\pgfmathresult.west) coordinate [midway] (X); \draw [#1] (X |- #2.north) -- (X |- #2.south);} – Milo Jul 24 '18 at 20:30
• and \newcommand*\mymatrixdashedlineH[4][]{ \pgfmathparse{int(#3+1)} \path (#2-#3-#4.south) -- (#2-\pgfmathresult-#4.north) coordinate [midway] (Y); \draw [#1] (Y -| #2.west) -- (Y -| #2.east);} – Milo Jul 24 '18 at 20:30
• So now you write, for example, \mymatrixdashedlineV[dashed]{m}{1}{3} where {1} indicates it comes after the 1st column, and {3} indicates it is midway between cells (3,1) and (3,2). – Milo Jul 24 '18 at 20:32