1. 程式人生 > >有向無環圖的自動佈局演算法

有向無環圖的自動佈局演算法

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

最近業餘在做一個基於結點的編輯工具玩, 遇到一個問題, 就是結點和連線多了, 經常會出現重疊交叉的問題, 導致圖看不清楚:


要是這個樣子, 還不如不用圖清楚呢, 所心就需要找一個方法來進行自動佈局, 理想情況是這樣的(手動整理結果):


當然, 手動整理的話, 每個人弄出來的結果都不一樣. 自動的演算法肯定沒有100%完美的, 但是總是能方便不少的

在google了一會兒後, 發現這種結點-線組成的圖是一有個學名的: directed acyclic graph, 例如這樣:


無非我這個圖結點上的連線點是有限制的, 但這個對於佈局演算法來說, 影響不大. 因為佈局只需要大體考慮每個結點的位置

那麼, 這個演算法需要滿足幾個條件: 

  1. 結點之間不能有重疊
  2. 連線之間儘量減少交差
  3. 結點之間是有基本的層次關係對齊的
初步看了一上, 這個演算法比較複雜, 是多種演算法的集合 自己不是很熟悉這方面的理論知識, 所以還是決定採用第三的演算法庫 C++可以使用的圖繪製演算法庫, 比較常見的有Graphviz, OGDF, Boost Graph 根據這個問題(http://stackoverflow.com/questions/2751826/which-c-graph-library-should-i-use)的推薦, 嘗試了OGDF, 效果還不錯(可惜是GPL協議)
  1. //------------------------------------------------------------------------------
  2. void
  3. QNodesEditor::autoLayout()  
  4. {  
  5.     usingnamespace 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.     // compute layout
  42.     SugiyamaLayout layout;  
  43.     FastHierarchyLayout* ohl = new FastHierarchyLayout;  
  44.     ohl->layerDistance(30);  
  45.     ohl->nodeDistance(25);  
  46.     layout.setLayout(ohl);  
  47.     layout.call(graphAttr);  
  48.     // update node position
  49.     forall_nodes(node, graph)  
  50.     {  
  51.         double x = graphAttr.x(node);  
  52.         double y = graphAttr.y(node);  
  53.         QNEBlock* item = nodeMap[node];  
  54.         item->setPos(y, x);  
  55.     }  
  56. }  
最終效果: