2008년 2월 6일 수요일

Graphviz

Graphviz(Graph Visualization Software)
Graphviz를 처음 접하게 된 것은 Doxygen 때문이었습니다. Java로 프로그램을 문서화할 때 클래스 다이어그램을 짚어넣어 주었으면 하는 바램으로 여러 툴을 찾아봤었는데 당시에는 Doxygen 밖에 없더군요. Doxygen에서는 그래프를 그려주기 위해서 이 Graphviz를 사용하고 있구요.

Introduction
요즘에 만들고 있는 JavaScript Bulid 툴(실제로는 JsDoc-toolkit과 JsLint, YUI-Compressor 등을 한 번에 구동하는 php 프로그램입니다.)에 클래스 다이어그램이 있으면 좋겠다는 개인적인 생각에, Graphviz를 다시 보게 되었습니다.
사용자 삽입 이미지

Wikipedia의 소개에 따르면,
  • Graphviz는 AT&T의 연구소에서 DOT 스크립트 언어를 사용하여, 다이어그램을 그리기 위해서 만들어진 프로그램이라고 합니다.
  • 오픈소스이며, CPL(Common Public License)를 사용하고 있습니다.
  • 다른 소프트웨어에서 라이브러리로 사용할 수 있는 기능도 제공되고 있습니다.
Graphviz는 그래프를 묘사하는 언어인 DOT language를 사용하여 Graph를 정의하고 이 정의한 내용을 필터를 통해서 다양한 이미지 포멧으로 생성하게 됩니다.

Usage
1. Graphviz를 사이트에서 다운로드하여 설치한 후에, GVedit을 실행하면 다음과 같은 창이 뜹니다.
사용자 삽입 이미지

2. File > New 또는 툴바의 세번째 아이콘(New File)을 클릭해서 Graphviz 새 창을 띄웁니다. 다음과 같이 입력 합니다.
사용자 삽입 이미지

3. Graphviz Settings 창을 띄워서 Layout Engine과 출력 파일에 대한 내용을 입력하고 OK 버튼을 누릅니다.
(레이아웃 엔진은 dot- command line tool, neato-undirected graph, twopi-radial graph, circo-circular graph, fdp, dotty, lefty 을 사용할 수 있습니다.)
사용자 삽입 이미지

4. 생성된  Graph를 확인한 후 이미지로 저장합니다. (dot을 레이아웃 엔진으로 사용하는 경우, undirected graph의 선이 그려지지 않는군요.)
사용자 삽입 이미지

DOT language
DOT 언어는 다이어그램을 정의하는 언어로 graphs, nodes, edges들을 정의할 수 있습니다. 그래프의 레이아웃은 크게 방향성 그래프(directed graph)와 비방향성 그래프인(undirected graph) 두가지로 나눌 수 있습니다. 각각은 digraph, graph 명령어를 사용하여 정의할 수 있습니다.

dot 레이아웃 엔진을 사용하게 되면 directed graph를 그릴 수 있고, neato를 사용하게 되면 undirected graph를 그릴 수 있습니다. 제가 본 튜토리얼이 dot을 사용하기 때문에, 모든 그래프는 digraph를 사용합니다.

directed graph의 간단한 사용 예제
// digraph는 방향성 그래프를 정의, digraph 뒤의 값은 그래프의 이름입니다.
digraph
GraphName {
    main -> parse -> execute;
    main -> init;
    /* 주석은 일반적인 주석 모두 동작합니다. */
    main -> cleanup;
    execute -> make_string;
    // 방향 선은 항상 '->' 붙여져서 선언되어야 합니다. '- >' 로 선언되면 처리가 안됩니다.
    execute -> printf;
    init -> make_string;
    main -> printf;
    execute -> compare;
}
결과:
사용자 삽입 이미지


 1. Node and Edge
Node의 디폴트 값은 shape=ellipse, width=.75 height=.5로 값이 설정되며, node의 이름이 label이 됩니다.
shape에 들어갈 수 있는 다른 값들은 box, circle, record, plaintext 등이 있습니다.

Node는 크게 두 종류의 도형으로 나뉠 수 있습니다. 하나는 다각형 기반(Polygon-based)의 도형과 리코드 기반(Record-based) 도형 두 가지로 나뉩니다.

