Livedoorブログからの移動

dotを使う時, node間の位置の調整には以下のようにrankを使う.

digraph {
  a -> b
  b -> c
  {rank = same; b; c}


しかし, これは subgraph間に適用することができない. 例えば, 以下のようにrankを設定すると, 画像の通りcluster0, cluster1という新たなnodeとして解釈される.
digraph {
  subgraph cluster0 {
     a  -> b
  }
  subgraph cluster1 {
     A -> B
  }
  a -> A
  b -> B
  {rank = same; cluster0; cluster1;}
}

sample


この場合は, newrankなる属性をいじれば対処可能(参考: http://stackoverflow.com/questions/6824431/placing-clusters-on-the-same-rank-in-graphviz)であり, あるいはconstraint=falseでも対処できるかもしれない.

例えば, 
digraph {
  newrank=true;
  subgraph cluster0 {
     a  -> b
  }
  subgraph cluster1 {
     A -> B
  }
  a -> A
  b -> B
  {rank=same; a; A;}


 は次のとおりとなる.

sample2


でも, このnewrankは, subgraph間にedgeが無いと使えない. 普通, 間にedgeがなければレイアウトがどうなっても重要な問題ではないんだけれども, 「作成途中のグラフ」 のような場合, edgeが無いsubgraph間で位置関係を整えたい場合も存在する.

例えば, 作成途中のクラス図を表す以下のようなケース.
digraph {
  rankdir=LR
  subgraph cluster0 {
     label=package1
     node0 [
shape=record
label="ClassName |+ field: package2.Interface|"]
  }
  subgraph cluster1 {
     label=package2
     node1 [
shape=record
label="Interface||"]
  }

 

この場合, 後にnode0 -> node1という辺が追加される可能性が高い. なので, 気持ち的にはnode0とnode1は横に並んでほしい(rankdir=LRなので). けど, 実際にdotで生成すると以下の通り.

sample3


こういう場合, node0 -> node1の辺を実際に追加しちゃう(invizで)のも手だけど, dummyのnodeを追加するという手もある. 下のような感じ.
(12/1 変更. dummy nodeは全clusterに1つずつ追加したほうが綺麗.
digraph {
  rankdir=LR
  subgraph cluster0 {
     label=package1
     Cluster0 [
shape=none
label=""
     ]
     node0 [
shape=record
label="ClassName |+ field: package2.Interface|"]
     {rank = same; Cluster0; node0; }
  }
  subgraph cluster1 {
     label=package2
     Cluster1 [
shape=none
label=""
     ]
     node1 [
shape=record
label="Interface||"]
     { rank=same; Cluster1; node1;}
  }
  Cluster0 -> Cluster1 [style=inviz]


この方法の利点としては, 後で追加するnode0 -> node1のエッジが現在はないため, 追加した時にinvizなedgeによってレイアウトが辺になるってことがないことが挙げられる. が, 著しく記述が面倒になるので自動生成向きの手法と言えるとおもう.