Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have a matrix where some cells have nodes while others don't, with rows and columns of varying size depending on the biggest nodes in them. In this matrix, I would like to have the background of rows and columns in the matrix with alternate colors. How can this be done?

Here's an example of a matrix to be coloured:

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

\begin{tikzpicture}
\matrix [matrix of nodes,
         row sep=2mm,
         column sep=1mm,
         nodes={draw, thick, circle, inner sep=1pt}] (ma)
 {
   & 1 & &[2mm]|[gray]|1\\
   & & 2 &|[gray]|2\\
   |[gray]|2 & & &|[gray]|2\\[4mm]
   3 & & & 3\\
 };
\end{tikzpicture}
share|improve this question
    
I've put your code example in to your original question, (I think I corrected \[4mm] to \\[4mm]; if it was meant to be \[4mm] then do put it back how it was.). –  Loop Space Mar 22 '11 at 21:33
    
It's still not clear to me, seeing Andrew's answer, if you want to put a background color to the "whole" rows and columns or just a background color to the nodes. –  Gonzalo Medina Mar 22 '11 at 22:45
    
@AndrewStacey @GonzaloMedina What I want is a background color to the "whole" rows (or columns), for example, the even "whole" rows are yellow and the odd ones are gray, but the nodes may be blue, orange, etc, as their fill color. –  Mário Mar 22 '11 at 22:57
add comment

4 Answers 4

up vote 10 down vote accepted

I misunderstood the question with my first answer. I'm going to leave that up there anyway, but now that I (think I) understand the question, here's a different answer.

I'll put the picture first:

backgrounded matrix in TikZ

Here's the code:

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

\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\begin{document}
\begin{tikzpicture}
    \matrix [matrix of nodes, row sep=2mm, column sep=1mm,  nodes={draw, thick, circle, inner sep=1pt}] (ma)
     { & 1 & &[2mm]|[gray]|1\\
       & & 2 &|[gray]|2\\
      |[gray]|2 & & &|[gray]|2\\[4mm]
       3 & & & 3\\
      };
\node[inner sep=0pt,fit=(ma-1-2) (ma-1-4)] (ma-row-1) {};
\node[inner sep=0pt,fit=(ma-2-3) (ma-2-4)] (ma-row-2) {};
\node[inner sep=0pt,fit=(ma-3-1) (ma-3-4)] (ma-row-3) {};
\node[inner sep=0pt,fit=(ma-4-1) (ma-4-4)] (ma-row-4) {};
\node[inner sep=0pt,fit=(ma-3-1) (ma-4-1)] (ma-col-1) {};
\node[inner sep=0pt,fit=(ma-1-2)] (ma-col-2) {};
\node[inner sep=0pt,fit=(ma-2-3)] (ma-col-3) {};
\node[inner sep=0pt,fit=(ma-1-4) (ma-2-4) (ma-3-4) (ma-4-4)] (ma-col-4) {};
\coordinate (ma-col-edge-1) at (ma.west);
\coordinate (ma-col-edge-2) at ($(ma-col-1.west)!.5!(ma-col-2.east)$);
\coordinate (ma-col-edge-3) at ($(ma-col-2.west)!.5!(ma-col-3.east)$);
\coordinate (ma-col-edge-4) at ($(ma-col-3.west)!.5!(ma-col-4.east)$);
\coordinate (ma-col-edge-5) at (ma.east);
\coordinate (ma-row-edge-1) at (ma.north);
\coordinate (ma-row-edge-2) at ($(ma-row-1.south)!.5!(ma-row-2.north)$);
\coordinate (ma-row-edge-3) at ($(ma-row-2.south)!.5!(ma-row-3.north)$);
\coordinate (ma-row-edge-4) at ($(ma-row-3.south)!.5!(ma-row-4.north)$);
\coordinate (ma-row-edge-5) at (ma.south);
\begin{pgfonlayer}{back}
\foreach \i in {1,...,4}
\foreach \j in {1,...,4} {
  \pgfmathparse{Mod(\i + \j,2) ? "red" : "blue"}
  \colorlet{sqbg}{\pgfmathresult}
  \pgfmathparse{int(\i+1)}
  \edef\ii{\pgfmathresult}
  \pgfmathparse{int(\j+1)}
  \edef\jj{\pgfmathresult}
  \fill[sqbg] (ma-col-edge-\i |- ma-row-edge-\j) rectangle (ma-col-edge-\ii |- ma-row-edge-\jj);
}
\end{pgfonlayer}
 \end{tikzpicture}
