Graphviz入門


Graphviz入門

安裝Graphviz

在官網上面下載相關文件,地址:http://www.graphviz.org/download/

graphviz簡介

graphviz是貝爾實驗室設計的一個開源的畫圖工具,它的強大主要體現在“所思即所得”(WYTIWYG,what you think is what you get),這是和office的“所見即所得“(WYSIWYG,what you see is what you get)完全不同的一種方式。它使用一個特定的DSL(領域特定語言): dot作為腳本語言,然后使用布局引擎來解析此腳本,並完成自動布局。它的輸入是一個用dot語言 編寫的繪圖腳本,通過對輸入腳本的解析,分析出其中的點,邊以及子圖,然后根據屬性進行繪制。graphviz提供豐富的導出格式,如常用的圖片格式,SVG,PDF格式等。用graphviz來繪圖的時候,你的主要工作就是編寫dot腳本,你只要關注圖中各個點之間的關系就好了,你不需要考慮如何安排各個節點的位置,怎樣布局能夠使你所繪制的圖看起來更美觀一些。

graphviz中包含了眾多的布局器:

  • dot 默認布局方式,渲染的圖具有明確方向性,主要用於有向圖
  • neato 渲染的圖缺乏方向性,基於spring-model(又稱force-based)算法
  • twopi 渲染的圖用放射性布局,徑向布局
  • circo 渲染的圖用環型布局,圓環布局
  • fdp 渲染的圖缺乏方向性,用於無向圖
  • sfdp 渲染大型的圖,圖片缺乏方向性

graphviz的設計初衷是對有向圖/無向圖等進行自動布局,開發人員使用dot腳本定義圖形元素,然后選擇算法進行布局,最終導出結果。

首先,在dot腳本中定義圖的頂點和邊,頂點和邊都具有各自的屬性,比如形狀,顏色,填充模式,字體,樣式等。然后使用合適的布局算法進行布局。布局算法除了繪制各個頂點和邊之外,需要盡可能的將頂點均勻的分布在畫布上,並且盡可能的減少邊的交叉(如果交叉過多,就很難看清楚頂點之間的關系了)。所以使用graphviz的一般流程為:

  • 定義一個圖,並向圖中添加需要的頂點和邊
  • 為頂點和邊添加樣式
  • 使用布局引擎進行繪制

一旦熟悉這種開發模式,就可以快速的將你的想法繪制出來。
graph1

第一個graphviz圖

比如,要繪制一個有向圖,包含5個節點a,b,c,d,e。其中a和b指向c,c和d指向e。可以定義下列腳本:

digraph test{
    a;
    b;
    c;
    d;
    e;

    a->c;
    b->c;
    c->e;
    d->e;

}

這里寫圖片描述
使用dot布局方式,繪制出來的效果如下:
默認的頂點中的文字為定義頂點變量的名稱,形狀為橢圓。邊的默認樣式為黑色實線箭頭,我們可以在腳本中做一下修改,將頂點改為方形,邊改為虛線。

設置點和線的形狀和顏色

在digraph的花括號內,添加頂點和邊的新定義:

node [shape="record"];
edge [style="dashed"];

則繪制的效果如下:
這里寫圖片描述
+ 進一步修改頂點和邊樣式

進一步,我們將頂點a的顏色改為淡綠色,並將c到d的邊改為紅色,腳本如下:

digraph test{
    node [shape="record"];
    edge [style="dashed"];
    a [style="filled", color="black", fillcolor="skyblue"];
    b;
    c;
    d;
    e;

    a->c;
    b->c;
    c->e;
    d->e [color="red"];

}

繪制的結果如下:
graph1-2
應當注意到,頂點和邊都接受屬性的定義,形式為在頂點和邊的定義之后加上一個由方括號括起來的key-value列表,每個key-value對由逗號隔開。如果圖中頂點和邊采用統一的風格,則可以在圖定義的首部定義node, edge的屬性。比如上圖中,定義所有的頂點為方框,所有的邊為虛線,在具體的頂點和邊之后定義的屬性將覆蓋此全局屬性。如特定與a的綠色,c到d的邊的紅色。

  • 以圖片為節點

