9

我有一组用 DOT 语言编码的有向图。我想构建一个图图,使得超图中的每个节点都是这些有向图之一。有没有办法在 GraphViz 框架内做到这一点?

我知道这gvpack将允许我将多个图形组合成一个 .dot 文件。但我不知道它是否允许我声明这些图之间的边。

4

2 回答 2

10

简短的回答是gvpack不声明子图之间的边。事实上,当子图之间有共同的节点名称时,gvpack重命名它们以避免冲突。但是,这是可以修复的。

例如,给定三个.dot文件1.dot

digraph {
    A -> B
    A -> C
    }

2.dot

digraph {
    D -> E
    E -> F
    }

...和3.dot

digraph {
    D -> G
    G -> A
    }

...运行gvpack -u 1.dot 2.dot 3.dot | dot -Tjpg -ogvp1.jpg给出以下图表gvp1.jpg

在此处输入图像描述

如您所见,gvpack已重新标记重复的节点名称。但是,我们可以使用 轻松反转重新标记gvpack -u 1.dot 2.dot 3.dot | sed 's/_gv[0-9]\+//g' | dot -Tjpg -ogvsub.jpg,从而生成以下图形gvsub.jpg

在此处输入图像描述

这种方法依赖于具有共同节点名称的子图,因此可能需要在子图.dot文件中插入额外的节点来实现这一点。

(编辑:上面的解决方案显示了节点合并但不与集群中的子图合并的图。以下解决方案显示了集群中的子图。)

给定.dot文件1.dot(这些文件与上面的文件相同,除了我给每个有向图命名):

digraph g1 {
    A -> B
    A -> C
    }

2.dot

digraph g2 {
    D -> E
    E -> F
    }

...和3.dot

digraph g3 {
    D -> G
    G -> A
    }

...连同hdr.dot

digraph GMaster {
    compound = true;
    g1 [style=invisible, height = 0, width = 0, label=""];
    g2 [style=invisible, height = 0, width = 0, label=""];
    g3 [style=invisible, height = 0, width = 0, label=""];
    g1 -> g2 [lhead=clusterg2, ltail=clusterg1];
    g1 -> g3 [lhead=clusterg3, ltail=clusterg1]

...和tail.dot

}

...我们可以运行cat 1.dot 2.dot 3.dot | sed 's/digraph \(\w*\) *{/subgraph cluster\1 { \1/' | cat hdr.dot - tail.dot | dot -Tjpg -oclust1.jpg以提供文件clust1.jpg

在此处输入图像描述

因此,在头文件中,我为每个子图添加了一个不可见节点,与子图同名,用于compound=true允许集群之间的边。我已经指定了要在集群之间绘制的边缘,并且我已经为不可见节点之间的每个边缘设置了lheadltail,以确保将正确的集群用作每个边缘的头部和尾部。在使用sed.

显示了节点 D、G 和 A 之间的边,因为这些节点在集群之间是公共的。此外,它们中的每一个都只显示在一个集群中。如果节点对集群来说是唯一的,那么集群之间唯一显示的边将是不可见节点之间的边。这可以在下图中看到,其中我已将节点重命名为3.dot

在此处输入图像描述

还有一个我还没有完全修复的缺陷。不可见节点仍然占用一点空间,因此集群框看起来不平衡,因为不可见节点位于可见节点旁边。这也意味着簇之间的边的头部指向簇框的一侧而不是中间。目前,我看不出对此能做些什么,除非我们准备好查看每个子图并找到一个已经在该子图/集群中的节点作为该子图/集群的代表节点(即我们为该集群绘制边缘的对象)。对于几个子图,这可以很容易地手动完成,但如果有很多子图,那就太乏味了。

相比之下,我上面使用的方法只需要我们知道集群的名称并可以将其插入到hdr.dot文件中。

我已经hdr.dot为这种情况手动构建了文件,但文件的内容hdr.dot可以从其他.dot文件中提取,使用sed, awkperl或者python如果有需要。hdr.dot如果有关应连接哪些集群的信息在某处可用,该脚本还可以插入边缘以将集群链接到其中。

于 2013-05-12T09:58:07.967 回答
0

处理此问题的最简单方法是允许gvpack执行重命名,而不是尝试反转它,而是断言节点的标签。

从...开始

digraph g1 {
  A -> B; 
  A -> C;
  A [label="A"];
  B [label="B"];
  C [label="C"];
}

digraph g2 {
  D -> E
  E -> F
  D [label="D"];
  E [label="E"];
  F [label="F"];
}

digraph g3 {
  D -> G
  G -> A
  D [label="D"];
  G [label="G"];
  A [label="A"];
}

你应该从最初的第一步得到你想要的结果

于 2020-02-09T23:20:14.970 回答