本文已加入專欄文章目錄,歸入「示例」文章系列。
流程圖,或者類似流程圖的繪圖目标,主要指具有以下特征的圖形組合:
- 多個帶框文本,框的形狀、框之間的位置關系有規律
- 多條帶箭頭的線,線連接配接框
(這裡是想說,在具體的使用場景和學科背景下,圖形們各有各的叫法和名字,但從繪圖的角度,它們和本文所介紹的「流程圖」有很大的共性。)
以下,在無明顯歧義時,我們用 node 代指「帶框文本」,用 arrow 代指「帶箭頭的線」。
在 tikz 文檔的 Tutorials 中,有
- Sec. 3 Tutorial: A Petri-Net for Hagen 和
- Sec. 5 Tutorial: Diagrams as Simple Graphs
兩個流程圖繪制的例子。本文提及的大部分内容,都能在這兩個官方文檔示例中找到介紹。
引入例子
一個簡陋的例子
documentclass{article}
usepackage{tikz}
usetikzlibrary{positioning, shapes.geometric}
begin{document}
begin{tikzpicture}[node distance=10pt]
node[draw, rounded corners] (start) {Start};
node[draw, below=of start] (step 1) {Step 1};
node[draw, below=of step 1] (step 2) {Step 2};
node[draw, diamond, aspect=2, below=of step 2] (choice) {Choice};
node[draw, right=30pt of choice] (step x) {Step X};
node[draw, rounded corners, below=20pt of choice] (end) {End};
draw[->] (start) -- (step 1);
draw[->] (step 1) -- (step 2);
draw[->] (step 2) -- (choice);
draw[->] (choice) -- node[left] {Yes} (end);
draw[->] (choice) -- node[above] {No} (step x);
draw[->] (step x) -- (step x|-step 2) -> (step 2);
end{tikzpicture}
end{document}
上述例子中,故意略去了
- 關于 node 的設定,如外框形狀
- 關于 arrow 的設定,例如線的樣式、箭頭的樣式等
- 關于顔色和字型的設定
這些都可以通過定義 tikz style 來解決。
上述例子中,值得關注的是這些:
- 少用、甚至不用具體坐标
- 第一個 node 省略了坐标,相當于指定了
(0, 0)
- 從第二個 node 開始,利用 tikz library
提供的選項(例如positioning
), 指定相對位置關系。below=of <node name>
- 效果是,每組 node,外框之間的(最小)距離都相同,是
指定的node distance
;在需要手動調整距離時,可以使用10pt
。right=<dimen> of <node name>
- 第一個 node 省略了坐标,相當于指定了
- 繪制 arrow 時,完全使用 node name
- 在繪制 arrow 時一并輸入 arrow 周圍的文字标注
配合設定好的 tikz style,這已經是一個功能完整、可以接受的流程圖繪制方式了。這種繪制方式,在 tikz 文檔 Sec. 3 和 Sec. 5 裡也能見到,但文檔中的例子沒有止步于此。
簡化 arrow 的輸入
例子中,繪制 arrow 用了一半的輸入行,而我們需要的不過是一串首尾相接的 arrow,例如
(start) -> (step 1) -> (step 2) -> (choice) -> (end)
。
graph
指令就接受這樣的輸入。(輸出效果同上,不重複貼圖)
documentclass{article}
usepackage{tikz}
usetikzlibrary{graphs, positioning, quotes, shapes.geometric}
begin{document}
begin{tikzpicture}[node distance=10pt]
node[draw, rounded corners] (start) {Start};
node[draw, below=of start] (step 1) {Step 1};
node[draw, below=of step 1] (step 2) {Step 2};
node[draw, diamond, aspect=2, below=of step 2] (choice) {Choice};
node[draw, right=30pt of choice] (step x) {Step X};
node[draw, rounded corners, below=20pt of choice] (end) {End};
graph{
(start) -> (step 1) -> (step 2) -> (choice) ->["Yes"left] (end);
(choice) ->["No"] (step x) ->[to path={|- (tikztotarget)}] (step 2);
};
end{tikzpicture}
end{document}
注意到
- 新加載了兩個 tikz library
- library
,用于提供graph
指令graph
- library
,将 node label 的輸入從quotes
簡化為label={[<options>]<text>}
"<text>"<options>
- library
- 雖然
的文法大大簡化了輸入,但是标記 node label 和繪制非簡單 arrow 變麻煩了,帶來了學習成本(start) -> (step 1) -> (step 2) -> (choice) -> (end)
- 如果使用了選項
,輸入use existing nodes=true
能進一步簡化為(start) -> (step 1) -> (step 2)
。start -> step 1 -> step 2
tikz 文檔 Sec. 5.4.1 就介紹了這種繪制方式:
- 帶框文字用
結合 tikz librarynode
positioning
- 帶箭頭的線用
結合 tikz librarygraph
quotes
其他可選的方案
- 帶框文字用
結合 tikz librarynode
,帶箭頭的線用操作符 edge。positioning
- 這是 tikz 文檔 Sec.3 的最終狀态
- 優勢:代碼結構清晰,額外學習成本低
- 劣勢:每條線對應一個 edge,線多時仍然麻煩
- 用
同時處理兩件事。graph
- 這是 tikz 文檔 Sec. 5 的最終狀态
- 優勢:線的标記十分輕量
- 劣勢:
- 一定的額外學習成本,包括;
- 兩件事同時做,代碼可讀性降低;
- 圖的生長方向由代碼自動決定,如需 100% 控制圖的最終效果,手動調整不可少
- 用
處理框的相對位置matrix
- 這是 tikz 文檔 Sec. 5.3 的狀态
- 優勢:配合 tikz library
的matrix
選項,在某些情況下能大幅簡化 node 的輸入matrix of nodes
具體例子 1, matrix
配合 tikz library chains
matrix
chains
注意,
- 示例隻是說明「在這種情況下,可以這麼做」,不傳遞類似「這是最佳操作」的資訊。
- 實際使用中,應結合圖形特征和自己的 tikz 知識,決定使用的輸入方式。
- 繪圖的首要目的是「能畫出來」,資源允許時兼顧輸入的優雅。
忽略 tikz style 的定義,隻看例 1 中 tikzpicture 環境内容的輸入方式和輸出效果。
begin{tikzpicture}
%% upper nodes
matrix (m1) [upper matrix] {
|[box 1=01000000]| sentry &
|[box 1=01000001]| 2 &
|[box 1=01001001]| 18 &
|[box 2=11000000]| sentry &
|[box 2=11000001]| 3 &
|[box 2=11010001]| 11 &
|[box 2=11011001]| 27
};
%% chain
begin{scope}[start chain]
foreach i in {1, ..., 7} {
chainin (m1-1-i);
}
end{scope}
%% lower nodes
matrix (m2) [at=(m1-1-3), lower matrix] {
0 & 1 & 2 & 3 & 4 & 5 & 6 & 7
};
%% curving arrows
draw[loose arrow=0.6, out=120, in=-50] (m2-1-3.north) to (m1-1-1.south);
draw[arrow, out=90, in=-105] (m2-1-4.north) to (m1-1-4.south);
end{tikzpicture}
解釋說明:
- 上下分成兩個
進行輸入,matrix
- 上方
的特征是,每個 node 頂部都有 node label,這裡以選項參數的形式輸入 node label textmatrix
- matrix of nodes 模式下,每個 node 的參數可通過
輸入|[<options>]|
- 上方
- 帶箭頭的線可分為兩類
- 連接配接(上方
)相鄰 node 的直線。因為 matrix 中的 node name 有規律,這裡借助 tikz librarymatrix
,以循環的方式适當簡化輸入chains
- 連接配接上下方 node 的曲線。
- 每條曲線對應一根三次貝塞爾曲線。
- 這裡通過選項
和in
控制曲線出發和到達時的角度,再通過選項out
控制曲線的「彎曲程度」。looseness
- 這三個選項的綜合效果,和直接調整貝塞爾曲線的控制點不等價;在額外使用選項
和in control
後,能等價。out control
- 連接配接(上方
例 1 有一個變體。主要變化是,在下方
matrix
中通過使用
set color=<color>
改變之後所有 nodes 的填充色,适當簡化下方
matrix
的輸入。
%% lower nodes
matrix (m2) [below right=30pt and -10pt of m1-1-3, lower matrix] {
0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 &
|[set color=DodgerBlue3]| 10 & 11 & 12 & 13 & 14 & 15
};
最後,看一眼例 1 的 tikz style 部分
% PassOptionsToPackage{x11names}{xcolor}
usetikzlibrary{
arrows.meta,
chains,
matrix,
positioning,
}
makeatletter
[email protected]{white}
tikzset{
set color/.code = { [email protected]{#1} },
%% node
box/.style = {
draw,
rounded corners=2mm,
minimum width=4.5em,
minimum height=2em,
font=color{white}ttfamily,
text height=1.5ex,
text depth=.25ex,
label={[black]above:#1},
},
box 1/.style = {box=#1, fill=orange},
box 2/.style = {box=#1, fill=cyan},
box 3/.style = {box=#1, fill=yellow!80!red},
lower box/.style = {
draw,
[email protected],
minimum height=2.5em,
inner xsep=2pt,
font=color{white}ttfamily
},
%% matrix
matrix default/.style = {
matrix of nodes
},
upper matrix/.style = {
matrix default,
column sep=15pt
},
lower matrix/.style = {
matrix default,
column sep=0pt,
set color=Green3, % set default color before key "lower box" is passed
nodes=lower box
},
%% arrow
arrow/.style = {
arrows={ -Triangle[angle=45:2.5pt 3] },
line width=1pt
},
%% chain
every on chain/.style = {
join=by arrow
},
}
makeatother
值得一看的是
-
的部分,展示了設定「固定寬高的 node」常用的選項box/.style
-
的部分,展示了通過 arrows 選項指定箭頭樣式的方式arrow/.style
具體例子 2,edge 與 node 分離,tikz library fit
fit
本例中,值得一看的是
- node 允許包含多行文本時,如何控制文本的最大寬度和對齊方式
- 使用單獨的
指令,通過 edge 集中繪制帶箭頭的線(能簡化為path
)graph
- 使用 tikz library
繪制「包含若幹個 node 的矩形」 ,避免使用具體坐标fit
其他技巧還涉及
- 利用坐标運算,使得通過
fit
繪制的矩形,關于包含的 nodes 左右對稱
(因為 edge 的 node label 偏向右側的緣故,如果矩形隻是 "fit" nodes 和 node labels,矩形看起來會偏右。)
- 允許指定增加值,以「讓某個 node 更高和更寬一些」,免去記憶預設高寬,簡化輸入
示例源碼
見項目 muzimuzhi/latex-examples 中的檔案
-
和tikz-example-flowchart1.*
-
tikz-example-flowchart2.*
(注:不包含簡陋示例)