Skip to content

Latest commit

 

History

History
1486 lines (1099 loc) · 58 KB

tikz.md

File metadata and controls

1486 lines (1099 loc) · 58 KB

texdoc tikz

用来替换pdf 粘贴过程额外h..i的正则表达式

\bh([\. ]+?)i\b # vscode 里面的正则, 元字符 . 需要转义成 \.
<$1>

introduction

欢迎来到TikZ和底层pgf系统的文档. 最初是小小的LaTeX style, 直接用pdfLaTeX创建我(Till Tantau)的博士论文中的图形, 现在已经发展成一个完整的图形语言, 它的手册有一千多页. TikZ提供的大量选项往往让初学者望而生畏; 但幸运的是, 这个文档附带了一些节奏缓慢的教程, 这些教程几乎可以教会你所有关于TikZ的知识, 而不需要你去阅读其他的内容.

我希望从 "什么是TikZ? "这个问题开始. 基本上, 它只是定义了一些TEX命令来绘制图形. 例如, 代码\tikz \draw (0pt,0pt) --(20pt,6pt); 产生线条. 代码\tikz \fill[orange] (1ex,1ex) circle (1ex);产生小橙子. 在某种意义上, 当你使用TikZ的时候, 你对你的图形进行了 "编程", 就像你在使用TEX时对你的文档进行了 "编程 "一样. 这也解释了TikZ的名字: TikZgnu's Not Unix 传统的一个递归缩写, 即 TikZ ist kein Zeichenprogramm, 意思是 "TikZ不是一个绘图程序", 提醒读者不要抱有错误的期待.

有了TikZ, 你可以为你的图形获得 "TEX-approach to typesetting" 的所有优点. 快速创建简单的图形, 精确的定位, 使用宏, 通常是卓越的排版. 同时你也

你也继承了所有的缺点: 学习曲线陡峭, 没有 WYSIWYG, 小的改变需要长时间的重新编译. 而且代码并不真正 "显示 "事物的样子.

现在我们知道了TikZ是什么, 那么 pgf 呢? 如前所述, TikZ最初是作为一个实现TEX图形宏的项目, 它既可以用于pdfLaTeX, 也可以用于经典的(基于PostScript)的LaTeX. 换句话说, 我想为TEX实现一种 "portable graphics format"--因此被称为pgf. 这些早期的宏仍然存在, 它们构成了本手册中描述的系统的 基本层.

但现在文档作者的大部分互动是基于TikZ的, TikZ自己已经成为一门独立的语言.

TikZ 的底层

page 27.

事实证明, 在TikZ下面有两个layers.

  • System layer: 这一层提供了对 驱动 中所发生的事情的完整抽象.

驱动程序是一个像dvipsdvipdfm这样的程序, 它接受一个 .dvi 文件作为输入, 并生成一个 .ps.pdf 文件. (pdftex程序也算作一个驱动程序, 尽管它不接受.dvi文件作为输入, 无所谓).

每个驱动程序都有自己的图形生成语法, 这让想以可移植方式创建图形的人感到头疼.

pgf系统层 抽象化了这些差异. 例如, 系统命令pgfsys@lineto{10pt}{10pt}将当前路径扩展到当前{pgfpicture}的坐标点(10pt, 10pt).

根据dvips, dvipdfmpdftex这些处理文件的具体程序, 系统命令将被转换为不同的\special命令. 系统层尽可能地 "简约", 因为每一个额外的命令, 都会使将pgf在移植到新的驱动程序上时需要更多的工作. 作为一个用户, 你不会直接使用系统层.

  • Basic layer : 基本层提供了一组基本命令, 使你能够以比直接使用系统层更简单的方式产生复杂的图形. 比直接使用系统层要容易得多. 例如, 系统层没有提供创建的命令, 因为可以由更基本的贝塞尔曲线组成(嗯...差不多). 然而, 作为一个用户, 你会希望有一个简单的命令来创建(至少我是这样想). 而不是要写下半页的贝塞尔曲线的支撑坐标. 因此, 基本层提供了一个命令\pgfpathcircle, 为你生成必要的曲线坐标.

基本层包括一个核心, 核心由几个相互依赖的软件包组成, 这些软件包只能被整体加载(en bloc), 还有一些额外的模块, 模块扩展核心, 以提供更多特殊用途的命令, 如node管理或plotting接口.

例如, beamer包只使用核心, 而不使用, 例如shapes模块.

理论上, TikZ本身只是几个可能的 frontends 之一. 它提供了命令组和一些特殊语法, 使基本层的使用更容易. 直接使用基本层的一个问题是, 用该层编写的代码往往过于 "冗长". 例如, 为了画一个简单的三角形, 你可能需要多达五个命令. 一条是在三角形的第一个角上开始一个路径. 一条用于将路径延伸到第二个角, 一条用于前往第三个角, 一条用于关闭路径. 以及一条用于实际绘制三角形的(而不是填充它). 在TikZ的前端, 所有这些都可以归结为简单的类似 METAFONT 的命令.

\draw (0,0) -- (1,0) -- (1,1) -- cycle;

实际上, TikZpgf的唯一 正式 的前端. 它让你可以使用pgf的所有功能, 但的所有功能, 但它的目的是要使其易于使用. 语法是METAFONTPSTRICKS的混合体, 也有一些我自己的想法.

除了TikZ之外, 还有其他的前端, 但它们更多的是作为 "技术研究", 而不是作为TikZ的重要替代品. 特别是pgfpict2e, 它使用pgf基本层, 重新实现了标准的LaTeX {picture}环境和命令, 例如\line\vector. 这个层并非真正"必要", 因为pict2e.sty包在重新实现{picture}环境方面至少做得很好. 相反, 这个包背后的想法是为了简单地演示如何实现一个前端.

由于大多数用户只使用TikZ, 几乎没有人会直接使用系统层, 所以本手册在第一部分主要是关于TikZ的. 基础层系统层将在最后解释.

Tutorial Karl

路径命令

p32, tikz中, path就是一连串的坐标, 在路径的开头可以选择:

  • \path 什么也不做
  • \draw 画出路径
  • \fill 填充路径
  • \filldraw 等等

对路径的操作. 用分号表示一条路径的结束. 每一条路径会有初始点, 当前点等等特殊坐标. 如current subpath start

路径构建命令和绘画命令相分离, 与路径构建的选项, 都在路径命令中指定; 与实际描绘相关的选项, 都在\draw,\fill等命令的选项中指定.

曲线 33 \draw (0,0) .. controls (1,1) and (2,1) .. (2,0);

圆形 (1,1) circle [radius=2pt] ellipse [x radius=20pt, y radius=10pt]

方形 \draw (0,0) rectangle (0.5,0.5); \draw[step=.5cm] (-1.4,-1.4) grid (1.4,1.4);

自定义格式, \tikzset,p35

help lines/.style={color=blue!50,very thin} %在环境内部任意地方定义格式, 后面可以调用
\tikzset{help lines/.style=very thin} %在文档开头, 定义全局格式
\tikzset{Karl's grid/.style={help lines,color=blue!50}} %格式可以嵌套
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4); % 使用 grid 绘制 参考格子

颜色线型等

绘制选项, p36

%颜色
color=<color>
draw=<color>
%%线型
ultra thin
very thin
thin
semithick
thick
very thick
ultra thick
%% 虚线
dashed
loosely dashed
densely dashed
dotted
loosely dotted
densely dotted
dash pattern

放大部分区域

剪切出图形的某一部分,clip

\draw[clip] (0.5,0.5) circle (.6cm);
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4);
...

画抛物线

抛物线,parabola, p38

\tikz \draw (0,0) rectangle (1,1)(0,0) parabola (1,1);
\tikz \draw[x=1pt,y=1pt] (0,0) parabola bend (4,16) (6,12);

填充封闭区域, 使用cycle进行封闭 39

