5

我有这个 Graphviz 图:

digraph
{
   rankdir="LR";
   overlap = true;
   Node[shape=record, height="0.4", width="0.4"];
   Edge[dir=none];

   A B C D E F G H I 

   A -> B -> C
   D -> E -> F
   G -> H -> I

   Edge[constraint=false]

   A -> D -> G

   subgraph clusterX
   {
      A
      B
   }

   subgraph clusterY
   {
      E
      H
      F
      I
   }
}

产生这个输出:

Graphviz 输出

我本来希望 A 和 D 之间的边缘长度最小化,以便节点排列为:

A B C
D E F
G H I

而不是

D E F
G H I
A B C

如果我删除子图定义,这将按预期工作。

为什么在引入子图时 Graphviz 将 ABC 放在底部?

4

1 回答 1

8

这实际上并不是关于最小化边长,特别是因为在示例中边是用属性定义的constraint=false

虽然这不是一个完整的答案,但我认为可以在以下两点中找到它:

  • 图中节点的出现顺序很重要
  • 更改rankdirLR包含不可预测(或至少难以预测)的行为,和/或可能仍然是一两个错误(搜索 rankdir)。

我会尽力解释并理解graphviz,但您可能想继续阅读Emden R. Gansner 在graphviz 邮件列表上的回复以及Stephen North 的以下答案 - 他们应该要知道,所以我会引用其中的一些......


为什么节点出现的顺序很重要?默认情况下,在自上而下的图中,首先提到的节点将出现在后续节点的左侧,除非边和约束导致更好的布局。

因此,如果没有集群 和rankdir=LR,图形看起来像这样(毫不奇怪):

A D G
B E H
C F I

到现在为止还挺好。但是rankdir=LR应用时会发生什么?

ERG 写道:

Dot 通过正常的 TB 布局处理 rankdir=LR,然后将布局逆时针旋转 90 度(当然,然后处理节点旋转、边缘方向等)。因此,子图 1 在 TB 布局中位于子图 2 的左侧,如您所料,然后在旋转后最终低于它。如果您希望子图 1 位于顶部,请将其列在图中的第二位。

所以如果这是正确的,没有集群,节点应该是这样的:

G H I
D E F
A B C

实际上,它们确实看起来像这样:

A B C
D E F
G H I

为什么?斯蒂芬诺斯 回复:

在某些时候,我们决定从上到下应该是默认设置,
即使图形是旋转的,所以有代码可以在内部翻转平面边缘。

因此,图形布局为 TB,逆时针旋转,平面边缘翻转:

A D G     G H I     A B C
B E H --> D E F --> D E F
C F I     A B C     G H I

虽然这对于简单的图表非常有效,但似乎当涉及到集群时,情况就有些不同了。通常边缘也会在簇内翻转(如clusterY),但在某些情况下,平面边缘翻转并不像人们想象的那样起作用。您的示例就是其中一种情况。

为什么这些边缘的翻转会出现错误或限制?因为在使用rankdir=TB.


幸运的是,变通方法通常很容易——例如,您可以使用节点的出现顺序来影响布局:

digraph
{
   rankdir="LR";
   node[shape=record, height="0.4", width="0.4"];
   edge[dir=none];

   E; // E is first node to appear
   A -> B -> C;
   D -> E -> F;
   G -> H -> I;

   edge[constraint=false]
   A -> D -> G;

   subgraph clusterX { A; B; }
   subgraph clusterY { E; F; H; I; }
}
于 2012-03-06T22:21:11.657 回答