赞
踩
LaTeX中的TikZ绘图功能很强,为了方便以后用到的时候好查询,所以这里把自己绘制过的内容记录在CSDN博客中。
在以下环境测试过
原图选自裘巍老师所著的《编译器设计之路》。下面介绍我用LaTeX绘制的过程、代码与一些说明。
过程介绍:
完整代码
\documentclass{article} % \usepackage{ctex} \usepackage{geometry} \usepackage[dvipsnames, svgnames, x11names]{xcolor} \usepackage{tikz} % \usetikzlibrary{positioning, arrows.meta} % \begin{document} % % 定义方框样式 \tikzset{ rect1/.style = { shape = rectangle, draw = green, text width = 3cm, align = center, minimum height = 1cm, } } % 定义箭头样式 \tikzset{ arrow1/.style = { draw = purple, thick, -{Latex[length = 4mm, width = 1.5mm]}, } } % 双箭头 \tikzset{ arrow2/.style = { draw = purple, thick, {Latex[length = 4mm, width = 1.5mm]}-{Latex[length = 4mm, width = 1.5mm]}, } } \begin{center} \begin{tikzpicture} % 绘制中间方框 \node[rect1, fill = green!60!white](词法){词法分析}; \node[rect1, fill = green!40!white, below = of 词法](语法){语法分析}; \node[rect1, fill = green!20!white, below = of 语法, text width = 5cm](语义中间){语义分析、中间表示生成}; \node[rect1, fill = green!60!black, below = of 语义中间](优化){\color{white}代码优化}; \node[rect1, fill = green!30!black, below = of 优化](生成){\color{white}代码生成}; % 绘制两侧方框 \node[rectangle, fill = red!20!white, draw = red, text width = 0.7cm, minimum height = 4cm, align = center, left = 2cm of 语义中间](符号){符号表管理}; \node[rectangle, fill = blue!20!white, draw = blue, text width = 0.7cm, minimum height = 4cm, align = center, right = 2cm of 语义中间](出错处理){出错处理}; % 绘制中间连线 \draw[arrow1](0, 50pt)node[right, yshift = -15pt]{输入源程序} -- (词法); \draw[arrow1](词法) -- node[right]{单词流}(语法); \draw[arrow1](语法) -- node[right]{文法}(语义中间); \draw[arrow1](语义中间) -- node[right]{中间表示}(优化); \draw[arrow1](优化) -- node[right]{优化后中间表示}(生成); \draw[arrow1](生成) -- node[right, yshift = 5pt, xshift = 5pt]{目标代码}++(0, -50pt); % 绘制两侧连线 \draw[arrow2](符号) -- (词法.west); \draw[arrow2](符号) -- (语法.west); \draw[arrow2](符号) -- (语义中间); \draw[arrow2](符号) -- (优化.west); \draw[arrow2](符号) -- (生成.west); \draw[arrow2](出错处理) -- (词法.east); \draw[arrow2](出错处理) -- (语法.east); \draw[arrow2](出错处理) -- (语义中间); \draw[arrow2](出错处理) -- (优化.east); \draw[arrow2](出错处理) -- (生成.east); % 绘制虚线框 \draw[thick, dashed, draw = purple](-125pt, 27pt)node[below right]{编译器前端} -- (125pt, 27pt) -- (125pt, -250pt) -- (-125pt, -250pt)node[above right]{编译器后端} -- (-125pt, 27pt); \draw[thick, dashed, draw = purple](-125pt, -135pt) -- (125pt, -135pt); \end{tikzpicture} \\\heiti 图 1-2 编译器结构图\songti \end{center} % \end{document}
效果如下:
说明
1. 绘制方框的语句格式一般是:
\node[样式](名称){标题};
注意结尾的分号,绘制线条的语句最后先要加分号。名称是调用时使用,可以用汉字,标题是显示出来的文字,二者不一样。
2. 样式的内容挺多的,方框中一般会用到形状(shape)、边框颜色(draw)、填充色(fill)、宽度(text width)、最小高度(minimum height)。颜色方面可以参考xcolor宏包的说明文档。长度单位有好几个,我一般常用的是pt与cm,1pt=0.351mm。
3. 位置的表示也包含在样式定义中,一般形式是:方位 = 尺寸 of 参考对象。方位包括left、right、below、above、below left、below right、above left、above right 8种,要注意below left这样的顺序不能颠倒。
4. 这里我们绘制箭头的库选用的是arrows.meta(24种箭头),这个库比arrows(14种)要丰富些。箭头的形状有很多种,详情请参考arrows.meta的24种箭头。本例中箭头样式选的是Latex样式,其后的length = 4mm, width = 1.5mm代表箭头的长宽,可以通过这两个参数调整箭头的锋锐程度。大家可以根据需要设置。
5. 在线条上加文字的方法是通过加入node来实现,注意node在连线(即–)中的位置,如果放在–左边就表示文字是在线段的左边,反之在右边。实际上我们可以通过xshift、yshift两个参数来随意调整位置。
6. 语句:
\draw[arrow1](0, 50pt)node[right, yshift = -15pt]{输入源程序} -- (词法);
其中的(0, 50pt)是指在绝对坐标0, 50pt处开始绘制,图形坐标的原点(0, 0)就是代码中第一个方框的中心处。如果我们在(0, 50pt)前加两个加号,例如++(0, 50pt),那就变成了相对坐标,是相对于上一个节点。例如语句:
\draw[arrow1](生成) -- node[right, yshift = 5pt, xshift = 5pt]{目标代码}++(0, -50pt);
++(0, -50pt)的意思就是相对于“生成”这个节点向下移动50pt的坐标。当我们不清楚绝对坐标时就用相对坐标来绘制,挺方便的。
完整代码
\documentclass{article} % \usepackage{ctex} \usepackage{geometry} \usepackage[dvipsnames, svgnames, x11names]{xcolor} \usepackage{tikz} % \usetikzlibrary{positioning, arrows.meta, calc} % % 定义箭头样式 \tikzset{ arrow1/.style = { draw = purple, thick, -{Latex[length = 4mm, width = 1.5mm]}, } } % 非终端 \tikzset{ nonterminal/.style = { rectangle, align = center, minimum size = 6mm, very thick, draw = red!50!black!50, top color = white, bottom color = red!50!black!20, } } % 终端 \tikzset{ terminal/.style = { rectangle, align = center, minimum size = 6mm, rounded corners = 3mm, very thick, draw = black!50, top color = white, bottom color = black!20, } } % \begin{document} \begin{center} \begin{tikzpicture}[node distance = 0.5cm] \node[terminal, text width = 1.6cm](uses){USES}; \node[nonterminal, text width = 1.6cm, right = 1cm of uses](unit){单元名}; \node[terminal, right = 1cm of unit](semicolon){;}; \node[terminal, below = of unit](comma){,}; \draw[arrow1](uses)--(unit); \draw[arrow1](unit)--(semicolon); \draw[arrow1](semicolon)--++(30pt, 0); \draw[arrow1]($ (unit.east)+(3mm, 0) $)|-(comma); \draw[arrow1](comma)-|($ (unit.west)-(6mm, 0) $); \end{tikzpicture} \end{center} \begin{center} \begin{tikzpicture} \node[terminal, text width = 1.6cm](var){VAR}; \node[nonterminal, text width = 1.6cm, right = 1cm of var](list){变量标识符列表}; \node[terminal, right = 1cm of list](colon){:}; \node[nonterminal, text width = 1.6cm, right = 1cm of colon](type){类型}; \node[terminal, right = 1cm of type](semicolon1){;}; \node[terminal, below = of list](semicolon2){;}; \node[terminal, below = 2cm of colon](semicolon3){;}; \draw[arrow1](var)--(list); \draw[arrow1](list)--(colon); \draw[arrow1](colon)--(type); \draw[arrow1](type)--(semicolon1); \draw[arrow1](semicolon1)--++(30pt, 0); \draw[arrow1]($ (list.east)+(3mm, 0) $)|-(semicolon2); \draw[arrow1](semicolon2)-|($ (list.west)-(6mm, 0) $); \draw[arrow1]($ (type.east)+(3mm, 0) $)|-(semicolon3); \draw[arrow1](semicolon3)-|($ (list.west)-(6mm, 0) $); \end{tikzpicture} \end{center} \end{document}
效果如下:
说明
1. 本例中因为使用了一些计算,所以引入了TikZ中的calc库,大家在usetikzlibrary命令处可以看见。
2. 圆角矩形的绘制就是加入rounded corners参数,当文字很少时,如上例中的分号与逗号,这个圆角矩形就变成了圆圈(实际上,这与minimum size的设置也有关)。
3. 从线段中间开始画线的方法,大家在代码中可以看见,代码意思很明显,不再解释。
4. 通过top color与bottom color的设置,实现了过渡效果。
5. 在语句“…(3mm, 0) $)|-(comma);”中,两个节点间的“|-”表示线段是先画竖线,再画横线,画多长,系统会自动判断。
6. 在“变量标识符列表”的定义语句中设置了text width参数,如果不设置的话,文字就不会换行。
代码
\hspace{-3cm}{ \begin{minipage}{15.4cm} \begin{tikzpicture}[node distance = 0.7cm] \node[terminal](function){FUNCTION}; \node[nonterminal, right = of function](name1){函数名}; \node[terminal, right = of name1](left){(}; \node[terminal, right = of left](var){VAR}; \node[nonterminal, right = of var](para){形参名}; \node[terminal, right = of para](colon1){:}; \node[nonterminal, right = of colon1](type1){类型名}; \node[terminal, right = of type1](right){)}; \node[terminal, right = of right](colon2){:}; \node[nonterminal, right = of colon2](type2){类型名}; \node[terminal, right = of type2](semicolon1){;}; \node[terminal, below = of para](semicolon2){;}; % ---------------------------------------------------------- \draw[arrow1](function)--(name1); \draw[arrow1](name1)--(left); \draw[arrow1](left)--(var); \draw[arrow1](var)--(para); \draw[arrow1](para)--(colon1); \draw[arrow1](colon1)--(type1); \draw[arrow1](type1)--(right); \draw[arrow1](right)--(colon2); \draw[arrow1](colon2)--(type2); \draw[arrow1](type2)--(semicolon1); \draw[arrow1]($ (var.west)+(-3mm, 0) $)--++(0, 6mm)-|($ (var.east)+(3mm, 0) $); \draw[arrow1]($ (type1.east)+(3mm, 0) $)|-(semicolon2); \draw[arrow1](semicolon2)-|($ (var.west)-(3mm, 0) $); \draw[arrow1]($ (name1.east)+(3mm, 0) $)--++(0, -20mm)-|($ (right.east)+(3mm, 0) $); % ---------------------------------------------------------- \node[nonterminal, below = 2.7cm of function, xshift = 1cm](description){说明部分}; \node[terminal, right = of description](begin){BEGIN}; \node[nonterminal, right = of begin, text width = 1.6cm](statement){语句}; \node[terminal, right = of statement](semicolon3){;}; \node[terminal, right = of semicolon3, text width = 1.6cm](end){END}; \node[terminal, right = of end](semicolon4){;}; % ---------------------------------------------------------- \draw[arrow1](description)--(begin); \draw[arrow1](begin)--(statement); \draw[arrow1](statement)--(semicolon3); \draw[arrow1](semicolon3)--(end); \draw[arrow1](end)--(semicolon4); \draw[arrow1](semicolon4)--++(30pt, 0); \draw[arrow1](semicolon1)--++(0, -2.5cm)-|(description); \draw[arrow1]($ (semicolon3.east)+(3mm, 0) $)--++(0, -6mm)-|($ (begin.east)+(3mm, 0) $); \end{tikzpicture} \end{minipage}
效果如下:
说明
1. 此例的导言区部分与示例二的一样。
2. 因为这幅图比较长,正常的A4纸显示不全,所以我用\hspace{-3cm}命令向左边距移了3厘米以保证完整显示。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。