代码之家  ›  专栏  ›  技术社区  ›  Robert Fraser

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

  •  15
  • Robert Fraser  · 技术社区  · 14 年前

    如果没有高效的Sugiyama等图形绘制算法,以树形形式(即顶部没有边缘的垂直,仅依赖于下一层的垂直等)在DAG中布局垂直是相当简单的。然而,有没有一个简单的算法来做到这一点,以最小化边缘交叉?(对于某些图形,可能无法完全消除边缘交叉。)一张图片表示一千个单词,因此是否有一种算法可以建议 something without crossing edges . ( compared to this )

    编辑:结果

    我接受了Senthil的建议graphviz/dot——快速查看文档可以确认这很容易 use it as a library or external tool the 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 回复  |  直到 14 年前
        1
  •  7
  •   spenthil    14 年前

    DOT似乎适合该法案:

    点-“分层”或分层 有向图的图纸。这个 布局算法针对 相同方向(从上到下或左 然后试图避免 边缘交叉并缩短边缘长度。

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

        2
  •  5
  •   Daniel Brückner Pradip    14 年前

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

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

    推荐文章