Я думаю, что первое, что нужно do должно было бы установить направление графика от нижнего к верхнему ранжированию по умолчанию слева направо, вставив:
rankdir=LR;
... в верхней части файла .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.
EDIT: Как автоматически вставить дополнительные узлы.
Учитывая .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, и я использовал ранжирование, как определено по умолчанию для верхнего порядка, а не слева направо, что я ' вышесказанное выше. Я точно не знаю, какой файл .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 скрипт, который я назвал 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
Можно ли получить список узлов, находящихся на ранге? Я думаю, что если это можно сделать, скрипты будут проще – soandos
К сожалению, я не знаю способ разбить ранг без вставки фиктивных узлов. Я пробовал взвешивающие края неравномерно, но это не корректирует ранг узла, как это определено 'dot', который (как представляется) строго определен топологией. Если вы используете некоторые другие пакеты макета (например, 'neato'), использование дифференциально взвешенных ребер может группировать некоторые узлы и разделять другие. Однако вы теряете иерархическое упорядочение, которое вы получаете с помощью 'dot'. – Simon