除了顏色,節點還可以使用圖片。不過需要注意的是,在使用圖片作為節點的時候,需要將本來的形狀設置為none,並且將label置為空字符串,避免出現文字對圖片的干擾。

digraph test{
    node [shape="record"];
    edge [style="dashed"];
    a [style="filled", color="black", fillcolor="skyblue"];
    b;
    c;
    d [shape="none", image="C:\Users\Marvin\Desktop\timg.jpg", label=""];
    e;

    a->c;
    b->c;
    c->e;
    d->e [color="red"];

}

graph1-3
digraph是有向圖,graph是無向圖,要注意,->用在有向圖中,–用在無向圖中表示一條邊,不能混用。

//digraph是有向圖,graph是無向圖,要注意,->用在有向圖中,--用在無向圖中表示一條邊,不能混用。
digraph G { //第一行給出了圖的類型和名字
    main -> parse -> execute; //當一個點第一次出現,它就被創建了
    main -> init; //用->標示符創建一條邊
    main -> cleanup;
    execute -> make_string;
    execute -> printf
    init -> make_string;
    main -> printf;
    execute -> compare;
}
//然后在cmd下用這個文件運行dot
//dot -Tps graph1.dot -o graph1.ps
//這是ps格式,你也可以改成jpg等格式。
//-Tps選擇了postscript output,
//就畫出了這個圖。

graph1

來看下一個稍微復雜點的例子,我們開始手動的設置一下圖的屬性。可以給點設置屬性,也可以給邊設置屬性。先來講講怎么設置邊的屬性,在每條邊后面的雙括號里設置邊的屬性。也可以在用edge設置邊的默認值。而給點設置屬性就必須給每個點單獨的設置一個屬性,node表示點的默認值。

//點的默認參數是shape=ellipse, width=.75, height=.5 and labeled by the node name.
//一些點的形狀在appendix.h 中,一些常用的形狀有bos,circle,record,plaintext。
digraph G {
    size ="4,4";// 把圖的尺寸設為4 inch,4 inch
    main [shape=box];//把main點的形狀設為方形
    main -> parse [weight=8]; //weight是設置了這條邊的重要程度,默認是1。
    parse -> execute;
    main -> init [style=dotted]; //讓這條線是點狀的
    main -> cleanup;
    execute -> { make_string; printf} //這條語句一次連了兩條線
    init -> make_string;
    edge [color=red]; // so is this 把邊的默認顏色設為了red
    main -> printf [style=bold,label="100 times"]; //label就是在邊上寫了一行字
    make_string [label="make a\nstring"];// 讓make_string變成了一個兩行的字符串(注意那個\n)。
    node [shape=box,style=filled,color=".7 .3 1.0"];// 設置了一下點的默認參數,藍色,這個被用在了compare中。
    execute -> compare;
}

畫出以下圖形:

graph2

//可以設置每條邊箭頭的方向,用dir,有forward(default),back,both,none 四種。
digraph html {
    A -> B[dir = both];
    B -> C[dir = none];
    C -> D[dir = back];
    D -> A[dir = forward];
}

graph3

//點的shape 除了record 和Mrecord 這兩種之外,其他的形狀都是多邊形,而我們可以對多邊形進行一下屬性上的設置,
//shape = polygon。Sides 用於設置它的邊數,peripheries 用於設置多邊形的外框的層數,
//regular = true 可以讓你的多邊形是一個規則的多邊形,orientation =*,可以讓你的多邊形旋轉一個角度,
//如orientation = 15 就是轉了15 度。Skew 后面跟一個(-1.0~1.0)的小數,能讓你的圖形斜切一個角度,distortion 是讓你的圖形產生透視效果。
digraph G {
    a -> b -> c;
    b -> 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];
}

graph4

digraph A{
    A -> B;
    A[orientation = 15, regular = true, shape = polygon, sides = 8, peripheries = 4, color= red style = filled];
    B[shape = polygon, sides = 4, skew = 0.5, color = blue];
}

