Tikz new command
If you have a lot of these diagrams then rather than just defining a macro that will shade a single cell I recommend defining a macro that will draw the entire grid. Macros make it much easier to read your LaTeX code and to edit or change it later on. This way something like
\Blocks{2}{(1,1)}
will draw a 2x2
grid with a shaded box in position (1,1)
and
\Blocks{4}{(1,1),(2,2),(3,3),(4,4)}
will draw a 4x4
grid with the SW-NE diagonal shaded.
In terms of the mechanics, you do not need to use coordinate calculations because tikz package has this built in:
\draw[fill=black](1,1) rectangle ++(1,1);
will draw a rectangle with lower left corner at position (1,1)
and top right corner at position (2,2)
.
Putting this together, I would write your macro like this:
\documentclass[border=2mm]{standalone}
\usepackage{tikz}
\usepackage{xparse}
\NewDocumentCommand\Blocks{ omm }
{% usage \Blocks[cols]{rows}{list of shaded cells}
\begin{tikzpicture}
\def\rowsForBlocks{#2}
\IfNoValueTF{#1}{\def\colsForBlocks{\rowsForBlocks}}
{\def\colsForBlocks{#1}}
% draw the grid
\foreach \row in {0,...,\colsForBlocks} {
\draw[gray](\row,0)-- ++(0,\rowsForBlocks);
}
\foreach \col in {0,...,\rowsForBlocks} {
\draw[gray](0,\col)-- ++(\colsForBlocks,0);
}
% shade the specified boxes
\foreach \cell in {#3} {
\draw[fill=black!50] \cell rectangle ++ (-1,-1);
}
\end{tikzpicture}
}
\begin{document}
\Blocks{2}{(1,1)}
\Blocks[3]{2}{(1,1),(2,2),(3,1)}
\Blocks{4}{(1,1),(2,2),(3,3),(4,4)}
\end{document}
This produces:
Note that I have given \Blocks
an optional argument (using \NewDocumentCommand
from xparse). By default, \Block
will draw a square grid of the specified size, however, the optional argument allows you to draw rectangular grids. As an example, in the code above
\Blocks[3]{2}{(1,1),(2,2),(3,1)}
draws a grid with 2 rows and 3 columns.
EDIT
As requested, here are some more details as to how this works. The \Blocks
macro takes three arguments:
- the number of columns in the grid (optional: defaults to # rows)
- the number of rows in the grid
- a comma separated list of cells to be shaded
These arguments are specified by the { omm }
in
\NewDocumentCommand\Blocks{ omm }{...}
Here, o
means optional argument, which is enclosed by [...]
when it is given, and m
means mandatory argument, which must always be specified even only as {}
(so \Blocks{3}{}
would draw an "empty" 3x3
grid). See the xparse manual for more details.
If there are r
rows and c
columns then the grid is drawn using Cartesian coordinates from the origin (0,0)
to (r,c)
and the cell labelled (x,y)
has vertices (x-1,y-1)
, (x-1,y)
, (x,y-1)
and (x,y)
. This is the easiest and most natural labeling to use because tikz uses Cartesian coordinates. If, instead, you want to use the half integer lattice the cleanest way to do this is to add scale=0.5
to the options for the tikzpicture
environment (see below).
The following lines from the top of the \Blocks
macro:
\def\rowsForBlocks{#2}
\IfNoValueTF{#1}{\def\colsForBlocks{\rowsForBlocks}}
{\def\colsForBlocks{#1}}
sets the number of rows and columns from the arguments to \Blocks
, which are then used to draw the grid. The \IfNoValueTF{#1}
checks to see whether or not the optional first argument is given -- the TF
stands for True
or False
, indicating \def\colsForBlocks{\rowsForBlocks}
is executed when the optional argument #1
is not given and \def\colsForBlocks{#1}
is executed when it is (again, see the xparse manual for details). After this, the lines
% draw the grid
\foreach \row in {0,...,\colsForBlocks} {
\draw[gray](\row,0)-- ++(0,\rowsForBlocks);
}
\foreach \col in {0,...,\rowsForBlocks} {
\draw[gray](0,\col)-- ++(\colsForBlocks,0);
}
simply draws the corresponding grid by drawing the lines that make up its rows and columns. Finally, the lines
% shade the specified boxes
\foreach \cell in {#3} {
\draw[fill=black!50] \cell rectangle ++ (-1,-1);
}
loop over the third argument, #3
, which is a comma separated list of (x,y)
coordinates for the shaded cells. The ++ (-1,-1)
says that the cell corresponding to (x,y)
is equal to the rectangle with "outside corners" corresponding to the Cartesian coordinates (x,y)
and (x,y)+(-1,-1)=(x-1,y-1)
.
In the OP, the square corresponding to (x,y)
has "outer corners" with Cartesian coordinates (x,-y)
and (x,-y-1)
. To achieve this is a little cumbersome using tikz but one way to do this is by changing the syntax so that coordinates are separated by braces. For example,
\Blocks[3]{2}{{0,0},{1,0},{2,0}}
The advantage of using braces is that it allows us to extract x
and y
from a \cell
by treating {x,y}
as a pgf array, which we can do using:
\pgfmathsetmacro\x{{\cell}[0]}% extract x coordinate
\pgfmathsetmacro\y{{\cell}[1]}% extract y coordinate
(Note that pgf arrays are 0-based.) Once we have \x
and \y
we can draw the OP's cell using:
\draw[fill=black!50] (\x,-\y) rectangle ++ (1,-1);
Strictly speaking we should use
\draw[fill=black!50] (0.5*\x,-0.5*\y) rectangle ++ (0.5,-0.5);
but, as shown below, it is "nicer" to do this by rescaling the environment (if there was text in these diagrams this might not be the best way but, as currently stated, this is not the case).
In the comments the OP said that it would be good if the macro could also change the height of the cells as well. We can do this by adding a second optional argument to \Blocks
. To be able to use the two optional arguments independently we should delimit the second optional argument by something other than [...]
. I normally use <...>
when I want two optional arguments, so the definition of \Blocks
will now look like:
\NewDocumentCommand\Blocks{ D<>{0.5} o m m }{...}
The D<>{0.5}
says that there is a second optional argument delimited by <...>
with a default value of 0.5
-- this will become scale=#1
in the tikzpicture
environment. So, by default, the scale will be 50% which gives the half integer lattice points used by the OP. Putting this together, here is a new version of the code using the OP's coordinates for the cells:
\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}
\NewDocumentCommand\Blocks{ D<>{0.5} o m m }
{% usage \Blocks<scale>[cols]{rows}{list of shaded cells}
\begin{tikzpicture}[scale=#1]
\def\rowsForBlocks{#3}
\IfNoValueTF{#2}{\def\colsForBlocks{\rowsForBlocks}}
{\def\colsForBlocks{#2}}
% draw the grid
\foreach \row in {0,...,\colsForBlocks} {
\draw[gray](\row,0)-- ++(0,-\rowsForBlocks);
}
\foreach \col in {0,...,\rowsForBlocks} {
\draw[gray](0,-\col)-- ++(\colsForBlocks,0);
}
% shade the specified boxes
\foreach \cell in {#4} {
\pgfmathsetmacro\x{{\cell}[0]}% extract x coordinate from cell
\pgfmathsetmacro\y{{\cell}[1]}% extract y coordinate from cell
\draw[fill=black!50] (\x,-\y) rectangle ++ (1,-1);
}
\end{tikzpicture}
}
\begin{document}
\Blocks<1>{2}{{0,0}} % scale = 1 = 1.0 = 100%
\Blocks[3]{2}{{0,0},{1,0},{2,0}} % default scale = 0.5 = 50%
\Blocks<0.3>[3]{2}{{0,0},{1,1},{2,0}} % scale = 0.3 = 30%
\Blocks<0.1>{4}{{0,0},{1,1},{2,2},{3,3}} % scale = 0.1 = 10 %
\end{document}
Here is the new output:
You have to enable coordinate computations: see section 13.5 in the manual of TikZ/PGF.
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\cell}[2]{%
\fill [black!50] ($0.5*(#1,-#2)$) rectangle ($0.5*(#1,-#2)+(0.5,0.5)$)%
}
\begin{document}
\begin{tikzpicture}
\cell{1}{1};
\cell{3}{2};
\cell{0}{3};
\draw[step=0.5cm,gray,very thin] (2.0,-2.0) grid (0.0, 0.0);
\end{tikzpicture}
\end{document}
If you are drawing many of these grids, it may be convenient to specify only two things; grid size (\N
below) and filled cells as (col,row
). The following code uses only these two parameters to draw two grids of size 4
and 10
and fill some chosen cells. Further defining a new command can be more convenient for this.
Note:
TikZ grids are known to be not very accurate, (see my answer to a question on TikZ grids ), so, mixing grids with absolute coordinates can lead to errors. This is why I manually draw the grid using a loop.
\documentclass{article}
\usepackage{tikz}
\begin{document}
\def\N{4} % (1) grid size
\begin{tikzpicture}[very thin]
\foreach \n in {0,...,\N}
\draw (0,.5*\n)--(.5*\N,.5*\n) (.5*\n,0)--(.5*\n,.5*\N);
\foreach \c in {(1,2),(2,4),(4,3)} % (2) fill cells (col,row)
\draw[fill=black!50,scale=.5] \c rectangle ++(-1,-1);
\end{tikzpicture}
\bigskip
\def\N{10}
\begin{tikzpicture}[very thin]
\foreach \n in {0,...,\N}
\draw (0,.5*\n)--(.5*\N,.5*\n) (.5*\n,0)--(.5*\n,.5*\N);
\foreach \c in {(2,5),(4,2),(8,6)}
\draw[fill=black,scale=.5] \c rectangle ++(-1,-1);
\end{tikzpicture}
\end{document}