1-1. Node와 관련된 기본적인 문법을 사용하는 경우
digraph GraphName {
    // graph의 크기를 설정합니다.
    size="3,3";

    // 전체 노드들에게 Verdana 폰트를 사용하도록 설정합니다.
    node [fontname="Tahoma", fontsize="10"];

    // main의 shape을 정의합니다.
    main [shape=box;];    

    // (integer)weight는 node와 node 사이의 간격을 정합니다.
    main -> parse [weight=0];   
    parse -> execute;

    // main과 init을 잊는 edge 스타일을 정의합니다. bold, filled 등의 값 설정 가능.
    main -> init [style=dotted];
    main -> cleanup;

    // execute->make_string;execute->printf;를 한 번에 표기합니다.
    execute -> {make_string; printf};
    init -> make_string;

    // label 설정 및 peripheries(표면)을 두 개의 선으로 처리합니다.
    // label의 값은 escape string처리됩니다. 공백도 '\ '로 입력해야 합니다.

    make_string [label="make a\n string",peripheries=2];

    // 이하로 설정되는 edge의 색을 red로 설정합니다.

    edge [color=red];

    // main과 printf를 잊는 edge의 label을 설정합니다.
    main -> printf [style=bold,label="100 times",fontname="arial"];

    // node의 도형을 설정합니다. 위에 설정된 node 값에 대한 설정은 상속
    node [shape=box,style=filled,color=".7 .3 1.0"];
    execute -> compare;
}
위의 다이어그램으로 생성한 결과는 다음과 같습니다.
사용자 삽입 이미지

Shape에 관한 기본적인 내용을 보시려면 여기, "Node Shapes" 항목을 참조해서 보시면 되고, Node, Edge, Graph에 설정할 수 있는 세부 속성들을 보시려면 여기, "Node, Edge and Graph Attributes"를 보시면 됩니다.

1-2. shape이 polygon인 여러 도형을 정의하는 경우
digraph G {
    // lightblue로 채워져 있고 3개의 선으로 둘러싸인 오각형 도형을 정의
    a [shape=polygon,sides=5,peripheries=3,color=lightblue,style=filled];
    // 사각형이나 40도 정도 기울어진 hello world 레이블을 가진 도형
    c [shape=polygon,sides=4,skew=.4,label="hello world"];
    // 엎어진 삼각형
    d [shape=invtriangle];
    // 아래 쪽으로 들어간, 뒤틀린 사각형
    e [shape=polygon,sides=4,distortion=.7];
    a ->b ->c;
    b ->d;
}
1-2의 예제의 결과
사용자 삽입 이미지
  2. Labels
node의 shape이 record와 Mrecord의 label을 사용하여서 shape 내부에 수직 또는 수평의 box를 넣을 수 있습니다.
  • label의 값을 구분하는 필드로 |를 사용할 수 있습니다. 연속하여 사용가능
    rlable -> field('|'field)*
  • 수직/수평의 방향은 {}를 사용하여 정의할 수 있으며, 중첩해서 선언이 가능합니다.
  • label 앞에 <>로 된 boxlabel을 선언할 수 있습니다.
    field->boxLabel|"rlabel"
    boxLabel -> ['<'string'>'][string]
digraph G {
     struct1 [shape=record, label="<f0> left|<f1> mid\ dle|<f2> right"];
    struct2 [shape=record, label="<f0> one|<f1>two"];
    // Mrecord는 모서리가 둥근 것을 제외하고는 record와 동일
    struct3 [shape=Mrecord, label="hello\nworld|{b|{c|<here> d|e}|f}|g|h"];
    struct4 [shape=record,
        label="{className|method1\nmethod2|property1\nproperty2}"]
    struct1 -> struct2;
    struct1 -> struct3;
    struct1:f2 -> struct4;
    left_indicator -> struct1:f0;
    right_indicator -> struct1:f2;
}
위 label의 결과는 다음과 같습니다.
사용자 삽입 이미지

 label을 이용하여 클래스 다이어그램 만들기 [예제 출처: UML Diagrams Using Graphviz Dot]
digraph G {
    // graph의 폰트 정의
    fontname = "Bitstream Vera Sans"
    fontsize = 8

    // node 공통의 폰트, shape 정의
    node [
        fontname = "Bitstream Vera Sans"
        fontsize = 8
        shape = "record"
    ]

    // edge 공통의 폰트, shape 정의
    edge [
        fontname = "Bitstream Vera Sans"
        fontsize = 8   
    ]

    // Anaimal node 정의 \l을 사용하여 label의 align 정의
    // \r을 사용하면 right align이 됩니다.
    Animal [
        label = "{Animal|+ name : string\l+ age : int\l|+ die() : void\l}"
    ]

    // subgraph clusterAnimalImpl 정의
    subgraph clusterAnimalImpl {
        // subgraph의 label을 정의합니다.
        label = "Package animal.impl"

        //Dog 클래스를 정의
        Dog [
            label = "{Dog||+ bark() : void\l}"
        ]
       
        // Cat 클래스를 정의
        Cat [
            label = "{Cat||+ meow() : void\l}"
        ]
    }

    // cluster 내에서 사용할 edge를 정의
    edge [
        arrowhead = "empty"
    ]

    Dog -> Animal
    Cat -> Animal

    // Dog에서 Cat으로 가는 edge를 정의
    edge [
        arrowhead = "none"
        // edge의 head/tail의 label을 정의
        headlabel = "0..*"
        taillabel = "0..*"
    ]
    Dog -> Cat
}
결과값
사용자 삽입 이미지
관련자료:

댓글 없음:

댓글 쓰기