【问题标题】:Graphviz and dot files--horizontal and vertical node alignment; intervening nodesGraphviz 和 dot 文件——水平和垂直节点对齐;中间节点
【发布时间】:2021-01-27 03:01:57
【问题描述】:

我正在尝试显示“圆柱形”有向图(以从左到右的格式),以便可以在圆柱体上绘制它们。这些图由连续的节点列组成。最右边的节点列(用虚线描绘)是最左边的节点列的替代品 - 这样可以打印出图形,卷成圆柱体,并且虚线节点与第一列的“下方”完全对齐节点,它显示了一个连续的(圆柱形)循环图。

我已经成功使用“rank”来获得柱状节点对齐,并使用“group”来建议第一列和最后一列之间的水平节点对齐。 (这个Graphviz Dot vertical alignment of nodes 特别有用。)

问题是,我不希望第一列和最后一列的节点都垂直展开(见图);与任何其他节点列中的节点之间相比,第一个 col 的节点(最后一个 col 的节点同上)之间不应有更多的垂直分隔。但相应的第一个/最后一个 col 节点必须保持水平对齐。 (我想它试图让节点对 1->15 和 13->16 中的水平不可见边缘不受中间节点的阻碍;但我不需要它们不受阻碍。节点 1 和 13(以及 15 和 16)应该有大约与(例如)节点 5 和 14 之间的垂直间隔相同。)

TL;DR 版本

  • 图形从左到右“流动”,按“节点列”的顺序排列(fwiw 所有边都指向右侧,并且总是只走一步,从第 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

}

【问题讨论】:

    标签: layout graphviz


    【解决方案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
    }
    

    给予:

    【讨论】:

    • 不使用 rankdir="LR",而使用“rank”来强制/建议左右列节点在相同高度成对排列确实效果更好。问题是这扰乱了中间节点列的结构——“{rank=same; 5, 14}”等行是故意的,垂直堆叠中间节点(图作为节点列的进展) .这些“圆柱”图也可能变得更加复杂,因此在某些行上强制“中间”节点(或通过组和不可见边强制它们按列中的特定堆叠顺序)对于干净的布局可能过于严格。跨度>
    【解决方案2】:

    要回答我自己的问题,可以在 .dot/.gv 文件上运行 dot,并将输出另存为文本(其中包括有关节点和边缘位置等的大量详细信息)。这可以对节点放置进行大量控制,同时允许一定程度的自动化——将其留给字符串操作例程(我使用的是 Octave,它有足够的功能)。

    所以,创建一个初步的点文件。它将包含所有节点列,包括右侧的“环绕”节点列(它会根据需要垂直对齐 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 优化的位置数据的带注释的文件,然后被刮取用于节点位置。然后调整最后一列节点的位置,使它们的高度与第一列中的互补节点相匹配。然后将所有节点记录在一个新的点文件中,并强制它们的位置(使用<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编译的(它尊重点文件中的pos="<x>,<y>!";AFAICT纯“点”没有)。 (我认为,Neato 通常包含在 graphviz 包中。)Neato 喜欢绘制直边,因此打开样条线选项会有所帮助:

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

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

    但是,Neato 可能会将边缘符号放在边缘线的顶部。最繁琐的部分可能是从中间带注释的点文件中抓取pos= 数据。也许有更好的方法来做到这一点。

    【讨论】:

    • gvpr(也包含在 Graphviz 包中)将很容易地“抓取” pos 值。这是打印节点名称和位置的单行代码:gvpr 'N{print ($.name, "::", $.pos)}' xxx.dot
    猜你喜欢
    • 2015-01-21
    • 2018-05-16
    • 2021-11-30
    • 1970-01-01
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-27
    相关资源
    最近更新 更多