\end{document}

(It is probably longer than it needs to be.) The problem is that without digging deep in to the internals of PGF, it's difficult (if not impossible) to know what size the matrix is going to be until it is all laid out, making it difficult to put in the backgrounds as each node is put in place. So we don't. We wait until the matrix has been computed and put in the backgrounds afterwards. To ensure that they are genuine backgrounds, we use PGF's layering capabilities. This ensures that the backgrounds are put behind the matrix.

The next step is to get them the right size. We can't simply put a rectangle around each node for two reasons: not every square has a node, and the nodes don't necessarily fill their places. Nonetheless, the positions of the nodes still give us enough information to figure out where to put the rectangles so that they do line up. The way that we find this information is as follows. We start by putting a rectangle around all the nodes in a particular row or column (this uses the fit library; this also has to be done manually as not all the entries in the matrix have nodes so not all the labels are used - if there were an easy way to test a label to see if it is associated to a node then this could be automated). Looking at a row, then the lower edge of the rectangle tells us the minimum coordinate of all the nodes in that row. Similarly, the upper edge of the rectangle containing the row below tells us the maximum coordinate of all the nodes in that row. We split the difference and plant a coordinate node at that point. This tells us where the horizontal line dividing these two rows should be. We repeat this for each row and column.

Then for each cell, we look at the dividing lines on each side and use those to fill a rectangle (of the appropriate colour). Say that we are in cell (2,3). Then we look at the coordinate between rows 1 and 2 and the coordinate between columns 2 and 3. We then intersect the horizontal line through the first with the vertical line through the second. This gives us the coordinate of the upper-left corner of that cell. In a similar fashion, we get the coordinate of the lower-right corner of that cell. This is enough to draw the rectangle. (Doing these calculations requires the calc library.)

Update: Jake wanted a more automatic method, so here it is. The command \labelcells{matrix label}{num of rows}{num of cols} puts a rectangular node on each matrix cell with label matrix label-cell-i-j and size such that the rectangles exactly tile the matrix. These can then be used to paint the backgrounds, or whatever.

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

