为什么Graphviz在引入子图时不再最小化边长

时间:2012-03-06 16:09:45

标签: graphviz dot subgraph

我有这个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 output

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

A B C
D E F
G H I

而不是

D E F
G H I
A B C

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

为什么Graphviz在引入子图时将A B C置于底部?

1 个答案:

答案 0 :(得分:6)

这并不是最小化边长,尤其是在示例中,边是使用属性constraint=false定义的。

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

  • 图表中节点的外观顺序非常重要。
  • rankdir更改为LR包含不可预测(或至少难以预测)的行为,和/或可能仍然是一两个错误(search rankdir)。

我会尽力解释并理解graphviz,但你可能想继续阅读this reply of Emden R. Gansner on the graphviz mailing list以及Stephen North的以下答案 - 他们应该知道,所以我会引用一些......


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

因此,如果没有群集和rankdir=LR,图表就会显示为这样(没有惊喜):

A D G
B E H
C F I

到目前为止,这么好。但是在应用rankdir=LR时会发生什么?

ERG写道:

  

Dot通过普通TB布局处理rankdir = LR,然后旋转   逆时针旋转90度(当然,然后处理   节点旋转,边缘方向等)。因此,第一个是   您可以像在TB布局中一样位于子图2的左侧   期待,然后在旋转后最终低于它。如果你想   第一个要在顶部,在图表中将其列为第二个。

因此,如果这是正确的,没有集群,节点应该如下所示:

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; }
}