绘制有向非循环图:最小化边缘交叉?

时间:2010-05-17 21:47:27

标签: c# wpf algorithm graph graph-drawing

以树形式在DAG中布置顶点(即顶部没有内边的顶点,顶点仅依赖于下一层的顶点,等等)相当简单,没有图形绘制算法,如Efficient Sugiyama。但是,是否有一个简单的算法可以最大限度地减少边缘交叉? (对于某些图表,可能无法完全消除边缘交叉。)一张图片说了千言万语,所以有一个算法会建议something without crossing edges。 (compared to this)。

编辑:结果

我接受了Senthil建议使用graphviz / dot - 快速查看文档确认use it as a library or external toolthe output format is surprisingly easy to parse非常容易。但是,我最终选择使用GraphSharp,因为我已经在使用.NET等(虽然它绝对不像dot那样强大)。结果是“足够好”,通过一点边缘路由和调整可以做得更好(模糊文本是因为3.5 WPF)。

Automatically layed-out graph http://public.blu.livefilestore.com/y1pEY8I95GtlzcxZzhDMhhKoUyejT_sVVZ4jlsDK2fdl6XAR4WV4-yuSesY6chXokmAZxdJXZ4Bv674TqwpT1-fOg/dag3.gif

这是完整的 C#代码(这是引用QuickGraph或GraphSharp的所有代码 - 是的;就这么简单):

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 个答案:

答案 0 :(得分:7)

点似乎适合这个账单:

  

点 - “层次”或分层   有向图的图纸。该   布局算法的目标是边缘   相同的方向(从上到下,或左   (右)然后试图避免   边缘交叉并减少边长。

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

答案 1 :(得分:5)

您可以尝试使用Topological Sorting。在第一步中,您可以通过执行拓扑排序并始终在单个图层中对独立节点进行分组来确定布局的级别(从上到下)。对于有向无环图,这将永远成功。

然后你可以尝试对每一层(从左到右)进行拓扑排序,考虑输入和输出端口以及可能相邻的层的位置。我对这一步的图像有点模糊,但我可以想象它对于像你的例子这样的图形是可行的。