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

War*_*now 3 dot graphviz 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
   }
}
Run Code Online (Sandbox Code Playgroud)

产生这个输出:

Graphviz输出

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

A B C
D E F
G H I
Run Code Online (Sandbox Code Playgroud)

而不是

D E F
G H I
A B C
Run Code Online (Sandbox Code Playgroud)

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

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

mar*_*pet 6

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

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

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

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


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

因此,没有集群rankdir=LR,图表看起来像这样(没有惊喜):

A D G
B E H
C F I
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但是什么时候rankdir=LR应用?

ERG写道:

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

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

G H I
D E F
A B C
Run Code Online (Sandbox Code Playgroud)

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

A B C
D E F
G H I
Run Code Online (Sandbox Code Playgroud)

为什么?Stephen North回答说:

在某些时候,我们决定从上到下应该是默认值,
即使图形被旋转,所以有代码在内部翻转平边.

因此,图形布局为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
Run Code Online (Sandbox Code Playgroud)

虽然这对于简单的图表非常有效,但似乎在涉及集群时,事情会有所不同.通常边缘也会在簇内翻转(如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; }
}
Run Code Online (Sandbox Code Playgroud)