#include #include #include #include #include #include #include ///\file \ingroup misc ///Simple graph drawer namespace lemon { ///Data structure representing RGB colors. ///Data structure representing RGB colors. ///\ingroup misc class Color { double _r,_g,_b; public: ///Default constructor Color() {} ///Constructor Color(double r,double g,double b) :_r(r),_g(g),_b(b) {}; ///Returns the red component double getR() {return _r;} ///Returns the green component double getG() {return _g;} ///Returns the blue component double getB() {return _b;} ///Set the color components void set(double r,double g,double b) { _r=r;_g=g;_b=b; }; }; ///Default traits class of \ref GraphToEps ///Default traits class of \ref GraphToEps /// ///\c G is the type of the underlying graph. template struct DefaultGraphToEpsTraits { typedef G Graph; typedef typename Graph::Node Node; typedef typename Graph::NodeIt NodeIt; typedef typename Graph::Edge Edge; typedef typename Graph::EdgeIt EdgeIt; typedef typename Graph::InEdgeIt InEdgeIt; typedef typename Graph::OutEdgeIt OutEdgeIt; const Graph &g; std::ostream& os; ConstMap > _coords; ConstMap _nodeSizes; ConstMap _nodeColors; ConstMap _edgeColors; ConstMap _edgeWidths; double _edgeWidthScale; double _nodeScale; double _xBorder, _yBorder; double _scale; double _nodeBorderQuotient; bool _drawArrows; double _arrowLength, _arrowWidth; bool _enableParallel; bool _pleaseRemoveOsStream; ///Constructor ///Constructor ///\param _g is a reference to the graph to be printed ///\param _os is a reference to the output stream. ///\param _os is a reference to the output stream. ///\param _pros If it is \c true, then the \c ostream referenced by \c _os ///will be explicitly deallocated by the destructor. ///By default it is std::cout DefaultGraphToEpsTraits(const G &_g,std::ostream& _os=std::cout, bool _pros=false) : g(_g), os(_os), _coords(xy(1,1)), _nodeSizes(1.0), _nodeColors(Color(1,1,1)), _edgeColors(Color(0,0,0)), _edgeWidths(1), _edgeWidthScale(0.3), _nodeScale(1.0), _xBorder(10), _yBorder(10), _scale(1.0), _nodeBorderQuotient(.1), _drawArrows(false), _arrowLength(1), _arrowWidth(0.3), _enableParallel(false), _pleaseRemoveOsStream(_pros) {} }; ///Helper class to implement the named parameters of \ref graphToEps() ///Helper class to implement the named parameters of \ref graphToEps() ///\todo Is 'helper class' a good name for this? /// template class GraphToEps : public T { typedef typename T::Graph Graph; typedef typename Graph::Node Node; typedef typename Graph::NodeIt NodeIt; typedef typename Graph::Edge Edge; typedef typename Graph::EdgeIt EdgeIt; typedef typename Graph::InEdgeIt InEdgeIt; typedef typename Graph::OutEdgeIt OutEdgeIt; bool dontPrint; class edgeLess { const Graph &g; public: edgeLess(const Graph &_g) : g(_g) {} bool operator()(Edge a,Edge b) const { Node ai=min(g.source(a),g.target(a)); Node aa=max(g.source(a),g.target(a)); Node bi=min(g.source(b),g.target(b)); Node ba=max(g.source(b),g.target(b)); return ai struct CoordsTraits : public T { const X &_coords; CoordsTraits(const T &t,const X &x) : T(t), _coords(x) {} }; ///Sets the map of the node coordinates ///Sets the map of the node coordinates. ///\param x must be a node map with xy or xy values. template GraphToEps > coords(const X &x) { dontPrint=true; return GraphToEps >(CoordsTraits(*this,x)); } template struct NodeSizesTraits : public T { const X &_nodeSizes; NodeSizesTraits(const T &t,const X &x) : T(t), _nodeSizes(x) {} }; ///Sets the map of the node sizes ///Sets the map of the node sizes ///\param x must be a node map with \c double (or convertible) values. template GraphToEps > nodeSizes(const X &x) { dontPrint=true; return GraphToEps >(NodeSizesTraits(*this,x)); } template struct EdgeWidthsTraits : public T { const X &_edgeWidths; EdgeWidthsTraits(const T &t,const X &x) : T(t), _edgeWidths(x) {} }; ///Sets the map of the edge widths ///Sets the map of the edge widths ///\param x must be a edge map with \c double (or convertible) values. template GraphToEps > edgeWidths(const X &x) { dontPrint=true; return GraphToEps >(EdgeWidthsTraits(*this,x)); } template struct NodeColorsTraits : public T { const X &_nodeColors; NodeColorsTraits(const T &t,const X &x) : T(t), _nodeColors(x) {} }; ///Sets the map of the node colors ///Sets the map of the node colors ///\param x must be a node map with \ref Color values. template GraphToEps > nodeColors(const X &x) { dontPrint=true; return GraphToEps >(NodeColorsTraits(*this,x)); } template struct EdgeColorsTraits : public T { const X &_edgeColors; EdgeColorsTraits(const T &t,const X &x) : T(t), _edgeColors(x) {} }; ///Sets the map of the edge colors ///Sets the map of the edge colors ///\param x must be a edge map with \ref Color values. template GraphToEps > edgeColors(const X &x) { dontPrint=true; return GraphToEps >(EdgeColorsTraits(*this,x)); } ///Sets a global scale factor for node sizes ///Sets a global scale factor for node sizes /// GraphToEps &nodeScale(double d) {_nodeScale=d;return *this;} ///Sets a global scale factor for edge widths ///Sets a global scale factor for edge widths /// GraphToEps &edgeWidthScale(double d) {_edgeWidthScale=d;return *this;} ///Sets a global scale factor for the whole picture ///Sets a global scale factor for the whole picture /// GraphToEps &scale(double d) {_scale=d;return *this;} ///Sets the width of the border around the picture ///Sets the width of the border around the picture /// GraphToEps &border(double b) {_xBorder=_yBorder=b;return *this;} ///Sets the width of the border around the picture ///Sets the width of the border around the picture /// GraphToEps &border(double x, double y) { _xBorder=x;_yBorder=y;return *this; } ///Sets whether to draw arrows ///Sets whether to draw arrows /// GraphToEps &drawArrows(bool b=true) {_drawArrows=b;return *this;} ///Sets the length of the arrowheads ///Sets the length of the arrowheads /// GraphToEps &arrowLength(double d) {_arrowLength*=d;return *this;} ///Sets the width of the arrowheads ///Sets the width of the arrowheads /// GraphToEps &arrowWidth(double d) {_arrowWidth*=d;return *this;} ///Enables parallel edges ///Enables parallel edges ///\todo Unimplemented GraphToEps &enableParallel(bool b=true) {_enableParallel=b;return *this;} ~GraphToEps() { if(dontPrint) return; os << "%!PS-Adobe-2.0 EPSF-2.0\n"; //\todo: Chech whether the graph is empty. BoundingBox bb; for(NodeIt n(g);n!=INVALID;++n) { double ns=_nodeSizes[n]*_nodeScale; xy p(ns,ns); bb+=p+_coords[n]; bb+=-p+_coords[n]; } os << "%%BoundingBox: " << bb.left()* _scale-_xBorder << ' ' << bb.bottom()*_scale-_yBorder << ' ' << bb.right()* _scale+_xBorder << ' ' << bb.top()* _scale+_yBorder << '\n'; //x1 y1 x2 y2 cr cg cb w os << "/l { setlinewidth setrgbcolor newpath moveto lineto stroke } bind def\n"; os << "/c { newpath dup 3 index add 2 index moveto 0 360 arc } bind def\n"; // x y r cr cg cb os << "/n { setrgbcolor 2 index 2 index 2 index c fill\n" << " 0 0 0 setrgbcolor dup " << _nodeBorderQuotient << " mul setlinewidth " << 1+_nodeBorderQuotient/2 << " div c stroke\n" << " } bind def\n"; os << "/arrl " << _arrowLength << " def\n"; os << "/arrw " << _arrowWidth << " def\n"; // l dx_norm dy_norm os << "/lrl { 2 index mul exch 2 index mul exch rlineto pop} bind def\n"; //len w dx_norm dy_norm x1 y1 cr cg cb os << "/arr { setrgbcolor /y1 exch def /x1 exch def /dy exch def /dx exch def\n" << " /w exch def /len exch def\n" // << " 0.1 setlinewidth x1 y1 moveto dx len mul dy len mul rlineto stroke" << " newpath x1 dy w 2 div mul add y1 dx w 2 div mul sub moveto\n" << " len w sub arrl sub dx dy lrl\n" << " arrw dy dx neg lrl\n" << " dx arrl w add mul dy w 2 div arrw add mul sub\n" << " dy arrl w add mul dx w 2 div arrw add mul add rlineto\n" << " dx arrl w add mul neg dy w 2 div arrw add mul sub\n" << " dy arrl w add mul neg dx w 2 div arrw add mul add rlineto\n" << " arrw dy dx neg lrl\n" << " len w sub arrl sub neg dx dy lrl\n" << " closepath fill } bind def\n"; os << "\ngsave\n"; if(_scale!=1.0) os << _scale << " dup scale\n"; os << "%Edges:\ngsave\n"; vector el; if(_enableParallel) { for(EdgeIt e(g);e!=INVALID;++e) el.push_back(e); sort(el.begin(),el.end(),edgeLess(g)); } for(NodeIt n(g);n!=INVALID;++n) for(OutEdgeIt e(g,n);e!=INVALID;++e) if(_drawArrows) { xy d(_coords[g.target(e)]-_coords[g.source(e)]); double l=sqrt(d.normSquare()); d/=l; xy x1(d*_nodeScale*_nodeSizes[g.source(e)]+ _coords[g.source(e)]); os << l-(_nodeSizes[g.source(e)]+ _nodeSizes[g.target(e)])*_nodeScale << ' ' << _edgeWidths[e]*_edgeWidthScale << ' ' << d.x << ' ' << d.y << ' ' << x1.x << ' ' << x1.y << ' ' << _edgeColors[e].getR() << ' ' << _edgeColors[e].getG() << ' ' << _edgeColors[e].getB() << " arr\n"; } else os << _coords[g.source(e)].x << ' ' << _coords[g.source(e)].y << ' ' << _coords[g.target(e)].x << ' ' << _coords[g.target(e)].y << ' ' << _edgeColors[e].getR() << ' ' << _edgeColors[e].getG() << ' ' << _edgeColors[e].getB() << ' ' << _edgeWidths[e]*_edgeWidthScale << " l\n"; os << "grestore\n%Nodes:\ngsave\n"; for(NodeIt n(g);n!=INVALID;++n) os << _coords[n].x << ' ' << _coords[n].y << ' ' << _nodeSizes[n]*_nodeScale << ' ' << _nodeColors[n].getR() << ' ' << _nodeColors[n].getG() << ' ' << _nodeColors[n].getB() << " n\n"; os << "grestore\ngrestore\n"; //CleanUp: if(_pleaseRemoveOsStream) {delete &os;} } }; ///Generates an EPS file from a graph ///\ingroup misc ///Generates an EPS file from a graph. ///\param g is a reference to the graph to be printed ///\param os is a reference to the output stream. ///By default it is std::cout /// ///This function also has a lot of \ref named-templ-param "named parameters", ///they are declared as the members of class \ref GraphToEps. The following ///example shows how to use these parameters. ///\code /// graphToEps(g).scale(10).coords(coords) /// .nodeScale(2).nodeSizes(sizes) /// .edgeWidthScale(.4); ///\endcode ///\sa GraphToEps template GraphToEps > graphToEps(G &g,std::ostream& os=std::cout) { return GraphToEps >(DefaultGraphToEpsTraits(g,os)); } ///Generates an EPS file from a graph ///\ingroup misc ///Generates an EPS file from a graph. ///\param g is a reference to the graph to be printed ///\param file_name is the output file_name. /// ///This function also has a lot of \ref named-templ-param "named parameters", ///they are declared as the members of class \ref GraphToEps. The following ///example shows how to use these parameters. ///\code /// graphToEps(g).scale(10).coords(coords) /// .nodeScale(2).nodeSizes(sizes) /// .edgeWidthScale(.4); ///\endcode ///\sa GraphToEps ///\todo Avoid duplicated documentation ///\bug Exception handling is missing? (Or we can just ignore it?) template GraphToEps > graphToEps(G &g,char *file_name) { return GraphToEps > (DefaultGraphToEpsTraits(g,*new ofstream(file_name),true)); } } using namespace lemon; class ColorSet : public MapBase { public: Color operator[](int i) const { switch(i%8){ case 0: return Color(0,0,0); case 1: return Color(1,0,0); case 2: return Color(0,1,0); case 3: return Color(0,0,1); case 4: return Color(1,1,0); case 5: return Color(1,0,1); case 6: return Color(0,1,1); case 7: return Color(1,1,1); } return Color(0,0,0); } } colorSet; int main() { ListGraph g; typedef ListGraph::Node Node; typedef ListGraph::NodeIt NodeIt; typedef ListGraph::Edge Edge; typedef xy Xy; Node n1=g.addNode(); Node n2=g.addNode(); Node n3=g.addNode(); Node n4=g.addNode(); Node n5=g.addNode(); ListGraph::NodeMap coords(g); ListGraph::NodeMap sizes(g); ListGraph::NodeMap colors(g); ListGraph::EdgeMap ecolors(g); ListGraph::EdgeMap widths(g); coords[n1]=Xy(50,50); sizes[n1]=1; colors[n1]=1; coords[n2]=Xy(50,70); sizes[n2]=2; colors[n2]=2; coords[n3]=Xy(70,70); sizes[n3]=1; colors[n3]=3; coords[n4]=Xy(70,50); sizes[n4]=2; colors[n4]=4; coords[n5]=Xy(85,60); sizes[n5]=3; colors[n5]=5; Edge e; e=g.addEdge(n1,n2); ecolors[e]=0; widths[e]=1; e=g.addEdge(n2,n3); ecolors[e]=0; widths[e]=1; e=g.addEdge(n3,n5); ecolors[e]=0; widths[e]=3; e=g.addEdge(n5,n4); ecolors[e]=0; widths[e]=1; e=g.addEdge(n4,n1); ecolors[e]=0; widths[e]=1; e=g.addEdge(n2,n4); ecolors[e]=1; widths[e]=2; e=g.addEdge(n3,n4); ecolors[e]=2; widths[e]=1; graphToEps(g,"proba.eps").scale(10).coords(coords). nodeScale(2).nodeSizes(sizes). nodeColors(composeMap(colorSet,colors)). edgeColors(composeMap(colorSet,ecolors)). edgeWidthScale(.4).edgeWidths(widths); graphToEps(g,"proba_arr.eps").scale(10).coords(coords). nodeScale(2).nodeSizes(sizes). nodeColors(composeMap(colorSet,colors)). edgeColors(composeMap(colorSet,ecolors)). edgeWidthScale(.4).edgeWidths(widths). drawArrows().arrowWidth(1).arrowLength(1) ; }