\begin{tikzpicture}[scale=3]
\clip (-0.1,-0.2) rectangle (1.1,0.75);
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4);
\draw (-1.5,0) -- (1.5,0);
\draw (0,-1.5) -- (0,1.5);
\draw (0,0) circle [radius=1cm];
\filldraw[fill=green!20!white, draw=green!50!black] (0,0) -- (3mm,0mm)
arc [start angle=0, end angle=30, radius=3mm] -- cycle;
\end{tikzpicture}

渐变色

渐变色,shade,p39

\shadedraw[left color=gray,right color=green, draw=green!50!black](0,0) -- (3mm,0mm)

相对坐标

定义命令, 相对坐标指定, p40

(30:1cm |- 0,0) % 垂直线与水平线的交点, |- 左边的坐标对应铅垂线, 右边的对应水平线.
\def\rectanglepath{-- ++(1cm,0cm) -- ++(0cm,1cm) -- ++(-1cm,0cm) -- cycle} %% 连续的相对指定, 后一个坐标相对于前一个, 使用 \def 在任意地方定义一个命令替换.
\def\rectanglepath{-- +(1cm,0cm) -- +(1cm,1cm) -- +(0cm,1cm) -- cycle} %% 基于相同root的相对坐标, 后面几个坐标相对于同一个最初坐标

路径交点

路径的交点, 使用name path在后面引用.

可以使用(1,{tan(30)})这种坐标形式, tikz的数学引擎可以处理tan(30), 但是外面需要包围一层{}, 否则会与坐标的语法冲突. 一般情况中, 遇到含有()的坐标, 也需要用{}包裹起来.

% 在导言区 \tikz 后面添加 \usetikzlibrary{intersections}
\path [name path=upward line] (1,0) -- (1,1); % 给第一条路径命名, \path 只计算路径, 不进行实际描绘.
\path [name path=sloped line] (0,0) -- (30:1.5cm);  % 第二条路径, 画得稍微长一点, 保证有交点
\draw [name intersections={of=upward line and sloped line, by=x}] [very thick,orange] (1,0) -- (x);

添加箭头, 通过->选项, 可以指定在路径末端加上箭头.

\usetikzlibrary {arrows.meta}
\begin{tikzpicture}[>=Stealth]
\draw [->] (0,0) arc [start angle=180, end angle=30, radius=10pt];
\draw [<<-,very thick] (1,0) -- (1.5cm,10pt) -- (2cm,0pt) -- (2.5cm,10pt);
\end{tikzpicture}

作用域 scope

参考p42, scope. 类似于其他程序中的局部变量. 在导言区添加\usetikzlibrary {scopes}.

例如在tikzpicture环境中使用tikz-feynman包的feynman环境定义的fermion edge style.

\begin{tikzpicture}
{[every edge/.style={controls=+(27:3) and +(153:3), /tikzfeynman/fermion}]
\path (0,0) edge (0,1);
}  % 末尾无需分号
\end{tikzpicture}

every edge/.styletikzpicture环境中的handle.


可以给选项设置作用范围, 如果想让整个环境都生效, 可以把选项传递给\tikz命令或者{tikzpicture}环境. 如果希望定义一个局部环境, 可以使用{scope}环境. 例如:

\begin{tikzpicture}[ultra thick]
\draw (0,0) -- (0,1);
\begin{scope}[thin] % 在这里给出选项
\draw (1,0) -- (1,1);
\draw (2,0) -- (2,1);
\end{scope}
\draw (3,0) -- (3,1);
\end{tikzpicture}

\clip的生效范围也受\scope的控制, 只在scope范围内有效. 类似于\draw的选项, 其实不是\draw的选项, 而是提供给path的选项, 可以在路径命令序列的任何地方提供. 大部分graphic选项是对整个路径生效的, 所以选项的位置不重要. 例如, 下面三种用法是等价的. 如果同时给出thick and thin, 后一个覆盖前一个的效果.

\begin{tikzpicture}
\draw[thin] (0,0) --(1,0);
\draw (0,1) [thin] --(1,1);
\draw (0,2) --(1,2) [thin];
\end{tikzpicture}

大部分图形选项应用于整个路径, 而所有的变形选项,transformation 只作用于跟在其后的路径片段.

路径变形

page 43. 路径变形选项. 图像的最后位置是由TikZ, TeX, PDF共同决定的. tikz 提供了一些选项可以在自己的坐标系统内变换图像的位置. 并且运行在路径中途修改变换方式. 例如:

\begin{tikzpicture}[even odd rule,rounded corners=2pt,x=10pt,y=10pt]
\filldraw[fill=yellow!80!black] (0,0)  rectangle (1,1) [xshift=5pt,yshift=5pt]  (0,0)  rectangle (1,1) [rotate=30]   (-1,-1) rectangle (2,2);
\end{tikzpicture}

这类选项有:

x=<value>, y=<value>, z=<value>. 例如:

\draw[x=2cm,color=red] (0,0.1) -- +(1,0);
\draw[x={(2cm,0.5cm)},color=red] (0,0) -- (1,0); %含有逗号的坐标需要放在括号里 escape
  • xshift=<dimension>,yshift=<dimension>:用来平移
  • shift={<coordinate>}: 平移到指定的点, 如shift={(1,0)}, shift={+(1,0)}, 必须加上{}, 以避免TeX把坐标解析成两个选项. 例如:
\draw[shift={(1,1)},blue] (0,0) -- (1,1) -- (1,0);
\draw[shift={(30:1cm)},red] (0,0) -- (1,1) -- (1,0);
  • rotate=<degree>: 旋转特定角度. 以及rotate around:绕指定的点旋转.
  • scale=<factor>: 放大或者缩小指定的倍数. 以及xscale, yscale. xscale=-1表示翻转.
  • xslant=<factor>, yslant=<factor>: 倾斜
  • cm: 指定任意的变换矩阵.

详细可以参考 page 373: 25 Transformations

循环

latex本身有循环的命令, pstricks具有\multido命令. tikz也引入了自己的循环命令\foreach, 它定义在\pgffor中, \tikz会自动\include这个命令. 语法是\foreach \x in {1,2,3} {$x =\x $,}. 循环区域用列表指定, 要循环的指令也放在一个{}中. 如果不用{}包裹, 就把下一个;之前的命令当作循环指令. 例如下面的语句绘制一个坐标系:

\begin{tikzpicture}[scale=3]
\draw[step=.5cm,gray,very thin] (-1.4,-1.4) grid (1.4,1.4);
\draw[->] (-1.5,0) -- (1.5,0);
\draw[->] (0,-1.5) -- (0,1.5);
\foreach \x in {-1cm,-0.5cm,1cm}
\draw (\x,-1pt) -- (\x,1pt);
\foreach \y in {-1cm,-0.5cm,0.5cm,1cm}
\draw (-1pt,\y) -- (1pt,\y);
\end{tikzpicture}

也可以结合平移使用:

\foreach \x in {-1,-0.5,1} \draw[xshift=\x cm] (0pt,-1pt) -- (0pt,1pt);

\foreach也可以使用c式的范围指定: {a,...,b}, 必须使用无量纲的实数. {1,3,...,11}则可以指定步长. 两种语法可以混合, 例如:

\tikz \foreach \x in {1,3,...,11} \draw (\x,0) circle (0.4cm);

循环可以嵌套:

\begin{tikzpicture}
\foreach \x in {1,2,...,5,7,8,...,12}
\foreach \y in {1,...,5}
{
  \draw (\x,\y) +(-.5,-.5) rectangle ++(.5,.5);
  \draw (\x,\y) node{\x,\y};
}
\end{tikzpicture}

为了方便, 还有一种key/value型的循环语法: foreach \key/\value in {1/a,2/b,3/c}, key/value/分隔开. 在每一次循环中, \key\value的值将一一对应. 如果循环域中, 某一项只给出了key, 会默认value等于key.

