之前有朋友问我如何使用$\LaTeX$绘图.

之前我写文章通常是使用绘图工具先保存为图片格式, 然后再插入文章的. 了解了下发现Latex可以使用TikZ宏包来进行绘制, 功能很强大, 函数图像、统计图、各类流程图都不在话下, 甚至是复杂信号流图如FFT的蝶形图也能简单地画出来, 这里放个绘制实例供参考.

TikZ一般与PGF宏包一起使用, 两者是上层封装与底层实现的关系. 学习的途径无疑是查阅参考文档与模仿例子, 在这里可以下载到TikZ与PGF完整参考文档, 而在这里能看到丰富的应用, 另外在一些问答社区如这里可以找到一些实用的使用技巧.

TikZ的参考文档虽然有1000多页, 但对于简单应用, 参看前面几个应用的Tutorial就够了. 但如果你是个完美主义者或是有强迫症, 那么这又会是一个天坑!TikZ是一个复杂的“所思即所得”的绘制系统, 相对于同样强大的Visio来说.

下面是一个如何绘制流程图的例子. 完成后大概是这个样子:

首先需要加载TikZ宏包以及绘图需要用到的库.

1
2
3
4
%------------------------------------- TIKZ --------------------
\usepackage{tikz}%画图
\usetikzlibrary{graphs,arrows,shapes,chains,quotes}
%------------------------------------- TIKZ END-----------------

然后实际的绘图是在tikzpicture环境中进行的, 就像figure环境一样.

1
2
3
4
5
\begin{tikzpicture}[环境及元素设定]
...
绘图语句
...
\end{tikzpicture}

绘制流程图的过程大概是这样的: 首先你需要先设定一些元素及基本的环境, 比如矩形框、菱形框、各类符号等等;然后是在图中定位他们, 定位的方法有很多, 由于我们要化的图像比较规则, 这里使用\matrix的定位方法;最后是连接与标注, 同样有很多方法, 怎么方便怎么来就行了.

元素及环境的设定写在[]中实现起来大概是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
\begin{tikzpicture}[
%inner sep=2mm, %边框到形状内字符距离
> = stealth, %箭头样式
-> /.style={thick ,black},
-- /.style={thick ,black},
line width=1pt,%连接线宽
every edge quotes/.style={font=\tiny,auto}, %标注样式
every node/.style={font=\tiny},
point/.style={inner sep=0pt}, %point样式
terminal/.style={circle,draw,thin,inner sep=0pt}, %terminal样式
rect/.style={rectangle,draw,thick,inner sep=0pt,minimum size=6mm,font=\itshape}, %rect样式
plus/.style={circle,draw,thick,inner sep=-1pt}, %plus样式
hv path/.style={to path={-| (\tikztotarget)}}, %直角折线样式
vh path/.style={to path={|- (\tikztotarget)}}
]
...
元素定位语句
连接与标注语句
...
\end{tikzpicture}

然后在“元素定位语句”处使用\matrix进行排布元素, 使用matrix的语法和\begin{aligned}类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
%流程图骨架
% t1 p2 r1 r2 pl1 t2
% r3 r4
% p4 r5 r6 p5

\matrix[row sep=5mm,column sep=10mm]{
\node (t1) [terminal] {}; &
\node (p2) [point] {}; &
\node (r1) [rect] {$\downarrow 2$}; & &
\node (r2) [rect] {$\uparrow 2$}; &
\node (pl1) [plus] {$+$}; &
\node (t2) [terminal] {}; & \\
%------------------------------------------
& \node (r3) [rect] {左移1}; & & & & \node (r4) [rect] {右移1}; \\
%------------------------------------------
& \node (p4) [point] {}; &
\node (r5) [rect] {$\downarrow 2$}; & &
\node (r6) [rect] {$\uparrow 2$}; &
\node (p5) [point] {}; & \\
};

连接与标注语句
...

此时看起来应该是这个效果:

其中的\node是非常常用的命令, 可以用于放置元素与标签. ()中为node别名(在其他地方可以使用别名调用相应元素);[]中为node属性, 可以设定之前定义的各种元素样式;{}中为node内部的字符串, 比如某个被定义为矩形框的node中的字符串, 支持使用Latex的数学公式的插入. 更详细的说明参考文档.

流程图中的连接与标注往往是同时进行的. 标注分为两种, 一种是在连接线上, 另一种是需要定位到某个固定的点. 连接这里使用\graph命令, 这里需要两条通路, 顺便可以在quotes库的帮助下完成线上标注.

