Graphviz 探索
简介
graphviz是贝尔实验室开发的一个开源的工具包,它使用一个特定的DSL(领域特定语言):dot作为脚本语言,然后使用布局引擎来解析此脚本,并完成自动布局。graphviz提供丰富的导出格式,如常用的图片格式,SVG,PDF格式等。graphviz中包含了众多的布局器:
- dot 默认布局方式,主要用于有向图
- neato 基于spring-model(又称force-based)算法
- twopi 径向布局
- circo 圆环布局
- fdp 用于无向图
graphviz的设计初衷是对有向图/无向图等进行自动布局,开发人员使 用dot脚本定义图形元素,然后选择算法进行布局,最终导出结果。
首先,在dot脚本中定义图的顶点和边,顶点和边都具有各自的属性,比如形状,颜色,填充模式,字体,样式等。然后使用合适的布局算法进行布局。布局算法除了绘制各个顶点和边之外,需要尽可能的将顶点均匀的分布在画布上,并且尽可能的减少边的交叉(如果交叉过多,就很难看清楚顶点之间的关系了)。
所以使用graphviz的一般流程为:- 定义一个图,并向图中添加需要的顶点和边
- 为顶点和边添加样式
- 使用布局引擎进行绘制
绘制dot图
常用参数 格式:dot -T<type> -o<outfile> <infile.dot> 【dot 可以替换为circo等其他算法】 输入文件是<infile.dot>,生成的格式由<type>指定,生成的文件是<outfile>。 其中-T<type>包括: -Tps (PostScript), -Tsvg -Tsvgz (Structured Vector Graphics), -Tfig (XFIG graphics), -Tmif (FrameMaker graphics), -Thpgl (HP pen plotters), -Tpcl (Laserjet printers), -Tpng -Tgif (bitmap graphics), -Tdia (GTK+ based diagrams), -Timap (imagemap files for httpd servers for each node or edge that has a non-null "href" attribute.), -Tcmapx (client-side imagemap for use in html and xhtml).
下面简单介绍下DOT语言的语法:
无向图 :在最简单的应用中,DOT语言可以用来描述一张无向图。无向图显示了对象间最简单的关系,例如人之间的友谊。使用关键字graph开始一张无向图的定义,并用大括号包含要描述的节点,双连字号(–)被用来描述节点间的关系。另外,一行的末尾需要加上分号(;)。
graph graphname { a -- b -- c; b -- d; }
有向图 :类似于无向图,DOT语言也可以用来描述一张有向图,类似于流程图和树状图。其语法与无向图相似,但要在图的最开始使用关键字’digraph’,并用箭头(->)表示节点直接的关系。
digraph graphname { a -> b -> c; b -> d; }
属性 :DOT语言中,可以对节点和边添加不同的属性。这些属性可以控制节点和边的显示样式,例如颜色,形状和线形。可以在语句和句尾的分号间放置一对方括号,并在其中中放置一个或多个属性-值对。多个属性可以被逗号和空格(, )分开。节点的属性被放置在只包含节点名称的表达式后。
graph graphname { // label属性可以改变节点的显示名称 a [label="Foo"]; // 节点形状被改变了 b [shape=box]; // a-b边和b-c边有相同的属性 a -- b -- c [color=blue]; b -- d [style=dotted]; }
注释 :DOT语言支持C语言与C++风格的单行与多行注释。另外,也支持Shell脚本风格的以#开头的注释。
// 单行注释 /* 多行注释 */ # 如此的行也会被忽略。
Example-1
digraph{ //attributes label="byPass-Branch" node[color=red]; //nodes headNode->node1; headNode->node2; node1->node3; node[shape=box,color=black]; //distribute headNode->fromApp1[style=dotted]; headNode->fromApp2[style=dotted]; headNode->headApp[style=dotted,color=blue]; node1->headApp[style=dotted]; node1->fromApp3[style=dotted]; node1->node1App[style=dotted,color=blue]; node2->headApp[style=dotted]; node2->node2App[style=dotted,color=blue]; node3->node1App[style=dotted]; node3->fromApp4[style=dotted]; node3->node3App[style=dotted,color=blue]; }
Example-2
digraph G { size="200,200"; center=true; rankdir=LR; //子图 subgraph cluster_0 { style=filled; color=lightgrey; node [style=filled,color=white]; a0 -> a1 -> a2 -> a3; label = "process #1"; } subgraph cluster_1 { node [style=filled]; b0 -> b1 -> b2 -> b3; label = "process #2"; color=blue; subgraph cluster_2 { node [style=filled]; c1[shape=circle,peripheries=3,color=red]; c0 -> c1 -> c2 -> c3[color=yellow]; label = "process #3"; color=blue } } //定义节点和边的属性 start -> a0[style=bold,label="first",decorate=true]; start -> b0; a1 -> b3[style=dotted,color=red,dir=both]; b2 -> a3[style=dashed,dir=back]; a3 -> a0[weight=20]; a3 -> end; {b3,c3} -> end; a3[shape=ellipse,color=green]; a1[color=red,style=diagonals]; a2[shape=plaintext,color=orange]; start [shape=Mdiamond]; end [shape=Msquare,label="make a\n End"]; }
Example-3
digraph G { //多边形 end->a->{e,c,d}; a[shape=polygon,sides=5,peripheries=3,color=lightblue,style=filled]; c[shape=polygon,sides=4,skew=.4,label="hello world"]; d[shape=invtriangle]; e[shape=polygon,sides=4,distortion=.7]; node1[shape=record,label="left0|middile0|right0"]; node2[shape=record,label="O1|{O2|{O3|04}|O5}|O6"]; node1->node2; //record 记录 node[shape=record]; struct1[shape=record,label="<f0>left|<f1> mid\ dle|<f2> right"]; struct2[shape=record,label="<f0> one|<f1> two"]; struct3[shape=record,label="hello\nworld |{ b |{c|<here> d|e}| f}| g | h"]; struct1->struct2; struct1->struct3; }
Example-4
digraph st2{ fontname = "Verdana"; fontsize = 10; rankdir=TB; node [fontname = "Verdana", fontsize = 10, color="skyblue", shape="record"]; edge [fontname = "Verdana", fontsize = 10, color="crimson", style="solid"]; st_hash_type [label="{<head>st_hash_type|(*compare)|(*hash)}"]; st_table_entry [label="{<head>st_table_entry|hash|key|record|<next>next}"]; st_table [label="{st_table|<type>type|num_bins|num_entries|<bins>bins}"]; st_table:bins -> st_table_entry:head; st_table:type -> st_hash_type:head; st_table_entry:next -> st_table_entry:head [style="dashed", color="forestgreen"]; }
使用Circo算法