# HG changeset patch # User alpar # Date 1105215416 0 # Node ID 8226427845bc355098e1f4f985ef76c90520e7b5 # Parent e3433c0241235b44cd851f47d26e2f64b9bf3887 - Parallel edge support (without arrowheads) - Texts on the nodes diff -r e3433c024123 -r 8226427845bc src/work/alpar/graph_to_eps.cc --- a/src/work/alpar/graph_to_eps.cc Sat Jan 08 20:12:50 2005 +0000 +++ b/src/work/alpar/graph_to_eps.cc Sat Jan 08 20:16:56 2005 +0000 @@ -1,3 +1,19 @@ +/* -*- C++ -*- + * src/lemon/graph_to_eps.h - Part of LEMON, a generic C++ optimization library + * + * Copyright (C) 2004 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport + * (Egervary Combinatorial Optimization Research Group, EGRES). + * + * Permission to use, modify and distribute this software is granted + * provided that this copyright notice appears in all copies. For + * precise terms see the accompanying LICENSE file. + * + * This software is provided "AS IS" with no warranty of any kind, + * express or implied, and with no claim as to its suitability for any + * purpose. + * + */ + #include #include #include @@ -8,8 +24,9 @@ #include -///\file \ingroup misc -///Simple graph drawer +///\ingroup misc +///\file +///\brief Simple graph drawer namespace lemon { @@ -74,7 +91,14 @@ bool _drawArrows; double _arrowLength, _arrowWidth; + bool _showNodes, _showEdges; + bool _enableParallel; + double _parEdgeDist; + + bool _showNodeText; + ConstMap _nodeTexts; + double _nodeTextSize; bool _pleaseRemoveOsStream; ///Constructor @@ -95,7 +119,10 @@ _nodeScale(1.0), _xBorder(10), _yBorder(10), _scale(1.0), _nodeBorderQuotient(.1), _drawArrows(false), _arrowLength(1), _arrowWidth(0.3), - _enableParallel(false), _pleaseRemoveOsStream(_pros) {} + _showNodes(true), _showEdges(true), + _enableParallel(false), _parEdgeDist(1), + _showNodeText(false), _nodeTexts(false), _nodeTextSize(1), + _pleaseRemoveOsStream(_pros) {} }; ///Helper class to implement the named parameters of \ref graphToEps() @@ -130,7 +157,16 @@ (aa==ba && ai==g.source(a) && bi==g.target(b)))); } }; - + bool isParallel(Edge e,Edge f) const + { + return (g.source(e)==g.source(f)&&g.target(e)==g.target(f))|| + (g.source(e)==g.target(f)&&g.target(e)==g.source(f)); + } + static xy rot(xy v) + { + return xy(v.y,-v.x); + } + public: GraphToEps(const T &t) : T(t), dontPrint(false) {}; @@ -159,6 +195,21 @@ dontPrint=true; return GraphToEps >(NodeSizesTraits(*this,x)); } + template struct NodeTextsTraits : public T { + const X &_nodeTexts; + NodeTextsTraits(const T &t,const X &x) : T(t), _nodeTexts(x) {} + }; + ///Sets the text printed on the nodes + + ///Sets the text printed on the nodes + ///\param x must be a node map with type that can be pushed to a standard + ///ostream. + template GraphToEps > nodeTexts(const X &x) + { + dontPrint=true; + _showNodeText=true; + return GraphToEps >(NodeTextsTraits(*this,x)); + } template struct EdgeWidthsTraits : public T { const X &_edgeWidths; EdgeWidthsTraits(const T &t,const X &x) : T(t), _edgeWidths(x) {} @@ -247,9 +298,33 @@ ///Enables parallel edges ///Enables parallel edges - ///\todo Unimplemented + ///\todo Partially implemented GraphToEps &enableParallel(bool b=true) {_enableParallel=b;return *this;} + ///Sets the distance + + ///Sets the distance + /// + GraphToEps &parEdgeDist(double d) {_parEdgeDist*=d;return *this;} + + ///Hides the edges + + ///Hides the edges + /// + GraphToEps &hideEdges(bool b=true) {_showEdges=!b;return *this;} + ///Hides the nodes + + ///Hides the nodes + /// + GraphToEps &hideNodes(bool b=true) {_showNodes=!b;return *this;} + + ///Sets the size of the node texts + + ///Sets the size of the node texts + /// + GraphToEps &nodeTextSize(double d) {_nodeTextSize=d;return *this;} + + ~GraphToEps() { if(dontPrint) return; @@ -268,53 +343,104 @@ << bb.bottom()*_scale-_yBorder << ' ' << bb.right()* _scale+_xBorder << ' ' << bb.top()* _scale+_yBorder << '\n'; - //x1 y1 x2 y2 cr cg cb w + //x1 y1 x2 y2 x3 y3 cr cg cb w + os << "/lb { setlinewidth setrgbcolor newpath moveto\n" + << " 4 2 roll 1 index 1 index curveto stroke } bind def\n"; 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"; + os << "/c { newpath dup 3 index add 2 index moveto 0 360 arc closepath } 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"; + << " 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" + << " /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"; + << " 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 << "/cshow { 2 index 2 index moveto\n" + << " dup stringwidth pop neg 2 div fosi .35 mul neg rmoveto show pop pop} 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)]+ + if(_showEdges) + if(_enableParallel) { + vector el; + for(EdgeIt e(g);e!=INVALID;++e) el.push_back(e); + sort(el.begin(),el.end(),edgeLess(g)); + + typename vector::iterator j; + for(typename vector::iterator i=el.begin();i!=el.end();i=j) { + for(j=i+1;j!=el.end()&&isParallel(*i,*j);++j) ; + + 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 { + double sw=0; + for(typename vector::iterator e=i;e!=j;++e) + sw+=_edgeWidths[*e]*_edgeWidthScale+_parEdgeDist; + sw-=_parEdgeDist; + sw/=-2.0; + xy d(_coords[g.target(*i)]-_coords[g.source(*i)]); + double l=sqrt(d.normSquare()); + d/=l; + for(typename vector::iterator e=i;e!=j;++e) { + sw+=_edgeWidths[*e]*_edgeWidthScale/2.0; + xy m(_coords[g.target(*e)]+_coords[g.source(*e)]); + m=m/2.0+rot(d)*sw/.75; + os << _coords[g.source(*e)].x << ' ' + << _coords[g.source(*e)].y << ' ' + << m.x << ' ' << m.y << ' ' + << _coords[g.target(*e)].x << ' ' + << _coords[g.target(*e)].y << ' ' + << _edgeColors[*e].getR() << ' ' + << _edgeColors[*e].getG() << ' ' + << _edgeColors[*e].getB() << ' ' + << _edgeWidths[*e]*_edgeWidthScale << " lb\n"; + sw+=_edgeWidths[*e]*_edgeWidthScale/2.0+_parEdgeDist; + } + } + } + } + else 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 << ' ' @@ -322,8 +448,8 @@ << _edgeColors[e].getR() << ' ' << _edgeColors[e].getG() << ' ' << _edgeColors[e].getB() << " arr\n"; - } - else os << _coords[g.source(e)].x << ' ' + } + else os << _coords[g.source(e)].x << ' ' << _coords[g.source(e)].y << ' ' << _coords[g.target(e)].x << ' ' << _coords[g.target(e)].y << ' ' @@ -332,15 +458,24 @@ << _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 << ' ' + if(_showNodes) + 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"; + if(_showNodeText) { + os << "grestore\n%Node texts:\ngsave\n"; + os << "/fosi " << _nodeTextSize << " def\n"; + os << "(Helvetica) findfont fosi scalefont setfont\n"; + os << "0 0 0 setrgbcolor\n"; + for(NodeIt n(g);n!=INVALID;++n) + os << _coords[n].x << ' ' << _coords[n].y + << " (" << _nodeTexts[n] << ") cshow\n"; + } os << "grestore\ngrestore\n"; - //CleanUp: if(_pleaseRemoveOsStream) {delete &os;} } @@ -364,9 +499,10 @@ /// .edgeWidthScale(.4); ///\endcode ///\sa GraphToEps +///\sa graphToEps(G &g, char *file_name) template GraphToEps > -graphToEps(G &g,std::ostream& os=std::cout) +graphToEps(G &g, std::ostream& os=std::cout) { return GraphToEps >(DefaultGraphToEpsTraits(g,os)); @@ -374,22 +510,12 @@ ///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?) +//\ingroup misc +///This function does the same as +///\ref graphToEps(G &g,std::ostream& os) +///but it writes its output into the file \c file_name +///instead of a stream. +///\sa graphToEps(G &g, std::ostream& os) template GraphToEps > graphToEps(G &g,char *file_name) @@ -398,6 +524,21 @@ (DefaultGraphToEpsTraits(g,*new ofstream(file_name),true)); } +//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?) } @@ -422,6 +563,16 @@ } } colorSet; +class IdMap :public MapBase +{ + const ListGraph &g; +public: + IdMap(const ListGraph &_g) :g(_g) {} + Value operator[](Key n) const { return g.id(n); } +}; + + + int main() { ListGraph g; @@ -458,16 +609,39 @@ e=g.addEdge(n2,n4); ecolors[e]=1; widths[e]=2; e=g.addEdge(n3,n4); ecolors[e]=2; widths[e]=1; + IdMap id(g); + graphToEps(g,"proba.eps").scale(10).coords(coords). nodeScale(2).nodeSizes(sizes). nodeColors(composeMap(colorSet,colors)). edgeColors(composeMap(colorSet,ecolors)). - edgeWidthScale(.4).edgeWidths(widths); + edgeWidthScale(.4).edgeWidths(widths). + nodeTexts(id).nodeTextSize(3); + 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) - ; + nodeTexts(id).nodeTextSize(3). + drawArrows().arrowWidth(1).arrowLength(1); + + e=g.addEdge(n1,n4); ecolors[e]=2; widths[e]=1; + e=g.addEdge(n4,n1); ecolors[e]=1; widths[e]=2; + + e=g.addEdge(n1,n2); ecolors[e]=1; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=2; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=3; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=4; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=5; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=6; widths[e]=1; + e=g.addEdge(n1,n2); ecolors[e]=7; widths[e]=1; + + graphToEps(g,"proba_par.eps").scale(10).coords(coords). + nodeScale(2).nodeSizes(sizes). + nodeColors(composeMap(colorSet,colors)). + edgeColors(composeMap(colorSet,ecolors)). + edgeWidthScale(.4).edgeWidths(widths). + nodeTexts(id).nodeTextSize(3). + enableParallel().parEdgeDist(1.5); }