1
2
3
4
5
6
7
8
9
10
11
12
\graph[use existing nodes]{
t1 ->
r1 ->["$g[n]=x[2n]$","$G(e^{j\omega})$"']
r2 ->
pl1 -> t2;

t1 ->[hv path]
r3 ->[vh path]
r5 ->["$h[n]=x[2n+1]$","$H(e^{j\omega})$"']
r6 ->[hv path]
r4 ->["$x[x-1]$" right] pl1;
};

特殊点的标注使用\node:

1
2
3
4
\node [above] at (t1) {$x[n]$};
\node [above] at (t2) {$x[n]$};
\node [below] at (p4) {$x[n+1]$};
\node [below] at (p5) {$0\leqslant n\leqslant N-1$};

呼, 这样就完成了.

在TizK的帮助下能轻松绘出FFT的蝶形流图, 我从这个例子中得到了帮助. 下面是完整16点FFT蝶形图的实现code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{arrows,graphs,decorations.markings}

\begin{document}
\pagestyle{empty}

% 辅助计数器
\newcounter{x}
\newcounter{y}

\begin{tikzpicture}[
yscale=.5,
xscale=1.5,
%node distance=1cm,
auto,
> = stealth,
-> /.style={thick, black},
-- /.style={thick, black},
->-/.style={decoration={
markings,
mark=at position #1 with {\arrow{>}}},
postaction={decorate}},
n/.style={
circle,
draw,
minimum size=2pt,
inner sep=0pt,
outer sep=0pt},
]
% The strategy is to create nodes with names: N-column-row -> N-列-行

%----------------------- 输入输出 -------------------------------------------------------
% 输入结点命名规则 N-0-0 ... N-0-15
% 输出结点命名规则 N-8-0 ... N-8-15
% Draw inputs
\foreach \y in {0,...,15}
\node[n,pin={[pin edge={<-}]left:$x[\y]$}]
(N-0-\y) at (0,-\y) {};

% Draw outputs \y计数 idx显示
\foreach \y / \idx in {0/0,1/8,2/4,3/12,4/2,5/10,6,7/14,
8/1,9,10/5,11/13,12/3,13/11,14/7,15}
\node[n, pin={[pin edge={->}]right:$X[\idx]$}] (N-8-\y) at (6,-\y) {};

%-------------------------- 画连接点 ---------------------------------------------------
% 0 1 2 3 4 5 6 7 8
% in o - o o - o o - o - o out

% 7个中间连接点
\foreach \y in {0,...,15}
\foreach \x / \c in {1/1, 1.5/2, 2.5/3, 3/4, 4/5, 4.5/6, 5.5/7}
\node[n] (N-\c-\y) at (\x,-\y) {};

%----------------------- 画连接线 -----------------------------------------------------
% 水平线连接

\foreach \y in {0,...,15}%行
\foreach \x in {0,2,4,6}%列
{
\setcounter{x}{\x}\stepcounter{x}
\path (N-\x-\y) edge[->-=.87] (N-\arabic{x}-\y);
}

\foreach \y in {0,...,15}%行
\foreach \x in {1,3,5,7}%列
{
\setcounter{x}{\x}\stepcounter{x}
\path (N-\x-\y) edge[->-=.5] (N-\arabic{x}-\y);
}

% 斜线连接
\foreach \sourcey / \desty in {0/8,1/9,2/10,3/11,
4/12,5/13,6/14,7/15,
8/0,9/1,10/2,11/3,
12/4,13/5,14/6,15/7}
\path (N-0-\sourcey) edge[->-=.04] (N-1-\desty);


\foreach \sourcey / \desty in {0/4,1/5,2/6,3/7,
4/0,5/1,6/2,7/3,
8/12,9/13,10/14,11/15,
12/8,13/9,14/10,15/11}
\path (N-2-\sourcey) edge[->-=.97] (N-3-\desty);


\foreach \sourcey / \desty in {0/2,1/3,2/0,3/1,
4/6,5/7,6/4,7/5,
8/10,9/11,10/8,11/9,
12/14,13/15,14/12,15/13}
\path (N-4-\sourcey) edge[->-=.95] (N-5-\desty);

\foreach \sourcey / \desty in {0/1,1/0,2/3,3/2,
4/5,5/4,6/7,7/6,
8/9,9/8,10/11,11/10,
12/13,13/12,14/15,15/14}
\path (N-6-\sourcey) edge[->-=.3] (N-7-\desty);


%----------------------- 标注旋转因子 -----------------------------------------------

% W_16
\setcounter{y}{8}
\foreach \i in {0,...,7}
{
\path (N-1-\arabic{y}) edge[->-=.5] node {\tiny $W^{\i}_{16}$} (N-2-\arabic{y});
\node[below] at (N-1-\arabic{y}.south) {\tiny -1};
\stepcounter{y}
}

% W_8
\setcounter{y}{4}
\foreach \j in {1,2}
{
\foreach \i in {0,2,4,6}
{
\path (N-3-\arabic{y}) edge[->-=.5] node {\tiny $W^{\i}_{16}$} (N-4-\arabic{y});
\node[below] at (N-3-\arabic{y}.south) {\tiny -1};
\stepcounter{y}
}
\addtocounter{y}{4}
}

% W_4
\setcounter{y}{2}
\foreach \j in {1,...,4}
{
\foreach \i in {0,4}
{
\path (N-5-\arabic{y}) edge[->-=.5] node {\tiny $W^{\i}_{16}$} (N-6-\arabic{y});
\node[below] at (N-5-\arabic{y}.south) {\tiny -1};
\stepcounter{y}
}
\addtocounter{y}{2}
}

% W_2
\setcounter{y}{1}
\foreach \j in {1,...,8}
{
\foreach \i in {0}
{
\path (N-7-\arabic{y}) edge[->-=.5] node {\tiny $W^{\i}_{16}$} (N-8-\arabic{y});
\node[below] at (N-7-\arabic{y}.south) {\tiny -1};
\stepcounter{y}
}
\addtocounter{y}{1}
}

\end{tikzpicture}
\end{document}