我正在尝试递归绘制 C k x C k,其中 C k是康托集的第 k 次迭代。我有一个命令,它递归调用自身四次。如果我N
作为参数传递#6
,输出应该是 4 个小N
方块。但它只适用于N
= 1, 2。如果我做任何更高的操作,我只会得到 4 个方块,分布在四个角上。它们的大小合适,但数量不够。
显然,问题与有关\pgfmathtruncatemacro\C{#6 - 1}
。作为测试,我\draw (0,0) circle (#6);
在此行之前插入,然后在此行之后插入,输出并不总是相同的。似乎以\pgfmathtruncatemacro\C{#6 - 1}
某种方式改变了的值#6
(在相同的递归级别),但我不明白为什么。
\usepackage{tikz, pgf, ifthen}
\newcommand\cantorsquare[6]{ %x y x y coordinates, dissection coordinates, nesting
%the coordinates are listed in the same order as coordinates for a rectangle
\ifthenelse{#6 = 0} %base case
{
\fill (#1, #2) rectangle (#3, #4);
}
{
{
%find the coordinates for the first smaller square
\pgfmathsetmacro\A{#1 + (1 - #5) * (#3 - #1) / 2}
\pgfmathsetmacro\B{#2 + (1 - #5) * (#4 - #2) / 2}
\pgfmathtruncatemacro\C{#6 - 1}
\cantorsquare{#1}{#2}{\A}{\B}{#5}{\C}
}
{
\pgfmathsetmacro\A{#1 + (1 - #5) * (#3 - #1) / 2}
\pgfmathsetmacro\B{#4 - (1 - #5) * (#4 - #2) / 2}
\pgfmathtruncatemacro\C{#6 - 1}
\cantorsquare{#1}{\B}{\A}{#4}{#5}{\C}
}
{
\pgfmathsetmacro\A{#3 - (1 - #5) * (#3 - #1) / 2}
\pgfmathsetmacro\B{#2 + (1 - #5) * (#4 - #2) / 2}
\pgfmathtruncatemacro\C{#6 - 1}
\cantorsquare{\A}{#2}{#3}{\B}{#5}{\C}
}
{
\pgfmathsetmacro\A{#3 - (1 - #5) * (#3 - #1) / 2}
\pgfmathsetmacro\B{#4 - (1 - #5) * (#4 - #2) / 2}
\pgfmathtruncatemacro\C{#6 - 1}
\cantorsquare{\A}{\B}{#3}{#4}{#5}{\C}
}
}
}
\begin{document}
\begin{tikzpicture} \cantorsquare{0}{0}{5}{5}{0.3}{3} \end{tikzpicture}
\end{document}
答案1
我同意迈克尔的分析:这是一个扩展问题。当你传递\C
第六个元素时,它#6
会在宏主体中被替换,\C
因此如果\C
更改,那么也会更改最终的的值#6
。具体来说,当您\pgfmathtruncatemacro\C{#6 - 1}
这样做时,您正在重新定义#6
。
话虽如此,我不确定扩展哪里出了问题,因为您已正确分组了递归调用,因此\C
一次迭代中的修改不会影响其他迭代。此外,当我运行您的代码时,我得到了我期望看到的答案。所以我怀疑这是旧版本的 PGF 的问题,它进行了全局分配,而它应该进行本地分配。您使用的是哪个版本的 PGF?
我发现 LaTeX3 有很多有用的东西可以帮助解决这些扩展问题。特别是,可以确保价值传递的是宏的参数,而不是宏本身。所以这是用 LaTeX3 重写的代码。通过传递值而不是宏,我可以避免对递归调用进行分组。
\documentclass{article}
%\url{http://tex.stackexchange.com/q/136907/86}
\usepackage{tikz}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_nopar:Npn \cantor_sq:nnnnnn #1#2#3#4#5#6
{
\int_compare:nTF {#6 = 0}
{
\fill (\fp_to_decimal:n {#1},\fp_to_decimal:n {#2}) rectangle (\fp_to_decimal:n {#3},\fp_to_decimal:n {#4});
}
{
%find the coordinates for the first smaller square
\fp_set:Nn \l_tmpa_fp {#1 + (1 - #5) * (#3 - #1) / 2}
\fp_set:Nn \l_tmpb_fp {#2 + (1 - #5) * (#4 - #2) / 2}
\int_set:Nn \l_tmpa_int {#6 - 1}
\cantor_sq:nnVVnV {#1}{#2} \l_tmpa_fp \l_tmpb_fp {#5} \l_tmpa_int
\fp_set:Nn \l_tmpa_fp {#1 + (1 - #5) * (#3 - #1) / 2}
\fp_set:Nn \l_tmpb_fp {#4 - (1 - #5) * (#4 - #2) / 2}
\int_set:Nn \l_tmpa_int {#6 - 1}
\cantor_sq:nVVnnV {#1} \l_tmpb_fp \l_tmpa_fp {#4}{#5} \l_tmpa_int
\fp_set:Nn \l_tmpa_fp {#3 - (1 - #5) * (#3 - #1) / 2}
\fp_set:Nn \l_tmpb_fp {#2 + (1 - #5) * (#4 - #2) / 2}
\int_set:Nn \l_tmpa_int {#6 - 1}
\cantor_sq:VnnVnV \l_tmpa_fp {#2}{#3} \l_tmpb_fp {#5} \l_tmpa_int
\fp_set:Nn \l_tmpa_fp {#3 - (1 - #5) * (#3 - #1) / 2}
\fp_set:Nn \l_tmpb_fp {#4 - (1 - #5) * (#4 - #2) / 2}
\int_set:Nn \l_tmpa_int {#6 - 1}
\cantor_sq:VVnnnV \l_tmpa_fp \l_tmpb_fp {#3}{#4}{#5} \l_tmpa_int
}
}
\cs_generate_variant:Nn \cantor_sq:nnnnnn {nnVVnV,nVVnnV,VnnVnV,VVnnnV}
\DeclareDocumentCommand \cantorsquare { m m m m m m }
{
\cantor_sq:nnnnnn {#1}{#2}{#3}{#4}{#5}{#6}
}
\ExplSyntaxOff
\begin{document}
\begin{tikzpicture}
\cantorsquare{0}{0}{5}{5}{0.3}{3}
\end{tikzpicture}
\begin{tikzpicture}
\cantorsquare{0}{0}{5}{5}{0.3}{6}
\end{tikzpicture}
\end{document}
从更大的放大图可以看出,小矩形看起来不均匀是渲染的瑕疵。
答案2
我不知道您使用的软件包的详细信息,因此我很难下结论,但您似乎被这样一个事实所困扰:作为参数传递的宏在执行调用时不会被展开,而只有在处理它们出现的替换文本时才会展开。这可能会破坏您的递归。要验证这一点,您可以使用 执行代码\tracingmacros=1
。
您可以使用寄存器来强制扩展,从而取代您的使用
\cantorsquare{#1}{#2}{\A}{\B}{#5}{\C}
经过
\begingroup
\let\cantorsquare=0\relax % Freeze expansion in edef
\count0=\A\relax
\count2=\B\relax
\count4=\C\relax
\toks0={#1}%
\toks2={#2}%
\toks4={#5}%
\edef\next{%
\cantorsquare{\the\toks0}{\the\toks2}{\the\count0}{\the\count2}{\the\toks4}{\the\count4}%
}%
\expandafter
\endgroup
\next
它准备一个“调用”,cantorsquare
其中\A
等被替换为它们的替换文本 — 假设这些是数字。您应该相应地编辑四个调用中的每一个。
请注意,TeX 是一种非常特殊的计算机语言,因为它不与函数之类的东西一起工作,而是为您提供编写程序后续内容的可能性——这就是宏的用途。如果您想用 TeX 编写复杂的程序,那么了解寄存器\edef
等会容易得多。
这有帮助吗?
编辑:顺便说一句,如果你对用程序生成图形感兴趣,我强烈建议你看看 METAPOST!这是一个惊人的软件——编程起来和 TeX 一样有趣。
答案3
编辑:下面添加了lualatex
版本。
正如已经指出的那样,问题在于扩展,并且很容易纠正。然而,这里有一个不同的解决方案,需要最新的 PGF/TikZ CVS 版本。
\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{math}
\begin{document}
\begin{tikzpicture}
\tikzmath{
function cantorsquare(\x, \y, \s, \f, \d) {
if (\d == 0) then {
{ \fill (\x, \y) rectangle ++(\s, \s); };
} else {
\u1 = \f*\s;
\u2 = (1-\f)*\s;
cantorsquare(\x, \y, \u1, \f, \d-1);
cantorsquare(\x+\u2, \y, \u1, \f, \d-1);
cantorsquare(\x, \y+\u2, \u1, \f, \d-1);
cantorsquare(\x+\u2, \y+\u2, \u1, \f, \d-1);
};
};
\S = 5;
for \d in {0,...,5}{
\x = int(\d/3) * (\S+1);
\y = mod(\d,3) * (\S+1);
{ \draw (\x-0.1, -\y-0.1) rectangle ++(\S+0.2,\S+0.2); };
cantorsquare(\x, -\y, \S, 1/3, \d);
};
}
\end{tikzpicture}
\end{document}
下面是使用lualatex
它不需要最新的 CVS,并且很多快点。
\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\directlua{
function cantorsquare(x, y, s, f, d)
local u1, u2
if d == 0 then
tex.print("\noexpand\\fill (" .. x .. "," .. y .. ") rectangle ++(" .. s .. "," .. s .. ");")
else
u1 = f*s
u2 = (1-f)*s;
cantorsquare(x, y, u1, f, d-1)
cantorsquare(x+u2, y, u1, f, d-1)
cantorsquare(x, y+u2, u1, f, d-1)
cantorsquare(x+u2, y+u2, u1, f, d-1)
end
end
}
\begin{document}
\begin{tikzpicture}
\foreach \d [evaluate={\S=5; \x=int(\d/3)*(\S+1); \y=mod(\d,3)*(\S+1);}] in {0,...,5}{
\draw (\x-0.1, -\y-0.1) rectangle ++(\S+0.2,\S+0.2);
\directlua{cantorsquare(\x, -\y, \S, 1/3, \d)}
}
\end{tikzpicture}
\end{document}
结果和以前一样。
答案4
我不知道这个的具体规格事物但这里有一个递归尝试append after command
(以前的版本使用过path picture
但只适用于矩形)。
代码
\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\tikzset{
if/.code n args={3}{% forest.sty
\pgfmathparse{#1}%
\ifnum\pgfmathresult=0 \pgfkeysalso{#3}\else\pgfkeysalso{#2}\fi},
cantorsquare/.style n args={3}{% #1 = current leve, #2 = max level, #3 = factor
if={#1==#2}{cantorsquare end}{cantorsquare go on={#1+1}{#2}{#3}}},
every cantorsquare/.style={shape=rectangle, outer sep=+0pt, ultra thin},
cantorsquare end/.style={every cantorsquare, fill},
cantorsquare go on/.style n args={3}{
every cantorsquare,
append after command={
\pgfextra{\let\myTikzlastnode\tikzlastnode}
let \p{@dim}=($(\myTikzlastnode.north east)-(\myTikzlastnode.south west)$) in
[next nodes/.style={inner sep=+0pt, outer sep=+0pt, minimum width=(#3)*\x{@dim},
minimum height=(#3)*\y{@dim}, cantorsquare={#1}{#2}{#3}}]
node [next nodes, above right] at (\myTikzlastnode.south west) {}
node [next nodes, above left] at (\myTikzlastnode.south east) {}
node [next nodes, below right] at (\myTikzlastnode.north west) {}
node [next nodes, below left] at (\myTikzlastnode.north east) {}}}}
\begin{document}
\foreach \lev in {1, ..., 5, 4, 3, 2}{
\begin{tikzpicture}
\node[cantorsquare/.expanded={1}{\lev}{.3333}, minimum size=2cm] {};
\end{tikzpicture}}
\foreach \lev in {1, ..., 5, 4, 3, 2}{
\begin{tikzpicture}[every cantorsquare/.append style={shape=circle},
every cantorsquare/.append style=draw]
\node[cantorsquare/.expanded={1}{\lev}{.3333}, minimum size=2cm] {};
\end{tikzpicture}}
\end{document}
输出