\makeatletter
\newcommand{\labelcells}[3]{%
% #1 = matrix name
% #2 = number of rows
% #3 = number of columns
\foreach \labelcell@i in {1,...,#2} {
     \def\labelcell@rownodes{}
     \foreach \labelcell@j in {1,...,#3} {
      \pgfutil@ifundefined{pgf@sh@ns@#1-\labelcell@i-\labelcell@j}{}{
           \xdef\labelcell@rownodes{\labelcell@rownodes\space(#1-\labelcell@i-\labelcell@j)}
         }
       }
       \node[inner sep=0pt,fit=\labelcell@rownodes] (#1-row-\labelcell@i) {};
     }
     \foreach \labelcell@j in {1,...,#3} {
     \def\labelcell@colnodes{}
     \foreach \labelcell@i in {1,...,#2} {
      \pgfutil@ifundefined{pgf@sh@ns@#1-\labelcell@i-\labelcell@j}{}{
           \xdef\labelcell@colnodes{\labelcell@colnodes\space(#1-\labelcell@i-\labelcell@j)}
         }
       }
       \node[inner sep=0pt,fit=\labelcell@colnodes] (#1-col-\labelcell@j) {};
     }

\coordinate (#1-col-edge-1) at (#1.west);
\foreach \labelcell@i in {2,...,#3} {
  \pgfmathparse{int(\labelcell@i - 1)}
  \edef\labelcell@j{\pgfmathresult}
\coordinate (#1-col-edge-\labelcell@i) at ($(#1-col-\labelcell@j.west)!.5!(#1-col-\labelcell@i.east)$);
}
\pgfmathparse{int(#3+1)}
\edef\labelcell@j{\pgfmathresult}
\coordinate (#1-col-edge-\labelcell@j) at (#1.east);

\coordinate (#1-row-edge-1) at (#1.north);
\foreach \labelcell@i in {2,...,#2} {
  \pgfmathparse{int(\labelcell@i - 1)}
  \edef\labelcell@j{\pgfmathresult}
\coordinate (#1-row-edge-\labelcell@i) at ($(#1-row-\labelcell@j.south)!.5!(#1-row-\labelcell@i.north)$);
}
\pgfmathparse{int(#2+1)}
\edef\labelcell@j{\pgfmathresult}
\coordinate (#1-row-edge-\labelcell@j) at (#1.south);

\foreach \labelcell@i in {1,...,#2}
  \foreach \labelcell@j in {1,...,#3} {
  \pgfmathparse{int(\labelcell@i+1)}
  \edef\labelcell@ii{\pgfmathresult}
  \pgfmathparse{int(\labelcell@j+1)}
  \edef\labelcell@jj{\pgfmathresult}
    \node[inner sep=0pt,fit=(#1-col-edge-\labelcell@i |- #1-row-edge-\labelcell@j) (#1-col-edge-\labelcell@ii |- #1-row-edge-\labelcell@jj)]  (#1-cell-\labelcell@i-\labelcell@j) {};
}
}
\makeatother

\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\begin{document}
\begin{tikzpicture}
\matrix [matrix of nodes, row sep=2mm, column sep=1mm, nodes={draw, thick, circle, inner sep=1pt}] (ma)
     { & 1 & &[2mm]|[gray]|1\\
       & & 2 &|[gray]|2\\
      |[gray]|2 & & &|[gray]|2\\[4mm]
       3 & & & 3\\
      };
\labelcells{ma}{4}{4}
\begin{pgfonlayer}{back}
\foreach \i in {1,...,4}
\foreach \j in {1,...,4} {
  \pgfmathparse{Mod(\i + \j,2) ? "red" : "blue"}
  \colorlet{sqbg}{\pgfmathresult}
       \fill[sqbg] (ma-cell-\i-\j.north west) rectangle (ma-cell-\i-\j.south east);
}
\end{pgfonlayer}
 \end{tikzpicture}
\end{document}
share|improve this answer
    
Excellent! With a bit of a headache, this could probably be automated a bit more. Also, by using nodes in empty cells=true, you can make sure that all cells contain nodes. –  Jake Mar 23 '11 at 11:48
    
@Jake: Yes, it would be nice to automate it a little, but I wanted to see if it was what was wanted first, and to see if anyone else had a better way (such as your good self!). I thought about nodes in empty cells=true but since the nodes in this matrix are drawn that actually puts circles in each empty cell. What I thought might be slicker is to have a test to see if matrix-i-j is a node name and include it if it is. But that would involve digging a little to see how PGF stores its node names. –  Loop Space Mar 23 '11 at 11:54
1  
@Jake: Okay, that wasn't all the hard to figure out. PGF stores the node names in pgf@sh@ns@<nodename> so we just test if that's defined or not. The rest is simple. –  Loop Space Mar 23 '11 at 13:01
    
Brillant! You got it, albeit too complex code. Unfortunately, I don't have the TeX/LaTeX mastery to make your solution an extension to PGF or a .sty file. Thanks all you for your efforts. Cheers! –  Mário Mar 23 '11 at 14:11
2  
Very impressive! –  Jake Mar 23 '11 at 15:26
show 2 more comments

Here is an alternative solution, by explicitly comparing the x and y values of the node borders and finding the min/max for each row and column. The code is basically just a bunch of nested \foreach loops (made unreadable by added ck@'s to avoid name clashes). The drawback of this method is that it doesn't take added whitespace (e.g. via \\[4mm]) into account. It wouldn't be too hard to add that though (by comparing with xmin/ymax of the next column/row).

result

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

\pgfdeclarelayer{back}
\pgfsetlayers{back,main}

\makeatletter
% draw a checkerboard in the background of a matrix
% #1: name of the matrix
% #2: rows in the matrix
% #3: columns in the matrix
% #4: row sep
% #5: column sep
% #6: first color
% #7: second color
\newcommand\checkermatrix[7]{
    \def\ck@rows{#2}
    \def\ck@cols{#3}
    \begin{pgfonlayer}{back}
        \foreach \ck@row in {1,...,\ck@rows} {
            % find minimum and maximum y coordinate for the row
            \pgfextracty\pgf@ya{\pgfpointanchor{#1}{north}}
            \edef\ck@ymin{\the\pgf@ya}
            \pgfextracty\pgf@ya{\pgfpointanchor{#1}{south}}
            \edef\ck@ymax{\the\pgf@ya}
            \foreach \ck@col in {1,...,\ck@cols} {
                \pgfutil@ifundefined{pgf@sh@ns@#1-\ck@row-\ck@col}{}{
                    \pgfextracty\pgf@ya{\pgfpointanchor{#1-\ck@row-\ck@col}{south}}
                    \pgfmathparse{min(\ck@ymin,\the\pgf@ya)}
                    \xdef\ck@ymin{\pgfmathresult}
                    \pgfextracty\pgf@ya{\pgfpointanchor{#1-\ck@row-\ck@col}{north}}
                    \pgfmathparse{max(\ck@ymax,\the\pgf@ya)}
                    \xdef\ck@ymax{\pgfmathresult}
                }
            }
            % adjust for row separation
            \pgfmathsetmacro{\ck@ymin}{\ck@ymin - #4/2}
            \pgfmathsetmacro{\ck@ymax}{\ck@ymax + #4/2}

            % loop through nodes in the row
            \foreach \ck@col in {1,...,\ck@cols} {
                % find x coordinates of the boundary
                \pgfextractx\pgf@xa{\pgfpointanchor{#1}{east}}
                \edef\ck@xmin{\the\pgf@xa}
                \pgfextractx\pgf@xa{\pgfpointanchor{#1}{west}}
                \edef\ck@xmax{\the\pgf@xa}
                \foreach \ck@rrow in {1,...,\ck@rows} {
                    \pgfutil@ifundefined{pgf@sh@ns@#1-\ck@rrow-\ck@col}{}{
                        \pgfextractx\pgf@xa{\pgfpointanchor{#1-\ck@rrow-\ck@col}{west}}
                        \pgfmathparse{min(\ck@xmin,\the\pgf@xa)}
                        \xdef\ck@xmin{\pgfmathresult}
                        \pgfextractx\pgf@xa{\pgfpointanchor{#1-\ck@rrow-\ck@col}{east}}
                        \pgfmathparse{max(\ck@xmax,\the\pgf@xa)}
                        \xdef\ck@xmax{\pgfmathresult}
                    }
                }
                % adjust for col separation
                \pgfmathsetmacro{\ck@xmin}{\ck@xmin - #5/2}
                \pgfmathsetmacro{\ck@xmax}{\ck@xmax + #5/2}

                % define color
                \pgfmathparse{Mod(\ck@row + \ck@col,2) ? "#6" : "#7"}
                \colorlet{sqbg}{\pgfmathresult}

                \fill[sqbg] (\ck@xmin*1pt,\ck@ymin*1pt) rectangle (\ck@xmax*1pt, \ck@ymax*1pt);
            }
        } 
    \end{pgfonlayer}
}
\makeatother

\begin{document}
\begin{tikzpicture}
\matrix [matrix of nodes,
         row sep=2mm,
         column sep=1mm,
         nodes={draw, thick, circle, inner sep=1pt},
         cells={fill=red}] (ma)
 {
   & 1 & &[2mm]|[gray]|1\\
   & & 2 &|[gray]|2\\
   |[gray]|2 & & &|[gray]|222\\[4mm]
   3 & & & 3\\
 };

\checkermatrix{ma}{4}{4}{2mm}{1mm}{red}{blue}

\end{tikzpicture}
\end{document}
share|improve this answer
add comment

You can use every odd column, every even column and their analogous for rows (described in Section 17.3.3 Cell Styles and Options of the pgf manual)

A little example:

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

\begin{document}

\begin{tikzpicture}
\matrix (mymtrx) [matrix of nodes,%
  minimum size=10mm,%
  every odd column/.style={nodes={fill=red!60}},%
  every even column/.style={nodes={fill=blue!30}},%
  execute at empty cell=\node {\vphantom{23}};%
]
{
  8 & 327 & & -35 \\
 65 & & & -3 \\
  & 125 & 64 & 38 \\
};
\end{tikzpicture}

\end{document}

share|improve this answer
    
This unfortunately won't work as easily if the cells have different sizes (which is part of Mário's problem, I believe). –  Jake Mar 22 '11 at 20:56
    
@Jake: you're right I had overlooked that condition. –  Gonzalo Medina Mar 22 '11 at 21:03
    
Well, it seems to work only when row sep=0 and column sep=0 and all nodes are squares, but it does not work in the other cases, like when rows and columns are of varying size and nodes are circles. –  Mário Mar 22 '11 at 21:04
1  
@Mário: can you please post an example of the kind of matrix that you are considering? –  Gonzalo Medina Mar 22 '11 at 21:09
    
\begin{tikzpicture} \matrix [matrix of nodes, row sep=2mm, column sep=1mm, nodes={draw, thick, circle, inner sep=1pt}] (ma) { & 1 & &[2mm]|[gray]|1\ & & 2 &|[gray]|2\|[gray]|2 & & &|[gray]|2\[4mm] 3 & & & 3\}; \end{tikzpicture} –  Mário Mar 22 '11 at 21:25
show 3 more comments

I think that this is possible, assuming that I'm interpreting what you want correctly. TikZ allows you to execute some arbitrary code at the start of a node, and the current column and row numbers are hidden away in two counters \pgfmatrixcurrentcolumn and \pgfmatrixcurrentrow. So using those, and a little pgfmath wizardry, I came up with the following:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}
\colorlet{nodebg}{blue}
\begin{document}
\begin{tikzpicture}
    \matrix [matrix of nodes,
             row sep=2mm,
             column sep=1mm,
             nodes={
                    execute at begin node={
        \pgfmathparse{Mod(\pgfmatrixcurrentrow + \pgfmatrixcurrentcolumn,2) ? "blue" : "red"}
        \xglobal\colorlet{nodebg}{\pgfmathresult}},
         preaction={fill=nodebg},
         draw, thick, circle, inner sep=1pt}] (ma)
     { & 1 & &[2mm]|[gray]|1\\
       & & 2 &|[gray]|2\\
      |[gray]|2 & & &|[gray]|2\\[4mm]
       3 & & & 3\\
      }; \end{tikzpicture}
\end{document}

(Warning: I'm using PGF2.10. I don't know if any of the stuff I'm using in the above is new in that version.) I'm not sure why I needed to specify the fill as a preaction, all I know is that when I took that out, the nodes where the colour was specified in the matrix got obliterated.

Here's the result:

coloured nodes

By using more complicated mathematical expressions, more variation would be possible. Or the colour specification could be more complicated, as xcolor allows blending of colours.

share|improve this answer
    
@GonzaloMedina There is here some missundertanding: what I want is to have the nodes painted in white, and have all even rows and columns, say, in gray. For example, is it possible to make a checkers board with the matrix nodes, where some cells have colored checkers ? Hope I have helped to make this clear now. –  Mário Mar 22 '11 at 23:04
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.