Graghvizでグラフ構造からグラフを描画する

GraphvizはDOT言語で記述されたグラフ構造からグラフを出力するソフトフェア。

* [Graphviz | Graphviz - Graph Visualization Software](http://www.graphviz.org/)

# インストール

homebrewだと

```sh
$ brew install graphviz
```

でインストールできる。

# サンプルコード

以下にまるっとしたサンプルコードを置く。色々な例を書くためにかなりごちゃごちゃしている。

```c
digraph sample1 /* ←idはつけなくもよい */ {
    layout=dot // デフォルトはdot。dotは階層を持ったグラフを描くのに適している。他にfdp, neato, circo, osageなど。=の両側には空白があってもよい

    // graph (node, edge以外の部分)の設定
    graph [ // graph []で囲まなくても良い。カンマで区切らなくてもいいっぽい
        label="サンプルだよ☆",   // 全体のラベル。文字列は"（ダブルクォート）で囲む。'（シングルクォート）は使えない
        // labelloc = t,             ラベルを上に指定
        // labeljust = l,            ラベルを左寄せ（l）、右寄せ（r）
        fontname="Ricty Discord", // font指定。デフォルトはTimes-Roman
        fontsize=10,              // デフォルトは14
        fontcolor="#d33682",      // デフォルトはblack。16進数表記"#0033aF"や、0から1の実数で色、彩度、明度をカンマorスペース区切りで指定"0.6,.4 1"できる
        bgcolor="#fdf6e3",        // 背景色の設定。
        // rotate=90                 90度回転。指定できるのは90のみ。指定したら、何も出力されなくなった。
        // size="4,3!",              インチ指定で、横、縦。縦横比は保たれる。
                                  // !をつけないとデフォルトのサイズから縮小はされるが、拡大はされない
        rankdir=TB,               // ノードの配置の仕方。デフォルトTBで上から下に向かって配置される。他にBT,LR,RLがある。
    ]

    // nodeの設定
    // nodeの宣言。node[]で設定する前なのでデフォルトの設定になる
    A;

    // nodeの設定
    node [
        shape=diamond,       // nodeの形。デフォルトはellipse
        width=1.5,           // 幅。設定しないと自動で調節してくれる
        height=0.5,          // 高さ
        // fixedsize=false,     デフォルトfalse。trueにすると中に入っている文字よりも小さくできる
        style=rounded,       // 枠線のスタイル。デフォルトはsolid。
        color="#268bd2",     // 枠線の色
        fontname="Monaco",
        fontcolor="#b58900",
        peripheries=2,       // 二重線。
    ]

    // nodeの宣言。上の設定が反映される
    B; C;

    // nodeの設定を上書き
    node [
        shape=circle
        style=striped
        width=0.5
        height=0.5
        color="#93a1a1"
        style=filled,         // 塗りつぶし
        fillcolor="#eee8d5",  // 塗りつぶしのカラー
        peripheries=1
    ]

    // nodeの宣言。上書きされた設定が反映される
    D;

    // nodeごとに個別の設定ができる
    E [
        label="E\lsecond line\nthird line\r" // labelの設定。デフォルトは\N(nodeのid)。
                                             // \nでセンタリング改行、\lで左寄せ改行、\rで右寄せ改行
        width=2
        shape=box
        style=dashed
    ];

    F [
        image="tmsanrinsha_icon.jpg" // 画像を指定できる
        shape=none                   // recordは指定しても画像が表示されない。
        label=""
        xlabel="\N"                  // 外側にnodeのidを表示。位置は決められないっぽい
        color="#fdf6e3"              // 枠を見せたくないので背景色に
    ]

    G [
        shape=none,
        // labelにtable, br, font, imgのタグが使える。
        // imgはtableの中にしかかけない。imageとbrは/>で閉じる
        // <>で全体を囲む
        label=<<table border="0"><tr><td><img src="tmsanrinsha_icon.jpg" /></td></tr><tr><td><font color="red">red</font><br />G</td></tr></table>>,
    ]

    H [
        shape=record,             // recordを指定すると線で区切ってラベルを書ける
        label="left|center|right" // |で区切る。{}で囲むと縦に並ぶようになる。もっと複雑なものが描ける
    ]

    // edgeの設定
    edge [
        color = "#859900",
        labelfloat=false,  // ラベルの重なりを許さない（デフォルト）
    ];

    // edgeの個別の設定は矢印の横に書く
    A -> A;
    A -> B [label="A → B"];                // edgeにもlabelが使える
    A -> C [arrowsize=2];                   // 矢印のサイズ
    {rank=same; B; C}                       // BとCを同じランクにする
    C -> D [
        arrowhead=odot, arrowtail=inv       // 矢印の始端と終端の形。arrowtailが効かない？
        weight=2                            // C-D間のedgeの重みを上げるとなるべく近く、垂直なるように配置される
    ];
    B -> E [dir=back, arrowhead=inv];       // 矢印の向き。back, both, none
    D -> E [dir=both];
    E -> F;
    E -> G -> H;                            // まとめてかける
    F -> {J, K}

    // サブグラフ。名前をclusterで始めると枠ができて、ラベルも指定できる
    // サブグラフの中にサブグラフ、サブグラフ間のedgeはdot layoutではできない。
    // fdp layoutならできる
    subgraph cluster1 {
        label="cluster1";
        I [shape=ellipse color=black, fontcolor=black, fontname="Times-Roman", fillcolor="#fdf6e3" ]
        I -> A;
        {rank=same; A; I;}
    }

}
```

コードと同じディレクトリに画像も置いた状態で、例えばpngに変換するには


```sh
$ dot -T png sample1.dot -o sample1.png
```

と打つと画像が出力される。

![2015-09-20-194311.png](https://qiita-image-store.s3.amazonaws.com/0/58084/c8a29faa-fc38-547d-1517-1f150fdf2f60.png)

# Node

nodeのshape一覧

* [Node Shapes | Graphviz - Graph Visualization Software](http://www.graphviz.org/content/node-shapes)

# Edge

edgeのshape一覧

* [Arrow Shapes | Graphviz - Graph Visualization Software](http://www.graphviz.org/content/arrow-shapes)

# color

color name一覧

* [Color Names | Graphviz - Graph Visualization Software](http://www.graphviz.org/content/color-names)


# 画像の埋め込み

node Fでやっているようにimage属性を使うか、node GでやっているようにlabelでHTMLを使う。

画像にラベルを入れる方法として、xlabelを使うと画像の外側にラベルを置くことができる

```c
F [
    image="tmsanrinsha_icon.jpg"
    shape=none
    label=""
    xlabel="\N"
    color="#fdf6e3"
]
```

しかしこの方法だとラベルの位置を指定できない。
image属性を使って、下にラベルをおきたい場合は、下に余白のある画像を作るかheightを少し大きめにとって

```c
F [
image="tmsanrinsha_icon.jpg"
shape=none
label="\N"
height=2
labelloc=b
fillcolor="#fdf6e3"
color="#fdf6e3"
]
```

としてやる。

# HTML-Like Label

* labelにtable, br, font, imgのなどのタグが使える。
* imgはtableの中にしかかけない。imageとbrは/&gt;で閉じる
* &lt;&gt;で全体を囲む

以下が参考になる

* [clmemo@aka: Graphviz に画像を埋め込む; 例. 画面遷移を描く](http://at-aka.blogspot.jp/2009/06/graphviz.html)
* [Node Shapes | Graphviz - Graph Visualization Softwareの#html](http://www.graphviz.org/content/node-shapes#html)

# cluster

layoutをfdpにするとnodeとcluster、cluster間でedgeを設定できて、subgraphの中にsubgraphを作ることも可能のようだ。

* [Graphviz Example: Undirected Graph Clusters | Graphviz - Graph Visualization Software](http://www.graphviz.org/content/fdpclust)

# その他

* 無向グラフと有向グラフを混ぜることはできない。

# 参考資料

* [Graphviz チュートリアル](http://homepage3.nifty.com/kaku-chan/graphviz/index.html)
* [Documentation | Graphviz - Graph Visualization Software](http://www.graphviz.org/Documentation.php) 公式ドキュメント
* [Gvizの目次 - Rubyの世界からGraphvizの世界にこんにちは！](http://melborne.github.io/2014/02/27/gviz-posts/) rubyのgraphvizパッケージを使った色々な例が載っている
* [rich-iannone/DiagrammeR](https://github.com/rich-iannone/DiagrammeR) Rからgraphvizをつかうパッケージ
* [Q-BPM:プロセスダイアグラム画像の書き方 - Q-BPM](http://ja.q-bpm.org/mediawiki/index.php/Q-BPM:%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%83%80%E3%82%A4%E3%82%A2%E3%82%B0%E3%83%A9%E3%83%A0%E7%94%BB%E5%83%8F%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9) 配置の調節するのに参考になる
* [Graphvizの出力サイズ指定 (1) - とあるソフトウェア開発者のブログ](http://d.hatena.ne.jp/simply-k/20100804/1280880922) sizeの指定

# Vim

* [wannesm/wmgraphviz.vim](https://github.com/wannesm/wmgraphviz.vim)

syntaxやomni補完が使えるようになるプラグイン。
コンパイルでできるがそれはquickrunでやっている。

omni補完をneocompleteで使うための設定

```vim
if !exists('g:neocomplete#force_omni_input_patterns')
    let g:neocomplete#force_omni_input_patterns = {}
endif
let g:neocomplete#force_omni_input_patterns.dot = '\%(=\|,\|\[\)\s*\w*'
```

forceで設定しないとうまく行かなかった。パターンについてはhttps://github.com/wannesm/wmgraphviz.vim/blob/master/ftplugin/dot.vim のGraphvizCompleteを見て設定。

スニペットは大したものがないので、設定しなくてもよいが、設定するならこんな感じ。

```vim
let g:neosnippet#snippets_directory= [
\   '~/.vim/bundle/vim-snippets/snippets',
\   '~/.vim/bundle/wmgraphviz.vim/snippets',
\]
```

## quickrunの設定

開いているdotのコードからpng画像を生成(ファイル名は拡張子をpngに変更したもの）して、それをopenする設定(mac用)。
`open -g`でバックグラウンドで起動しようとしたけどだめだった。

```vim
let g:quickrun_config['dot'] = {
\ 'hook/cd/directory'              : '%S:p:h',
\ 'command'                        : 'dot',
\ 'cmdopt'                         : '',
\ 'exec'                           : ['%c -T png %s -o %s:r.png', 'open %s:r.png'],
\ 'outputter/quickfix/errorformat' : 'Error: %f: %m in line %l %.%#,%EError: %m,%C%m,%Z%m'
\}
```

dotのコンパイルエラーは二種類あって、一つはdotのシンタックスエラー

```
Error: sample1.dot: syntax error in line 21 near ';'
```

で、もう一つはHTML-Likeなlabelを設定した時のエラー

```
Error: Unknown HTML element <able> on line 1
in label of node G
```

や

```
Error: syntax error in line 1
... </td><tr> ...
in label of node G
```

でこちらはファイル内の行数は入ってない。

それぞれerrorformatの1つ目、2つ目に対応している。