天天看點

pb代碼graph繪圖表_[LaTeX 繪圖] tikz 繪制流程圖,概述和兩個示例

本文已加入專欄文章目錄,歸入「示例」文章系列。

流程圖,或者類似流程圖的繪圖目标,主要指具有以下特征的圖形組合:

  • 多個帶框文本,框的形狀、框之間的位置關系有規律
  • 多條帶箭頭的線,線連接配接框

(這裡是想說,在具體的使用場景和學科背景下,圖形們各有各的叫法和名字,但從繪圖的角度,它們和本文所介紹的「流程圖」有很大的共性。)

以下,在無明顯歧義時,我們用 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}
           
pb代碼graph繪圖表_[LaTeX 繪圖] tikz 繪制流程圖,概述和兩個示例

上述例子中,故意略去了

  • 關于 node 的設定,如外框形狀
  • 關于 arrow 的設定,例如線的樣式、箭頭的樣式等
  • 關于顔色和字型的設定

這些都可以通過定義 tikz style 來解決。

上述例子中,值得關注的是這些:

  • 少用、甚至不用具體坐标
    • 第一個 node 省略了坐标,相當于指定了

      (0, 0)

    • 從第二個 node 開始,利用 tikz library

      positioning

      提供的選項(例如

      below=of <node name>

      ), 指定相對位置關系。
    • 效果是,每組 node,外框之間的(最小)距離都相同,是

      node distance

      指定的

      10pt

      ;在需要手動調整距離時,可以使用

      right=<dimen> of <node name>

  • 繪制 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}
           

注意到

  1. 新加載了兩個 tikz library
    1. library

      graph

      ,用于提供

      graph

      指令
    2. library

      quotes

      ,将 node label 的輸入從

      label={[<options>]<text>}

      簡化為

      "<text>"<options>

  2. 雖然

    (start) -> (step 1) -> (step 2) -> (choice) -> (end)

    的文法大大簡化了輸入,但是标記 node label 和繪制非簡單 arrow 變麻煩了,帶來了學習成本
  3. 如果使用了選項

    use existing nodes=true

    ,輸入

    (start) -> (step 1) -> (step 2)

    能進一步簡化為

    start -> step 1 -> step 2

tikz 文檔 Sec. 5.4.1 就介紹了這種繪制方式:

  • 帶框文字用

    node

    結合 tikz library

    positioning

  • 帶箭頭的線用

    graph

    結合 tikz library

    quotes

其他可選的方案

  • 帶框文字用

    node

    結合 tikz library

    positioning

    ,帶箭頭的線用操作符 edge。
    • 這是 tikz 文檔 Sec.3 的最終狀态
    • 優勢:代碼結構清晰,額外學習成本低
    • 劣勢:每條線對應一個 edge,線多時仍然麻煩
  • graph

    同時處理兩件事。
    • 這是 tikz 文檔 Sec. 5 的最終狀态
    • 優勢:線的标記十分輕量
    • 劣勢:
      • 一定的額外學習成本,包括;
      • 兩件事同時做,代碼可讀性降低;
      • 圖的生長方向由代碼自動決定,如需 100% 控制圖的最終效果,手動調整不可少
  • matrix

    處理框的相對位置
    • 這是 tikz 文檔 Sec. 5.3 的狀态
    • 優勢:配合 tikz library

      matrix

      matrix of nodes

      選項,在某些情況下能大幅簡化 node 的輸入

具體例子 1,

matrix

配合 tikz library

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}
           
pb代碼graph繪圖表_[LaTeX 繪圖] tikz 繪制流程圖,概述和兩個示例

解釋說明:

  1. 上下分成兩個

    matrix

    進行輸入,
    1. 上方

      matrix

      的特征是,每個 node 頂部都有 node label,這裡以選項參數的形式輸入 node label text
    2. matrix of nodes 模式下,每個 node 的參數可通過

      |[<options>]|

      輸入
  2. 帶箭頭的線可分為兩類
    1. 連接配接(上方

      matrix

      )相鄰 node 的直線。因為 matrix 中的 node name 有規律,這裡借助 tikz library

      chains

      ,以循環的方式适當簡化輸入
    2. 連接配接上下方 node 的曲線。
      1. 每條曲線對應一根三次貝塞爾曲線。
      2. 這裡通過選項

        in

        out

        控制曲線出發和到達時的角度,再通過選項

        looseness

        控制曲線的「彎曲程度」。
      3. 這三個選項的綜合效果,和直接調整貝塞爾曲線的控制點不等價;在額外使用選項

        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 
  };
           
pb代碼graph繪圖表_[LaTeX 繪圖] tikz 繪制流程圖,概述和兩個示例

最後,看一眼例 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
           

值得一看的是

  • box/.style

    的部分,展示了設定「固定寬高的 node」常用的選項
  • arrow/.style

    的部分,展示了通過 arrows 選項指定箭頭樣式的方式

具體例子 2,edge 與 node 分離,tikz library

fit

pb代碼graph繪圖表_[LaTeX 繪圖] tikz 繪制流程圖,概述和兩個示例

本例中,值得一看的是

  • node 允許包含多行文本時,如何控制文本的最大寬度和對齊方式
  • 使用單獨的

    path

    指令,通過 edge 集中繪制帶箭頭的線(能簡化為

    graph

  • 使用 tikz library

    fit

    繪制「包含若幹個 node 的矩形」 ,避免使用具體坐标

其他技巧還涉及

  • 利用坐标運算,使得通過

    fit

    繪制的矩形,關于包含的 nodes 左右對稱

    (因為 edge 的 node label 偏向右側的緣故,如果矩形隻是 "fit" nodes 和 node labels,矩形看起來會偏右。)

  • 允許指定增加值,以「讓某個 node 更高和更寬一些」,免去記憶預設高寬,簡化輸入

示例源碼

見項目 muzimuzhi/latex-examples 中的檔案

  • tikz-example-flowchart1.*

  • tikz-example-flowchart2.*

(注:不包含簡陋示例)

繼續閱讀