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