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

以树forms在DAG中布置顶点(即顶部没有内边的顶点,顶点仅依赖于下一层上的顶点等)相当简单,没有图形绘制算法,如Efficient Sugiyama。 但是,是否有一个简单的算法可以最大限度地减少边缘交叉? (对于某些图形,可能无法完全消除边缘交叉。)图片说千言万语,所以有一种算法会暗示没有交叉边缘的东西 。 ( 与此相比 )。

编辑:结果

我已经接受了Senthil的建议graphviz / dot – 快速浏览文档确认它很容易用作库或外部工具 ,并且输出格式非常容易解析 。 然而,我最终选择使用GraphSharp,因为我已经在使用.NET等(虽然它绝对不像dot那么强大)。 结果是“足够好”,并且通过一点边缘路由和调整可以做得更好(模糊文本是因为3.5 WPF )。

自动布局图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(); var positions = new Dictionary(); var sizes = new Dictionary(); 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>(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>(); 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 { 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; } } } 

Dot似乎符合要求:

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

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

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

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