我对 LaTeX 还很陌生,在我正在写的论文中,我需要绘制许多带有填充矩形的网格(网格中的一些单元格应保留为白色,其他单元格则为黑色)。由于我不喜欢一遍又一遍地编写相同的代码,所以我考虑定义一个新命令tikz
(这可能吗?)。
\newcommand{\cell}[2]{
\fill[black!](#1*0.5, (-#2)*0.5)rectangle(#1*0.5 + 0.5, (-#2)*0.5+0.5)
}
这应该在 (0.5*x, -0.5*y, 0.5*(x+1), -0.5(y+1)) 处绘制一个深色矩形,其用途如下:
\begin{tikzpicture}
\cell{1}{1};
\draw[step=0.5cm,gray,very thin] (2.0,-2.0) grid (0.0, 0.0);
\end{tikzpicture}
但是它似乎不起作用,因为我收到以下错误消息:
! Package tikz Error: Giving up on this path. Did you forget a semicolon?
See the tikz package documentation for explanation.
Type H <return> for immediate help.
它出现在我使用命令的那一行\cell
。我做错了什么?
答案1
如果您有很多这样的图表,那么我建议您定义一个绘制整个网格的宏,而不是仅仅定义一个会为单个单元格着色的宏。宏使阅读 LaTeX 代码以及稍后编辑或更改代码变得更加容易。这样,
\Blocks{2}{(1,1)}
将在适当位置绘制一个2x2
带有阴影框的网格(1,1)
,并
\Blocks{4}{(1,1),(2,2),(3,3),(4,4)}
将绘制一个4x4
带有 SW-NE 对角线阴影的网格。
从力学角度来说,你不需要使用坐标计算,因为蒂克兹软件包中有这个内置内容:
\draw[fill=black](1,1) rectangle ++(1,1);
将绘制一个矩形,其左下角位于位置(1,1)
,右上角位于位置(2,2)
。
综合起来,我会像这样编写宏:
\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}
得出的结果为:
请注意,我给出了\Blocks
一个可选参数(使用\NewDocumentCommand
来自解析)。默认情况下,\Block
将绘制一个正方形指定大小的网格,但是,可选参数允许您绘制矩形的网格。例如,在上面的代码中
\Blocks[3]{2}{(1,1),(2,2),(3,1)}
绘制一个有 2 行 3 列的网格。
编辑
根据要求,以下是有关其工作原理的更多详细信息。该\Blocks
宏采用三个参数:
- 网格中的列数(可选:默认为#行)
- 网格中的行数
- 要着色的单元格的逗号分隔列表
{ omm }
这些参数由
\NewDocumentCommand\Blocks{ omm }{...}
这里,表示可选参数,当给出时,o
用括起来,表示强制参数,即使只有(因此会绘制一个“空”网格) ,也必须始终指定。请参阅[...]
m
{}
\Blocks{3}{}
3x3
解析手册了解更多详细信息。
如果有r
行和c
列,则网格使用笛卡尔坐标从原点(0,0)
到绘制(r,c)
,标记为的单元格(x,y)
具有顶点(x-1,y-1)
、(x-1,y)
和。这是最简单、最自然的标记,因为 tikz 使用笛卡尔坐标。如果您想使用半整数格,最干净的方法是添加到环境选项中(x,y-1)
(见下文)。(x,y)
scale=0.5
tikzpicture
宏顶部的内容如下\Blocks
:
\def\rowsForBlocks{#2}
\IfNoValueTF{#1}{\def\colsForBlocks{\rowsForBlocks}}
{\def\colsForBlocks{#1}}
将参数中的行数和列数设置为\Blocks
,然后使用这些参数绘制网格。\IfNoValueTF{#1}
检查是否给出了可选的第一个参数—— 代表TF
或True
,False
表示在未给出\def\colsForBlocks{\rowsForBlocks}
可选参数时执行,在给出可选参数时执行(再次参见#1
\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);
}
循环遍历第三个参数,它是阴影单元格的#3
逗号分隔坐标列表。表示对应于的单元格等于具有对应于笛卡尔坐标和的“外角”的矩形。(x,y)
++ (-1,-1)
(x,y)
(x,y)
(x,y)+(-1,-1)=(x-1,y-1)
在 OP 中,对应于的正方形(x,y)
具有“外角”,其笛卡尔坐标为(x,-y)
和(x,-y-1)
。要实现这一点有点麻烦,使用蒂克兹但有一种方法可以做到这一点,那就是改变语法,让坐标用括号分隔。例如,
\Blocks[3]{2}{{0,0},{1,0},{2,0}}
使用括号的优点是,它允许我们通过将其视为pgf 数组来从中提取x
和,我们可以使用以下命令进行操作:y
\cell
{x,y}
\pgfmathsetmacro\x{{\cell}[0]}% extract x coordinate
\pgfmathsetmacro\y{{\cell}[1]}% extract y coordinate
(请注意,pgf 数组是基于 0 的。)一旦我们有了\x
,\y
我们就可以使用以下方法绘制 OP 的单元格:
\draw[fill=black!50] (\x,-\y) rectangle ++ (1,-1);
严格来说,我们应该使用
\draw[fill=black!50] (0.5*\x,-0.5*\y) rectangle ++ (0.5,-0.5);
但是,如下所示,通过重新缩放环境来做到这一点“更好”(如果这些图表中有文字,这可能不是最好的方法,但正如目前所述,情况并非如此)。
在评论中,OP 说如果宏也可以改变单元格的高度就好了。我们可以通过向 中添加第二个可选参数来实现这一点\Blocks
。为了能够独立使用这两个可选参数,我们应该用 以外的其他东西来分隔第二个可选参数[...]
。我通常<...>
在需要两个可选参数时使用 ,因此 的定义\Blocks
现在看起来像:
\NewDocumentCommand\Blocks{ D<>{0.5} o m m }{...}
表示D<>{0.5}
有第二个可选参数,<...>
以默认值的0.5
——这将成为环境scale=#1
中的一部分tikzpicture
。因此,默认情况下,比例将为 50%,这将提供 OP 使用的半整数格点。综合起来,这是使用 OP 的单元格坐标的新版本代码:
\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}
这是新的输出:
答案2
您必须启用坐标计算:参见 TikZ/PGF 手册第 13.5 节。
\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}
答案3
如果您要绘制许多这样的网格,则仅指定两个参数可能会很方便:网格大小(\N
见下文)和填充单元格(col,row
)。以下代码仅使用这两个参数来绘制两个大小为和的网格4
并10
填充一些选定的单元格。为此,进一步定义新命令可能会更方便。
笔记:
众所周知,TikZ 网格不太准确,(请参阅我对 TikZ 网格问题的回答),因此,将网格与绝对坐标混合使用可能会导致错误。这就是我使用循环手动绘制网格的原因。
\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}