1 #include "COGDFLayout.h"
2
3 #include <qvge/CNodeEditorScene.h>
4 #include <qvge/CNode.h>
5 #include <qvge/CDirectEdge.h>
6
7 #include <ogdf/fileformats/GraphIO.h>
8
9 #include <ogdf/basic/Graph.h>
10 #include <ogdf/basic/GraphAttributes.h>
11 #include <ogdf/basic/LayoutModule.h>
12
13 #include <ogdf/misclayout/BalloonLayout.h>
14
15 #include <iostream> // std::ios, std::istream, std::cout
16 #include <fstream> // std::filebuf
17
18 #include <QMap>
19 #include <QApplication>
20 #include <QFileInfo>
21
22
COGDFLayout()23 COGDFLayout::COGDFLayout()
24 {
25 }
26
27
toVariant(ogdf::Shape shape)28 static QVariant toVariant(ogdf::Shape shape)
29 {
30 using namespace ogdf;
31
32 switch (shape)
33 {
34 case Shape::Rect: return "square";
35 case Shape::RoundedRect: return "rsquare";
36 case Shape::Ellipse: return "disc";
37 case Shape::Triangle: return "triangle";
38 case Shape::Pentagon: return "star";
39 case Shape::Hexagon: return "hexagon";
40 case Shape::Octagon: return "octagon";
41 case Shape::Rhomb: return "diamond";
42 case Shape::Trapeze: return "trapeze";
43 case Shape::Parallelogram: return "parallelogram";
44 case Shape::InvTriangle: return "triangle2";
45 case Shape::InvTrapeze: return "trapeze2";
46 case Shape::InvParallelogram: return "parallelogram2";
47 case Shape::Image: return "image";
48 }
49
50 return QVariant();
51 }
52
53
toVariant(ogdf::StrokeType stroke)54 static QVariant toVariant(ogdf::StrokeType stroke)
55 {
56 using namespace ogdf;
57
58 switch (stroke)
59 {
60 case StrokeType::Solid: return "solid";
61 case StrokeType::Dash: return "dashed";
62 case StrokeType::Dot: return "dotted";
63 case StrokeType::Dashdot: return "dashdot";
64 case StrokeType::Dashdotdot: return "dashdotdot";
65 default:;
66 }
67
68 return QVariant();
69 }
70
71
doLayout(ogdf::LayoutModule & layout,CNodeEditorScene & scene)72 void COGDFLayout::doLayout(ogdf::LayoutModule &layout, CNodeEditorScene &scene)
73 {
74 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
75
76 ogdf::Graph G;
77 ogdf::GraphAttributes GA(G, ogdf::GraphAttributes::nodeGraphics | ogdf::GraphAttributes::edgeGraphics);
78
79 // qvge -> ogdf
80 auto nodes = scene.getItems<CNode>();
81 auto edges = scene.getItems<CEdge>();
82
83 QMap<CNode*, ogdf::node> nodeMap;
84
85 for (CNode* node : nodes)
86 {
87 ogdf::node n = G.newNode();
88 // GA.x(n) = node->pos().x();
89 // GA.y(n) = node->pos().y();
90 GA.x(n) = 0;
91 GA.y(n) = 0;
92
93 nodeMap[node] = n;
94 }
95
96 for (CEdge* edge: edges)
97 {
98 ogdf::node n1 = nodeMap[edge->firstNode()];
99 ogdf::node n2 = nodeMap[edge->lastNode()];
100 ogdf::edge e = G.newEdge(n1, n2);
101 }
102
103
104 // ogdf layout
105 layout.call(GA);
106
107
108 // ogdf -> qvge
109 for (auto it = nodeMap.begin(); it != nodeMap.end(); ++it)
110 {
111 CNode* node = it.key();
112 ogdf::node n = it.value();
113
114 node->setPos(GA.x(n), GA.y(n));
115 }
116
117 // finalize
118 scene.setSceneRect(scene.itemsBoundingRect());
119
120 scene.addUndoState();
121
122
123 QApplication::restoreOverrideCursor();
124 }
125
126
graphTopologyToScene(const ogdf::Graph & G,const ogdf::GraphAttributes & GA,CNodeEditorScene & scene)127 void COGDFLayout::graphTopologyToScene(const ogdf::Graph &G, const ogdf::GraphAttributes &GA, CNodeEditorScene &scene)
128 {
129 //scene.reset();
130 scene.initialize();
131
132 // create nodes
133 QMap<ogdf::node, CNode*> nodeMap;
134
135 for (auto n: G.nodes)
136 {
137 CNode* node = new CNode;
138 scene.addItem(node);
139
140 nodeMap[n] = node;
141
142 if (GA.has(GA.nodeGraphics))
143 {
144 node->setPos(GA.x(n), GA.y(n));
145 }
146 }
147
148 for (auto e: G.edges)
149 {
150 CEdge* edge = new CDirectEdge;
151 scene.addItem(edge);
152
153 edge->setFirstNode(nodeMap[e->source()]);
154 edge->setLastNode(nodeMap[e->target()]);
155 }
156
157 // finalize
158 scene.setSceneRect(scene.itemsBoundingRect());
159 }
160
161
graphToScene(const ogdf::Graph & G,const ogdf::GraphAttributes & GA,CNodeEditorScene & scene)162 void COGDFLayout::graphToScene(const ogdf::Graph &G, const ogdf::GraphAttributes &GA, CNodeEditorScene &scene)
163 {
164 scene.reset();
165
166 // create nodes
167 QMap<ogdf::node, CNode*> nodeMap;
168
169 for (auto n: G.nodes)
170 {
171 CNode* node = scene.createNewNode();
172 scene.addItem(node);
173
174 nodeMap[n] = node;
175
176 if (GA.has(GA.nodeGraphics))
177 {
178 node->setPos(GA.x(n), GA.y(n));
179 node->setAttribute("size", QSizeF(GA.width(n), GA.height(n)));
180 node->setAttribute("shape", toVariant(GA.shape(n)));
181 }
182
183 if (GA.has(GA.nodeStyle))
184 {
185 auto c = GA.fillColor(n);
186 node->setAttribute("color", QColor(c.red(), c.green(), c.blue()));
187
188 auto sc = GA.strokeColor(n);
189 node->setAttribute("stroke.color", QColor(c.red(), c.green(), c.blue()));
190
191 node->setAttribute("stroke.style", toVariant(GA.strokeType(n)));
192 node->setAttribute("stroke.size", GA.strokeWidth(n));
193 }
194
195 int id = -1;
196 if (GA.has(GA.nodeId)) {
197 id = GA.idNode(n);
198 if (id >= 0) node->setId(QString::number(id));
199 }
200
201 if (GA.has(GA.nodeLabel)) {
202 auto label = QString::fromStdString(GA.label(n)); // label -> ID
203 if (id < 0 && label.size()) node->setId(label);
204 }
205
206 if (GA.has(GA.nodeTemplate)) {
207 auto label = QString::fromStdString(GA.templateNode(n)); // comment -> label
208 if (label.size())
209 node->setAttribute("label", label);
210 }
211
212 if (GA.has(GA.nodeWeight))
213 node->setAttribute("weight", GA.weight(n));
214 }
215
216
217 for (auto e: G.edges)
218 {
219 CEdge* edge = scene.createNewConnection();
220 scene.addItem(edge);
221
222 edge->setFirstNode(nodeMap[e->source()]);
223 edge->setLastNode(nodeMap[e->target()]);
224
225 if (GA.has(GA.edgeDoubleWeight))
226 edge->setAttribute("weight", GA.doubleWeight(e));
227 else if (GA.has(GA.edgeIntWeight))
228 edge->setAttribute("weight", GA.intWeight(e));
229
230 if (GA.has(GA.edgeLabel))
231 edge->setAttribute("label", QString::fromStdString(GA.label(e)));
232
233 if (GA.has(GA.edgeStyle))
234 {
235 auto c = GA.strokeColor(e);
236 edge->setAttribute("color", QColor(c.red(), c.green(), c.blue()));
237
238 edge->setAttribute("style", toVariant(GA.strokeType(e)));
239 }
240 }
241
242
243 // finalize
244 scene.setSceneRect(scene.itemsBoundingRect());
245 }
246
247
248 // file IO
249
loadGraph(const QString & filename,CNodeEditorScene & scene,QString * lastError)250 bool COGDFLayout::loadGraph(const QString &filename, CNodeEditorScene &scene, QString* lastError)
251 {
252 ogdf::Graph G;
253 ogdf::GraphAttributes GA(G, -1); // all attrs
254
255 QString format = QFileInfo(filename).suffix().toLower();
256
257 std::filebuf fb;
258 if (!fb.open (filename.toStdString(), std::ios::in))
259 return false;
260
261 std::istream is(&fb);
262
263 bool ok = false;
264
265 if (format == "gml")
266 {
267 ok = ogdf::GraphIO::readGML(GA, G, is);
268 }
269 else
270 if (format == "dot" || format == "gv")
271 {
272 ok = ogdf::GraphIO::readDOT(GA, G, is);
273
274 // normalize node positions
275 if (ok && GA.has(GA.nodeGraphics))
276 {
277 for (auto n : G.nodes)
278 {
279 if (GA.x(n) != 0.0 || GA.y(n) != 0.0)
280 {
281 GA.x(n) *= 72.0;
282 GA.y(n) *= -72.0;
283 GA.width(n) *= 72.0;
284 GA.height(n) *= 72.0;
285 }
286 }
287 }
288 }
289
290 if (ok)
291 {
292 autoLayoutIfNone(G, GA);
293 graphToScene(G, GA, scene);
294 scene.addUndoState();
295 }
296
297 fb.close();
298
299 return ok;
300 }
301
302
303 // privates
304
autoLayoutIfNone(const ogdf::Graph & G,ogdf::GraphAttributes & GA)305 bool COGDFLayout::autoLayoutIfNone(const ogdf::Graph &G, ogdf::GraphAttributes &GA)
306 {
307 if (GA.has(GA.nodeGraphics))
308 {
309 for (auto n : G.nodes)
310 {
311 if (GA.x(n) != 0.0 || GA.y(n) != 0.0)
312 return false;
313 }
314 }
315 else
316 return false;
317
318 ogdf::BalloonLayout layout;
319 layout.call(GA);
320
321 // factor x2
322 for (auto n : G.nodes)
323 {
324 GA.x(n) *= 2.0;
325 GA.y(n) *= 2.0;
326 }
327
328 return true;
329 }
330