graph5

//record 和Mrecord 的區別就是Mrecord 的角是圓的。Record 就是由衡的和豎的矩形組成的圖形。
digraph structs {
    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;
}

graph6

當你的線和線label 比較多時,可以給線的屬性decorate = true,使得每條線的label 與所屬線之間連線。還可以給每條線加上headlabel 和taillabel,給每條線的起始點和終點加上label,他們的顏色由labelfontcolor 來決定,而label 的顏色由fontcolor 來決定。

//
graph A{
    label = "I love you"; //給這幅圖設置,名字
    labelloc = b; //圖名字的位置在bottom,也可以是t
    labeljust = l; //圖名字的位置在left,也可以是r
    edge[decorate = true];
    C -- D[label = "s1"];
    C -- E[label = "s2"];
    C -- F[label = "s3"];
    D -- E[label = "s4"];
    D -- F[label = "s5"];
    edge[decorate = false, labelfontcolor = blue, fontcolor = red];
    C1 -- D1[headlabel = "c1", taillabel = "d1", label = "c1 - d1"];
}

graph7

在dot 中我們可以用html 語言寫一個table。在label 后用< >而不是”“就能引入html 語言。

//在dot 中我們可以用html 語言寫一個table。在label 后用< >而不是""就能引入html 語言。 digraph html { abc [shape=none, margin=0, label=<  <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> <TR><TD ROWSPAN="3"><FONT COLOR="red">hello</FONT><BR/>world</TD> <TD COLSPAN="3">b</TD> <TD ROWSPAN="3" BGCOLOR="lightgrey">g</TD> <TD ROWSPAN="3">h</TD> </TR> <TR><TD>c</TD> <TD PORT="here">d</TD> <TD>e</TD> </TR> <TR><TD COLSPAN="3">f</TD> </TR> </TABLE>>]; }

graph8

//這樣創造了一個5 行5 列的表格,我們可以在表格中打字。 digraph html { abc [shape=none, margin=0, label=<  <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"> <TR><TD>0</TD><TD>1</TD><TD>2</TD><TD>3</TD><TD>4</TD> </TR> <TR><TD>1</TD><TD></TD><TD></TD><TD></TD><TD></TD> </TR> <TR><TD>2</TD><TD></TD><TD></TD><TD></TD><TD></TD> </TR> <TR><TD>3</TD><TD></TD><TD></TD><TD></TD><TD></TD> </TR> <TR><TD>4</TD><TD></TD><TD></TD><TD></TD><TD></TD> </TR> </TABLE>>]; }

graph9

設置點和線的位置,子圖的概念

默認時圖中的線都是從上到下的,我們可以將其改為從左到右,在文件的最上層打入rankdir=LR 就是從左到右,默認是TB(top -> bottom),也可以是RL,BT。當圖中時間表之類的東西時,我們會需要點能排在一行(列),這時要用到rank,用花括號把rank=same,然后把需要並排的點一次輸入。

//
digraph html {
    rankdir = LR;
    {
        node[shape = plaintext];
        1995 -> 1996 -> 1997 -> 1998 -> 1999 -> 2000 -> 2001;
    }
    {
        node[shape = box, style = filled];
        WAR3 -> Xhero -> Footman -> DOTA;
        WAR3 -> Battleship;
    }
    {rank = same; 1996; WAR3;}
    {rank = same; 1998; Xhero; Battleship;}
    {rank = same; 1999; Footman;}
    {rank = same; 2001; DOTA;}
}

graph10

設立一條邊時,我們可以制定這條邊從起點的那個位置射出和從哪個位置結束。控制符有”n”,”ne”,”e”, “se”, “s”, “sw”, “w” 和 “nw”,具體效果見下:

digraph html {
    node[shape = box];
    c:n -> d[label = n];
    c1:ne -> d1[label = ne];
    c2:e -> d2[label = e];
    b:se -> a[label = se];
    c3:s -> d3[label = s];
    c4:sw -> d4[label = sw];
    c5:w -> d5[label = w];
    c6:nw -> d6[label = nw];
}

