有向无环图的自动布局算法

来源:互联网 发布:node.js 后端还是前端 编辑:程序博客网 时间:2024/05/16 19:18

原文地址:http://blog.csdn.net/xoyojank/article/details/8249719

最近业余在做一个基于结点的编辑工具玩, 遇到一个问题, 就是结点和连线多了, 经常会出现重叠交叉的问题, 导致图看不清楚:


要是这个样子, 还不如不用图清楚呢, 所心就需要找一个方法来进行自动布局, 理想情况是这样的(手动整理结果):


当然, 手动整理的话, 每个人弄出来的结果都不一样. 自动的算法肯定没有100%完美的, 但是总是能方便不少的

在google了一会儿后, 发现这种结点-线组成的图是一有个学名的: directed acyclic graph, 例如这样:


无非我这个图结点上的连接点是有限制的, 但这个对于布局算法来说, 影响不大. 因为布局只需要大体考虑每个结点的位置

那么, 这个算法需要满足几个条件: 

  1. 结点之间不能有重叠
  2. 连线之间尽量减少交差
  3. 结点之间是有基本的层次关系对齐的
基于这些限制条件, google到一个比较有名的算法Sugiyama's layout algorithm
初步看了一上, 这个算法比较复杂, 是多种算法的集合
自己不是很熟悉这方面的理论知识, 所以还是决定采用第三的算法库
C++可以使用的图绘制算法库, 比较常见的有Graphviz, OGDF, Boost Graph
根据这个问题(http://stackoverflow.com/questions/2751826/which-c-graph-library-should-i-use)的推荐, 尝试了OGDF, 效果还不错(可惜是GPL协议)
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //------------------------------------------------------------------------------  
  2. void  
  3. QNodesEditor::autoLayout()  
  4. {  
  5.     using namespace ogdf;  
  6.     Graph graph;  
  7.     // setup graph  
  8.     QMap<NodeElement*, QNEBlock*> nodeMap;  
  9.     foreach(QGraphicsItem * item, scene->items())  
  10.     {  
  11.         if (item->type() == QNEBlock::Type)  
  12.         {  
  13.             NodeElement* node = graph.newNode();  
  14.             item->setData(QNEBlock::Type, qVariantFromValue((void*)node));  
  15.             nodeMap[node] = (QNEBlock*)item;  
  16.         }  
  17.     }  
  18.     foreach(QGraphicsItem * item, scene->items())  
  19.     {  
  20.         if (item->type() == QNEConnection::Type)  
  21.         {  
  22.             QNEConnection* connection = (QNEConnection*)item;  
  23.             NodeElement* node1 = (NodeElement*)connection->port1()->block()->data(QNEBlock::Type).value<void*>();  
  24.             NodeElement* node2 = (NodeElement*)connection->port2()->block()->data(QNEBlock::Type).value<void*>();  
  25.             graph.newEdge(node1, node2);  
  26.         }  
  27.     }  
  28.     // node size  
  29.     GraphAttributes graphAttr(graph,  
  30.                               GraphAttributes::nodeGraphics | GraphAttributes::edgeGraphics |  
  31.                               GraphAttributes::nodeLabel | GraphAttributes::nodeColor |  
  32.                               GraphAttributes::edgeColor | GraphAttributes::edgeStyle |  
  33.                               GraphAttributes::nodeStyle | GraphAttributes::nodeTemplate);  
  34.     NodeElement* node;  
  35.     forall_nodes(node, graph)  
  36.     {  
  37.         QNEBlock* item = nodeMap[node];  
  38.         graphAttr.width(node) = item->getHeight();  
  39.         graphAttr.height(node) = item->getWidth();  
  40.     }  
  41.   
  42.     // compute layout  
  43.     SugiyamaLayout layout;  
  44.   
  45.     FastHierarchyLayout* ohl = new FastHierarchyLayout;  
  46.     ohl->layerDistance(30);  
  47.     ohl->nodeDistance(25);  
  48.     layout.setLayout(ohl);  
  49.   
  50.     layout.call(graphAttr);  
  51.   
  52.     // update node position  
  53.     forall_nodes(node, graph)  
  54.     {  
  55.         double x = graphAttr.x(node);  
  56.         double y = graphAttr.y(node);  
  57.         QNEBlock* item = nodeMap[node];  
  58.         item->setPos(y, x);  
  59.     }  
  60. }  
最终效果:

0 0
原创粉丝点击