赞
踩
本教程译、改编自TikZ官方文档。
\documentclass[tikz,margin=10pt]{standalone}
\usetikzlibrary{calc}
\usetikzlibrary{backgrounds}
\usetikzlibrary{through} % 为了更方便地画圆(已知圆心和圆上一点)
\usetikzlibrary{intersections}
\begin{document}
\begin{tikzpicture}
...
\end{tikzpicture}
\end{document}
当然我们可以用\draw (0,0)--(2,0);
这样的代码,但是这里A、B其实可以是任意某两个点,我只想表达连接A、B,为此我们可以先用\coordinate
命令定义出两个点。
\coordinate
命令是\path coordinate
命令的缩略版本。如果你不想命名,那么就不要写at。
\coordinate (A) at (0,0);
\coordinate (B) at (1.25,0.25);
\draw[blue] (A) -- (B);
现在我们添加标注,这需要在用coordinate
命令时随之设置,并且,它不需要画出连线就能显示,请看下例。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at (0,0);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at (1.25,0.25);
现在画出连线。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at (0,0);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at (1.25,0.25);
\draw[blue] (A) -- (B);
现在我们想让A、B真正任意起来,而TikZ恰好为我们提供了rand
函数,它可以产生一个-1到1之间的均匀分布随机数(而rnd
产生0到1之间的均匀分布随机数)。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at (0+0.1*rand,0.1*rand);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at (1.25+0.1*rand,0.25+0.1*rand);
\draw[blue] (A) -- (B);
这看起来还不错,不过我们想成向量加法的形式:
(
0
,
0
)
+
0.1
(
r
a
n
d
,
r
a
n
d
)
(0,0)+0.1(\mathrm{rand},\mathrm{rand})
(0,0)+0.1(rand,rand),这就需要用到calc
库了。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at ($ (0,0) + 0.1*(rand,rand) $);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at ($ (1.25,0.25) + 0.1*(rand,rand) $);
\draw[blue] (A) -- (B);
这种语法要求以($
起始,以$)
结束,注意,这儿的美元符号($ $)
并不代表行内数学公式排版,而是表示要进行运算。
画圆就有些棘手了,因为我们不想手工计算AB的长度,为此,我们可以使用TikZ中的let
操作。
let
操作,syntax:\path … let <assignment> ,<assignment>,<assignment> … in …;
遇到这样的语句,<assignment>
们会被依次计算,计算结果被暂存在寄存器中。随后我们就可以通过\p \n \x \y
这些命令来调用结果。
1)第一种赋值(assignment)语句具有该形式:\n<number register>={<formula>}
。<number register>
可以随便取什么名字,比如英文单词词组\n{hello world}
或者数字\n3
,如果是字母要加花括号。计算结果暂存于<number register>
中,有单位(如 p t 、 c m \mathrm{pt、cm} pt、cm)会保留单位。
例如,如果let \n1={1pt+2pt} \n2=<1+2> in ...
那么在省略号...
的部分\n1 \n2
就会分别扩展为 3 p t 、 3 \mathrm{3pt、3} 3pt、3。
2)第二种<assignment>
具有该形式:\p<point register>={<formula>}
。点位置寄存器存储单个点的位置,包括用 p t \mathrm{pt} pt计量的 x x x坐标和 y y y坐标。
当
\p{<point register>}
写在=
左侧,它只是被赋值了;而当它写在了右侧或者写在in
后面,它就会进行扩展。
\x{<point regitster>}
表示点的 x x x坐标,\y{<point regitster>}
表示点的 y y y坐标。
上述的宏(macro)只适用于let
操作。
在上例中,也可以使用|-
记号:
\fill[red] let \p1 = (first point), \p2 = (second point) in (\p1|-\p2) circle [radius=2pt];
注意,计算结果只在let-in语句有效,如果想要在语句之外也能使用计算结果,就要用coordinate
进行保留。
这边用到了感叹号表示法来表示中点,我试了试相加除以2,报错了。
这个做切线圆的我就看不懂感叹号表示法了。
circle
后面的第一层圆括号是circle
命令需要的:(<圆心>) circle (<半径>)
,第二层花括号是为了保证veclen
函数在计算环境中(还记得第一节的{tan(30)}
吗),而如果是做calc
允许的向量加减或者带感叹号的,就要放在($ $)
中。
现在我们想一次性画两个圆,所以我们可以先计算出半径,之后就不用再算了。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at ($ (0,0) + 0.1*(rnd,rand) $);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at ($ (1.25,0.25) + 0.1*(rand,rand) $);
\draw[blue] (A) -- (B);
\draw let
\p1 = ($ (B)-(A) $),
\n2 = {veclen(\x1,\y1)}
in (A) circle (\n2)
(B) circle (\n2);
在这段代码中,如果我们调用\n1
,它不仅不会扩展成任何数,而且会报错,这是因为\p \x \y
在同一个名字空间,而\n
在自己独立的名字空间里。
用TikZ的through
库可以更方便地画圆,我们的想法很简单,已知圆心A,且圆经过B,要作圆,而through
刚好实现了我们的想法。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at ($ (0,0) + 0.1*(rnd,rand) $);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at ($ (1.25,0.25) + 0.1*(rand,rand) $);
\draw[blue] (A) -- (B);
\node[draw,circle through=(B),label=left:$ D $] at (A) {};
\node[draw,circle through=(A),label=right:$ E $] at (B) {};
倒数第二行代码是这样运作的:首先创建一内外都没有空白(separation)的节点,然后设置节点形状为以A为圆心的圆,最后设定半径使得圆恰经过点B。
现在我们要找到交点。手工计算可就复杂了,好在TikZ给我们提供了intersections
库,使得我们可以计算出任意路径的交点。
\coordinate[label=left:\textcolor{blue}{$ A $}] (A) at ($ (0,0) + 0.1*(rnd,rand) $);
\coordinate[label=right:\textcolor{blue}{$ B $}] (B) at ($ (1.25,0.25) + 0.1*(rand,rand) $);
\draw[blue] (A) -- (B);
\node (D) [name path=D,draw,circle through=(B),label=left:$ D $] at (A) {};
\node (E) [name path=E,draw,circle through=(A),label=right:$ E $] at (B) {};
\path[name intersections={of=D and E,by={i1,i2}}];
\coordinate [label=above:$ C $] (C) at (i1);
\draw[red] (A)--(C);\draw[red] (B)--(C);
先指定两个路径名分别为D和E(虽然和节点名相同,但互不影响,因为他们在不同的名字空间),然后作交点,因为有两个交点,我用{i1,i2}
表示它们(如果不用by
指定交点名,则默认为intersection-1 intersection-2 ...
),我想交在上方的点,所以最后一行at (i1)
我们甚至可以在作交点时顺便标注。
\def\A{\textcolor{input}{$A$}} \def\B{\textcolor{input}{$B$}} \def\C{\textcolor{output}{$C$}} \def\D{$D$} \def\E{$E$} \colorlet{input}{blue!80!black} \colorlet{output}{red!70!black} \colorlet{triangle}{orange} \coordinate [label=left:\A] (A) at ($ (0,0) + .1*(rand,rand) $); \coordinate [label=right:\B] (B) at ($ (1.25,0.25) + .1*(rand,rand) $); \draw [input] (A) -- (B); \node [name path=D,help lines,draw,label=left:\D] (D) at (A) [circle through=(B)] {}; \node [name path=E,help lines,draw,label=right:\E] (E) at (B) [circle through=(A)] {}; \path [name intersections={of=D and E,by={[label=above:\C]C}}]; \draw [output] (A) -- (C) -- (B); \foreach \point in {A,B,C} \fill [black,opacity=.5] (\point) circle (2pt); \begin{pgfonlayer}{background} \fill[triangle!80] (A) -- (C) -- (B) -- cycle; \end{pgfonlayer} \node [below right, text width=10cm,align=justify] at (4,3) { \small\textbf{Proposition I}\par \emph{To construct an \textcolor{triangle}{equilateral triangle} on a given \textcolor{input}{finite straight line}.} \par\vskip1em Let \A\B\ be the given \textcolor{input}{finite straight line}. \dots };
注:设置背景也可以用上节的\begin{scope}[on background layer] \end{scope}
代码,这里用了pgfonlayer
环境,后面的background
放在花括号而不是中括号中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。