我有一个矩阵,其中有些单元格有节点,而有些没有,行和列的大小取决于其中最大的节点。在这个矩阵中,我希望矩阵中行和列的背景颜色交替。如何实现?
这是一个需要着色的矩阵的示例:
\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}
答案1
我误解了这个问题第一个答案。无论如何我都会把它留在那里,但是现在我(认为我)理解了这个问题,这里有一个不同的答案。
我先贴图吧:
代码如下:
\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}
(它可能比需要的要长。)问题是,如果不深入研究 PGF 的内部结构,很难(如果不是不可能的话)知道矩阵的大小,直到它全部布局好,这使得在每个节点到位时很难放入背景。所以我们不这样做。我们等到矩阵计算完毕后再放入背景然后。为了确保它们是真实的背景,我们使用 PGF 的分层功能。这确保背景被放置在后面矩阵。
下一步是让它们的尺寸合适。我们不能简单地在每个节点周围放一个矩形,原因有二:不是每个正方形有节点,节点不一定会填补它们的位置。尽管如此,节点的位置仍然为我们提供足够的信息来确定将矩形放在哪里,以便它们确实对齐。我们找到这些信息的方式如下。我们首先在特定行或列的所有节点周围放置一个矩形(这使用库fit
;这也必须手动完成,因为矩阵中并非所有条目都有节点,因此并非所有标签都被使用 - 如果有一种简单的方法来测试标签以查看它是否与节点相关联,那么这可以自动化)。查看一行,然后矩形的下边缘告诉我们该行中所有节点的最小坐标。类似地,包含下面一行的矩形的上边缘告诉我们该行中所有节点的最大坐标那行。我们分割差异并在该点处植入一个坐标节点。这告诉我们划分这两行的水平线应该在哪里。我们对每一行和每一列重复此操作。
然后,对于每个单元格,我们查看每边的分界线,并使用这些分界线填充一个矩形(具有适当的颜色)。假设我们在单元格 (2,3) 中。然后,我们查看第 1 行和第 2 行之间的坐标以及第 2 列和第 3 列之间的坐标。然后,我们将通过第一行的水平线与通过第二行的垂直线相交。这给了我们该单元格左上角的坐标。以类似的方式,我们得到该单元格右下角的坐标。这足以绘制矩形。(执行这些计算需要库calc
。)
更新:Jake 想要一种更自动化的方法,所以它就出现了。该命令\labelcells{matrix label}{num of rows}{num of cols}
在每个矩阵单元上放置一个矩形节点,并带有标签matrix label-cell-i-j
和大小,使得矩形完全平铺矩阵。然后可以使用它们来绘制背景,或者其他任何东西。
\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-\[email protected])!.5!(#1-col-\[email protected])$);
}
\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-\[email protected])!.5!(#1-row-\[email protected])$);
}
\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}
答案2
您可以使用every odd column
和every even column
它们的类似物来表示行(如第 17.3.3 节所述单元格样式和选项pgf 手册)
举个小例子:
\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}
答案3
这是另一种解决方案,通过显式比较节点边界的 x 和 y 值并找到每行和每列的最小值/最大值。代码基本上只是一堆嵌套\foreach
循环(通过添加ck@
's 以避免名称冲突而变得不可读)。这种方法的缺点是它没有考虑添加的空格(例如 via \\[4mm]
)。不过添加它并不太难(通过与下一列/行的xmin
/进行比较)。ymax
\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}
答案4
我认为这是可能的,假设我正确地解释了你想要什么。 TikZ 允许你在节点开始时执行一些任意代码,并且当前列和行号隐藏在两个计数器\pgfmatrixcurrentcolumn
和中\pgfmatrixcurrentrow
。 因此,使用这些和一些pgfmath
魔法,我想出了以下内容:
\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}
(警告:我正在使用PGF2.10
。我不知道上面使用的任何内容是否是该版本中的新内容。)我不确定为什么需要将其指定fill
为preaction
,我只知道当我将其取出时,矩阵中指定颜色的节点就会被消灭。
结果如下:
通过使用更复杂的数学表达式,可以实现更多的变化。或者颜色规范可以更复杂,因为xcolor
允许颜色混合。