添加文字

添加文字可以使用\node命令, 也可以用来添加任意形状. 通常的用法是\node[选项]{文字}. \node会放在当前位置, 也就是\node命令前面的那个坐标上. 当所有路径draw/fill/shade/clipped/whatever完成之后, 才绘制\node, 所以\node的图层在最上面.

  • 可以使用类似于anchor=north指定\node的哪一个锚点放在前面给定的坐标上. 也可以使用below=1pt直接指定\node的相对偏移.
  • 如果担心图形上的其他元素干扰了node的辨识度, 可以给node加上[fill=white]选项, 绘制一个白色的背景.
  • 如果把\node放在--后面, 默认会将\node的位置放在这条线段的中点. 可以使用pos=选项控制具体的位置, 也可以使用near start, near end等等指定大概位置.
  • 还可以使用[above, sloped]选项, 使曲线上方的\node贴合曲线斜率. 例如:
\begin{tikzpicture}
\draw (0,0) .. controls (6,1) and (9,1) ..
node[near start,sloped,above] {near start} node {midway}
node[very near end,sloped,below] {very near end} (12,0);
\end{tikzpicture}

如果\node中的文本量比较大, 需要控制换行, 可以使用类似text width=6cm的选项控制\node宽度. 完整的例子为:

\begin{tikzpicture}
  [scale=3,line cap=round,
    % 定义一些对象的格式
    axes/.style=,
    important line/.style={very thick},
    information text/.style={rounded corners,fill=red!10,inner sep=1ex}]
  % 定义一些对象的颜色
  \colorlet{anglecolor}{green!50!black}
  \colorlet{sincolor}{red}
  \colorlet{tancolor}{orange!80!black}
  \colorlet{coscolor}{blue}
  % 开始画图
  \draw[help lines,step=0.5cm] (-1.4,-1.4) grid (1.4,1.4);
  \draw (0,0) circle [radius=1cm];
  \begin{scope}[axes]
    \draw[->] (-1.5,0) -- (1.5,0) node[right] {$x$} coordinate(x axis);
    \draw[->] (0,-1.5) -- (0,1.5) node[above] {$y$} coordinate(y axis);
    \foreach \x/\xtext in {-1, -.5/-\frac{1}{2}, 1}
    \draw[xshift=\x cm] (0pt,1pt) -- (0pt,-1pt) node[below,fill=white] {$\xtext$};
    \foreach \y/\ytext in {-1, -.5/-\frac{1}{2}, .5/\frac{1}{2}, 1}
    \draw[yshift=\y cm] (1pt,0pt) -- (-1pt,0pt) node[left,fill=white] {$\ytext$};
  \end{scope}
  \filldraw[fill=green!20,draw=anglecolor] (0,0) -- (3mm,0pt)
  arc [start angle=0, end angle=30, radius=3mm];
  \draw (15:2mm) node[anglecolor] {$\alpha$};
  \draw[important line,sincolor]
  (30:1cm) -- node[left=1pt,fill=white] {$\sin \alpha$} (30:1cm |- x axis);
  \draw[important line,coscolor]
  (30:1cm |- x axis) -- node[below=2pt,fill=white] {$\cos \alpha$} (0,0);
  \path [name path=upward line] (1,0) -- (1,1);
  \path [name path=sloped line] (0,0) -- (30:1.5cm);
  \draw [name intersections={of=upward line and sloped line, by=t}]
  [very thick,orange] (1,0) -- node [right=1pt,fill=white]
  {$\displaystyle \tan \alpha \color{black}=
      \frac{{\color{red}\sin \alpha}}{\color{blue}\cos \alpha}$} (t);
  \draw (0,0) -- (t);
  \draw[xshift=1.85cm]
  node[right,text width=6cm,information text]
  {
    The {\color{anglecolor} angle $\alpha$} is $30^\circ$ in the
    example ($\pi/6$ in radians). The {\color{sincolor}sine of
        $\alpha$}, which is the height of the red line, is
    \[
      {\color{sincolor} \sin \alpha} = 1/2.
    \]
    By the Theorem of Pythagoras ...
  };
\end{tikzpicture}

pic: 图形复用

picpicture的简称. 通过预先定义的图片名字, 可以在指定的地方复用图形. 例如:

\usetikzlibrary {angles,quotes}
\begin{tikzpicture}[scale=3]
\coordinate (A) at (1,0);
\coordinate (B) at (0,0);
\coordinate (C) at (30:1cm);
\draw (A) -- (B) -- (C)
pic [draw=green!50!black, fill=green!20, angle radius=9mm,"$\alpha$"] {angle = A--B--C};
\end{tikzpicture}

这里调用了angles and quotes库. 前者预定义了angle图形, 后者可以简化参数输入为"标记", 而不需要输入label text="标记". {angle = A--B--C}表示angleBABC的夹角. \coordinate用于声明一个坐标点, 可以在后文引用.

tikz 设计原则

page 124, TikZ 遵循以下基本设计原则:

  1. 用于指定points的特殊语法.
  2. 指定path的特殊语法.
  3. Actions on paths.
  4. 图形参数的Key–value语法.
  5. nodes的特殊语法.
  6. trees的特殊语法.
  7. graphs的特殊语法.
  8. 对图形的参数分组.
  9. 坐标转换系统.

scope 作用范围

131 Using Scopes to Structure a Picture. 如果scope不生效的话, 可以尝试在导言区添加

\usetikzlibrary {scopes}

命令 \path 用于创建一个路径 (path),此命令可以带有图形选项 (graphic options),这些选项只对本路径有效. 使用简写形式的 scope 可以在路径内部插入一个 scope:

\tikz \draw (0,0) -- (1,1)
{[rounded corners, red] -- (2,0) -- (3,1)}
-- (3,0) -- (2,1);

