由于工作需要,经常需要在各类文档中绘制传统程序流程图。流程图当然可以在 Visio、亿图等工具进行绘制,截图(位图,不推荐)或是导出为 PDF (矢量图,推荐)后,再通过graphicx
宏包的\includegraphics
命令插入到 LaTeX 实现排版。这是一种简单直接的方法,但这种方法会存在如下问题:
因此,在 LaTeX 中直接使用 TikZ 工具绘制流程图,是一个相对较好的选择。
流程图的基本形状有矩形、菱形、平行四边形、圆角矩形等。可以使用TikZ的\node
命令布置这些形状结点,然后绘制对应流程线就可以实现流程图的绘制。
为绘制不同形状的结点,可以为\node
命令的可选参数中设置rectangle
、diamond
、trapezium
、rounded corners
等绘制属性。
显然,直接用 TikZ 代码进行流程图绘制是一个比较烦琐的过程,并且不符合 LaTeX 内容与格式分离的基本思想。同时,当有多个流程图需要绘制时,代码过于冗余,不便维护,代码的复用性也较差,不同流程图也容易产生不一致现象。
flowchart
宏包绘制为简化流程图的绘制代码,可以引入合适的 LaTeX 宏包进行流程图绘制。一个比较方便的宏包是flowchart
宏包,它提供了如下基本的流程图形状:
显然,该宏包提供的基本流程图形状不能完全符合我国的绘制习惯。关于该宏包的使用细节,请在命令行用命令查阅其使用说明书。
当然,可以将\node
命令的可选参数中的rectangle
、diamond
、trapezium
、rounded corners
等绘制属性用tikzset
命令定义为绘图样式,以实现代码的简化和代码的复用。在 Brent Longborough [1] 中,给出了使用这一方式的绘制样例和实现代码。
根据LaTeX内容与格式分离的基本思想,显然将\tikzset
样式定义及其它的设置封装为宏包,使用起来则更为方便。在完成这一封装之前,笔者刚好阅读了tikz-imagelabels 宏包的源代码,发现其key-value
实现思路非常值得借鉴。因此,基于 Brent Longborough [1] 的代码,结合 tikz-imagelabels 宏包的key-value
思路,笔者设计与制作了tikz-flowchart 宏包,以便在 LaTeX 中用 TikZ 实现流程图绘制,详见 [2]。
在tikzpicture
环境中,使用该宏包绘制流程图的基本过程是:
\flowchartset
命令设置全局(导言区)或局部绘制参数。\node proc, join {$k -= 1$};
命令采用proc
、test
、io
或term
各个node样式参数分别布置需要的流程框结点。\node coord, right=0.8 of t1 {}; cmark{1}
命令采用coord
样式布置其它需要的坐标点(用于流程线的转接)。\path (t1.south) to node [near start, xshift=1em] {$y$} (p2);
命令进行流程线条件标注。\draw norm -- (p2);
命令绘制流程线。个人建议在绘制流程前最好先用纸和笔绘制一个草图,然后再用tikz-flowchart
宏包进行绘制,如下图是一个素数判断的流程图草图:
根据该草图可以使用 LaTeX 代码绘制如下流程图:
\documentclass[margin=10pt]{standalone}
% 支持中文
\usepackage{ctex}
% 流程图绘制宏包
\usepackage{tikz-flowchart}
\begin{document} %在document环境中撰写文档
% 可以局部更改各参数
\flowchartset{
proc fill color = orange!10, % 顺序处理框填充颜色(默认取白色)
test fill color = green!30, % 判断框填充颜色(默认取白色)
io fill color = blue!30, % 输入/输出框填充颜色(默认取白色)
term fill color = red!30, % 开始/结束框填充颜色(默认取白色)
proc text width = 6em, % 顺序处理框宽度(默认取8em)
}
\begin{tikzpicture}
% 布置结点单元
\node [term] (st) {开始};
\node [proc, join] (p1) {\verb|int divisor|};
\node [test, join] (t1) {\verb|n <= 1|};
\node [proc, ] (p2) {\verb|divisor = 2|};
% 可以根据需要指定结点当前属性(如文字宽度)
\node [test, text width = 10em, join] (t2) {\\verb|divisor * divisor <= n|};
\node [test, text width = 8em] (t3) {\verb|n % divisor == 0|};
\node [proc, text width = 6em] (p3) {\verb|divisor++|};
\node [term, below = 1.6 of p3] (end) {结束};
\node [proc, left = 4.8 of t2] (p4) {\verb|return 0|};
\node [proc, right = 3.5 of p3] (p5) {\verb|return 0|};
\node [proc, right = 5.8 of t3] (p6) {\verb|return 1|};
% 布置用于连接的坐标结点,同时为其布置调试标记点。
\node [coord] (c1) at ($(p2.south)!0.5!(t2.north)$) {}; \cmark{1}
\node [coord, below = 0.25 of p3] (c2) {}; \cmark{2}
\node [coord, above = 0.5 of end] (c3) {}; \cmark{3}
\node [coord, left = 0.5 of t2] (ct) {}; \cmark{t}
\node [coord] (c4) at (c3 -| p5) {}; \cmark{4}
\node [coord] (c5) at (c2 -| ct) {}; \cmark{5}
% 判断框连线,每次绘制时,先绘制一个带有一个固定
% 位置标注的路径(path),然后再绘制箭头本身(arrow)。
\path (t1.south) -- node [near start, right] {$N$} (p2.north);
\draw [norm] (t1.south) -- (p2.north);
\path (t1.west) -| node [near start, above] {$Y$} (p4.north);
\draw [norm] (t1.west) -| (p4.north);
\path (t2.south) -- node [near start, right] {$Y$} (t3.north);
\draw [norm] (t2.south) -- (t3.north);
\path (t2.east) -| node [near start, above] {$N$} (p6.north);
\draw [norm] (t2.east) -| (p6.north);
\path (t3.south) -- node [near start, right] {$N$} (p3.north);
\draw [norm] (t3.south) -- (p3.north);
\path (t3.east) -| node [near start, above] {$Y$} (p5.north);
\draw [norm] (t3.east) -| (p5.north);
% 其它连线
\draw [norm](p3.south) |- (c5) |- (c1);
\draw [norm](p4.south) |- (c3);
\draw [norm](p4.south) |- (c3) -- (end);
\draw [norm](p5.south) -- (c4);
\draw [norm](p6.south) |- (c3);
\draw [norm](p6.south) |- (c3) -- (end);
\end{tikzpicture}
\end{document}
关于tikz-flowchart
宏包的使用细节,请参阅中的相关文档说明及使用 LaTeX 样例代码。
更多精品图在这里:https://www.latexstudio.net/index/details/index/mid/356.html
Happy TikZing!
参考链接
[1] http://www.texample.net/tikz/examples/author/brent-longborough/
[2] https://github.com/registor/tikz-flowchart/
我没有运行成功,从27行开始报错Undefined control sequence. ^^I^^Inode,类似报错一直到最后
@u4412 复制的时候多了反斜线了,已经修改好了,感谢反馈!