graph11

我們也可以在record 中給點定義一些port,因為record 類型中都是一個個格子。

digraph html {
    label = "Binary search tree";
    node[shape = record];
    A[label = "<f0> | <f1> A |<f2> "];
    B[label = "<f0> | <f1> B |<f2> "];
    C[label = "<f0> | <f1> C |<f2> "];
    D[label = "<f0> | <f1> D |<f2> "];
    E[label = "<f0> | <f1> E |<f2> "];
    A:f0:sw -> B:f1;
    A:f2:se -> C:f1;
    B:f0:sw -> D:f1;
    B:f2:se -> E:f1;
}

graph12

//構造一個HASH 表
digraph G {
    nodesep=.05;
    rankdir=LR;
    node [shape=record,width=.1,height=.1];

    node0 [label = "<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> | ",height=2.5];
    node [width = 1.5];
    node1 [label = "{<n> n14 | 719 |<p> }"];
    node2 [label = "{<n> a1 | 805 |<p> }"];
    node3 [label = "{<n> i9 | 718 |<p> }"];
    node4 [label = "{<n> e5 | 989 |<p> }"];
    node5 [label = "{<n> t20 | 959 |<p> }"] ;
    node6 [label = "{<n> o15 | 794 |<p> }"] ;
    node7 [label = "{<n> s19 | 659 |<p> }"] ;

    node0:f0 -> node1:n;
    node0:f1 -> node2:n;
    node0:f2 -> node3:n;
    node0:f5 -> node4:n;
    node0:f6 -> node5:n;
    node2:p -> node6:n;
    node4:p -> node7:n;
}

graph13

子圖的繪制

graphviz支持子圖,即圖中的部分節點和邊相對對立(軟件的模塊划分經常如此)。比如,我們可以將頂點c和d歸為一個子圖:

digraph test{
    node [shape="record"];
    edge [style="dashed"];
    a [style="filled", color="black", fillcolor="skyblue"];
    b;
    c;

    subgraph cluster_de{
        label="d and e";
        bgcolor="mintcream";
        d [shape="none", image="C:\Users\Marvin\Desktop\timg.jpg", label=""];
        e;
        }
    a->c;
    b->c;
    c->e;
    d->e [color="red"];

}

將d和e划分到cluster_de這個子圖中,標簽為d and e,並添加背景色,以方便與主圖區分開,繪制結果如下:

graph1-4

畫一個子圖就是subgraph cluster#,必須有cluster 前綴。

digraph g {
    subgraph cluster0 {
        //我是一個子圖,subgraph定義了我,
        node[style = filled, color = white];
        //我之內的節點都是這種樣式
        style = filled;
        //我的樣式是填充
        color = lightgrey;
        //我的顏色
        a0->a1->a2->a3;
        label = "prcess #1"
        //我的標題
    }

    subgraph cluster1 {
        //我也是一個子圖
        node[style = filled];
        b0->b1->b2->b3;
        label = "process #2";
        color = blue;
    }

    //定義完畢之后,下面還是連接了
    start->a0;
    start->b0;
    a1->b3;
    b2->a3;
    a3->end;
    b3->end;

    start[shape=Mdiamond];
    end[shape=Msquare];
}

graph14
當你想把一條邊連到一個子圖的邊界上,先輸入compound = true,然后就能用lhead 和ltail來設置連接的子圖了。

digraph G{
    compound=true;
    subgraph cluster0{
        a->b;
        a->c;
        b->d;
        c->d;
    }
    subgraph cluster1{
        e->g;
        e->f;
    }
    b->f[lhead=cluster1];
    d->e;
    c->g[ltail=cluster0,lhead=cluster1];
    c->e[ltail=cluster0];
    d->h;
}

