Ответ 1
Я думаю, что первое, что нужно сделать, это установить направление графика из нижнего уровня по умолчанию влево-вправо, вставив:
rankdir=LR;
... в верхней части файла .dot
, после первого {
. Это должно ориентировать график слева направо и тем самым сделать его гораздо более компактным для такого случая, который имеет длинные метки node. Точно как это можно было бы сделать, будет зависеть от формата callgraph.dot
, но при условии, что он выглядит примерно так:
digraph G {
node [shape=rectangle];
...
... то что-то вроде:
sed 's/digraph G {/digraph G { \n rankdir=LR;/'
... выполнит эту работу.
Еще один подход, который я использовал в прошлом, заключается в том, чтобы вставить фиктивные узлы в ребра, чтобы уменьшить количество узлов с одинаковым рангом (и поэтому будет рисоваться в одной строке (с rankdir=TB
, которая является по умолчанию) или столбца (с помощью rankdir=LR
). Это удобно делать при записи файлов .dot
вручную, но сложнее script.
Если вы хотите script вставлять лишние узлы в некоторых ребрах для распространения узлов, которые обычно находятся в одном ранге в нескольких рангах, вы можете сделать это, запустив dot -Tplain
для вывода текстового файла *, который включает ( среди прочего) список узлов с координатами X и Y центра каждого node. Затем вы можете использовать gawk
для чтения этого списка, найти любую большую группу узлов с той же координатой X (если rankdir=TB
) или координатой Y (если rankdir=LR
), а затем обработать оригинал .dot
, чтобы вставить лишний пуст node до (скажем) половины узлов в этой группе, чтобы группа была распределена по двум рангом, а не по одному. У меня не было случая сделать это сам, хотя.
* См. Emden Gansner, Eleftherios Koutsofios и Stephen North (2006) Рисование графиков с точкой, Приложение B.
EDIT: как автоматически вставлять дополнительные узлы.
Указано .dot
файл test1.dot
следующим образом:
digraph G {
n1 -> n20
n1 -> n21
n1 -> n22
n20 -> n3
n21 -> n3
n22 -> n3
}
..., который отображает график.
... running 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, и я использовал ранжирование, как определено по умолчанию для верхнего порядка, а не слева направо, что я ' вышесказанное выше. Я точно не знаю, какой файл .dot
выводится 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 script, который я назвал 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
, получим теперь:
... который дает нам то, что мы хотим, я думаю.