上面例子中,选项 rounded corners 的作用范围受到花括号的限制,并且颜色选项 red 没有起到作用,这是因为 \draw 的默认颜色是 draw=black,颜色 blackred 覆盖了. 还要注意开启 scope 的符号组合{[...]要放在坐标点之后, --之前.

除了\tikzpicture环境, 可以使用简洁的\tikz{path1;path2}命令, 例如:

\tikz[baseline]{
  \draw (0,0)--(2,0);\draw (0.5,0) to [out=90,in=90,looseness=1.5] (1.5,0);
\draw (0.5,0) to [out=-90,in=-90,looseness=1.5] (1.5,0);
}

坐标计算

page 148

指定坐标点

page 136, Specifying Coordinates page 148,TikZ Library calc, 可以计算坐标值.

坐标总是放在圆括号内, 一般的语法是 ([<options>]<coordinate specification>). 有两种指定坐标的方法:

明确指定坐标系统和参数, 使用xxx cs:这种语法

\draw (canvas cs:x=0cm,y=2mm)
-- (canvas polar cs:radius=2cm,angle=30);

或者可以隐式地指定, tikz 会根据格式自动判断坐标系统. 例如 (0,0) 对应笛卡尔坐标,(30:2) 对应极坐标 (其中 30 代表角度).

  • 基本使用:(1cm,2pt)
  • 极坐标:(30:1cm)
  • PGF-xy 坐标系统, 单位按照cm : (2,1)
  • PDF-xyz 坐标系统:(1,1,1)
  • 也可以使用利用之前定义的形状作为锚点,如:(first node.south)
  • 连续相对坐标:++(1cm,0pt),(1,0), ++(1,0), ++(0,1)给出(1,0), (2,0),(2,1)
  • 同源相对坐标:+(1,0) +(1,0) +(0,1) 给出 (1,0), (2,0), (1,1).

对图像进行全局伸缩, 可以指定xyz单位矢量的长度, 也可以通过画布变换

page 137: Coordinate system xyz page 43: Transformations

指定路径

路径是一些直线和曲线的组合. 部分使用metapost的语法,例如,一条三角形路径

(5pt,0pt) -- (0pt,0pt) -- (0pt,5pt) -- cycle

p168 The Let Operation

对路径的action

路径只是一系列直线和曲线的组合,但你尚未指定如何处理它. 可以绘制一条路径,填充一条路径,为其着色,对其进行裁剪或进行这些操作的任意组合.

draw,fill,shade,clip

例如

\path[draw] (0,0) rectangle (2ex,1ex)

\path[draw]: \draw \path[fill] :\filldraw \path[shade]:\shade and \shadedraw \path[clip]: \draw[clip] or \path[draw,clip]: \clip

所有这些命令只能在{tikzpicture}环境中使用. TikZ 允许您使用不同的颜色进行填充和描边.

自定义

自定义 style

\begin{tikzpicture}[abcde/.style={
  double distance=10pt,
  postaction={
  decorate,
  decoration={
      markings,
      mark=at position .5 with {\arrow[blue,line width=1mm]{>}},
    }
  }
  }]

  \begin{feynman}
  ...
  \end{feynman}

  \draw [help lines] grid (3,2);
  \draw[abcde]  (a1) -- (b1);

\end{tikzpicture}

指定线型

173 Graphic Parameters: Line Width, Line Cap, and Line Join

/tikz/dash pattern=<dash pattern> /tikz/dashed : 指定虚线模式的简写 Shorthand for setting a dashed dash pattern.

指定 node 节点的形状

p224, Nodes and Their Shapes

比如

\begin{tikzpicture}
\draw (0,0) node[minimum size=2cm,draw] {square};
\draw (0,-2) node[minimum size=2cm,draw,circle] {circle};
\end{tikzpicture}

p785: 72 Shape Library, 形状库

p229: 17.2.3 Common Options: Separations, Margins, Padding and Border Rotation: 给出了 node 一些几何参数的选项

p730: 63 Pattern Library : node 可以使用 pattern 填充, 某种图形模式.

添加任意装饰

p191 Arrows 箭头

p196 指定箭头大小, 形状

p212 Reference: Arrow Tips 与定义箭头形状参考

\usetikzlibrary{arrows.meta}

p365 Decorated Paths 装饰路径

p646 Arbitrary Markings 添加任意装饰

Decoration markings

marking可以被认为是"小图片", 或更准确地说是放置的some scope contents, 放置在路径的某个位置"上". 假设marking为简单的十字. 可以用以下代码产生:

\draw (-2pt,-2pt) -- (2pt,2pt);
\draw (2pt,-2pt) -- (-2pt,2pt);

如果我们将此代码用作路径上2cm处的marking, 则会发生以下情况: pgf先确定沿路径2cm的位置. 然后将坐标系平移到此处并旋转它, 使x轴正向与路径相切. 然后创建一个保护用的scope, 在内部执行上述代码--最后路径上出现一个叉叉.

marking允许在路径上放置一个或多个装饰. 除了后面讲的少数情况, decoration摧毁路径输入, 也就是说, 计算完成, 作完装饰之后, 路径就消失了. 一般需要postaction来添加装饰. postaction 表示完成路径绘制之后再进行操作.

\begin{tikzpicture}[decoration={
        markings,% 启用 markings
        mark=% mark 的形状
        at position 2cm
        with
          {
            \draw (-2pt,-2pt) -- (2pt,2pt);
            \draw (2pt,-2pt) -- (-2pt,2pt);
          }
      }
  ]
  \draw [help lines] grid (3,2);
  \draw [postaction={decorate}] (0,0) -- (3,1) arc (0:180:1.5 and 1); % postaction 表示画完路径之后再装饰, 再摧毁路径.
\end{tikzpicture}

Pics:复用图形组件

p263, picpicture的简称. 通过预先定义的图片名字, 可以在指定的地方复用图形. 例如先定义一个海鸥的形状:

\tikzset{
seagull/.pic={
% Code for a "seagull". Do you see it?...
\draw (-3mm,0) to [bend left] (0,0) to [bend left] (3mm,0);
}
}

使用\tikzset定义的是全局的, 整个文档都可以调用. 然后调用它:

\tikz \fill [fill=blue!20]
(1,1)
-- (2,2) pic {seagull}
-- (3,2) pic {seagull}
-- (3,1) pic [rotate=30] {seagull}
-- (2,1) pic [red] {seagull};

定义新的Pic类型

pic命令说明中所述, 要定义新的pic类型, 您需要

  1. 定义一个路径前缀为/tikz/picskey,
  2. /tikz/pics/code设置为piccode.

这可以使用.style handler 实现:

\tikzset{
  pics/seagull/.style ={
      % 当调用 seagull 的时候, 下面的代码会设置 seagull 的 code key:
      code = { %
          \draw (...) ... ;
        }
    }
}

一些简单的情况下, 可以直接使用.pic handler,

\tikzset{
    seagull/.pic = {
    \draw (...) ... ;
  }
}

handler只能对带有/tikz/前缀的key一起使用, 因此通常应将其用作TikZ命令或\tikzset命令的选项. 它使用<key>的路径, 并把其中的/tikz/替换为/tikz/pics/. 最终得到一个style, 能够执行code = some code. 大多数情况下, .pichandler足以设置keys. 但是, 在某些情况下确实需要使用第一个版本:

  • 当您的图片类型需要设置foregroundbackground代码时.
  • 如果给key提供了复杂的参数

例如:

\tikzset{
    pics/my circle/.style = {
    background code = { \fill circle [radius=#1]; }
  }
}
\tikz [fill=blue!30]

这里给my circle使用了参数.

  • <key>/.code n args={<m>}{<代码>}:传入m个参数{#1}{#2}{#3}..., m0~9, 不能多也不能少, 空格也可以作为参数.

19 Specifying Graphs

page 269; 19 Specifying Graphs

在本节中, 我们所说的图是指一组结点和一些边(有时也称为弧, 取决于连接方式), 如下面的图. 例如:

\usetikzlibrary {graphs}
\tikz \graph { a -> {b, c} -> d };

图的结点是正常的TikZ节点, edge也是节点之间普通的连线. graphs库中没有任何东西是你不能用普通的 \nodeedge命令来做的. 它的主要优势是只需指定哪些节点和边是存在的. 在画布为节点上寻找 "好位置 "的问题留给了图形绘制算法. 算法在本手册的第四部分描述, 这些算法不是graphs库的一部分;

事实上, 这些算法也可以优化edgenode命令创建的图形, 而不需要调用graphs库. 例如可以 load layered 图形绘制库, 使用\tikz[layered layout,..., 然后使用LuaTeX编译, 同样可以得到较好的排版.

节点链

graph的基本方法是写出一个节点链,

\usetikzlibrary {graphs}
\tikz [every node/.style = draw]
\graph {   foo -> bar -> blub;a -> b -> c;};

节点文字和->交替排列, ->算符用来产生箭头. 多条链之间用分号;或逗号,分隔. 节点名称默认等于其中的文字. 可以显式更改这种设定. 通过as key, 或者在节点后面加上slash/.

\usetikzlibrary {graphs}
\tikz \graph {
x1/$x_1$ -> x2 [as=$x_2$, red] -> x34/{$x_3,x_4$};
x1 -> [bend left] x34;
};

要使用特殊符号当作节点的名字,如,或虚线, 需要用引号"包裹. 例如

\usetikzlibrary {graphs}
\tikz \graph {
"$x_1$" -> "$x_2$"[red] -> "$x_3,x_4$";
"$x_1$" ->[bend left] "$x_3,x_4$";
};

chain 组

可以把多条chain放在一个{}中, 形成一个 chain group. 这样可以实现同时连接多个node. 前一个node或者groupexit points将连接到后一个nodeor groupentry points.

\usetikzlibrary {graphs}
\tikz \graph {
a -> { b -> c, d -> e} -> f};

树形图可以通过添加tree layout来使图形更加美观. 同时需要在导言区加上

\usetikzlibrary {graphdrawing}
\usegdlibrary {trees}

Edge标签和风格

可以给->connector 提供选项, 来定制风格.

\usetikzlibrary {graphs}
\tikz \graph {
  a ->[red] b --[thick] {c, d};
};

使用quotes语法, see Section 17.10.4, 可以方便的添加标签:

\usetikzlibrary {graphs,quotes}
\tikz \graph {
a ->[red, "foo"] b --[thick, "bar"] {c, d};
};

如果想给不同的edge指定不同的标签, 可以去掉给--提供的选项, 转而给node提供选项. 用>来表示进入nodeedge, 用<表示从node出射的edge.

\usetikzlibrary {graphs,quotes}
\tikz \graph {
a [< red] -> b -- {c [> blue], d [> "bar"']};
};

"bar"'后面的单引号'表示翻转标签bar的位置到下面. 使用这种语法可以创建带有特殊标签的树图.

\usetikzlibrary {graphs,quotes}
\tikz
  \graph [edge quotes={fill=white,inner sep=1pt},
    grow down, branch right, nodes={circle,draw}] {
    "" -> h [>"9"] -> { c [>"4"] -> { a [>"2"], e [>"0"] },j [>"7"]}
};

node 集合

当你在graph命令中写下节点文本时, 默认会创建一个新的节点, 除非这个节点已在同一个图形命令创建过. 特别是, 如果节点已经在graph的范围外声明过, 则会创建一个同名的新节点. 这并不总是理想的行为. 你可能希望使用已经定义好的节点画新的graph, 而不是在graph内部重新定义. 为此, 只需在节点名称周围加上圆括号(). 这将导致创建对已经存在的节点的引用.

\usetikzlibrary {graphs}
\tikz {
  \node (a) at (0,0) {A};
  \node (b) at (1,0) {B};
  \node (c) at (2,0) {C};
}
\graph { (a) -> (b) -> (c) };

你甚至可以更进一步. 一群节点可以通过添加选项set=<node set name>来标记为属于一个节点集. 然后, 在graph命令中, 你可以通过在节点集名称的周围加上括号(), 来引用这些节点.

\usetikzlibrary {graphs,shapes.geometric}
\tikz [new set=my nodes] {
\node [set=my nodes, circle, draw] at (1,1) {A};
\node [set=my nodes, rectangle, draw] at (1.5,0) {B};
\node [set=my nodes, diamond, draw] at (1,-1) {C};
\node (d)[star, draw] at (3,0) {D};
\graph { X -> (my nodes) -> (d) };
}

Graph 宏

由于图形中经常存在重复使用的部分, 为了便于指定这样的图形, 你可以定义图形宏. 一旦定义了图形宏, 你就可以使用图形的名称来复制它.

\usetikzlibrary {graphs.standard}
\tikz \graph { subgraph C_n [n=5, clockwise] -> mid };

graphs.standard库定义了许多这样的图, 包括n个节点上的 complete bipartite graph Kn,m, 具有shores sized nm, n个节点上的循环Cn, n个节点上的路径Pn, 以及n个节点上的独立集合In.

Graph表达式和颜色类

当使用graph命令构建图时, 它递归构建的, 将较小的图拼成较大的图. 在这个递归的拼合过程中, 图的节点会被隐含地着色 (概念上), 你也可以显式地为单个节点指定颜色, 甚至可以在指定(specify)图形时改变颜色. 所有具有相同颜色的节点形成一个所谓的颜色类(color class).

颜色类的强大之处在于, 特殊的连接运算符(connector operator)允许你在具有特定颜色的节点之间添加边. 例如, 在组的开头添加clique=red会使所有的节点被标记为(概念上)红色, 这些节点将会被连接成一个clique. 同样地, complete bipartite={red}{green}将在所有red节点和所有green节点之间增加边.

更高级的connector, 比如蝴蝶 connector, 允许你以一种花哨的方式在颜色类之间添加边.

\usetikzlibrary {graphs}
\tikz [x=8mm, y=6mm, circle]
\graph [nodes={fill=blue!70}, empty nodes, n=8] {
subgraph I_n [name=A] --[butterfly={level=4}]
subgraph I_n [name=B] --[butterfly={level=2}]
subgraph I_n [name=C] --[butterfly]
subgraph I_n [name=D] --
subgraph I_n [name=E]
};

绘制曲线

To 自由曲线

164 The To Path Operation 838 To Path Library page 841 75.4 Loops

  • 可以使用 To来绘制直线, 也可以用来绘制曲线.
\path ... to[<options>] <nodes> <coordinate or cycle> ...;

例如(a) to [out=135,in=45] (b)

各种选项

/tikz/out=<angle>
/tikz/in=<angle>
...

在给定出射和入射角度之后, /tikz/looseness=<number>选项中的<number>调控control points与初始点以及与终点的距离. 还可以用 /tikz/min distance=<distance>, /tikz/out min distance=<distance> 控制最小距离, 避免计算无解.

  • 使用 loop选项来绘制圈图曲线, 例如
\begin{tikzpicture}
  \begin{feynman}
  \draw  (0,0) edge [anti charged scalar,loop, looseness=30] (h3);
  \end{feynman}
\end{tikzpicture}

loop选项只接受一个参数, 即初始点, 终点位置和初始点相同, 然后把looseness设置为8, min distance设置为5mm. 如果想精确控制圈图的形状, 可以手动添加控制点, 例如:

\draw (a3) to [controls=+(45:1.5) and +(135:1.5)] (a3);

上面使用+(角度:距离)的方式指定控制点的坐标, and左右的坐标采用相对坐标的形式, 分别相对于路径的起点终点.

参考 page 33, 可以使用to的简称, 即..语法以 曲线 方式延伸路径:

.. controls <控制点1> and <控制点2> .. <终点>

你可以省略and <控制点2>, 这将导致使用控制点1两次.

如果要使用tikz-feynan定义的线型, 使用下面的edge, 并使用tikz-feynan定义的全称, 例如:

\draw (a3) edge [controls=+(30:3) and +(150:3), /tikzfeynman/fermion] (a4);

edge

17.12 Connecting Nodes: Using the Edge Operation

edge(边)操作的作用类似于主路径绘制完成后添加的to操作, 就像node是在主路径绘制完成后添加的. 这样, 每条边就可以有不同的外观.

node操作一样, edge的操作会暂时中止当前路径的构建, 并构建一个新的路径p. 这个新的路径p将在主路径绘制完毕后被绘制. 请注意, p可以与主路径的选项完全不同. 还要注意的是, 如果主路径中有几个edgenode操作, 每个操作都会创建自己的路径, 并且按照它们在主路径上出现的顺序来绘制.

\path … edge[<options>] <nodes> (<coordinate>) …;

edge操作的效果是, 在主路径之后, 下面的路径被添加到图片中:

\path[every edge,<options>] (\tikztostart) <path>;

这里, <path>to path. 注意, 与to操作所添加的路径不同, (\tikztostart)被添加到<path>之前, 这对to操作来说是不必要的, 因为这个坐标已经是主路径的一部分.

\tikztostartedge操作之前的路径上的最后一个坐标, 就像对nodeto操作一样. 然而, 这条规则有一个例外: 如果edge操作的前面是node操作, 那么这个刚刚声明的node就是起始坐标. 而不是像通常情况下那样, 是这个节点所处的坐标--一个微妙的区别. 在这方面, edgenodeto都不同.

如果一行有多个edge操作, 那么所有这些操作的起始坐标都是一样的, 但是目标坐标不同, 它们是主路径的一部分. 因此, 起始坐标就是第一个edge操作之前的坐标. 这一点与node类似, edge操作也不会修改当前路径. 特别是, 它不改变最后访问的坐标, 见下面的例子:

\begin{tikzpicture}
\node (a) at (0:1) {$a$};
\node (b) at (90:1) {$b$}     edge    [->]              (a);
\node (c) at (180:1) {$c$}    edge    [->]             (a)
                                                      edge    [<-]             (b);
\node (d) at (270:1) {$d$}  edge    [->]              (a)
                                                     edge    [dotted]     (b)
                                                     edge    [<-]               (c);
\end{tikzpicture}

arc 弧线

159 The Arc Operation

\path ... arc[<options>] ...;

从当前点开始画弧线, 可以用x radius and y radius指定半径, 用start angle, end angle, and delta angle指定角度.

也有一个较简捷的句法来指定圆弧: arc(<start angle>:<end angle>:<radius>) 或者 arc(<start angle>:<end angle>:<x radius> and <y radius>)

node 文字节点

node 指定

tikz-feynman包中的顶点相当于node, node的特点是需要添加文字(可以为空白--{}), 也就是类似下面这种. 在node周围会留下空白, 其实是node占据的空间.

\node[above right =0.7 and 4.2 of a1] {text}

参考 p229, 17.2.2 Predefined Shapes. 如果不想画出node, 只是给坐标分配名称, 例如(x), 并希望传播子可以直接连接(x). 可以使用coordinate. 它的效果类似于使用了(x.center), 不会把路径断开成几段. 可以完整的连接起来, 或者给包围的区域上色.

类似于

\coordinate[right =2.2  of a1] (a3); % 泡泡起点

这样后面不需要有{text}.

p224 基本语法:

\path ... node <foreach statements>  [<options>] (<name>) at (<coordinate>) : <animation attribute>
={<options>} {<node contents>} ...;

各部分规范的顺序. 在node{<node contents>}之间的所有内容都是可选的. 如果有<foreach>语句,它必须首先出现,紧接在node之后. 除此之外,节点规范的所有其他元素( <options> ,name,coordinateanimation attribute )的顺序都是任意的, 实际上,这些元素中的任何一个都可能多次出现(尽管对于namecoordinate,这没有意义). 例如

\vertex (a2) at (0,0){2};

at p158

\path ... circle[<options>] ...;
/tikz/at=<coordinate>

如果在<options>内部显式设置了此选项(或通过every circle样式间接设置), 则<coordinate>将用作圆的中心而不是当前点. 在一个封闭范围内给某个值设置at无效.

page 785,72 Shape Library: 可以指定node的形状, 有预定义的各种形状. page 563, Part V Libraries: 从这里开始是各种库, 有预定义的各种命令.

node位置的相对指定

page 240;

/tikz/below=<specification>
/tikz/left=<specification>
/tikz/right=<specification>
/tikz/below left=<specification>
/tikz/below right=<specification>
/tikz/above left=<specification>

/tikz/above left=<specification>类似above,但是<shifting parti>的指定更加复杂.

  1. <shifting part>形式为<number or dimension> and <number or dimension>的时候(注意中间有个and), 先向左移动, 再向右移动(通常这令人满意, 除非你使用了x and y选项, 修改了xy--坐标系的单位矢量. )
  2. <shifting part>形式为<number or dimension> 时, 也就是只给出一个参数, 向对角线方向(135度方向)移动$\frac{1}{2}\sqrt{2}cm$. 按照数学的说法, 就是按照$l_{2}-norm$理解, 相当于极坐标中的半径. 而<number or dimension> and <number or dimension>是按照$l_{1}-norm$理解.

node 引用外域node

page 260; 17.13 Referencing Nodes Outside the Current Picture

引用不同图片中的节点

可以在图片A中引用图片B中的node( 但不是很trivial) . 这意味着你可以创建图片和一个节点, 稍后你可以从其他位置画一条线(edge)到这个节点.

要引用不同图片中的节点, 请按以下步骤操作.

  1. 你需要在所有包含目标节点的图片上添加remember picture选项, 同时也要在所有包含起始节点的图片上添加remember picture选项.
  2. 您需要为路径或整个图片添加overlay选项, 这些图片包含对域外节点的引用. (这个选项可以关闭bounding box的计算) .
  3. 你需要使用一个支持remember picture驱动程序, pdfLaTeX是支持的, 并且你需要运行TeX两次. 关于幕后操作的更多细节, 见第107.3.2节. 让我们来看看这些选项的效果.
/tikz/remember picture=<boolean> 无默认, 初始值为 false

这个选项告诉TikZ, 它应该尝试记住当前图片在页面上的位置. 这种尝试可能会失败, 这取决于使用的是哪种后端驱动程序. 另外, 即使记忆成功, 这个位置可能也只在第二次运行TeX程序才能用. 如果可以记忆的话, 你可以考虑

\tikzset{every picture/.append style={remember picture}}

来使TikZ记住所有图片. 这将在.aux文件中为每张图片添加一行记录--通常不会很多.

然后, 你就不必担心记忆图片的问题了.

/tikz/overlay=<boolean> 默认为 true

这个选项主要是为了在引用其他图片中的节点时使用, 但你也可以在其他情况下使用它在其他情况下使用. 这个选项的作用是, 在计算当前图片的边界框时, 不会考虑到当前scope范围内的所有对象.

你需要在所有包含域外节点引用的路径( 或者至少是路径的所有部分) 指定这个选项. 否则, TikZ会试图使当前的图片足够大, 以涵盖其他图片中的节点. 然而, 在TeX的第二次运行中, 这将创建一个更大的图片, 导致图片越来越大. 除非你知道自己在做什么, 否则我建议在所有包含域外引用的图片上指定overlay选项.

现在让我们看几个例子. 这些例子只有在用支持remember picture的驱动程序处理文档时才有效.

在当前文本中, 我们放置两张图片, 包含名为n1n2的节点,

\tikz[remember picture] \node[circle,fill=red!50] (n1) {};
\tikz[remember picture] \node[fill=blue!50] (n2) {};

为了连接这些节点, 我们使用overlayremember picture 选项创建另一张图片.

\begin{tikzpicture}[remember picture,overlay]
\draw[->,very thick] (n1) -- (n2);
\end{tikzpicture}

注意, 最后一张图片似乎是空的. 实际上它的大小为零, 并且包含在它边界之外的箭头. 最后一个例子, 我们将另一张图片中的节点连接到前两个节点. 在这里, 我们只给连线提供了overlay选项, 我们不希望把这条线算作图片的一部分.

\begin{tikzpicture}[remember picture]
  \node (c) [circle,draw] {Big circle};
  \draw[overlay,->,very thick,red,opacity=.5] (c) to [bend left]  (n1)  (n1) -| (n2);
\end{tikzpicture}

引用当前页节点--绝对定位

有一个特殊的节点叫做current page, 可以用来访问当前页. 它是一个长方形的节点, 其south west锚点是页面的左下角, north east锚点是页面的右上角.

这个节点在内部是以特殊的方式处理的, 你可以引用它, 就像它已经被记忆过一样. 因此, 通过给图片增加remember pictureoverlay选项, 你可以在页面上绝对定位节点.

第一个例子将文本放在当前页面的左下角.

\begin{tikzpicture}[remember picture]
  \node[xshift=1cm,yshift=1cm] at (current page.south west)
              [text width=7cm, fill=red!20,rounded corners,above right]
              {
                This is an absolutely positioned text in the lower left corner. No shipout-hackery is used.
              }
\end{tikzpicture}

下一个例子在页面中间添加圆圈.

\begin{tikzpicture}[remember picture]
  \draw [line width=1mm,opacity=.25]
  (current page.center) circle (3cm);
\end{tikzpicture}

最后一个例子在页面上方叠加文字( 取决于例子的位置, 也可能出现在下一页).

\begin{tikzpicture}[remember picture,overlay]
\node [rotate=60,scale=10,text opacity=0.2] at (current page.center) {Example};
\end{tikzpicture}

Late 代码和Late 选项

给节点提供的所有选项只影响本节点自己. 虽然这在大多数情况下是件好事, 但你有时可能想让选项在 以后产生影响. 反过来说, 你有时可能会注意到一些选项只应该在后期被添加到节点上. 于此,可以使用下面这个版本的node路径命令:

\path … node also[<late options>](<name>) …;

请注意, <name>是强制性的, 不能在这里给出节点文本. 另外, 选项和节点标签的顺序必须如上.

节点<name>必须已经存在. <late options>是在局域scope中执行的. 这些选项中的大多数不会有任何影响, 因为你不能改变节点的外观. 也就是说, 你不能用late选项把一个红色节点变成绿色节点. 然而, 在<late options>选项里面给出append after commandprefix after command选项 (直接或间接地)确实能达到预期的效果. 给定的路径被执行, \tikzlastnode 被设置为 determined node.

所有这些的净效果是, 例如, 你可以提供label选项, 为已被创建的节点添加标签:

\begin{tikzpicture}
\node [draw,circle] (a) {Hello};
\node also [label=above:world] (a);
\end{tikzpicture}

正如 Section 14 所解释的, 你可以使用选项append after commandprefix after command在节点后添加路径. 下面的宏可能很有用.

  • \tikzlastnode; 展开为路径上的最后一个节点. 你也可以用下面的选项来代替node also语法.
/tikz/late options=<options> (无默认值)

这个选项可以在路径上给出( 但不能作为节点命令的参数) , 其效果与node alos命令相同. 在<options>中, 你应该使用name选项来指定你希望添加late option的节点

\begin{tikzpicture}
\node [draw,circle] (a) {Hello};
\path [late options={name=a, label=above:world}];
\end{tikzpicture}

颜色

透明度

page 353: 23 Transparency

常用透明度选项:

/tikz/draw opacity=<value>
/tikz/fill opacity=<value>

<value>除了数字取值[0,1]之外, 还可以下列预设:

/tikz/transparent
/tikz/ultra nearly transparent
/tikz/very nearly transparent
/tikz/nearly transparent
/tikz/semitransparent
/tikz/nearly opaque
/tikz/very nearly opaque
/tikz/ultra nearly opaque
/tikz/opaque

Graph Drawing

p416 ; 27 Introduction to Algorithmic Graph Drawing

算法图形绘制(Algorithmic graph drawing)( 或以下简称为图形绘制) 是以算法计算图形节点在页面上的位置的过程, 以便使图形 看起来漂亮. 它的想法是, 作为人类用户( 或者你碰巧是一台机器, 并且碰巧在阅读这篇文档) , 只需要指定哪些节点, 以及哪些连线存在于图中. 此外, 你可以添加一些 提示, 比如 这个节点应该在中心附近这条边很重要. 你并不指定节点和边的确切位置, 这是留给图形绘制算法的事情(graph drawing algorithm). 该算法将你对图形的描述作为输入, 然后决定节点应该放在页面何处.

自然, 画图是一门(黑色的)艺术. 确切地说, 没有 完美的方法来绘制图形.

与纯粹用TEX实现的pgfTikZ的其他部分不同, 图形绘制算法太过复杂, 无法直接用TEX实现. 与pgfTikZ的其他部分不同的是, 图形绘制算法过于复杂, 无法直接在TEX中实现. 相反, 编程语言Lua被用于 图形绘制库所使用的编程语言--这种编程语言已被集成到最近的TEX版本中. 这意味着:

  • 作为图形绘制引擎的用户, 你可以在你的文档中以通常的方式运行TEX, 而不需要调用外部程序.
  • 非常容易为Tikz实现新的会图算法, 因为可以使用Lua, 而不需要TeX的编程知识.

使用 Graph Drawing 系统

通常, 图形绘制引擎的 用户 只需在他们的图片上添加一个选项, 即可调用图形绘制算法. 这是一个典型的例子, 其中layered layout选项告诉TikZ 图应该使用所谓的layered graph drawing algorithm 来绘制(来摊开)(这些都将在后面解释). )

在所有的例子中, 节点的位置, 只有当所有的节点都被创建, 并指定了之后才被计算. 例如, 在最后一个例子中, 如果没有选项 spring electrical layout, 所有的节点都会被放在彼此的上面.

Graph Drawing 系统的层次

尽管下面几节介绍的图形绘制系统是作为pgf的一部分开发的, 但它可以独立于pgfTikZ使用. 它可以被任意的程序使用, 只要后者可以运行Lua. 为了实现这一点, 图形绘制系统由三层组成.

  1. 底部 我们有algorithmic layer. 这一层是用Lua写的, 包含了所有的图形绘制算法. 有趣的是, 选项也必须在这一层声明, 所以一个算法连同它使用的所有选项可以而且必须完全在这一层指定. 如果你打算实现一个新的图形绘制算法, 你将只对这一层的功能感兴趣. 算法通过一个定义明确的接口与图形绘制系统 交流, 该接口封装在InterfaceToAlgorithms类中.

  2. 顶部我们有显示层. 这个层实际上不是图形绘制系统的一部分. 相反, 它是一个 显示 图形的软件, TikZ只是这种软件的一个例子. 另一个例子是一个图形编辑器, 它使用图形绘制系统来布置图形它的子图. 还有比如命令行工具, 用于绘制文件中描述的图形. 最后, 你也可能希望使用图形绘制系统作为子程序来渲染在更大的程序中产生的图形.

由于显示层的不同实现可能是相当异质的, 所有的显示层必须通过一个特殊的接口与图形绘制系统进行通信, 该接口被封装在类InterfaceToDisplay中. 这个类的主要工作是提供一组方法, 用于指定一个图形有某些节点, 并且为它们设置某些选项. 然而, 这个接口也允许你查询所有被算法声明的选项, 包括它们的文档. 这样一来, 编辑器或命令行工具可以列出所有图形绘制算法, 以及它们应该如何配置.

  1. 算法层显示层通过绑定层 "绑定" 在一起. 关于待画图形的大部分bookkeeping(记录)工作是由图形绘制系统完成的, 与使用的算法显示层无关. 但有些事情仍然是针对具体显示层的. 例如, 一些算法可能会创建新的节点, 而这些算法可能需要知道这些节点有多大. 为此, 在算法运行期间, 显示层必须被 查询, 而实现这一回调(callback)是绑定层的工作. 通常情况下, 绑定层实现了从图形绘制系统显示层后向 通信. 而显示层的接口类只提供了从显示层调用的函数, 但它们不会 talk back.

所有与图形绘制有关的文件都位于generic/pgfgraphdrawing子目录下.

28 在TikZ中使用 Graph Drawing

\usetikzlibrary{graphdrawing} % LATEX and plainTEX
\usetikzlibrary[graphdrawing] % ConTEXt

这个包提供了自动绘制图形的功能. 它要求文件使用LuaTeX排版. 这个包应该在LuaTEX 0.54或更高版本中使用.

选择布局和库

当你加载graphdrawing库时, 图形绘制引擎被初始化. 这个库提供了图形绘制的基本框架, 包括本节中描述的所有选项. 然而这个库并没有加载任何实际的图形绘制算法. 为此, 你需要使用以下命令, 它是由graphdrawing库定义的.

\usegdlibrary{<list of libraries>}

该命令用于加载特殊的图形绘制库(命令名称中的gd代表 graph drawing). <list of libraries>是一个用逗号分隔的列表, 其中包括用Lua编程语言编写的库. (这就是为什么需要一个特殊的命令).

详细来说, 这个命令做了以下工作. 对于<list of libraries>中的每一个<name>:

  1. 检查LuaTEX是否可以对库文件pgf.gd.<name>.library调用require. LuaTEX的文件搜索机制将以通常的方式搜索texmf-trees, 文件名中的被转换成目录斜线.
  2. 如果上述方法失败, 尝试require字符串pgf.gd.<name>.
  3. 如果失败, 尝试require字符串<name>.library.
  4. 如果失败, 尝试要求字符串<name>. 如果这也失败, 打印一条错误信息.

上述情况的纯粹作用如下. 图形绘制算法的作者可以将多种算法捆绑在一起, 通过创建一个 ...xyz/library.lua 文件, 该文件内部对所有包含声明的文件调用require . 另一方面, 如果一个图形绘制算法完全适合存放在一个文件中, 也可以直接使用 \usegdlibrary 读取.

\usetikzlibrary{graphdrawing}
\usegdlibrary{trees,force}

不同的图形绘制库在下面的第3035节中有记录.

注意, 除了图形绘制库之外, 你可能还希望加载普通的TikZgraphs. 它提供了强大的graph path 命令, 以其易于使用的语法来指定图形. 但你可以独立于graphs使用图形绘制引擎, 例如结合childedge语法使用. 下面是典型的设置:

\usetikzlibrary{graphs, graphdrawing}
\usegdlibrary{trees, layered}

设置好之后, 你必须指定图形绘制引擎使用哪种布局算法, 应用到哪个scope中的节点. 通常情况下, 你只需在graphpath 操作中添加一个以... layout结尾的选项即可, 然后让图形绘制完成它的魔法

\usetikzlibrary {graphs,graphdrawing} \usegdlibrary {layered}
\tikz [rounded corners]
\graph [layered layout, sibling distance=8mm, level distance=8mm]
{
  a -> {  b,  c -> { d, e }
  } ->  f ->  a};

每当你使用这样的布局选项时, 你可以:

  • 以通常的方式创建节点. 节点将被完全创建, 但会被藏在一个内部表格中. 这意味着可以使用所有TikZ中对节点的选项. 你还可以为一个节点命名并在以后引用它.
  • 使用graph命令的语法(使用--, <-, -><->)创建edges, 或者使用edge命令, 或使用child命令. 然而, 这些边不会立即被创建. 相反, 基础层的命令\pgfgdedge将被调用, 它存储了 所有关于边缘的信息. edges的实际绘制只有在所有节点都被定位后才会发生.
  • 大多数可以传递给edge都会像预期的那样工作. 特别是, 你可以使用通常的node语法将标签添加到边上.
  • lablepin选项可以以通常的方式用于图形绘制 scope内的节点. 只是, lablepin在节点的定位中不会起任何作用, 它们是在节点最终被定位后加入.
  • 同样, 可以使用通常的隐式定位语法, 将节点放置在edge上.

下面是一些不会工作的东西.

  • 只有使用graph语法, edge命令或child命令创建的edges才能正确地将它们的连接信息传递给基础层. 当你在图形绘制scope内写下 \draw (a)--(b);时, 其中 ab 是在该scope内创建的节点, 你会得到一个错误消息/结果会看起来不对. 原因是通常的--不会被图形绘制引擎捕捉到, 因此, --试图立即连接两个不存在的节点(除了在一些内部表中).
  • edges的选项被执行两次: 一次是当edge\pgfgdedge 命令 "检查 "时(使用一些魔法来防止副作用), 然后在edge被实际创建时再执行一次. 幸运的是, 在几乎所有的情况下, 这不会是一个问题;但是如果你在你的edge选项里施展了邪恶的魔法, 你必须祈求佛祖保佑了. (不要整烂活, 顺便说一下) 如果你对细节感兴趣, 请看第29节.

key 管理

key 简述

p975. key 就是tikz中的各种关键字, 它们是通过包pgfkeys管理的. \usepackage{pgfkeys}.

key的例子: /tikz/coordinate system/x, 或者只用写/x. 这种写法类似于Unix上的文件路径. 可以使用绝对路径, 也可以使用相对路径. tikzkey名称经常包含空格. 调用key使用\pgfkeys命令. pgfkeys接受key=value形式的参数. 例如:

\pgfkeys{/my key=hallo, /your keys=something\strange, others=something else}

可以在key中存储一些code, 通常用handler来管理key的内容. 例如:

\pgfkeys{/my key/.code=The value is '#1'.}
\pgfkeys{/my key=hi!}

有许多handler可以用来定义key. 例如, 我们可以定义一个具有多个参数的key:

\pgfkeys{/my key/.code 2 args=The values are '#1' and '#2'.}
\pgfkeys{/my key={a1}{a2}}

常用handler有:

  • .default:提供默认值.
  • /.value required:必须指定参数.
  • /.value forbidden:不能指定参数.

tikz的所有key都以/tikz开头. 然而不必每次都显式加上这个前缀, 可以使用/.cd来声明默认目录.

\pgfkeys{/tikz/.cd,line width=1cm, linecap=round}

当处理一个key时, 除了直接执行某些代码, 也可以递归调用另一些keys, 这种key被称为styles. styles本质上就是key的列表. 例如:

\pgfkeys{/a/.code=(a:#1)}
\pgfkeys{/b/.code=(b:#1)}
\pgfkeys{/my style/.style={/a=foo,/b=bar,/a=#1}}
\pgfkeys{/my style=wow}

.styles也可以带有参数#1, 如同普通的.code.

The Key Tree

p975, key的组织方式类似Unix. /a/b.../x中, 前面的/a/b.../路径, 后面的x名称. pgfkeys包中有一些内部命令, 如\pgfkeyssetvalue, \pgfkeyslet等等. 但是通常用户不需要直接调用这些命令.

简要提一下\def, \let,\edef三个命令: What is the difference between \let and \def \let相当于直接赋值, 右边的式子计算之后赋给左边. \def命令相当于mma中的SetDelay:=, 会在被调用时重新计算. \edefexpand def的缩写, 也就是赋值之前右边的式子会被展开.

Key Handlers

handler相当于key的方法, 或者构造函数之类的. p984, 常用的 key handler:

  • <key>/.cd: 设置默认路径为key.
  • <key>/.is family: 当使用key时, 当前路径被设置为key,效果同<key>/.cd.
  • <key>/.default=<值> :设置key的默认值. 例如\pgfkeys{/width/.default=1cm}
  • <key>/.value required: 表明必须赋值.
  • <key>/.value forbidden: 表明禁止赋值.
  • <key>/.code=<代码>: 添加代码, 可以有一个参数#1
  • <key>/.code 2 args=<代码>:表示有两个参数{#1}{#2}, 如果第二个未给出, 将会是空字符. 如果传入first, 第一个参数将是f, 第二个将是irst, 所以需要用{}包裹起来.
  • <key>/.code n args={<m>}{<代码>}:传入m个参数{#1}{#2}{#3}..., m0~9, 不能多也不能少, 空格也可以作为参数.
  • <key>/.code args={<参数模式>}{<代码>}:可以指定任意的参数模式, 比如(#1/#2),调用对应的写成(first/second), 空格将被去掉.
  • <key>/.add code={<前缀代码>}{<后缀代码>}: 将代码添加到已经存在的key.
  • <key>/.prefix code=<前缀代码>: 类似.add code, 但只添加前缀.
  • <key>/.append code=<后缀代码>: 类似.add code, 但只添加后缀.

其中 .code 2 args.code args的区别见下面的例子:

\pgfkeys{/page size/.code 2 args={\paperheight=#2\paperwidth=#1}}
\pgfkeys{/page size={30cm}{20cm}}
% 同样的定义, 使用 .code args
\pgfkeys{/page size/.code args={#1 and #2}{\paperheight=#2\paperwidth=#1}}
\pgfkeys{/page size=30cm and 20cm}

Defining Styles

.stylekey的列表, 它的handlerkey基本相同, 只需要作替换.code->.style