![寻求帮助简化 TikZ Tonnetz](https://linux22.com/image/417786/%E5%AF%BB%E6%B1%82%E5%B8%AE%E5%8A%A9%E7%AE%80%E5%8C%96%20TikZ%20Tonnetz.png)
我创建了一个音调网络,Ti 中的一种音乐网络钾Z:
%LuaLaTeX
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[xscale=1.5,yscale=2.5]
\footnotesize
\begin{scope}
\newcommand*\columns{7}
\newcommand*\rows{2}
\clip(0,0.5) rectangle (\columns,\rows);
\foreach \x in {0,0.5,1,...,\columns}
\foreach \y in {0,0.5,1,...,\rows}
\foreach \z in {-1.5,-0.5,...,\columns}
{
\draw (0,\y) -- (\columns,\y);
\draw (\z,\rows) -- (\z+2,0);
\draw (\z,0) -- (\z+2,\rows);
}
\end{scope}
\draw[fill=gray!50] (3.5,1) -- (4.5,1) -- (4,1.5) -- cycle;
\foreach \toprow/\topx in {E/0.5,B/1.5,F$\sharp$/2.5,C$\sharp$/3.5,G$\sharp$/4.5,D$\sharp$/5.5,A$\sharp$/6.5}
\foreach \secondrow/\secondx in {C/0,G/1,D/2,A/3,E/4,B/5,F$\sharp$/6,C$\sharp$/7}
\foreach \thirdrow/\thirdx in {E$\flat$/0.5,B$\flat$/1.5,F/2.5,C/3.5,G/4.5,D/5.5,A/6.5}
\foreach \fourthrow/\fourthx in {C$\flat$/0,G$\flat$/1,D$\flat$/2,A$\flat$/3,E$\flat$/4,B$\flat$/5,F/6,C/7}
%\foreach \bottomrow/\bottomx in {E$\flat\flat$/0.5,B$\flat\flat$/1.5,F$\flat$/2.5,C$\flat$/3.5,G$\flat$/4.5,D$\flat$/5.5,A$\flat$/6.5}
{
\node[draw,circle,minimum size=0.75cm,fill=white] at (\topx,2) {\toprow};
\node[draw,circle,minimum size=0.75cm,fill=white] at (\secondx,1.5) {\secondrow};
\node[draw,circle,minimum size=0.75cm,fill=white] at (\thirdx,1) {\strut \thirdrow};
\node[draw,circle,minimum size=0.75cm,fill=white] at (\fourthx,0.5) {\strut \fourthrow};
%\node[draw,circle,minimum size=0.75cm,fill=white] at (\bottomx,0) {\strut \bottomrow};
}
\end{tikzpicture}
\end{document}
如上所述,注释掉这两行后,输出是正确的。然而,非常编译速度很慢。(甚至 Inkscape 和 PDF 阅读器打开文件的速度也很慢!)当我取消注释这两行以创建底行时,什么也没有产生;有时我会得到“超出 TeX 容量”的错误,但其他时候编译时间太长,我只能放弃。
理想情况下,我会在本章(本身是一本更大的书的一部分)中列出四到五个这样的图表,因此这些编译时间是不现实的。有没有办法简化这个图表以帮助它更快地编译?
答案1
正如前面提到的,你的问题是你在做同样的事情许多次,因为你嵌套了循环。以绘制网格的循环为例。最内层循环(在 上\z
)中的绘制指令在该循环的每次迭代中重复。但整个循环再次重复第二个循环(在 上\y
)的每次迭代。并且该循环在最外层循环(在 上\x
)的每次迭代中重复。因此,由于 x 循环中有 10 个步骤,z 循环中有 14 个步骤,因此您将绘制每条水平线 140 次。
节点也会发生同样的事情。每个连续循环都会在其所在循环的每次迭代中重复,因此当您达到 5 级深度时,每级有 7 或 8 次迭代,您最终会绘制每个节点超过 3000 次。
对于网格,你可以这样做:
\begin{scope}
\newcommand*\columns{7}
\newcommand*\rows{2}
\clip(0,-\pgflinewidth) rectangle (\columns,\rows);
\foreach \y in {0,0.5,1,...,\rows}
\draw (0,\y) -- (\columns,\y);
%the previous loop has ended, start a new:
\foreach \z in {-1.5,-0.5,...,\columns}
{
\draw (\z,\rows) -- (\z+2,0);
\draw (\z,0) -- (\z+2,\rows);
}
\end{scope}
水平线画在一个循环中,对角线画在另一个循环中。
对于节点,您可以执行相同的操作,每行一个循环:
\foreach \toprow [count=\topx] in {E,B,F$\sharp$,C$\sharp$,G$\sharp$,D$\sharp$,A$\sharp$}
\node[note] at (\topx-0.5,2) {\toprow};
\foreach \secondrow [count=\secondx from 0] in {C,G,D,A,E,B,F$\sharp$,C$\sharp$}
\node[note] at (\secondx,1.5) {\secondrow};
\foreach \thirdrow[count=\thirdx] in {E$\flat$,B$\flat$,F,C,G,D,A}
\node[note] at (\thirdx-0.5,1) {\strut \thirdrow};
\foreach \fourthrow[count=\fourthx from 0] in {C$\flat$,G$\flat$,D$\flat$,A$\flat$,E$\flat$,B$\flat$,F,C}
\node[note] at (\fourthx,0.5) {\strut \fourthrow};
\foreach \bottomrow[count=\bottomx] in {E$\flat\flat$,B$\flat\flat$,F$\flat$,C$\flat$,G$\flat$,D$\flat$,A$\flat$}
\node[note] at (\bottomx-0.5,0) {\strut \bottomrow};
即,您不是拥有 ,而是foreach x (foreach y (foreach z <do stuff x y and z> ) )
拥有foreach x <do stuff x>
,然后foreach y <do stuff y>
等等。
上面我利用了count
的功能\foreach
,这样你就不必费心摆弄语法了a/b
。你还可以迭代列表列表:
\foreach \notelist [count=\row from 0] in {
{E$\flat\flat$,B$\flat\flat$,F$\flat$,C$\flat$,G$\flat$,D$\flat$,A$\flat$},
{C$\flat$,G$\flat$,D$\flat$,A$\flat$,E$\flat$,B$\flat$,F,C},
{E$\flat$,B$\flat$,F,C,G,D,A},
{C,G,D,A,E,B,F$\sharp$,C$\sharp$},
{E,B,F$\sharp$,C$\sharp$,G$\sharp$,D$\sharp$,A$\sharp$}}
\foreach \note [count=\column from 0,evaluate={\colX=\column+0.5-mod(\row,2)/2;}] in \notelist
\node [note] at (\colX,\row*0.5) {\strut \note};
需要进行一些简单的计算才能获得正确的坐标。
完整代码,包含节点的两个变体:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[
xscale=1.5,yscale=2.5981,
note/.style={draw,circle,minimum size=0.75cm,fill=white},
every node/.append style={font=\footnotesize}
]
%\footnotesize
\begin{scope}
\newcommand*\columns{7}
\newcommand*\rows{2}
\clip(0,-\pgflinewidth) rectangle (\columns,\rows);
\foreach \y in {0,0.5,1,...,\rows}
\draw (0,\y) -- (\columns,\y);
\foreach \z in {-1.5,-0.5,...,\columns}
{
\draw (\z,\rows) -- (\z+2,0);
\draw (\z,0) -- (\z+2,\rows);
}
\end{scope}
\draw[fill=gray!50] (3.5,1) -- (4.5,1) -- (4,1.5) -- cycle;
%\foreach \toprow [count=\topx] in {E,B,F$\sharp$,C$\sharp$,G$\sharp$,D$\sharp$,A$\sharp$}
% \node[note] at (\topx-0.5,2) {\toprow};
%
%\foreach \secondrow [count=\secondx from 0] in {C,G,D,A,E,B,F$\sharp$,C$\sharp$}
% \node[note] at (\secondx,1.5) {\secondrow};
%
%\foreach \thirdrow[count=\thirdx] in {E$\flat$,B$\flat$,F,C,G,D,A}
% \node[note] at (\thirdx-0.5,1) {\strut \thirdrow};
%
%\foreach \fourthrow[count=\fourthx from 0] in {C$\flat$,G$\flat$,D$\flat$,A$\flat$,E$\flat$,B$\flat$,F,C}
% \node[note] at (\fourthx,0.5) {\strut \fourthrow};
%
%\foreach \bottomrow[count=\bottomx] in {E$\flat\flat$,B$\flat\flat$,F$\flat$,C$\flat$,G$\flat$,D$\flat$,A$\flat$}
% \node[note] at (\bottomx-0.5,0) {\strut \bottomrow};
\foreach [count=\row from 0] \notelist in {
{E$\flat\flat$,B$\flat\flat$,F$\flat$,C$\flat$,G$\flat$,D$\flat$,A$\flat$},
{C$\flat$,G$\flat$,D$\flat$,A$\flat$,E$\flat$,B$\flat$,F,C},
{E$\flat$,B$\flat$,F,C,G,D,A},
{C,G,D,A,E,B,F$\sharp$,C$\sharp$},
{E,B,F$\sharp$,C$\sharp$,G$\sharp$,D$\sharp$,A$\sharp$}}
\foreach \note [count=\column from 0,evaluate={\colX=\column+0.5-mod(\row,2)/2;}] in \notelist
\node [note] at (\colX,\row*0.5) {\strut \note};
\end{tikzpicture}
\end{document}
答案2
我不会不必要地嵌套循环,而是在单个循环中测试要显示的节点数(每行 7;8;7 和 8)。
%LuaLaTeX
\documentclass[tikz,border=5mm]{standalone}
%\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[xscale=1.5,yscale=2.5981]
\footnotesize
\begin{scope}
\newcommand*\columns{7}
\newcommand*\rows{2}
\clip(0,0.5) rectangle (\columns,\rows);
%\foreach \x in {0,0.5,1,...,\columns}
\foreach \y in {0,0.5,1,...,\rows}
\foreach \z in {-1.5,-0.5,...,\columns}
{
\draw (0,\y) -- (\columns,\y);
\draw (\z,\rows) -- (\z+2,0);
\draw (\z,0) -- (\z+2,\rows);
}
\end{scope}
\draw[fill=gray!50] (3.5,1) -- (4.5,1) -- (4,1.5) -- cycle;
\foreach \row/\x [count=\n]in {E/0.5,B/1.5,F$\sharp$/2.5,C$\sharp$/3.5,G$\sharp$/4.5,D$\sharp$/5.5,A$\sharp$/6.5,C/0,G/1,D/2,A/3,E/4,B/5,F$\sharp$/6,C$\sharp$/7,E$\flat$/0.5,B$\flat$/1.5,F/2.5,C/3.5,G/4.5,D/5.5,A/6.5,C$\flat$/0,G$\flat$/1,D$\flat$/2,A$\flat$/3,E$\flat$/4,B$\flat$/5,F/6,C/7}
%\foreach \bottomrow/\bottomx in {E$\flat\flat$/0.5,B$\flat\flat$/1.5,F$\flat$/2.5,C$\flat$/3.5,G$\flat$/4.5,D$\flat$/5.5,A$\flat$/6.5}
{
\ifnum \n < 8
\node[draw,circle,minimum size=0.75cm,fill=white] at (\x,2) {\row};
\else
\ifnum \n <16
\node[draw,circle,minimum size=0.75cm,fill=white] at (\x,1.5) {\row};
\else
\ifnum \n <23
\node[draw,circle,minimum size=0.75cm,fill=white] at (\x,1) {\strut \row};
\else
\node[draw,circle,minimum size=0.75cm,fill=white] at (\x,0.5) {\strut \row};
\fi
\fi
\fi
%\node[draw,circle,minimum size=0.75cm,fill=white] at (\bottomx,0) {\strut \bottomrow};
}
\end{tikzpicture}