Graphviz和dot文件--水平和垂直节点对齐; 中间节点

3
我正在尝试显示“圆柱形”的双音节字母组合(从左到右的格式),以便它们可以绘制在圆柱上。这些图形由连续的节点列组成。右侧的节点列(用虚线表示)是左侧节点列的替代品,以便该图形可以打印出来,卷成圆柱形,并且将虚线节点与第一列节点完全对齐,在圆柱上显示一个连续的环形图。
我已经成功地使用“rank”来获取列节点的对齐方式,并使用“group”来建议第一列和最后一列之间的水平节点对齐方式。(此链接:Graphviz Dot vertical alignment of nodes 尤其有帮助。)

问题是,我不想让第一列和最后一列的节点垂直散开(见图片);与任何其他节点列中的节点之间相比,第一列的节点(最后一列节点同理)之间不应有更多的垂直间隔。但是,各自的第一列/最后一列节点必须保持水平对齐。 (我想它试图通过中间节点将节点对1->15和13->16之间的水平无形边缘保留不被阻挡;但我不需要它们不被阻挡。节点1和13(以及15和16)应该与节点5和14之间的垂直间隔大致相同。)
TL;DR版本:问题是第一列和最后一列的节点不能竖直分开,但要求保持水平对齐。
  • 图表从左到右“流动”,以一系列“节点列”的顺序排列(值得一提的是,所有边缘都指向右侧,并且始终仅向前移动一步,从第j列到第j+1列)。
  • 最右侧的列需要是左侧列的“重复”(反映了圆柱形环绕),它应该看起来像是左侧列的复制粘贴,在相同的垂直高度处。
  • 这也适用于更复杂的图表(例如this one; 左侧和右侧列的节点相当对齐,但我担心离群值),并且必须来自自动化过程(无需手工微调)。
digraph {

node[label=""];
rankdir="LR"
1->8[label="c"]
13->8[label="a"]
8->12[label="b"]
12->10[label="a"]
10->5[label="a"]
10->14[label="b"]
5->2[label="a"]
5->9[label="b"]
14->9[label="a"]
2->4[label="b"]
9->4[label="a"]

// "wraparound" nodes
node[label="", style="dashed"];
4->15[label="a"] // since "a" connected 4 to 1, 15 must be in the same group as 1 (subs for 1)
node[label="", style="dashed"];
4->16[label="c"]

{rank=same; 1[group=g1]; 13[group=g2]}
{rank=same; 8}
{rank=same; 12}
{rank=same; 10}
{rank=same; 5, 14}
{rank=same; 2, 9}
{rank=same; 4}

// new nodes also need to have same rank; groups should pair corresponding nodes, w/ correct edge letter
{rank=same; 15[group=g1]; 16[group=g2]}

edge[style=invis];
1->15
13->16

}
2个回答

3
为了回答我的问题,可以在.dot/.gv文件上运行dot,并将输出保存为文本(其中包括有关节点和边缘位置等的详细信息)。这可以在允许一定程度的自动化的同时,对节点放置提供很多控制 - 将其留给字符串操作例程(我正在使用具有足够功能的Octave)。
因此,创建一个初步的dot文件。它将具有所有节点列,包括右侧的“环绕”节点列(并且它将垂直对齐rh列的任何方式)。
digraph {
rankdir="LR"
1 [label="1"]
4 [label="2"]
10 [label="3"]
12 [label="1", style="dashed"]
13 [label="2", style="dashed"]
14 [label="3", style="dashed"]
node[label=""];
1->2[label="b"]
4->2[label="a"]
4->6[label="b"]
4->9[label="c"]
10->6[label="a"]
2->5[label="c"]
6->11[label="c"]
9->5[label="a"]
9->11[label="b"]
5->7[label="b"]
11->7[label="a"]
7->3[label="a"]
7->8[label="b"]
3->12[label="a"]
3->13[label="b"]
8->13[label="a"]
8->14[label="b"]
{rank=same; 1, 4, 10}
{rank=same; 2, 6, 9}
{rank=same; 5, 11}
{rank=same; 7}
{rank=same; 3, 8}
{rank=same; 12, 13, 14}
}

初步结果: 初步结果图片 使用dot -Tdot选项编译它,这将输出一个文本文件,其中列出了节点位置(x,y)坐标(以pos="<x>,<y>"格式)。然后,从带有dot优化位置数据的注释文件中提取节点位置。接下来,调整最后一列节点的位置,使其高度与第一列中对应节点的高度相匹配。然后,在新的dot文件中记录所有节点,并强制指定它们的位置(使用<node#>[pos="<x>,<y>!"]语法)。
// add this to file
inputscale=72 // this tells neato to use the proper scaling; lower=nodes farther apart
1[pos="27,170!", label="1"];
4[pos="27,94!", label="2"];
10[pos="27,18!", label="3"];
node[label=""];
2[pos="127,151!"];
6[pos="127,37!"];
9[pos="127,94!"];
5[pos="227,121!"];
11[pos="227,67!"];
7[pos="327,94!"];
3[pos="427,121!"];
8[pos="427,67!"];
12[pos="527,170!", label="1", style="dashed"] // aligned with node ID 1
13[pos="527,94!", label="2", style="dashed"] // aligned with node ID 4
14[pos="527,18!", label="3", style="dashed"] // aligned with node ID 10

这个结果是使用neato编译的(它遵循dot文件中的pos="<x>,<y>!";据我所知,普通的“dot”不行)。 (我认为,neato通常包含在graphviz包中。)Neato喜欢绘制直线边缘,因此最好打开splines选项:

dot -Kneato -Gsplines=true -Gsep=.3 -Tps in_dot_file -o out_graph.ps

最终完全对齐的结果图片如下: 最终完全对齐的结果图片

然而Neato可能会将边缘符号放在边缘线的顶部。最繁琐的部分可能是从中间注释的dot文件中提取pos=数据。也许有更好的方法来解决这个问题。


1
gvpr(也包含在Graphviz软件包中)可以轻松地“抓取”pos值。以下是一个一行代码的示例,用于打印节点名称和位置:gvpr 'N{print ($.name, "::", $.pos)}' xxx.dot - undefined

1

(你所有的node[label=""];语句都是冗余的)
如果您删除group属性,它几乎可以产生您想要的结果,但虚线节点不完全对齐。
以下是对您的图表不同的看法:

digraph {
  node[label=""];
  {
    rank=same
    1 8 12 10 14 9 4 15 [style=dashed]
  }
  {
    rank=same
    13 5 2 16 [style=dashed]
  }

1->8[label="c"]
13->8[label="a"]
8->12[label="b"]
12->10[label="a"]
10->5[label="a"]
10->14[label="b"]
5->2[label="a"]
5->9[label="b"]
14->9[label="a"]
2->4[label="b"]
9->4[label="a"]

// "wraparound" nodes
node[style="dashed"];
4->15[label="a"] 
node[style="dashed"];
4->16[label="c"]

edge[style=invis weight=90];
1 ->13 
15->16
}

给出: enter image description here

不使用rankdir="LR",而是使用"rank"来强制/建议左右列节点在相同高度上成对排列确实效果更好。问题在于这会破坏中间节点列的结构——"{rank=same; 5, 14}"等行是有意为之的,目的是将中间节点垂直堆叠(图形作为节点列的进展)。这些"圆柱"图也可能变得更加复杂,因此强制将"中间"节点放置在特定行上(或通过组和不可见边将它们强制放置在列中的特定堆叠顺序)可能对于清晰的布局来说过于限制性。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接