有个画图需求是需要生成特定排版的关系图。
graphviz 对于一般情况都是自动排的,有时候会抽风,比如这种情况:
而我们想要这样:
对初学者提一下 ,如何实现 3 2 5 ,1467 从左到右排列呢?待会会顺便讲解这个的实现方法。
可以看到 3 就是不能在 2 的左边 。而代码中加入了限制:
subgraph {
rankdir = LR;
rank = same;
s3 -> s2 [style = invis;];
s2 -> s5 [style = invis;];
}
这里 invis 边就是用来控制位置用的。因此我们继续加入一些限制来吧这个 3 移动回到 2 的左边。
subgraph {
s1 -> s4 [style = invis;];
s4 -> s6 [style = invis;];
s6 -> s7 [style = invis;];
s1 -> start [style = invis;];
}
subgraph {
rankdir = LR;
rank = same;
start [style = invis;];
start -> s3 [style = invis;];
s3 -> s2 [style = invis;];
s3 -> s5 [style = invis;];
}
原理是通过添加额外的节点和边(仅用于排版,输出时隐藏)以及 rank 限制条件,来控制生成节点尽量满足需要的排版。不过这个实际和算法执行起来算出来的效果有关。
上图中框起来的是 invis 边。
下面进行讲解:
红色、橙色:为了限制节点的顺序是水平的 3 2 5 排序摆放。1 4 6 7 水平顺序摆放,否则会错乱,graphviz 会自己想怎么排就怎么拍(设置 rankdir = LR 能生效的前提是他们节点之间有有向边!)
绿色:为了把 3 从 graphviz 糟糕算法生成的右边拉回到左边,至于为什么会这样,其实和 graphviz 用的算法和选择策略有关,可以参照Graphviz中的资料,实际就是算法因为是比较通用的,在这个 case 下,算法恰好生成的时候认为 3 再右边更加的均匀。
实际效率需求的生产中,我们还是用 GUI 工具吧,这种除了要通过代码生成的情景或者大量的数据下之外,比如这种需要对特定节点特定边的位置进行控制的,没有太大必要去用脚本工具画图。。
附录,完整代码:
digraph b {
overlap = false;
splines = true;
s8 [label = 8;group = g1;];
s2 [label = 3;group = g2;];
s3 [label = 2;group = g2;];
s5 [label = 5;group = g2;];
s1 [label = 1;group = g3;];
s4 [label = 4;group = g3;];
s6 [label = 6;group = g3;];
s7 [label = 7;group = g3;];
s8 -> s3 [dir = back;];
s1 -> s3;
s3 -> s6 [dir = back;];
s4 -> s3;
s7 -> s3;
s2 -> s3;
s3 -> s2;
s3 -> s5;
s5 -> s3;
subgraph a {
rankdir = TB;
rank = same;
s2;
s3;
s5;
}
subgraph {
s1 -> s4 [style = invis;];
s4 -> s6 [style = invis;];
s6 -> s7 [style = invis;];
s1 -> start [style = invis;];
}
subgraph {
rankdir = LR;
rank = same;
start [style = invis;];
start -> s2 [style = invis;];
s2 -> s3 [style = invis;];
s3 -> s5 [style = invis;];
}
subgraph c {
rankdir = TB;
rank = same;
s1;
s4;
s6;
s7;
}
subgraph d {
rank = same;
s8;
}
rank = same;
}