graph15
多邊形結點(http://www.graphviz.org/doc/info/shapes.html)

下面顯示了可能的多邊形形狀。

img img img img
box polygon ellipse oval
img img img img
circle point egg triangle
img img img img
plaintext plain diamond trapezium
img img img img
parallelogram house pentagon hexagon
img img img img
septagon octagon doublecircle doubleoctagon
img img img img
tripleoctagon invtriangle invtrapezium invhouse
img img img img
Mdiamond Msquare Mcircle rect
img img img img
rectangle square star none
img img img img
underline cylinder note tab
img img img img
folder box3d component promoter
img img img img
cds terminator utr primersite
img img img img
restrictionsite fivepoverhang threepoverhang noverhang
img img img img
assembly signature insulator ribosite
img img img img
rnastab proteasesite proteinstab rpromoter
img img img
rarrow larrow lpromoter

數據結構的可視化

實際開發中,經常要用到的是對復雜數據結構的描述,graphviz提供完善的機制來繪制此類圖形。

一個hash表的數據結構

比如一個hash表的內容,可能具有下列結構:

struct st_hash_type {
    int (*compare) ();
    int (*hash) ();
};

struct st_table_entry {
    unsigned int hash;
    char *key;
    char *record;
    st_table_entry *next;
};

struct st_table {
    struct st_hash_type *type;
    int num_bins; /* slot count */
    int num_entries; /* total number of entries */
    struct st_table_entry **bins; /* slot */
};

繪制hash表的數據結構

從代碼上看,由於結構體存在引用關系,不夠清晰,如果層次較多,則很難以記住各個結構之間的關系,我們可以通過下圖來更清楚的展示:
graph1-5

腳本如下:

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"];
}

狀態圖

有限自動機示意圖
graph1-6

上圖是一個簡易有限自動機,接受a及a結尾的任意長度的串。其腳本定義如下:

digraph automata_0 {
  size = "8.5, 11";
  fontname = "Microsoft YaHei";
  fontsize = 10;

  node [shape = circle, fontname = "Microsoft YaHei", fontsize = 10];
  edge [fontname = "Microsoft YaHei", fontsize = 10];

  0 [ style = filled, color=lightgrey ];
  2 [ shape = doublecircle ];

  0 -> 2 [ label = "a " ];
  0 -> 1 [ label = "other " ];
  1 -> 2 [ label = "a " ];
  1 -> 1 [ label = "other " ];
  2 -> 2 [ label = "a " ];
  2 -> 1 [ label = "other " ];

  "Machine: a" [ shape = plaintext ];
}

形狀值為plaintext的表示不用繪制邊框,僅展示純文本內容,這個在繪圖中,繪制指示性的文本時很有用,如上圖中的Machine: a。

其他實例

一棵簡單的抽象語法樹(AST)

表達式 (3+4)*5 在編譯時期,會形成一棵語法樹,一邊在計算時,先計算3+4的值,最后與5相乘。
graph1-7

對應的腳本如下:

digraph ast{
  fontname = "Microsoft YaHei";
  fontsize = 10;

  node [shape = circle, fontname = "Microsoft YaHei", fontsize = 10];
  edge [fontname = "Microsoft YaHei", fontsize = 10];
  node [shape="plaintext"];

  mul [label="mul(*)"];
  add [label="add(+)"];

  add -> 3
  add -> 4;
  mul -> add;
  mul -> 5;
}

簡單的UML類圖

下面是一簡單的UML類圖,Dog和Cat都是Animal的子類,Dog和Cat同屬一個包,且有可能有聯系(0..n)。
graph1-8
腳本如下:

digraph G{

  fontname = "Courier New"
  fontsize = 10

  node [ fontname = "Courier New", fontsize = 10, shape = "record" ];
  edge [ fontname = "Courier New", fontsize = 10 ];

  Animal [ label = "{Animal |+ name : String\l+ age : int\l|+ die() : void\l}" ];

      subgraph clusterAnimalImpl{
          bgcolor="yellow"
          Dog [ label = "{Dog||+ bark() : void\l}" ];
          Cat [ label = "{Cat||+ meow() : void\l}" ];
      };

  edge [ arrowhead = "empty" ];

  Dog->Animal;
  Cat->Animal;
  Dog->Cat [arrowhead="none", label="0..*"];
}

狀態圖

