我正在运行 Ubuntu 11.04 版本PGF
软件包版本为 2.00-1。我无法运行发布到
如何高效地生成所有可能的维恩图(如下图所示)?
\documentclass{standalone}
%\url{https://tex.stackexchange.com/q/67395/86}
\usepackage{tikz}
\makeatletter
\def\venn@strip#1#2\venn@STOP{%
\def\venn@next{#1}%
\gdef\venn@rest{#2}%
}
\newcommand{\venn}[1]{%
\begin{tikzpicture}
\coordinate (A) at (0,0);
\coordinate (B) at (2,0);
\coordinate (C) at (1,{sqrt(3)});
\coordinate (S-SE) at (5,-3);
\coordinate (S-NW) at (-3,{sqrt(3)+3});
\edef\venn@rest{#100000000}%
\foreach \i in {0,...,7} {
\begin{scope}[even odd rule]
\expandafter\venn@strip\venn@rest\venn@STOP
\ifnum\venn@next=1\relax
\pgfmathparse{Mod(\i,2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
\path[clip] \pgfmathresult (A) circle[radius=2];
\pgfmathparse{Mod(floor(\i/2),2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
\path[clip] \pgfmathresult (B) circle[radius=2];
\pgfmathparse{Mod(floor(\i/4),2) == 1 ? "(S-SE) rectangle (S-NW)" : ""}
\path[clip] \pgfmathresult (C) circle[radius=2];
\fill[rounded corners,red] (S-SE) rectangle (S-NW);
\fi
\end{scope}
}
\draw[ultra thick] (A) circle[radius=2];
\draw[ultra thick] (B) circle[radius=2];
\draw[ultra thick] (C) circle[radius=2];
\draw[ultra thick,rounded corners] (S-SE) rectangle (S-NW);
\end{tikzpicture}
}
\makeatother
\newcommand{\allvendiagrams}{
% To generate the lot:
\foreach \j in {0,...,255} {
\def\venncode{}
\foreach \k in {0,...,7} {
\pgfmathparse{Mod(floor(\j/2^\k),2) == 1 ? "\venncode1" : "\venncode0"}
\global\let\venncode=\pgfmathresult
}
\venn{\venncode}
}
}
\begin{document}
\venn{10000000}
\venn{01000000}
\venn{11000000}
\end{document}
在我将 documentclass 更改为后article
,出现此错误
! Package PGF Math Error: Unknown function `Mod'
我以为可能是我的版本PGF
太旧了,所以缺少这个Mod
功能。我sudo apt-get update
尝试获取所有缺少的更新,但错误并没有消失。我需要将 Ubuntu 升级到更高版本才能解决此消息吗?
Mod
在上面的代码中用替换所有实例之后mod
,我收到此错误
! Illegal unit of measure (pt inserted).
<to be read again>
?
l.57 \venn{10000000}
我需要帮助来解决此错误并成功运行此示例。
\venn 命令接受 8 位二进制字符串,遍历每个数字,如果某个数字为 1,则填充特定区域。例如,调用 \venn{00000010} 将填充圆 A 中不与圆 B 或圆 C 重叠的所有内容。
二进制字符串迭代相关代码:
\def\venn@strip#1#2\venn@STOP{%
\def\venn@next{#1}%
\gdef\venn@rest{#2}%
}
\edef\venn@rest{#100000000}%
\foreach \i in {0,...,7} {
\begin{scope}[even odd rule]
\expandafter\venn@strip\venn@rest\venn@STOP
\ifnum\venn@next=1\relax
..............
我浏览了有关 \def 语句的基本信息http://en.wikibooks.org/wiki/TeX/def并对第二个参数结束和宏内容开始之后的 \venn@strip 中的 \venn@STOP 感到困惑。我见过的所有示例都具有宏名称,后跟任何可选参数,后跟宏的内容。
在 \venn{00000010} 调用中,在“for”循环开始之前,\venn@rest 将被赋值为 0000001000000000。在第一次迭代中,\venn@next 将被赋值为 0000001000000000(与传递给第一个参数的值相同),\venn@rest 将被赋值为“”(无),与传递给 \venn@strip 的第二个参数(即 \venn@STOP(此时尚未定义))的值相同。\ifnum 语句期望 \venn@next 中二进制字符串中与当前迭代相对应的位置为 0 或 1。我看不出代码中哪里发生了这种数字剥离。
\relax 指令要求 TeX 不执行任何操作。不确定为什么我们需要 \relax 作为 \ifnum 语句的一部分。
在 \venn{00000010} 的调用中,与填充相关的语句是
\begin{scope}[even odd rule]
\path[clip] (S-SE) rectangle (S-NW) (A) circle[radius=2];
\path[clip] (B) circle[radius=2];
\path[clip] (C) circle[radius=2];
\fill[rounded corners,red] (S-SE) rectangle (S-NW);
\end{scope}
在 for 循环的第 2 次迭代中,\i = 1,这是唯一在调用中填充任何内容的迭代。
根据 PGF 手册中所述,剪切路径将绘画限制在特定区域内。此外,多个剪切会累积,并且始终针对当前范围内指定的所有剪切区域的交集进行剪切。
第一个 clip 语句将 A 处的圆作为剪切区域。第二个 clip 语句将 A 和 B 处的圆的交点作为剪切区域。第三个 clip 语句将圆 A、B 和 C 的交点区域作为剪切区域。最后,当矩形被填充时,我希望剪切区域被填充。相反,填充的是圆 A 中不与圆 B 或 C 重叠的区域。
奇偶规则可能使填充按其方式工作。手册中解释了此规则,即发射射线并计算我们击中路径的次数。我不明白如何使用它。
答案1
让我先从...开始\venn@strip
。
\def\venn@strip#1#2\venn@STOP{%
\def\venn@next{#1}%
\gdef\venn@rest{#2}%
}
这里,我们有一个稍微复杂一点的宏参数规范。宏参数规范包括定义的左括号。在本例中,它是#1#2\venn@STOP
。因此,当 TeX 遇到\venn@strip
then 时,它会尽力匹配该模式。您可以在类似以下内容中详细了解它的具体功能TeX 按主题分类,但让我试着解释一下 TeX 在这情况。该模式#1#2\venn@STOP
表示:抓取直到下一个出现标记 的所有内容\venn@STOP
,将其放入参数中#1
,并根据#2
以下规则:如果没有,则不要放入任何内容;如果只有一个东西,则将其放入#1
;如果有更多,则放入一个东西#1
,其余的放入#2
。此外,\venn@STOP
不是检查并丢弃,因此其定义无关紧要。因此,我们的想法是要评估\venn@strip 01011011010100001010\venn@STOP
。然后它将抓住第一个0
as #1
,1011011010100001010
as #2
,并丢弃\venn@STOP
。
关键是我们不知道字符串其余部分中有多少内容是我们要取的第一个字符,所以我们使用分隔宏来确保得到其余部分。
现在,来调用它:
\expandafter\venn@strip\venn@rest\venn@STOP
这\expandafter
是关键。这延伸\venn@strip
并扩展\venn@rest
前 \venn@strip
被考虑。因此,当\venn@strip
开始其工作时,流是实际上 \venn@strip010101101010100\venn@STOP
并且可以去掉第一个字符进行测试。
(多余的零毫无意义。它们是为了防止用户没有输入 8 位数的代码。)
下一点:
\ifnum\venn@next=1\relax
\ifnum
是贪婪的。对于它的每个部分(在本例中,在 之前和之后=
),它将尝试尽可能多地获取数字。1
如果下一个可能是一个数字,它可能不会在 处停止。为了防止这种情况(因为我懒得去弄清楚下一个是什么), 就\relax
在那里。本质上,\relax
扮演着“这里没有数字”标志的角色。
最后一点:
\path[clip] (S-SE) rectangle (S-NW) (A) circle[radius=2];
这就是 的作用even odd rule
所在。这条路径实际上是一个矩形,里面有一个圆圈。在 下even odd rule
,被视为“位于”此路径内的区域是位之间矩形和圆形。在圆形内,我们穿过道路两次“逃向无限”——一次是圆形,两次是矩形。因此,剪辑是外部圆心在 (A) 的圆,对应于该圆的补圆。很巧妙,不是吗?(这部分灵感来自本网站某处的反向剪辑问题。)