2 * src/lemon/graph_to_eps.h - Part of LEMON, a generic C++ optimization library
4 * Copyright (C) 2004 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
5 * (Egervary Combinatorial Optimization Research Group, EGRES).
7 * Permission to use, modify and distribute this software is granted
8 * provided that this copyright notice appears in all copies. For
9 * precise terms see the accompanying LICENSE file.
11 * This software is provided "AS IS" with no warranty of any kind,
12 * express or implied, and with no claim as to its suitability for any
23 #include<lemon/maps.h>
24 #include<lemon/list_graph.h>
29 ///\brief Simple graph drawer
33 ///Data structure representing RGB colors.
35 ///Data structure representing RGB colors.
41 ///Default constructor
44 Color(double r,double g,double b) :_r(r),_g(g),_b(b) {};
45 ///Returns the red component
46 double getR() {return _r;}
47 ///Returns the green component
48 double getG() {return _g;}
49 ///Returns the blue component
50 double getB() {return _b;}
51 ///Set the color components
52 void set(double r,double g,double b) { _r=r;_g=g;_b=b; };
55 ///Default traits class of \ref GraphToEps
57 ///Default traits class of \ref GraphToEps
59 ///\c G is the type of the underlying graph.
61 struct DefaultGraphToEpsTraits
64 typedef typename Graph::Node Node;
65 typedef typename Graph::NodeIt NodeIt;
66 typedef typename Graph::Edge Edge;
67 typedef typename Graph::EdgeIt EdgeIt;
68 typedef typename Graph::InEdgeIt InEdgeIt;
69 typedef typename Graph::OutEdgeIt OutEdgeIt;
76 ConstMap<typename Graph::Node,xy<double> > _coords;
77 ConstMap<typename Graph::Node,double > _nodeSizes;
79 ConstMap<typename Graph::Node,Color > _nodeColors;
80 ConstMap<typename Graph::Edge,Color > _edgeColors;
82 ConstMap<typename Graph::Edge,double > _edgeWidths;
84 double _edgeWidthScale;
87 double _xBorder, _yBorder;
89 double _nodeBorderQuotient;
92 double _arrowLength, _arrowWidth;
94 bool _showNodes, _showEdges;
100 ConstMap<typename Graph::Node,bool > _nodeTexts;
101 double _nodeTextSize;
103 bool _pleaseRemoveOsStream;
107 ///\param _g is a reference to the graph to be printed
108 ///\param _os is a reference to the output stream.
109 ///\param _os is a reference to the output stream.
110 ///\param _pros If it is \c true, then the \c ostream referenced by \c _os
111 ///will be explicitly deallocated by the destructor.
112 ///By default it is <tt>std::cout</tt>
113 DefaultGraphToEpsTraits(const G &_g,std::ostream& _os=std::cout,
116 _coords(xy<double>(1,1)), _nodeSizes(1.0),
117 _nodeColors(Color(1,1,1)), _edgeColors(Color(0,0,0)),
118 _edgeWidths(1), _edgeWidthScale(0.3),
119 _nodeScale(1.0), _xBorder(10), _yBorder(10), _scale(1.0),
120 _nodeBorderQuotient(.1),
121 _drawArrows(false), _arrowLength(1), _arrowWidth(0.3),
122 _showNodes(true), _showEdges(true),
123 _enableParallel(false), _parEdgeDist(1),
124 _showNodeText(false), _nodeTexts(false), _nodeTextSize(1),
125 _pleaseRemoveOsStream(_pros) {}
128 ///Helper class to implement the named parameters of \ref graphToEps()
130 ///Helper class to implement the named parameters of \ref graphToEps()
131 ///\todo Is 'helper class' a good name for this?
133 template<class T> class GraphToEps : public T
135 typedef typename T::Graph Graph;
136 typedef typename Graph::Node Node;
137 typedef typename Graph::NodeIt NodeIt;
138 typedef typename Graph::Edge Edge;
139 typedef typename Graph::EdgeIt EdgeIt;
140 typedef typename Graph::InEdgeIt InEdgeIt;
141 typedef typename Graph::OutEdgeIt OutEdgeIt;
148 edgeLess(const Graph &_g) : g(_g) {}
149 bool operator()(Edge a,Edge b) const
151 Node ai=min(g.source(a),g.target(a));
152 Node aa=max(g.source(a),g.target(a));
153 Node bi=min(g.source(b),g.target(b));
154 Node ba=max(g.source(b),g.target(b));
156 (ai==bi && (aa < ba ||
157 (aa==ba && ai==g.source(a) && bi==g.target(b))));
160 bool isParallel(Edge e,Edge f) const
162 return (g.source(e)==g.source(f)&&g.target(e)==g.target(f))||
163 (g.source(e)==g.target(f)&&g.target(e)==g.source(f));
165 static xy<double> rot(xy<double> v)
167 return xy<double>(v.y,-v.x);
171 GraphToEps(const T &t) : T(t), dontPrint(false) {};
173 template<class X> struct CoordsTraits : public T {
175 CoordsTraits(const T &t,const X &x) : T(t), _coords(x) {}
177 ///Sets the map of the node coordinates
179 ///Sets the map of the node coordinates.
180 ///\param x must be a node map with xy<double> or xy<int> values.
181 template<class X> GraphToEps<CoordsTraits<X> > coords(const X &x) {
183 return GraphToEps<CoordsTraits<X> >(CoordsTraits<X>(*this,x));
185 template<class X> struct NodeSizesTraits : public T {
187 NodeSizesTraits(const T &t,const X &x) : T(t), _nodeSizes(x) {}
189 ///Sets the map of the node sizes
191 ///Sets the map of the node sizes
192 ///\param x must be a node map with \c double (or convertible) values.
193 template<class X> GraphToEps<NodeSizesTraits<X> > nodeSizes(const X &x)
196 return GraphToEps<NodeSizesTraits<X> >(NodeSizesTraits<X>(*this,x));
198 template<class X> struct NodeTextsTraits : public T {
200 NodeTextsTraits(const T &t,const X &x) : T(t), _nodeTexts(x) {}
202 ///Sets the text printed on the nodes
204 ///Sets the text printed on the nodes
205 ///\param x must be a node map with type that can be pushed to a standard
207 template<class X> GraphToEps<NodeTextsTraits<X> > nodeTexts(const X &x)
211 return GraphToEps<NodeTextsTraits<X> >(NodeTextsTraits<X>(*this,x));
213 template<class X> struct EdgeWidthsTraits : public T {
214 const X &_edgeWidths;
215 EdgeWidthsTraits(const T &t,const X &x) : T(t), _edgeWidths(x) {}
217 ///Sets the map of the edge widths
219 ///Sets the map of the edge widths
220 ///\param x must be a edge map with \c double (or convertible) values.
221 template<class X> GraphToEps<EdgeWidthsTraits<X> > edgeWidths(const X &x)
224 return GraphToEps<EdgeWidthsTraits<X> >(EdgeWidthsTraits<X>(*this,x));
227 template<class X> struct NodeColorsTraits : public T {
228 const X &_nodeColors;
229 NodeColorsTraits(const T &t,const X &x) : T(t), _nodeColors(x) {}
231 ///Sets the map of the node colors
233 ///Sets the map of the node colors
234 ///\param x must be a node map with \ref Color values.
235 template<class X> GraphToEps<NodeColorsTraits<X> >
236 nodeColors(const X &x)
239 return GraphToEps<NodeColorsTraits<X> >(NodeColorsTraits<X>(*this,x));
241 template<class X> struct EdgeColorsTraits : public T {
242 const X &_edgeColors;
243 EdgeColorsTraits(const T &t,const X &x) : T(t), _edgeColors(x) {}
245 ///Sets the map of the edge colors
247 ///Sets the map of the edge colors
248 ///\param x must be a edge map with \ref Color values.
249 template<class X> GraphToEps<EdgeColorsTraits<X> >
250 edgeColors(const X &x)
253 return GraphToEps<EdgeColorsTraits<X> >(EdgeColorsTraits<X>(*this,x));
255 ///Sets a global scale factor for node sizes
257 ///Sets a global scale factor for node sizes
259 GraphToEps<T> &nodeScale(double d) {_nodeScale=d;return *this;}
260 ///Sets a global scale factor for edge widths
262 ///Sets a global scale factor for edge widths
264 GraphToEps<T> &edgeWidthScale(double d) {_edgeWidthScale=d;return *this;}
265 ///Sets a global scale factor for the whole picture
267 ///Sets a global scale factor for the whole picture
269 GraphToEps<T> &scale(double d) {_scale=d;return *this;}
270 ///Sets the width of the border around the picture
272 ///Sets the width of the border around the picture
274 GraphToEps<T> &border(double b) {_xBorder=_yBorder=b;return *this;}
275 ///Sets the width of the border around the picture
277 ///Sets the width of the border around the picture
279 GraphToEps<T> &border(double x, double y) {
280 _xBorder=x;_yBorder=y;return *this;
282 ///Sets whether to draw arrows
284 ///Sets whether to draw arrows
286 GraphToEps<T> &drawArrows(bool b=true) {_drawArrows=b;return *this;}
287 ///Sets the length of the arrowheads
289 ///Sets the length of the arrowheads
291 GraphToEps<T> &arrowLength(double d) {_arrowLength*=d;return *this;}
292 ///Sets the width of the arrowheads
294 ///Sets the width of the arrowheads
296 GraphToEps<T> &arrowWidth(double d) {_arrowWidth*=d;return *this;}
298 ///Enables parallel edges
300 ///Enables parallel edges
301 ///\todo Partially implemented
302 GraphToEps<T> &enableParallel(bool b=true) {_enableParallel=b;return *this;}
308 GraphToEps<T> &parEdgeDist(double d) {_parEdgeDist*=d;return *this;}
314 GraphToEps<T> &hideEdges(bool b=true) {_showEdges=!b;return *this;}
319 GraphToEps<T> &hideNodes(bool b=true) {_showNodes=!b;return *this;}
321 ///Sets the size of the node texts
323 ///Sets the size of the node texts
325 GraphToEps<T> &nodeTextSize(double d) {_nodeTextSize=d;return *this;}
330 if(dontPrint) return;
332 os << "%!PS-Adobe-2.0 EPSF-2.0\n";
333 //\todo: Chech whether the graph is empty.
334 BoundingBox<double> bb;
335 for(NodeIt n(g);n!=INVALID;++n) {
336 double ns=_nodeSizes[n]*_nodeScale;
341 os << "%%BoundingBox: "
342 << bb.left()* _scale-_xBorder << ' '
343 << bb.bottom()*_scale-_yBorder << ' '
344 << bb.right()* _scale+_xBorder << ' '
345 << bb.top()* _scale+_yBorder << '\n';
346 //x1 y1 x2 y2 x3 y3 cr cg cb w
347 os << "/lb { setlinewidth setrgbcolor newpath moveto\n"
348 << " 4 2 roll 1 index 1 index curveto stroke } bind def\n";
349 os << "/l { setlinewidth setrgbcolor newpath moveto lineto stroke } bind def\n";
350 os << "/c { newpath dup 3 index add 2 index moveto 0 360 arc closepath } bind def\n";
352 os << "/n { setrgbcolor 2 index 2 index 2 index c fill\n"
353 << " 0 0 0 setrgbcolor dup "
354 << _nodeBorderQuotient << " mul setlinewidth "
355 << 1+_nodeBorderQuotient/2 << " div c stroke\n"
357 os << "/arrl " << _arrowLength << " def\n";
358 os << "/arrw " << _arrowWidth << " def\n";
360 os << "/lrl { 2 index mul exch 2 index mul exch rlineto pop} bind def\n";
361 //len w dx_norm dy_norm x1 y1 cr cg cb
362 os << "/arr { setrgbcolor /y1 exch def /x1 exch def /dy exch def /dx exch def\n"
363 << " /w exch def /len exch def\n"
364 // << " 0.1 setlinewidth x1 y1 moveto dx len mul dy len mul rlineto stroke"
365 << " newpath x1 dy w 2 div mul add y1 dx w 2 div mul sub moveto\n"
366 << " len w sub arrl sub dx dy lrl\n"
367 << " arrw dy dx neg lrl\n"
368 << " dx arrl w add mul dy w 2 div arrw add mul sub\n"
369 << " dy arrl w add mul dx w 2 div arrw add mul add rlineto\n"
370 << " dx arrl w add mul neg dy w 2 div arrw add mul sub\n"
371 << " dy arrl w add mul neg dx w 2 div arrw add mul add rlineto\n"
372 << " arrw dy dx neg lrl\n"
373 << " len w sub arrl sub neg dx dy lrl\n"
374 << " closepath fill } bind def\n";
375 os << "/cshow { 2 index 2 index moveto\n"
376 << " dup stringwidth pop neg 2 div fosi .35 mul neg rmoveto show pop pop} def\n";
379 if(_scale!=1.0) os << _scale << " dup scale\n";
381 os << "%Edges:\ngsave\n";
384 if(_enableParallel) {
386 for(EdgeIt e(g);e!=INVALID;++e) el.push_back(e);
387 sort(el.begin(),el.end(),edgeLess(g));
389 typename vector<Edge>::iterator j;
390 for(typename vector<Edge>::iterator i=el.begin();i!=el.end();i=j) {
391 for(j=i+1;j!=el.end()&&isParallel(*i,*j);++j) ;
394 // xy<double> d(_coords[g.target(e)]-_coords[g.source(e)]);
395 // double l=sqrt(d.normSquare());
397 // xy<double> x1(d*_nodeScale*_nodeSizes[g.source(e)]+
398 // _coords[g.source(e)]);
399 // os << l-(_nodeSizes[g.source(e)]+
400 // _nodeSizes[g.target(e)])*_nodeScale << ' '
401 // << _edgeWidths[e]*_edgeWidthScale << ' '
402 // << d.x << ' ' << d.y << ' '
403 // << x1.x << ' ' << x1.y << ' '
404 // << _edgeColors[e].getR() << ' '
405 // << _edgeColors[e].getG() << ' '
406 // << _edgeColors[e].getB() << " arr\n";
410 for(typename vector<Edge>::iterator e=i;e!=j;++e)
411 sw+=_edgeWidths[*e]*_edgeWidthScale+_parEdgeDist;
414 xy<double> d(_coords[g.target(*i)]-_coords[g.source(*i)]);
415 double l=sqrt(d.normSquare());
417 for(typename vector<Edge>::iterator e=i;e!=j;++e) {
418 sw+=_edgeWidths[*e]*_edgeWidthScale/2.0;
419 xy<double> m(_coords[g.target(*e)]+_coords[g.source(*e)]);
420 m=m/2.0+rot(d)*sw/.75;
421 os << _coords[g.source(*e)].x << ' '
422 << _coords[g.source(*e)].y << ' '
423 << m.x << ' ' << m.y << ' '
424 << _coords[g.target(*e)].x << ' '
425 << _coords[g.target(*e)].y << ' '
426 << _edgeColors[*e].getR() << ' '
427 << _edgeColors[*e].getG() << ' '
428 << _edgeColors[*e].getB() << ' '
429 << _edgeWidths[*e]*_edgeWidthScale << " lb\n";
430 sw+=_edgeWidths[*e]*_edgeWidthScale/2.0+_parEdgeDist;
435 else for(NodeIt n(g);n!=INVALID;++n)
436 for(OutEdgeIt e(g,n);e!=INVALID;++e)
438 xy<double> d(_coords[g.target(e)]-_coords[g.source(e)]);
439 double l=sqrt(d.normSquare());
441 xy<double> x1(d*_nodeScale*_nodeSizes[g.source(e)]+
442 _coords[g.source(e)]);
443 os << l-(_nodeSizes[g.source(e)]+
444 _nodeSizes[g.target(e)])*_nodeScale << ' '
445 << _edgeWidths[e]*_edgeWidthScale << ' '
446 << d.x << ' ' << d.y << ' '
447 << x1.x << ' ' << x1.y << ' '
448 << _edgeColors[e].getR() << ' '
449 << _edgeColors[e].getG() << ' '
450 << _edgeColors[e].getB() << " arr\n";
452 else os << _coords[g.source(e)].x << ' '
453 << _coords[g.source(e)].y << ' '
454 << _coords[g.target(e)].x << ' '
455 << _coords[g.target(e)].y << ' '
456 << _edgeColors[e].getR() << ' '
457 << _edgeColors[e].getG() << ' '
458 << _edgeColors[e].getB() << ' '
459 << _edgeWidths[e]*_edgeWidthScale << " l\n";
460 os << "grestore\n%Nodes:\ngsave\n";
462 for(NodeIt n(g);n!=INVALID;++n)
463 os << _coords[n].x << ' ' << _coords[n].y << ' '
464 << _nodeSizes[n]*_nodeScale << ' '
465 << _nodeColors[n].getR() << ' '
466 << _nodeColors[n].getG() << ' '
467 << _nodeColors[n].getB() << " n\n";
469 os << "grestore\n%Node texts:\ngsave\n";
470 os << "/fosi " << _nodeTextSize << " def\n";
471 os << "(Helvetica) findfont fosi scalefont setfont\n";
472 os << "0 0 0 setrgbcolor\n";
473 for(NodeIt n(g);n!=INVALID;++n)
474 os << _coords[n].x << ' ' << _coords[n].y
475 << " (" << _nodeTexts[n] << ") cshow\n";
477 os << "grestore\ngrestore\n";
480 if(_pleaseRemoveOsStream) {delete &os;}
485 ///Generates an EPS file from a graph
488 ///Generates an EPS file from a graph.
489 ///\param g is a reference to the graph to be printed
490 ///\param os is a reference to the output stream.
491 ///By default it is <tt>std::cout</tt>
493 ///This function also has a lot of \ref named-templ-param "named parameters",
494 ///they are declared as the members of class \ref GraphToEps. The following
495 ///example shows how to use these parameters.
497 /// graphToEps(g).scale(10).coords(coords)
498 /// .nodeScale(2).nodeSizes(sizes)
499 /// .edgeWidthScale(.4);
502 ///\sa graphToEps(G &g, char *file_name)
504 GraphToEps<DefaultGraphToEpsTraits<G> >
505 graphToEps(G &g, std::ostream& os=std::cout)
508 GraphToEps<DefaultGraphToEpsTraits<G> >(DefaultGraphToEpsTraits<G>(g,os));
511 ///Generates an EPS file from a graph
514 ///This function does the same as
515 ///\ref graphToEps(G &g,std::ostream& os)
516 ///but it writes its output into the file \c file_name
517 ///instead of a stream.
518 ///\sa graphToEps(G &g, std::ostream& os)
520 GraphToEps<DefaultGraphToEpsTraits<G> >
521 graphToEps(G &g,char *file_name)
523 return GraphToEps<DefaultGraphToEpsTraits<G> >
524 (DefaultGraphToEpsTraits<G>(g,*new ofstream(file_name),true));
527 //Generates an EPS file from a graph.
528 //\param g is a reference to the graph to be printed
529 //\param file_name is the output file_name.
531 //This function also has a lot of \ref named-templ-param "named parameters",
532 //they are declared as the members of class \ref GraphToEps. The following
533 //example shows how to use these parameters.
535 // graphToEps(g).scale(10).coords(coords)
536 // .nodeScale(2).nodeSizes(sizes)
537 // .edgeWidthScale(.4);
540 //\todo Avoid duplicated documentation
541 //\bug Exception handling is missing? (Or we can just ignore it?)
545 using namespace lemon;
547 class ColorSet : public MapBase<int,Color>
550 Color operator[](int i) const
553 case 0: return Color(0,0,0);
554 case 1: return Color(1,0,0);
555 case 2: return Color(0,1,0);
556 case 3: return Color(0,0,1);
557 case 4: return Color(1,1,0);
558 case 5: return Color(1,0,1);
559 case 6: return Color(0,1,1);
560 case 7: return Color(1,1,1);
566 class IdMap :public MapBase<ListGraph::Node,int>
570 IdMap(const ListGraph &_g) :g(_g) {}
571 Value operator[](Key n) const { return g.id(n); }
579 typedef ListGraph::Node Node;
580 typedef ListGraph::NodeIt NodeIt;
581 typedef ListGraph::Edge Edge;
590 ListGraph::NodeMap<Xy> coords(g);
591 ListGraph::NodeMap<double> sizes(g);
592 ListGraph::NodeMap<int> colors(g);
593 ListGraph::EdgeMap<int> ecolors(g);
594 ListGraph::EdgeMap<int> widths(g);
596 coords[n1]=Xy(50,50); sizes[n1]=1; colors[n1]=1;
597 coords[n2]=Xy(50,70); sizes[n2]=2; colors[n2]=2;
598 coords[n3]=Xy(70,70); sizes[n3]=1; colors[n3]=3;
599 coords[n4]=Xy(70,50); sizes[n4]=2; colors[n4]=4;
600 coords[n5]=Xy(85,60); sizes[n5]=3; colors[n5]=5;
604 e=g.addEdge(n1,n2); ecolors[e]=0; widths[e]=1;
605 e=g.addEdge(n2,n3); ecolors[e]=0; widths[e]=1;
606 e=g.addEdge(n3,n5); ecolors[e]=0; widths[e]=3;
607 e=g.addEdge(n5,n4); ecolors[e]=0; widths[e]=1;
608 e=g.addEdge(n4,n1); ecolors[e]=0; widths[e]=1;
609 e=g.addEdge(n2,n4); ecolors[e]=1; widths[e]=2;
610 e=g.addEdge(n3,n4); ecolors[e]=2; widths[e]=1;
614 graphToEps(g,"proba.eps").scale(10).coords(coords).
615 nodeScale(2).nodeSizes(sizes).
616 nodeColors(composeMap(colorSet,colors)).
617 edgeColors(composeMap(colorSet,ecolors)).
618 edgeWidthScale(.4).edgeWidths(widths).
619 nodeTexts(id).nodeTextSize(3);
621 graphToEps(g,"proba_arr.eps").scale(10).coords(coords).
622 nodeScale(2).nodeSizes(sizes).
623 nodeColors(composeMap(colorSet,colors)).
624 edgeColors(composeMap(colorSet,ecolors)).
625 edgeWidthScale(.4).edgeWidths(widths).
626 nodeTexts(id).nodeTextSize(3).
627 drawArrows().arrowWidth(1).arrowLength(1);
629 e=g.addEdge(n1,n4); ecolors[e]=2; widths[e]=1;
630 e=g.addEdge(n4,n1); ecolors[e]=1; widths[e]=2;
632 e=g.addEdge(n1,n2); ecolors[e]=1; widths[e]=1;
633 e=g.addEdge(n1,n2); ecolors[e]=2; widths[e]=1;
634 e=g.addEdge(n1,n2); ecolors[e]=3; widths[e]=1;
635 e=g.addEdge(n1,n2); ecolors[e]=4; widths[e]=1;
636 e=g.addEdge(n1,n2); ecolors[e]=5; widths[e]=1;
637 e=g.addEdge(n1,n2); ecolors[e]=6; widths[e]=1;
638 e=g.addEdge(n1,n2); ecolors[e]=7; widths[e]=1;
640 graphToEps(g,"proba_par.eps").scale(10).coords(coords).
641 nodeScale(2).nodeSizes(sizes).
642 nodeColors(composeMap(colorSet,colors)).
643 edgeColors(composeMap(colorSet,ecolors)).
644 edgeWidthScale(.4).edgeWidths(widths).
645 nodeTexts(id).nodeTextSize(3).
646 enableParallel().parEdgeDist(1.5);