我想的第一件事可以通过插入:
将图形的方向从默认的从下到上的排名从左到右设置为从左到右
...靠近.dot
文件的顶部,在第一个{
之后。这应该使图形从左到右定向,从而使它对于像这样具有长节点标签的情况更加紧凑。究竟怎么可以这样做将取决于callgraph.dot
的格式,但是,假设它看起来是这样的:
digraph G {
node [shape=rectangle];
...
...然后是这样的:
sed 's/digraph G {/digraph G { \n rankdir=LR;/'
...会做的工作。
我过去采用的另一种方法是将虚拟节点插入边缘以减少具有相同等级的节点的数量(并且因此将被绘制在同一行中(使用rankdir=TB
,这是默认值)或列(与rankdir=LR
)。当手动编写.dot
文件但很难编写脚本时,可以直接使用
如果您想脚本在某些边缘插入额外的节点来传播通常处于相同等级的节点你可以通过运行dot -Tplain
来输出一个纯文本文件*,其中包含(其中包含)节点列表以及每个节点中心的X坐标和Y坐标。然后,您可以使用gawk
来读取该列表,找到具有相同X坐标(如果rankdir=TB
)或Y坐标(如果rankdir=LR
)的任何大组节点,然后处理原始.dot
文件以在(例如)一半之前插入额外的空白节点的组中的节点,以便该组分散在两个等级而不是一个等级上。不过,我自己也没有机会这样做。
*请参阅埃姆登Gansner,埃莱夫塞里奥Koutsofios和斯蒂芬北(2006)Drawing graphs with dot,附录B.
编辑:如何自动插入额外的节点。
给定一个文件.dot
test1.dot
如下:
digraph G {
n1 -> n20
n1 -> n21
n1 -> n22
n20 -> n3
n21 -> n3
n22 -> n3
}
...产生所示的曲线图。
...运行dot -Tplain test1.dot >test1.plain
给出了文件test1.plain
:
graph 1 2.75 2.5
node n1 1.375 2.25 0.75 0.5 n1 solid ellipse black lightgrey
node n20 0.375 1.25 0.75 0.5 n20 solid ellipse black lightgrey
node n21 1.375 1.25 0.75 0.5 n21 solid ellipse black lightgrey
node n22 2.375 1.25 0.75 0.5 n22 solid ellipse black lightgrey
node n3 1.375 0.25 0.75 0.5 n3 solid ellipse black lightgrey
edge n1 n20 4 1.1726 2.0394 1.0313 1.9019 0.83995 1.7159 0.68013 1.5605 solid black
edge n1 n21 4 1.375 1.9958 1.375 1.8886 1.375 1.7599 1.375 1.6405 solid black
edge n1 n22 4 1.5774 2.0394 1.7187 1.9019 1.9101 1.7159 2.0699 1.5605 solid black
edge n20 n3 4 0.57736 1.0394 0.71875 0.90191 0.91005 0.71592 1.0699 0.56054 solid black
edge n21 n3 4 1.375 0.99579 1.375 0.88865 1.375 0.7599 1.375 0.64045 solid black
edge n22 n3 4 2.1726 1.0394 2.0313 0.90191 1.8399 0.71592 1.6801 0.56054 solid black
stop
因此,我们现在可以在两个文件处理一起。我将为此使用Python,因为在Python中执行它比在Awk中执行起来要容易一些。为了这个例子,我已经将排名中的节点数量限制为2,并且我已经使用默认的从下到上排序而不是从左到右排序定义的排名,上面已经提出过。我不知道clang
文件是由clang
输出的,所以可能需要稍微修改一下这个例子来考虑这个问题。
import sys,re;
plain = open(sys.argv[2])
nodesInRank = {}
for line in plain:
x = line.split()
rankloc = 3 # rank is in the y column for the vertical case.
# Change this to rankloc = 2 for the horizontal case
if len(x) > 0 and x[0] == "node":
nodesInRank[x[rankloc]] = nodesInRank.get(x[rankloc],[]) + [x[1]]
maxNodesInRank = 2
dummies = set()
for n in nodesInRank.values():
if len(n) > maxNodesInRank:
dummies = dummies | set(n[:len(n)//2])
dot = open(sys.argv[1])
for line in dot:
line = line.rstrip()
line2 = ""
for d in dummies:
m = "-> +%s" % (d)
if re.search(m,line):
line = re.sub(m,"-> dummy_%s [dir = none]\n dummy_%s -> %s" % (d,d,d),line)
line2 = '\tdummy_%s [shape=none, width=0, height=0, label=""];' % (d)
print (line)
if len(line2) > 0:
print (line2)
鉴于这种Python脚本,我已经叫breakrank.py
,我现在就可以运行它:
python breakrank.py test1.dot test1.plain >test_dummy.dot
...这使在test_dummy.dot
如下:
digraph G {
n1 -> dummy_n20 [dir = none]
dummy_n20 -> n20
dummy_n20 [shape=none, width=0, height=0, label=""];
n1 -> n21
n1 -> n22
n20 -> n3
n21 -> n3
n22 -> n3
}
如果我们通过dot
运行此,我们现在得到:
......这让我们想要什么,我想。
肯定比我有更好的,但有没有办法随机分裂一个等级,并使其显示好像它是两个级别(没有明确插入虚拟节点) – soandos 2013-04-25 23:05:45
是否有可能得到一个排名靠前的节点列表?我认为,如果可以这样做,脚本将更容易 – soandos 2013-04-25 23:14:52
不幸的是,我不知道一种方法来分裂一个排名,而不插入虚拟节点。我尝试过对边缘加权不均匀,但不会调整'dot'布局的节点的等级,这是由拓扑严格定义的。如果您使用某些其他布局包(例如'neato'),则使用差分加权边可以聚集一些节点并分隔其他节点。但是,您失去了使用'dot'获得的等级顺序。 – Simon 2013-04-25 23:26:35