1 /** \file
2 * \brief Implements DOT write functionality of class GraphIO.
3 *
4 * \author Łukasz Hanuszczak
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32 #include <ogdf/fileformats/GraphIO.h>
33 #include <ogdf/fileformats/DOT.h>
34
35 namespace ogdf {
36
37 namespace dot {
38
39
40 template <typename T>
writeAttribute(std::ostream & out,bool & separator,const std::string & name,const T & value)41 static inline void writeAttribute(
42 std::ostream &out, bool &separator,
43 const std::string &name, const T &value)
44 {
45 if(separator) {
46 out << ", ";
47 }
48
49 out << name << "=\"" << value << "\"";
50 separator = true;
51 }
52
53
writeAttributes(std::ostream & out,const GraphAttributes & GA,const node & v)54 static inline void writeAttributes(
55 std::ostream &out,
56 const GraphAttributes &GA, const node &v)
57 {
58 const long flags = GA.attributes();
59
60 out << "[";
61
62 bool separator = false; // Wheter to put separator before attribute.
63
64 if(flags & GraphAttributes::nodeId) {
65 writeAttribute(out, separator, "id", GA.idNode(v));
66 }
67
68 if(flags & GraphAttributes::nodeLabel) {
69 writeAttribute(out, separator, "label", GA.label(v));
70 }
71
72 if(flags & GraphAttributes::nodeTemplate) {
73 writeAttribute(out, separator, "comment", GA.templateNode(v));
74 }
75
76 if(flags & GraphAttributes::nodeGraphics) {
77 writeAttribute(out, separator, "width", GA.width(v));
78 writeAttribute(out, separator, "height", GA.height(v));
79 writeAttribute(out, separator, "shape", dot::toString(GA.shape(v)));
80
81 out << ", pos=\"" << GA.x(v) << "," << GA.y(v);
82 if(flags & GraphAttributes::threeD) {
83 out << "," << GA.z(v);
84 }
85 out << "\"";
86 }
87
88 if(flags & GraphAttributes::nodeLabelPosition) {
89 // No need to check separator, as `nodeLabel` must be set as well
90 // and is handled earlier.
91 out << ", labelpos=\"" << GA.xLabel(v) << "," << GA.yLabel(v);
92 if(flags & GraphAttributes::threeD) {
93 out << "," << GA.zLabel(v);
94 }
95 out << "\"";
96 }
97
98 if(flags & GraphAttributes::nodeStyle) {
99 writeAttribute(out, separator, "color", GA.strokeColor(v));
100 writeAttribute(out, separator, "fillcolor", GA.fillColor(v));
101 writeAttribute(out, separator, "stroketype", toString(GA.strokeType(v)));
102 writeAttribute(out, separator, "strokewidth", GA.strokeWidth(v));
103 writeAttribute(out, separator, "fillpattern", toString(GA.fillPattern(v)));
104 writeAttribute(out, separator, "fillbgcolor", GA.fillBgColor(v));
105 }
106
107 if(flags & GraphAttributes::nodeType) {
108 writeAttribute(out, separator, "type", int(GA.type(v)));
109 }
110
111 if(flags & GraphAttributes::nodeWeight) {
112 writeAttribute(out, separator, "weight", GA.weight(v));
113 }
114
115 out << "]";
116 }
117
118
writeAttributes(std::ostream & out,const GraphAttributes & GA,const edge & e)119 static inline void writeAttributes(
120 std::ostream &out,
121 const GraphAttributes &GA, const edge &e)
122 {
123 const long flags = GA.attributes();
124
125 out << "[";
126
127 bool comma = false; // Whether to put comma before attribute.
128
129 if(flags & GraphAttributes::edgeLabel) {
130 writeAttribute(out, comma, "label", GA.label(e));
131 }
132
133 if(flags & GraphAttributes::edgeDoubleWeight) {
134 writeAttribute(out, comma, "weight", GA.doubleWeight(e));
135 } else if(flags & GraphAttributes::edgeIntWeight) {
136 writeAttribute(out, comma, "weight", GA.intWeight(e));
137 }
138
139 if(flags & GraphAttributes::edgeGraphics) {
140 // This should be legal cubic B-Spline in the future.
141 std::stringstream sstream;
142 std::ios_base::fmtflags currentFlags = sstream.flags();
143 sstream.flags(currentFlags | std::ios::fixed);
144 for(const DPoint &p : GA.bends(e)) {
145 sstream << p.m_x << "," << p.m_y << " ";
146 }
147 sstream.flags(currentFlags);
148
149 writeAttribute(out, comma, "pos", sstream.str());
150 }
151
152 if(flags & GraphAttributes::edgeArrow) {
153 writeAttribute(out, comma, "dir", dot::toString(GA.arrowType(e)));
154 }
155
156 if(flags & GraphAttributes::edgeStyle) {
157 writeAttribute(out, comma, "color", GA.strokeColor(e));
158 writeAttribute(out, comma, "stroketype", GA.strokeType(e));
159 writeAttribute(out, comma, "strokewidth", GA.strokeWidth(e));
160 }
161
162 if(flags & GraphAttributes::edgeType) {
163 writeAttribute(out, comma, "type", dot::toString(GA.type(e)));
164 }
165
166 if(flags & GraphAttributes::edgeSubGraphs) {
167 const uint32_t mask = GA.subGraphBits(e);
168
169 // Iterate over all subgraphs and print the ones the edge is part of.
170 std::stringstream sstream;
171 for (size_t sg = 0; sg < sizeof(mask) * 8; ++sg) {
172 if((1 << sg) & mask) {
173 sstream << (sg == 0 ? "" : " ") << sg;
174 }
175 }
176 writeAttribute(out, comma, "available_for", sstream.str());
177 }
178
179 out << "]";
180 }
181
182
writeAttributes(std::ostream & out,const ClusterGraphAttributes & CA,const cluster & c)183 static inline bool writeAttributes(
184 std::ostream &out,
185 const ClusterGraphAttributes &CA, const cluster &c)
186 {
187 const long flags = CA.attributes();
188 bool separator = false;
189
190 if(flags & ClusterGraphAttributes::clusterGraphics) {
191 writeAttribute(out, separator, "width", CA.width(c));
192 writeAttribute(out, separator, "height", CA.height(c));
193 out << ", pos=\"" << CA.x(c) << "," << CA.y(c) << "\"";
194 }
195 if(flags & ClusterGraphAttributes::clusterStyle) {
196 writeAttribute(out, separator, "color", CA.strokeColor(c));
197 writeAttribute(out, separator, "stroketype", CA.strokeType(c));
198 writeAttribute(out, separator, "strokewidth", CA.strokeWidth(c));
199 writeAttribute(out, separator, "fillpattern", CA.fillPattern(c));
200 writeAttribute(out, separator, "fillcolor", CA.fillColor(c));
201 writeAttribute(out, separator, "fillbgcolor", CA.fillBgColor(c));
202 }
203 if(flags & ClusterGraphAttributes::clusterLabel) {
204 writeAttribute(out, separator, "label", CA.label(c));
205 }
206 if(flags & ClusterGraphAttributes::clusterTemplate) {
207 writeAttribute(out, separator, "comment", CA.templateCluster(c));
208 }
209 return separator;
210 }
211
212
writeHeader(std::ostream & out,const int & depth,const GraphAttributes * GA,bool writeAttributes=true)213 static inline bool writeHeader(
214 std::ostream &out, const int &depth,
215 const GraphAttributes *GA, bool writeAttributes = true)
216 {
217 if(GA) {
218 GraphIO::indent(out, depth) << (GA->directed() ? "digraph" : "graph")
219 << " G {\n";
220 } else {
221 GraphIO::indent(out, depth) << "digraph G {\n";
222 return false;
223 }
224
225 if (!writeAttributes) return false;
226
227 bool whitespace = false;
228
229 if(GA->has(GraphAttributes::threeD)) {
230 GraphIO::indent(out, depth + 1) << "graph [";
231 writeAttribute(out, whitespace, "dim", 3);
232 out << "]\n";
233 }
234 return whitespace;
235 }
236
writeHeader(std::ostream & out,const int & depth,const ClusterGraphAttributes * CA,cluster rootCluster,cluster c,int clusterId)237 static inline bool writeHeader(
238 std::ostream &out, const int &depth,
239 const ClusterGraphAttributes *CA, cluster rootCluster,
240 cluster c, int clusterId)
241 {
242 if (rootCluster == c) {
243 writeHeader(out, depth, CA, false);
244 }
245 else {
246 GraphIO::indent(out, depth) << "subgraph cluster" << clusterId << " {\n";
247 }
248
249 if (!CA) return false;
250
251 std::ostringstream attr;
252 attr.setf(std::ios::fixed);
253
254 bool separator = false;
255 separator = writeAttributes(attr, *CA, c);
256 if(CA->has(GraphAttributes::threeD)) {
257 writeAttribute(attr, separator, "dim", 3);
258 }
259 string attributes = attr.str();
260 if (!attributes.empty()) {
261 GraphIO::indent(out, depth + 1) << "graph ["
262 << attributes << "]\n";
263 }
264
265 return separator;
266 }
267
268
writeEdge(std::ostream & out,const int & depth,const GraphAttributes * GA,const edge & e)269 static inline bool writeEdge(
270 std::ostream &out, const int &depth,
271 const GraphAttributes *GA, const edge &e)
272 {
273 GraphIO::indent(out, depth) << e->source()
274 << (GA && !GA->directed() ? " -- " : " -> ")
275 << e->target();
276
277 if(GA) {
278 out << " ";
279 writeAttributes(out, *GA, e);
280 }
281
282 out << "\n";
283 return true;
284 }
285
286
writeNode(std::ostream & out,const int & depth,const GraphAttributes * GA,const node & v)287 static inline bool writeNode(
288 std::ostream &out, const int &depth,
289 const GraphAttributes *GA, const node &v
290 )
291 {
292 // Write a node iff it has some attributes or has no edges.
293 if(!GA && v->degree() > 0) {
294 return false;
295 }
296
297 GraphIO::indent(out, depth) << v;
298
299 if(GA) {
300 out << " ";
301 writeAttributes(out, *GA, v);
302 }
303
304 out << "\n";
305 return true;
306 }
307
308
writeCluster(std::ostream & out,int depth,const ClusterArray<std::vector<edge>> & edgeMap,const ClusterGraph & C,const ClusterGraphAttributes * CA,const cluster & c,int & clusterId)309 static bool writeCluster(
310 std::ostream &out, int depth,
311 const ClusterArray < std::vector<edge> > &edgeMap,
312 const ClusterGraph &C, const ClusterGraphAttributes *CA, const cluster &c,
313 int &clusterId)
314 {
315 std::ios_base::fmtflags currentFlags = out.flags();
316 out.flags(currentFlags | std::ios::fixed);
317 bool result = out.good();
318
319 if(result) {
320 bool whitespace; // True if a whitespace should printed (readability).
321
322 whitespace = writeHeader(out, depth++, CA, C.rootCluster(), c, clusterId);
323 clusterId++;
324
325 if (whitespace) {
326 out << "\n";
327 }
328
329 // Recursively export all subclusters.
330 whitespace = false;
331 for (cluster child : c->children) {
332 writeCluster(out, depth, edgeMap, C, CA, child, clusterId);
333 whitespace = true;
334 }
335
336 if (whitespace) {
337 out << "\n";
338 }
339
340 // Then, print all nodes whithout an adjacent edge.
341 whitespace = false;
342 for (node v : c->nodes) {
343 whitespace |= writeNode(out, depth, CA, v);
344 }
345
346 if (whitespace) {
347 out << "\n";
348 }
349
350 // Finally, we print all edges for this cluster (ugly version for now).
351 const std::vector<edge> &edges = edgeMap[c];
352 whitespace = false;
353 for (auto &e : edges) {
354 whitespace |= writeEdge(out, depth, CA, e);
355 }
356
357 GraphIO::indent(out, --depth) << "}\n";
358 }
359
360 out.flags(currentFlags);
361
362 return result;
363 }
364
365
writeGraph(std::ostream & out,const Graph & G,const GraphAttributes * GA)366 static bool writeGraph(
367 std::ostream &out,
368 const Graph &G, const GraphAttributes *GA)
369 {
370 std::ios_base::fmtflags currentFlags = out.flags();
371 out.flags(currentFlags | std::ios::fixed);
372
373 bool result = out.good();
374
375 if(result) {
376 bool whitespace = false;
377
378 whitespace |= writeHeader(out, 0, GA);
379
380 if (whitespace) {
381 out << "\n";
382 }
383
384 // We need to print all the nodes that do not have any adjacent edge.
385 whitespace = false;
386 for (node v : G.nodes) {
387 whitespace |= dot::writeNode(out, 1, GA, v);
388 }
389
390 if (whitespace) {
391 out << "\n";
392 }
393
394 // In this dummy version we just output list of all edges. It works, sure,
395 // but is ugly as hell. A nicer approach has to be developed in future.
396 whitespace = false;
397 for (edge e : G.edges) {
398 whitespace |= dot::writeEdge(out, 1, GA, e);
399 }
400
401 out << "}\n";
402 }
403
404 out.flags(currentFlags);
405
406 return result;
407 }
408
409 }
410
411
writeDOT(const Graph & G,std::ostream & out)412 bool GraphIO::writeDOT(const Graph &G, std::ostream &out)
413 {
414 return dot::writeGraph(out, G, nullptr);
415 }
416
417
writeDOT(const GraphAttributes & GA,std::ostream & out)418 bool GraphIO::writeDOT(const GraphAttributes &GA, std::ostream &out)
419 {
420 return dot::writeGraph(out, GA.constGraph(), &GA);
421 }
422
423
writeDOT(const ClusterGraph & C,std::ostream & out)424 bool GraphIO::writeDOT(const ClusterGraph &C, std::ostream &out)
425 {
426 const Graph &G = C.constGraph();
427 int id = 1;
428
429 // Assign a list of edges for each cluster. Perhaps usage of std::vector
430 // here needs reconsideration - vector is fast but usage of STL iterators
431 // is ugly without C++11 for-each loop.
432 ClusterArray< std::vector<edge> > edgeMap(C);
433 for(edge e : G.edges) {
434 const node s = e->source(), t = e->target();
435 edgeMap[C.commonCluster(s, t)].push_back(e);
436 }
437
438 return dot::writeCluster(out, 0, edgeMap, C, nullptr, C.rootCluster(), id);
439 }
440
441
writeDOT(const ClusterGraphAttributes & CA,std::ostream & out)442 bool GraphIO::writeDOT(const ClusterGraphAttributes &CA, std::ostream &out)
443 {
444 const Graph &G = CA.constGraph();
445 const ClusterGraph &C = CA.constClusterGraph();
446 int id = 1;
447
448 // Assign a list of edges for each cluster. Perhaps usage of std::vector
449 // here needs reconsideration - vector is fast but usage of STL iterators
450 // is ugly without C++11 for-each loop.
451 ClusterArray< std::vector<edge> > edgeMap(C);
452 for(edge e : G.edges) {
453 const node s = e->source(), t = e->target();
454 edgeMap[C.commonCluster(s, t)].push_back(e);
455 }
456
457 return dot::writeCluster(out, 0, edgeMap, C, &CA, C.rootCluster(), id);
458 }
459
460 }
461