読者です 読者をやめる 読者になる 読者になる

Graphviz::DSLをリリースしました。

perl graphviz

https://metacpan.org/release/SYOHEX/Graphviz-DSL-0.01


Ruby Gemの Gvizの移植版ということで Graph::Gvizという名前を
つけていたんですが、それだけだと何をするものかわからないという
意見をいただいたので Graphviz::DSLという名前にしました。

機能

基本的には Rubyの Gvizと同じですが、現状 Gvizの方が機能が
充実しています(おいおい実装するつもりです)。

Gvizから変更した部分

極力 DSLに書いたとおり、DOTファイルに出力されるようにしました。
以下に違いが出る例を示します。


Graphvizのサイトに載っているサンプルで次のような DOTファイルがあります。

digraph G {
   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
    }
    start -> a0;
    start -> b0;
    a1 -> b3;
    b2 -> a3;
    a3 -> a0;
    a3 -> end;
    b3 -> end;
  
    start [shape=Mdiamond];
    end [shape=Msquare];
}

この DOTファイルからは次のような画像が生成されます。
http://www.graphviz.org/Gallery/directed/cluster.png


同じものを Graphviz::DSLで書くと以下のようになります。

#!/usr/bin/env perl
use strict;
use warnings;

use lib qw(../lib);
use Graphviz::DSL;

my $graph = graph {
    subgraph {
        name 'cluster_0';
        nodes style => 'filled', color => 'white';
        global style => 'filled', color => 'lightgrey', label => 'process#1';

        route a0 => 'a1';
        route a1 => 'a2';
        route a2 => 'a3';
    };

    subgraph {
        name 'cluster_1';
        nodes style => 'filled';
        global color => 'blue', label => 'process#2';

        route b0 => 'b1';
        route b1 => 'b2';
        route b2 => 'b3';
    };

    route start => [qw/a0 b0/];
    route a1 => 'b3';
    route b2 => 'a3';
    route a3 => [qw/a0 end/];
    route b3 => 'end';

    node 'start', shape => 'Mdiamond';
    node 'end',   shape => 'Mdiamond';
};

$graph->save(path => 'sample', type => 'png');

生成される画像は次のようになります。同じですね。


一方 Gvizの場合を示します。

#!/usr/bin/env ruby
# -*- coding:utf-8 -*-

require "gviz"

gv = Gviz.new
gv.graph do
  subgraph {
    nodes :style => 'filled', :color => 'white'
    global :style => 'filled', :color => 'lightgrey', :label => 'process#1'

    route :a0 => 'a1'
    route :a1 => 'a2'
    route :a2 => 'a3'
  }

  subgraph {
    nodes :style => 'filled'
    global :style => 'filled', :label => 'process#2'

    route :b0 => 'b1'
    route :b1 => 'b2'
    route :b2 => 'b3'
  }

  route :start => [:a0, :b0]
  route :a1 => :b3
  route :b2 => :a3
  route :a3 => [:a0, :end]
  route :b3 => :end;

  node :start, :shape => 'Mdiamond'
  node :end, :shape => 'Mdiamond'
end

gv.save(:sample1, :png)

生成される画像を示します。少し異なってしまいます。

これは DOTファイルはオブジェクトを書く順番が重要なようで、
同じ内容でも順番を変更することで見た目が変わってしまうため
です。Gvizでは同じオブジェクトの順番は保持されるのですが、
各オブジェクトをまとめて出力するという実装になっているので、
このようなことが起きてしまいました。(実装としては Gvizの
ようにするのが極めて当たり前だと思います。むしろなんで DOTに
こんな性質があるのかと問いたくなります。)


Graphviz::DSLでは極力順番を維持するように実装しているので
書いたままに結果が得られるのではないかと思います。

おわりに

DOTを書くのが嫌だから、wrapperを作って Graphvizを使いやすく
というのが Graphvizに関するモジュールの基本だと思っていたん
ですが、Gvizを見たとき、DOTに近い感じだけど OOPよりもわかり
やすいなぁと思いました。そういう気づかないことって他にもあると
思うので、様々な視点で考えられるようにしていきたいと思いました。