DOT语言(graph description language)

图片来源:https://www.pixiv.net/artworks/97882990

基本

文件后缀

.dot或者.gv

节点和边

digraph graphname{
    // Node
    node1 
    node2

    // Edge
    node1 -> node2
}

无向图

graph graphname {
     a -- b -- c;
     b -- d;
}

有向图

digraph graphname {
    a -> b -> c;
    b -> d;
}

注释

// C风格单行注释

/* C风格
   多行
   注释 */

# Shell风格注释

命令行渲染命令

 dot -Tsvg input.dot > output.svg

子图(subgraph)和集群(cluster)

可以用“{}”创建一个匿名的子图,仅仅起到语句块的作用

digraph graphname {
    a -> {b,c}
}

可以给子图设置“集群”属性让graphviz给子图渲染一个边框,设置方法有两种

  1. 子图命名以“cluster”开头
  2. “cluster=true”(VSCode的插件“Graphviz (dot) language support for Visual Studio Code”不认这个属性)
digraph cats {
  subgraph cluster_big_cats {
    // This subgraph is a cluster, because the name begins with "cluster"
    
    "Lion";
    "Snow Leopard";
  }

  subgraph domestic_cats {
    // This subgraph is also a cluster, because cluster=true.
    cluster=true;

    "Siamese";
    "Persian";
  }

  subgraph not_a_cluster {
    // This subgraph is not a cluster, because it doesn't start with "cluster",
    // nor sets cluster=true.
    
    "Wildcat";
  }
}

子图中定义的节点也是全局共享的

digraph graphname{
    subgraph cluster_1 {
        node1 
        node2
    }
    
    subgraph cluster_2 {
        node1 -> node3
    }
}

属性(Attributes)

方向

属性名:rankdir

作用:设置渲染开始方向

可选值:TB(默认), LR, BT, RL

graph graphname {
    rankdir = RL;  // Right to Left
    a -- b -- c;
    b -- d;
}
graph graphname {
    rankdir = BT;  // Bottom to Top
    a -- b -- c;
    b -- d;
}

标签

属性名:lable

作用:设置图、子图、节点、边等显示的内容

digraph plants{
  label="Plants"
  subgraph cluster_flowers {
    label="Flowers"

    sf [label="Sunflowers"]
    ab [label="Almond Blossom"]
  }

  subgraph cluster_Bambusoideae {
    label="Bambusoideae"

    sht [label="bamboo shoot"]
    bam [label="bamboo"]
    
    sht -> bam [label="grow into"]
  }
}

节点形状

属性名:shape

作用:设置节点形状

可选值:值较多,参看https://graphviz.org/doc/info/shapes.html#polygon

其他

Graphviz画树的输出优化脚本

graph tree {
    node [shape=circle]
    0 -- 1
    0 -- 2
    1 -- 3
    1 -- 4
    2 -- 5
    2 -- 6
}
优化前
优化后

优化命令,其中tree.g文件即为Emden Gansner大佬的优化脚本

dot tree.dot | gvpr -c -ftree.g | neato -n -Tsvg -otree_1.svg
// Date:   2010-06-21
// Author: Emden R. Gansner
// From:   [[email protected]]

BEGIN {
  double tw[node_t];    // width of tree rooted at node
  double nw[node_t];    // width of node
  double xoff[node_t];  // x offset of root from left side of its tree
  double sp = 36;       // extra space between left and right subtrees
  double wd, w, w1, w2; 
  double x, y, z;
  edge_t e1, e2;
  node_t n;
}
BEG_G {
  $.bb = "";
  $tvtype=TV_postfwd;   // visit root after all children visited
}
N {
  sscanf ($.width, "%f", &w);
  w *= 72;  // convert inches to points
  nw[$] = w;
  if ($.outdegree == 0) {
    tw[$] = w;
    xoff[$] = w/2.0;
  }
  else if ($.outdegree == 1) {
    e1 = fstout($);
    w1 = tw[e1.head];    
    tw[$] = w1 + (sp+w)/2.0;
    if (e1.side == "left")
      xoff[$] = tw[$] - w/2.0;
    else
      xoff[$] = w/2.0;
  }
  else {
    e1 = fstout($);
    w1 = tw[e1.head];    
    e2 = nxtout(e1);
    w2 = tw[e2.head];    
    wd = w1 + w2 + sp;
    if (w > wd)
      wd = w;
    tw[$] = wd;
    xoff[$] = w1 + sp/2.0;
  }
}
BEG_G {
  $tvtype=TV_fwd;   // visit root first, then children
}
N {
  if ($.indegree == 0) {
    sscanf ($.pos, "%f,%f", &x, &y);
    $.pos = sprintf("0,%f", y);
  }
  if ($.outdegree == 0) return;
  sscanf ($.pos, "%f,%f", &x, &y);
  wd = tw[$];
  e1 = fstout($);
  n = e1.head;
  sscanf (n.pos, "%f,%f", &z, &y);
  if ($.outdegree == 1) {
    if (e1.side == "left")
      n.pos = sprintf("%f,%f",  x - tw[n] - sp/2.0 + xoff[n], y);
    else
      n.pos = sprintf("%f,%f", x + sp/2.0 + xoff[n], y);
  }
  else {
    n.pos = sprintf("%f,%f", x - tw[n] - sp/2.0 + xoff[n], y);
    e2 = nxtout(e1);
    n = e2.head;
    sscanf (n.pos, "%f,%f", &z, &y);
    n.pos = sprintf("%f,%f", x + sp/2.0 + xoff[n], y);
  }
}

参考:

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注