graph1-9

腳本:

digraph finite_state_machine {
  rankdir = LR;
  size = "8,5"

  node [shape = doublecircle];

  LR_0 LR_3 LR_4 LR_8;

  node [shape = circle];

  LR_0 -> LR_2 [ label = "SS(B)" ];
  LR_0 -> LR_1 [ label = "SS(S)" ];
  LR_1 -> LR_3 [ label = "S($end)" ];
  LR_2 -> LR_6 [ label = "SS(b)" ];
  LR_2 -> LR_5 [ label = "SS(a)" ];
  LR_2 -> LR_4 [ label = "S(A)" ];
  LR_5 -> LR_7 [ label = "S(b)" ];
  LR_5 -> LR_5 [ label = "S(a)" ];
  LR_6 -> LR_6 [ label = "S(b)" ];
  LR_6 -> LR_5 [ label = "S(a)" ];
  LR_7 -> LR_8 [ label = "S(b)" ];
  LR_7 -> LR_5 [ label = "S(a)" ];
  LR_8 -> LR_6 [ label = "S(b)" ];
  LR_8 -> LR_5 [ label = "S(a)" ];
}

時序圖

““
digraph G {
rankdir=”LR”;
node[shape=”point”, width=0, height=0];
edge[arrowhead=”none”, style=”dashed”]

{
    rank="same";
    edge[style="solided"];
    LC[shape="plaintext"];
    LC -> step00 -> step01 -> step02 -> step03 -> step04 -> step05;
}

{
    rank="same";
    edge[style="solided"];
    Agency[shape="plaintext"];
    Agency -> step10 -> step11 -> step12 -> step13 -> step14 -> step15;
}

{
    rank="same";
    edge[style="solided"];
    Agent[shape="plaintext"];
    Agent -> step20 -> step21 -> step22 -> step23 -> step24 -> step25;
}

step00 -> step10 [label="sends email new custumer", arrowhead="normal"];
step11 -> step01 [label="declines", arrowhead="normal"];
step12 -> step02 [label="accepts", arrowhead="normal"];
step13 -> step23 [label="forward to", arrowhead="normal"];
step24 -> step14;
step14 -> step04 [arrowhead="normal"];

}
““
graph1-10

復雜實例

graph1-11
腳本如下

digraph G { rankdir=LR node [shape=plaintext] 05 a [ label=<  <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR> <TD ROWSPAN="3" BGCOLOR="yellow">class</TD> </TR> <TR> <TD PORT="here" BGCOLOR="lightblue">qualifier</TD> </TR> </TABLE>> ] b [shape=ellipse style=filled label=<  <TABLE BGCOLOR="bisque"> <TR> <TD COLSPAN="3">elephant</TD> <TD ROWSPAN="2" BGCOLOR="chartreuse" VALIGN="bottom" ALIGN="right">two</TD> </TR> <TR> <TD COLSPAN="2" ROWSPAN="2"> <TABLE BGCOLOR="grey"> <TR> <TD>corn</TD> </TR> <TR> <TD BGCOLOR="yellow">c</TD> </TR> <TR> <TD>f</TD> </TR> </TABLE> </TD> <TD BGCOLOR="white">penguin</TD> </TR> <TR> <TD COLSPAN="2" BORDER="4" ALIGN="right" PORT="there">4</TD> </TR> </TABLE>> ] c [ label=<  long line 1<BR/>line 2<BR ALIGN="LEFT"/>line 3<BR ALIGN="RIGHT"/>> ] subgraph { rank=same b c } a:here -> b:there [dir=both arrowtail = diamond] c -> b d [shape=triangle] d -> c [label=<  <TABLE> <TR> <TD BGCOLOR="red" WIDTH="10"> </TD> <TD>Edge labels<BR/>also</TD> <TD BGCOLOR="blue" WIDTH="10"> </TD> </TR> </TABLE>> ] }

Reference

Graphviz從入門到不精通

Graphviz - Graph Visualization Software

Graphviz-Documentation

使用 Graphviz 畫拓撲圖

Graphviz


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM