有向無環圖的自動佈局演算法
阿新 • • 發佈:2019-01-10
原文地址:http://blog.csdn.net/xoyojank/article/details/8249719
最近業餘在做一個基於結點的編輯工具玩, 遇到一個問題, 就是結點和連線多了, 經常會出現重疊交叉的問題, 導致圖看不清楚:
要是這個樣子, 還不如不用圖清楚呢, 所心就需要找一個方法來進行自動佈局, 理想情況是這樣的(手動整理結果):
當然, 手動整理的話, 每個人弄出來的結果都不一樣. 自動的演算法肯定沒有100%完美的, 但是總是能方便不少的
在google了一會兒後, 發現這種結點-線組成的圖是一有個學名的: directed acyclic graph, 例如這樣:
無非我這個圖結點上的連線點是有限制的, 但這個對於佈局演算法來說, 影響不大. 因為佈局只需要大體考慮每個結點的位置
那麼, 這個演算法需要滿足幾個條件:
- 結點之間不能有重疊
- 連線之間儘量減少交差
- 結點之間是有基本的層次關係對齊的
-
//------------------------------------------------------------------------------
- void
- QNodesEditor::autoLayout()
- {
- usingnamespace ogdf;
- Graph graph;
- // setup graph
- QMap<NodeElement*, QNEBlock*> nodeMap;
- foreach(QGraphicsItem * item, scene->items())
- {
- if (item->type() == QNEBlock::Type)
- {
-
NodeElement* node = graph.newNode();
- item->setData(QNEBlock::Type, qVariantFromValue((void*)node));
- nodeMap[node] = (QNEBlock*)item;
- }
- }
- foreach(QGraphicsItem * item, scene->items())
- {
- if (item->type() == QNEConnection::Type)
- {
- QNEConnection* connection = (QNEConnection*)item;
- NodeElement* node1 = (NodeElement*)connection->port1()->block()->data(QNEBlock::Type).value<void*>();
- NodeElement* node2 = (NodeElement*)connection->port2()->block()->data(QNEBlock::Type).value<void*>();
- graph.newEdge(node1, node2);
- }
- }
- // node size
- GraphAttributes graphAttr(graph,
- GraphAttributes::nodeGraphics | GraphAttributes::edgeGraphics |
- GraphAttributes::nodeLabel | GraphAttributes::nodeColor |
- GraphAttributes::edgeColor | GraphAttributes::edgeStyle |
- GraphAttributes::nodeStyle | GraphAttributes::nodeTemplate);
- NodeElement* node;
- forall_nodes(node, graph)
- {
- QNEBlock* item = nodeMap[node];
- graphAttr.width(node) = item->getHeight();
- graphAttr.height(node) = item->getWidth();
- }
- // compute layout
- SugiyamaLayout layout;
- FastHierarchyLayout* ohl = new FastHierarchyLayout;
- ohl->layerDistance(30);
- ohl->nodeDistance(25);
- layout.setLayout(ohl);
- layout.call(graphAttr);
- // update node position
- forall_nodes(node, graph)
- {
- double x = graphAttr.x(node);
- double y = graphAttr.y(node);
- QNEBlock* item = nodeMap[node];
- item->setPos(y, x);
- }
- }