Рисование направленных ациклических графов: минимизация пересечения ребер?

выкладывание вертикалей в DAG в виде дерева (т. е. вертикалей без внутренних ребер сверху, вертикалей, зависящих только от тех, что на следующем уровне и т. д.) довольно прост без алгоритмов рисования графов, таких как эффективная Сугияма. Однако есть ли простой алгоритм для этого, который минимизирует пересечение ребер? (Для некоторых графиков может быть невозможно полностью устранить пересечение ребер.) Изображение говорит тысячу слов, так что есть алгоритм, который предложил бы что-то без пересечение краев. (по сравнению с этим).

EDIT: результат

Я принял предложение Senthil graphviz / dot-быстрый взгляд на документы подтверждает, что это очень легко используйте его как библиотеку или внешний инструмент и выходной формат удивительно легко разобрать. Тем не менее, я решил использовать GraphSharp вместо этого, так как я уже использую .NET и т. д. (Хотя он определенно не такой мощный, как dot). Результат "достаточно хорош", и может быть сделан намного лучше с небольшой маршрутизацией края и настройкой (размытый текст из-за 3.5 WPF).

автоматически продуманная графика http://public.blu.livefilestore.com/y1pEY8I95GtlzcxZzhDMhhKoUyejT_sVVZ4jlsDK2fdl6XAR4WV4-yuSesY6chXokmAZxdJXZ4Bv674TqwpT1-fOg/dag3.gif

здесь полное код C# (это весь код, который ссылается на QuickGraph или GraphSharp -- yeah; это было так просто):

internal static class LayoutManager
{
    private const string ALGORITHM_NAME = "EfficientSugiyama";
    private const bool MINIMIZE_EDGE_LENGTH = true;
    private const double VERTEX_DISTANCE = 25;
    private const double LAYER_DISTANCE = 25;
    private const double MIN_CANVAS_OFFSET = 20;

    public static void doLayout(GraphCanvas canvas)
    {
        // TODO use a background thread
        // TODO add comments
        canvas.IsEnabled = false;
        canvas.Cursor = Cursors.Wait;
        var graph = new BidirectionalGraph<GraphNode, LayoutEdge>();
        var positions = new Dictionary<GraphNode, Point>();
        var sizes = new Dictionary<GraphNode, Size>();
        foreach(var node in canvas.nodes)
        {
            var size = node.RenderSize;
            graph.AddVertex(node);
            positions.Add(node, new Point(node.left + size.Width / 2, node.top + size.Height / 2));
            sizes.Add(node, size);
        }
        foreach(var edge in canvas.edges)
        {
            graph.AddEdge(new LayoutEdge(edge));
        }

        var context = new LayoutContext<GraphNode, LayoutEdge, BidirectionalGraph<GraphNode, LayoutEdge>>(graph, positions, sizes, LayoutMode.Simple);
        var parameters = new EfficientSugiyamaLayoutParameters();
        parameters.VertexDistance = VERTEX_DISTANCE;
        parameters.MinimizeEdgeLength = MINIMIZE_EDGE_LENGTH;
        parameters.LayerDistance = LAYER_DISTANCE;
        var factory = new StandardLayoutAlgorithmFactory<GraphNode, LayoutEdge, BidirectionalGraph<GraphNode, LayoutEdge>>();
        var algorithm = factory.CreateAlgorithm(ALGORITHM_NAME, context, parameters);
        algorithm.Compute();
        canvas.deselectAll();

        var minx = algorithm.VertexPositions.Select(kvp => kvp.Value.X - (kvp.Key.RenderSize.Width / 2)).Aggregate(Math.Min);
        var miny = algorithm.VertexPositions.Select(kvp => kvp.Value.Y - (kvp.Key.RenderSize.Height / 2)).Aggregate(Math.Min);
        minx -= MIN_CANVAS_OFFSET;
        miny -= MIN_CANVAS_OFFSET;
        minx = minx < 0 ? -minx : 0;
        miny = miny < 0 ? -miny : 0;
        foreach(var kvp in algorithm.VertexPositions)
        {
            var node = kvp.Key;
            var pos = kvp.Value;
            node.left = (pos.X - (node.RenderSize.Width / 2)) + minx;
            node.top = (pos.Y - (node.RenderSize.Height / 2)) + miny;
        }
        canvas.Cursor = Cursors.Arrow;
        canvas.IsEnabled = true;
    }

    private sealed class LayoutEdge : IEdge<GraphNode>
    {
        private readonly ConnectingEdge _edge;
        public LayoutEdge(ConnectingEdge edge) { _edge = edge; }
        public GraphNode Source { get { return _edge.output.node; } }
        public GraphNode Target { get { return _edge.input.node; } }
    }

2 ответов


Dot кажется, что это будет соответствовать счету:

dot - "иерархический" или слоистый чертежи направленных графиков. Этот алгоритм компоновки направлен ребрами в одном направлении (сверху вниз или слева справа), а затем пытается избежать пересечение кромок и уменьшение длины кромок.

https://docs.google.com/viewer?url=http://www.graphviz.org/pdf/dotguide.pdf


вы можете попробовать использовать Топологическая Сортировка. На первом этапе вы можете определить уровни (сверху вниз) макета, выполнив топологическую сортировку и всегда группируя независимые узлы в одном слое. Это всегда будет успешным для направленных ациклических графов.

тогда вы могли бы попытаться выполнить топологический вид каждого слоя (слева направо) с учетом местоположения портов ввода и вывода и, вероятно, соседних слоев. Мой образ этого шаг немного размыт, но я могу себе представить, что это выполнимо для графиков, таких как ваш пример.