lemon/graph_to_eps.h
author hegyi
Mon, 21 Nov 2005 18:03:20 +0000
changeset 1823 cb082cdf3667
parent 1676 c3e416514759
child 1875 98698b69a902
permissions -rw-r--r--
NewMapWin has become Dialog instead of Window. Therefore it is created dynamically, when there is need for it, instead of keeping one instance in memory. This solution is slower, but more correct than before.
     1 /* -*- C++ -*-
     2  * lemon/graph_to_eps.h - Part of LEMON, a generic C++ optimization library
     3  *
     4  * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     5  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     6  *
     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.
    10  *
    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
    13  * purpose.
    14  *
    15  */
    16 
    17 #ifndef LEMON_GRAPH_TO_EPS_H
    18 #define LEMON_GRAPH_TO_EPS_H
    19 
    20 #include <sys/time.h>
    21 
    22 #include<iostream>
    23 #include<fstream>
    24 #include<sstream>
    25 #include<algorithm>
    26 #include<vector>
    27 
    28 #include <ctime>
    29 #include <cmath>
    30 
    31 #include<lemon/invalid.h>
    32 #include<lemon/xy.h>
    33 #include<lemon/maps.h>
    34 #include<lemon/bezier.h>
    35 
    36 
    37 ///\ingroup io_group
    38 ///\file
    39 ///\brief Simple graph drawer
    40 ///
    41 ///\author Alpar Juttner
    42 
    43 namespace lemon {
    44 
    45 ///Data structure representing RGB colors.
    46 
    47 ///Data structure representing RGB colors.
    48 ///\ingroup misc
    49 class Color
    50 {
    51   double _r,_g,_b;
    52 public:
    53   ///Default constructor
    54   Color() {}
    55   ///Constructor
    56   Color(double r,double g,double b) :_r(r),_g(g),_b(b) {};
    57   ///Returns the red component
    58   double & red() {return _r;}
    59   ///Returns the red component
    60   const double & red() const {return _r;}
    61   ///Returns the green component
    62   double & green() {return _g;}
    63   ///Returns the green component
    64   const double & green() const {return _g;}
    65   ///Returns the blue component
    66   double & blue() {return _b;}
    67   ///Returns the blue component
    68   const double & blue() const {return _b;}
    69   ///Set the color components
    70   void set(double r,double g,double b) { _r=r;_g=g;_b=b; };
    71 };
    72 
    73 ///Maps <tt>int</tt>s to different \ref Color "Color"s
    74 
    75 ///This map assigns one of the predefined \ref Color "Color"s
    76 ///to each <tt>int</tt>. It is possible to change the colors as well as their
    77 ///number. The integer range is cyclically mapped to the provided set of colors.
    78 ///
    79 ///This is a true \ref concept::ReferenceMap "reference map", so you can also
    80 ///change the actual colors.
    81 
    82 class ColorSet : public MapBase<int,Color>
    83 {
    84   std::vector<Color> colors;
    85 public:
    86   ///Constructor
    87 
    88   ///Constructor
    89   ///\param have_white indicates whether white is
    90   ///amongst the provided color (\c true) or not (\c false). If it is true,
    91   ///white will be assigned to \c 0.
    92   ///\param num the number of the allocated colors. If it is \c 0
    93   ///the default color configuration is set up (26 color plus the while).
    94   ///If \c num is less then 26/27 then the default color list is cut. Otherwise
    95   ///the color list is filled repeatedly with the default color list.
    96   ///(The colors can be changed later on.)
    97   ColorSet(bool have_white=false,int num=0)
    98   {
    99     do {
   100       if(have_white) colors.push_back(Color(1,1,1));
   101 
   102       colors.push_back(Color(0,0,0));
   103       colors.push_back(Color(1,0,0));
   104       colors.push_back(Color(0,1,0));
   105       colors.push_back(Color(0,0,1));
   106       colors.push_back(Color(1,1,0));
   107       colors.push_back(Color(1,0,1));
   108       colors.push_back(Color(0,1,1));
   109       
   110       colors.push_back(Color(.5,0,0));
   111       colors.push_back(Color(0,.5,0));
   112       colors.push_back(Color(0,0,.5));
   113       colors.push_back(Color(.5,.5,0));
   114       colors.push_back(Color(.5,0,.5));
   115       colors.push_back(Color(0,.5,.5));
   116       
   117       colors.push_back(Color(.5,.5,.5));
   118       colors.push_back(Color(1,.5,.5));
   119       colors.push_back(Color(.5,1,.5));
   120       colors.push_back(Color(.5,.5,1));
   121       colors.push_back(Color(1,1,.5));
   122       colors.push_back(Color(1,.5,1));
   123       colors.push_back(Color(.5,1,1));
   124       
   125       colors.push_back(Color(1,.5,0));
   126       colors.push_back(Color(.5,1,0));
   127       colors.push_back(Color(1,0,.5));
   128       colors.push_back(Color(0,1,.5));
   129       colors.push_back(Color(0,.5,1));
   130       colors.push_back(Color(.5,0,1));
   131     } while(int(colors.size())<num);
   132     //    colors.push_back(Color(1,1,1));
   133     if(num>0) colors.resize(num);
   134   }
   135   ///\e
   136   Color &operator[](int i)
   137   {
   138     return colors[i%colors.size()];
   139   }
   140   ///\e
   141   const Color &operator[](int i) const
   142   {
   143     return colors[i%colors.size()];
   144   }
   145   ///\e
   146   void set(int i,const Color &c)
   147   {
   148     colors[i%colors.size()]=c;
   149   }
   150   ///Sets the number of the exiting colors.
   151   void resize(int s) { colors.resize(s);}
   152   ///Returns the number of the existing colors.
   153   std::size_t size() const { return colors.size();}
   154 };
   155 
   156 ///Returns a visible distinct \ref Color
   157 
   158 ///Returns a \ref Color which is as different from the given parameter
   159 ///as it is possible.
   160 inline Color distantColor(const Color &c) 
   161 {
   162   return Color(c.red()<.5?1:0,c.green()<.5?1:0,c.blue()<.5?1:0);
   163 }
   164 ///Returns black for light colors and white for the dark ones.
   165 
   166 ///Returns black for light colors and white for the dark ones.
   167 inline Color distantBW(const Color &c){
   168   double v=(.2125*c.red()+.7154*c.green()+.0721*c.blue())<.5?1:0;
   169   return Color(v,v,v);
   170 }
   171 
   172 template<class MT>
   173 class _NegY {
   174 public:
   175   typedef typename MT::Key Key;
   176   typedef typename MT::Value Value;
   177   const MT &map;
   178   int yscale;
   179   _NegY(const MT &m,bool b) : map(m), yscale(1-b*2) {}
   180   Value operator[](Key n) { return Value(map[n].x,map[n].y*yscale);}
   181 };
   182 
   183 ///Default traits class of \ref GraphToEps
   184 
   185 ///Default traits class of \ref GraphToEps
   186 ///
   187 ///\c G is the type of the underlying graph.
   188 template<class G>
   189 struct DefaultGraphToEpsTraits
   190 {
   191   typedef G Graph;
   192   typedef typename Graph::Node Node;
   193   typedef typename Graph::NodeIt NodeIt;
   194   typedef typename Graph::Edge Edge;
   195   typedef typename Graph::EdgeIt EdgeIt;
   196   typedef typename Graph::InEdgeIt InEdgeIt;
   197   typedef typename Graph::OutEdgeIt OutEdgeIt;
   198   
   199 
   200   const Graph &g;
   201 
   202   std::ostream& os;
   203   
   204   typedef ConstMap<typename Graph::Node,xy<double> > CoordsMapType;
   205   CoordsMapType _coords;
   206   ConstMap<typename Graph::Node,double > _nodeSizes;
   207   ConstMap<typename Graph::Node,int > _nodeShapes;
   208 
   209   ConstMap<typename Graph::Node,Color > _nodeColors;
   210   ConstMap<typename Graph::Edge,Color > _edgeColors;
   211 
   212   ConstMap<typename Graph::Edge,double > _edgeWidths;
   213 
   214   double _edgeWidthScale;
   215   
   216   double _nodeScale;
   217   double _xBorder, _yBorder;
   218   double _scale;
   219   double _nodeBorderQuotient;
   220   
   221   bool _drawArrows;
   222   double _arrowLength, _arrowWidth;
   223   
   224   bool _showNodes, _showEdges;
   225 
   226   bool _enableParallel;
   227   double _parEdgeDist;
   228 
   229   bool _showNodeText;
   230   ConstMap<typename Graph::Node,bool > _nodeTexts;  
   231   double _nodeTextSize;
   232 
   233   bool _showNodePsText;
   234   ConstMap<typename Graph::Node,bool > _nodePsTexts;  
   235   char *_nodePsTextsPreamble;
   236   
   237   bool _undir;
   238   bool _pleaseRemoveOsStream;
   239 
   240   bool _scaleToA4;
   241 
   242   std::string _title;
   243   std::string _copyright;
   244 
   245   enum NodeTextColorType 
   246     { DIST_COL=0, DIST_BW=1, CUST_COL=2, SAME_COL=3 } _nodeTextColorType;
   247   ConstMap<typename Graph::Node,Color > _nodeTextColors;
   248 
   249   bool _autoNodeScale;
   250   bool _autoEdgeWidthScale;
   251 
   252   bool _negY;
   253   ///Constructor
   254 
   255   ///Constructor
   256   ///\param _g is a reference to the graph to be printed
   257   ///\param _os is a reference to the output stream.
   258   ///\param _os is a reference to the output stream.
   259   ///\param _pros If it is \c true, then the \c ostream referenced by \c _os
   260   ///will be explicitly deallocated by the destructor.
   261   ///By default it is <tt>std::cout</tt>
   262   DefaultGraphToEpsTraits(const G &_g,std::ostream& _os=std::cout,
   263 			  bool _pros=false) :
   264     g(_g), os(_os),
   265     _coords(xy<double>(1,1)), _nodeSizes(1.0), _nodeShapes(0),
   266     _nodeColors(Color(1,1,1)), _edgeColors(Color(0,0,0)),
   267     _edgeWidths(1), _edgeWidthScale(0.3),
   268     _nodeScale(1.0), _xBorder(10), _yBorder(10), _scale(1.0),
   269     _nodeBorderQuotient(.1),
   270     _drawArrows(false), _arrowLength(1), _arrowWidth(0.3),
   271     _showNodes(true), _showEdges(true),
   272     _enableParallel(false), _parEdgeDist(1),
   273     _showNodeText(false), _nodeTexts(false), _nodeTextSize(1),
   274     _showNodePsText(false), _nodePsTexts(false), _nodePsTextsPreamble(0),
   275     _undir(false),
   276     _pleaseRemoveOsStream(_pros), _scaleToA4(false),
   277     _nodeTextColorType(SAME_COL), _nodeTextColors(Color(0,0,0)),
   278     _autoNodeScale(false),
   279     _autoEdgeWidthScale(false),
   280     _negY(false)
   281   {}
   282 };
   283 
   284 ///Helper class to implement the named parameters of \ref graphToEps()
   285 
   286 ///Helper class to implement the named parameters of \ref graphToEps()
   287 ///\todo Is 'helper class' a good name for this?
   288 ///
   289 ///\todo Follow PostScript's DSC.
   290 /// Use own dictionary.
   291 ///\todo Useful new features.
   292 /// - Linestyles: dotted, dashed etc.
   293 /// - A second color and percent value for the lines.
   294 template<class T> class GraphToEps : public T 
   295 {
   296   // Can't believe it is required by the C++ standard
   297   using T::g;
   298   using T::os;
   299 
   300   using T::_coords;
   301   using T::_nodeSizes;
   302   using T::_nodeShapes;
   303   using T::_nodeColors;
   304   using T::_edgeColors;
   305   using T::_edgeWidths;
   306 
   307   using T::_edgeWidthScale;
   308   using T::_nodeScale;
   309   using T::_xBorder;
   310   using T::_yBorder;
   311   using T::_scale;
   312   using T::_nodeBorderQuotient;
   313   
   314   using T::_drawArrows;
   315   using T::_arrowLength;
   316   using T::_arrowWidth;
   317   
   318   using T::_showNodes;
   319   using T::_showEdges;
   320 
   321   using T::_enableParallel;
   322   using T::_parEdgeDist;
   323 
   324   using T::_showNodeText;
   325   using T::_nodeTexts;  
   326   using T::_nodeTextSize;
   327 
   328   using T::_showNodePsText;
   329   using T::_nodePsTexts;  
   330   using T::_nodePsTextsPreamble;
   331   
   332   using T::_undir;
   333   using T::_pleaseRemoveOsStream;
   334 
   335   using T::_scaleToA4;
   336 
   337   using T::_title;
   338   using T::_copyright;
   339 
   340   using T::NodeTextColorType;
   341   using T::CUST_COL;
   342   using T::DIST_COL;
   343   using T::DIST_BW;
   344   using T::_nodeTextColorType;
   345   using T::_nodeTextColors;
   346 
   347   using T::_autoNodeScale;
   348   using T::_autoEdgeWidthScale;
   349 
   350   using T::_negY;
   351 
   352   // dradnats ++C eht yb deriuqer si ti eveileb t'naC
   353 
   354   typedef typename T::Graph Graph;
   355   typedef typename Graph::Node Node;
   356   typedef typename Graph::NodeIt NodeIt;
   357   typedef typename Graph::Edge Edge;
   358   typedef typename Graph::EdgeIt EdgeIt;
   359   typedef typename Graph::InEdgeIt InEdgeIt;
   360   typedef typename Graph::OutEdgeIt OutEdgeIt;
   361 
   362   static const int INTERPOL_PREC;
   363   static const double A4HEIGHT;
   364   static const double A4WIDTH;
   365   static const double A4BORDER;
   366 
   367   bool dontPrint;
   368 
   369 public:
   370   ///Node shapes
   371 
   372   ///Node shapes
   373   ///
   374   enum NodeShapes { 
   375     /// = 0
   376     ///\image html nodeshape_0.png
   377     ///\image latex nodeshape_0.eps "CIRCLE shape (0)" width=2cm
   378     CIRCLE=0, 
   379     /// = 1
   380     ///\image html nodeshape_1.png
   381     ///\image latex nodeshape_1.eps "SQUARE shape (1)" width=2cm
   382     ///
   383     SQUARE=1, 
   384     /// = 2
   385     ///\image html nodeshape_2.png
   386     ///\image latex nodeshape_2.eps "DIAMOND shape (2)" width=2cm
   387     ///
   388     DIAMOND=2
   389   };
   390 
   391 private:
   392   class edgeLess {
   393     const Graph &g;
   394   public:
   395     edgeLess(const Graph &_g) : g(_g) {}
   396     bool operator()(Edge a,Edge b) const 
   397     {
   398       Node ai=std::min(g.source(a),g.target(a));
   399       Node aa=std::max(g.source(a),g.target(a));
   400       Node bi=std::min(g.source(b),g.target(b));
   401       Node ba=std::max(g.source(b),g.target(b));
   402       return ai<bi ||
   403 	(ai==bi && (aa < ba || 
   404 		    (aa==ba && ai==g.source(a) && bi==g.target(b))));
   405     }
   406   };
   407   bool isParallel(Edge e,Edge f) const
   408   {
   409     return (g.source(e)==g.source(f)&&
   410 	    g.target(e)==g.target(f)) ||
   411       (g.source(e)==g.target(f)&&
   412        g.target(e)==g.source(f));
   413   }
   414   template<class TT>
   415   static std::string psOut(const xy<TT> &p) 
   416     {
   417       std::ostringstream os;	
   418       os << p.x << ' ' << p.y;
   419       return os.str();
   420     }
   421   static std::string psOut(const Color &c) 
   422     {
   423       std::ostringstream os;	
   424       os << c.red() << ' ' << c.green() << ' ' << c.blue();
   425       return os.str();
   426     }
   427   
   428 public:
   429   GraphToEps(const T &t) : T(t), dontPrint(false) {};
   430   
   431   template<class X> struct CoordsTraits : public T {
   432   typedef X CoordsMapType;
   433     const X &_coords;
   434     CoordsTraits(const T &t,const X &x) : T(t), _coords(x) {}
   435   };
   436   ///Sets the map of the node coordinates
   437 
   438   ///Sets the map of the node coordinates.
   439   ///\param x must be a node map with xy<double> or \ref xy "xy<int>" values. 
   440   template<class X> GraphToEps<CoordsTraits<X> > coords(const X &x) {
   441     dontPrint=true;
   442     return GraphToEps<CoordsTraits<X> >(CoordsTraits<X>(*this,x));
   443   }
   444   template<class X> struct NodeSizesTraits : public T {
   445     const X &_nodeSizes;
   446     NodeSizesTraits(const T &t,const X &x) : T(t), _nodeSizes(x) {}
   447   };
   448   ///Sets the map of the node sizes
   449 
   450   ///Sets the map of the node sizes
   451   ///\param x must be a node map with \c double (or convertible) values. 
   452   template<class X> GraphToEps<NodeSizesTraits<X> > nodeSizes(const X &x)
   453   {
   454     dontPrint=true;
   455     return GraphToEps<NodeSizesTraits<X> >(NodeSizesTraits<X>(*this,x));
   456   }
   457   template<class X> struct NodeShapesTraits : public T {
   458     const X &_nodeShapes;
   459     NodeShapesTraits(const T &t,const X &x) : T(t), _nodeShapes(x) {}
   460   };
   461   ///Sets the map of the node shapes
   462 
   463   ///Sets the map of the node shapes.
   464   ///The availabe shape values
   465   ///can be found in \ref NodeShapes "enum NodeShapes".
   466   ///\param x must be a node map with \c int (or convertible) values. 
   467   ///\sa NodeShapes
   468   template<class X> GraphToEps<NodeShapesTraits<X> > nodeShapes(const X &x)
   469   {
   470     dontPrint=true;
   471     return GraphToEps<NodeShapesTraits<X> >(NodeShapesTraits<X>(*this,x));
   472   }
   473   template<class X> struct NodeTextsTraits : public T {
   474     const X &_nodeTexts;
   475     NodeTextsTraits(const T &t,const X &x) : T(t), _nodeTexts(x) {}
   476   };
   477   ///Sets the text printed on the nodes
   478 
   479   ///Sets the text printed on the nodes
   480   ///\param x must be a node map with type that can be pushed to a standard
   481   ///ostream. 
   482   template<class X> GraphToEps<NodeTextsTraits<X> > nodeTexts(const X &x)
   483   {
   484     dontPrint=true;
   485     _showNodeText=true;
   486     return GraphToEps<NodeTextsTraits<X> >(NodeTextsTraits<X>(*this,x));
   487   }
   488   template<class X> struct NodePsTextsTraits : public T {
   489     const X &_nodePsTexts;
   490     NodePsTextsTraits(const T &t,const X &x) : T(t), _nodePsTexts(x) {}
   491   };
   492   ///Inserts a PostScript block to the nodes
   493 
   494   ///With this command it is possible to insert a verbatim PostScript
   495   ///block to the nodes.
   496   ///The PS current point will be moved to the centre of the node before
   497   ///the PostScript block inserted.
   498   ///
   499   ///Before and after the block a newline character is inserted so you
   500   ///don't have to bother with the separators.
   501   ///
   502   ///\param x must be a node map with type that can be pushed to a standard
   503   ///ostream.
   504   ///
   505   ///\sa nodePsTextsPreamble()
   506   ///\todo Offer the choise not to move to the centre but pass the coordinates
   507   ///to the Postscript block inserted.
   508   template<class X> GraphToEps<NodePsTextsTraits<X> > nodePsTexts(const X &x)
   509   {
   510     dontPrint=true;
   511     _showNodePsText=true;
   512     return GraphToEps<NodePsTextsTraits<X> >(NodePsTextsTraits<X>(*this,x));
   513   }
   514   template<class X> struct EdgeWidthsTraits : public T {
   515     const X &_edgeWidths;
   516     EdgeWidthsTraits(const T &t,const X &x) : T(t), _edgeWidths(x) {}
   517   };
   518   ///Sets the map of the edge widths
   519 
   520   ///Sets the map of the edge widths
   521   ///\param x must be a edge map with \c double (or convertible) values. 
   522   template<class X> GraphToEps<EdgeWidthsTraits<X> > edgeWidths(const X &x)
   523   {
   524     dontPrint=true;
   525     return GraphToEps<EdgeWidthsTraits<X> >(EdgeWidthsTraits<X>(*this,x));
   526   }
   527 
   528   template<class X> struct NodeColorsTraits : public T {
   529     const X &_nodeColors;
   530     NodeColorsTraits(const T &t,const X &x) : T(t), _nodeColors(x) {}
   531   };
   532   ///Sets the map of the node colors
   533 
   534   ///Sets the map of the node colors
   535   ///\param x must be a node map with \ref Color values.
   536   ///
   537   ///\sa ColorSet
   538   template<class X> GraphToEps<NodeColorsTraits<X> >
   539   nodeColors(const X &x)
   540   {
   541     dontPrint=true;
   542     return GraphToEps<NodeColorsTraits<X> >(NodeColorsTraits<X>(*this,x));
   543   }
   544   template<class X> struct NodeTextColorsTraits : public T {
   545     const X &_nodeTextColors;
   546     NodeTextColorsTraits(const T &t,const X &x) : T(t), _nodeTextColors(x) {}
   547   };
   548   ///Sets the map of the node text colors
   549 
   550   ///Sets the map of the node text colors
   551   ///\param x must be a node map with \ref Color values. 
   552   ///
   553   ///\sa ColorSet
   554   template<class X> GraphToEps<NodeTextColorsTraits<X> >
   555   nodeTextColors(const X &x)
   556   {
   557     dontPrint=true;
   558     _nodeTextColorType=CUST_COL;
   559     return GraphToEps<NodeTextColorsTraits<X> >
   560       (NodeTextColorsTraits<X>(*this,x));
   561   }
   562   template<class X> struct EdgeColorsTraits : public T {
   563     const X &_edgeColors;
   564     EdgeColorsTraits(const T &t,const X &x) : T(t), _edgeColors(x) {}
   565   };
   566   ///Sets the map of the edge colors
   567 
   568   ///Sets the map of the edge colors
   569   ///\param x must be a edge map with \ref Color values. 
   570   ///
   571   ///\sa ColorSet
   572   template<class X> GraphToEps<EdgeColorsTraits<X> >
   573   edgeColors(const X &x)
   574   {
   575     dontPrint=true;
   576     return GraphToEps<EdgeColorsTraits<X> >(EdgeColorsTraits<X>(*this,x));
   577   }
   578   ///Sets a global scale factor for node sizes
   579 
   580   ///Sets a global scale factor for node sizes.
   581   /// 
   582   /// If nodeSizes() is not given, this function simply sets the node
   583   /// sizes to \c d.  If nodeSizes() is given, but
   584   /// autoNodeScale() is not, then the node size given by
   585   /// nodeSizes() will be multiplied by the value \c d.
   586   /// If both nodeSizes() and autoNodeScale() are used, then the
   587   /// node sizes will be scaled in such a way that the greatest size will be
   588   /// equal to \c d.
   589   GraphToEps<T> &nodeScale(double d) {_nodeScale=d;return *this;}
   590   ///Turns on/off the automatic node width scaling.
   591 
   592   ///Turns on/off the automatic node width scaling.
   593   ///
   594   ///\sa nodeScale()
   595   ///
   596   GraphToEps<T> &autoNodeScale(bool b=true) {
   597     _autoNodeScale=b;return *this;
   598   }
   599 
   600   ///Negates the Y coordinates.
   601 
   602   ///Negates the Y coordinates.
   603   ///
   604   ///\todo More docs.
   605   ///
   606   GraphToEps<T> &negateY(bool b=true) {
   607     _negY=b;return *this;
   608   }
   609 
   610   ///Sets a global scale factor for edge widths
   611 
   612   /// Sets a global scale factor for edge widths.
   613   ///
   614   /// If edgeWidths() is not given, this function simply sets the edge
   615   /// widths to \c d.  If edgeWidths() is given, but
   616   /// autoEdgeWidthScale() is not, then the edge withs given by
   617   /// edgeWidths() will be multiplied by the value \c d.
   618   /// If both edgeWidths() and autoEdgeWidthScale() are used, then the
   619   /// edge withs will be scaled in such a way that the greatest width will be
   620   /// equal to \c d.
   621   GraphToEps<T> &edgeWidthScale(double d) {_edgeWidthScale=d;return *this;}
   622   ///Turns on/off the automatic edge width scaling.
   623 
   624   ///Turns on/off the automatic edge width scaling.
   625   ///
   626   ///\sa edgeWidthScale()
   627   ///
   628   GraphToEps<T> &autoEdgeWidthScale(bool b=true) {
   629     _autoEdgeWidthScale=b;return *this;
   630   }
   631   ///Sets a global scale factor for the whole picture
   632 
   633   ///Sets a global scale factor for the whole picture
   634   ///
   635 
   636   GraphToEps<T> &scale(double d) {_scale=d;return *this;}
   637   ///Sets the width of the border around the picture
   638 
   639   ///Sets the width of the border around the picture
   640   ///
   641   GraphToEps<T> &border(double b) {_xBorder=_yBorder=b;return *this;}
   642   ///Sets the width of the border around the picture
   643 
   644   ///Sets the width of the border around the picture
   645   ///
   646   GraphToEps<T> &border(double x, double y) {
   647     _xBorder=x;_yBorder=y;return *this;
   648   }
   649   ///Sets whether to draw arrows
   650 
   651   ///Sets whether to draw arrows
   652   ///
   653   GraphToEps<T> &drawArrows(bool b=true) {_drawArrows=b;return *this;}
   654   ///Sets the length of the arrowheads
   655 
   656   ///Sets the length of the arrowheads
   657   ///
   658   GraphToEps<T> &arrowLength(double d) {_arrowLength*=d;return *this;}
   659   ///Sets the width of the arrowheads
   660 
   661   ///Sets the width of the arrowheads
   662   ///
   663   GraphToEps<T> &arrowWidth(double d) {_arrowWidth*=d;return *this;}
   664   
   665   ///Scales the drawing to fit to A4 page
   666 
   667   ///Scales the drawing to fit to A4 page
   668   ///
   669   GraphToEps<T> &scaleToA4() {_scaleToA4=true;return *this;}
   670   
   671   ///Enables parallel edges
   672 
   673   ///Enables parallel edges
   674   GraphToEps<T> &enableParallel(bool b=true) {_enableParallel=b;return *this;}
   675   
   676   ///Sets the distance 
   677   
   678   ///Sets the distance 
   679   ///
   680   GraphToEps<T> &parEdgeDist(double d) {_parEdgeDist*=d;return *this;}
   681   
   682   ///Hides the edges
   683   
   684   ///Hides the edges
   685   ///
   686   GraphToEps<T> &hideEdges(bool b=true) {_showEdges=!b;return *this;}
   687   ///Hides the nodes
   688   
   689   ///Hides the nodes
   690   ///
   691   GraphToEps<T> &hideNodes(bool b=true) {_showNodes=!b;return *this;}
   692   
   693   ///Sets the size of the node texts
   694   
   695   ///Sets the size of the node texts
   696   ///
   697   GraphToEps<T> &nodeTextSize(double d) {_nodeTextSize=d;return *this;}
   698 
   699   ///Sets the color of the node texts to be different from the node color
   700 
   701   ///Sets the color of the node texts to be as different from the node color
   702   ///as it is possible
   703   ///
   704   GraphToEps<T> &distantColorNodeTexts()
   705   {_nodeTextColorType=DIST_COL;return *this;}
   706   ///Sets the color of the node texts to be black or white and always visible.
   707 
   708   ///Sets the color of the node texts to be black or white according to
   709   ///which is more 
   710   ///different from the node color
   711   ///
   712   GraphToEps<T> &distantBWNodeTexts()
   713   {_nodeTextColorType=DIST_BW;return *this;}
   714 
   715   ///Gives a preamble block for node Postscript block.
   716   
   717   ///Gives a preamble block for node Postscript block.
   718   ///
   719   ///\sa nodePsTexts()
   720   GraphToEps<T> & nodePsTextsPreamble(const char *str) {
   721     _nodePsTextsPreamble=str ;return *this;
   722   }
   723   ///Sets whether the the graph is undirected
   724 
   725   ///Sets whether the the graph is undirected
   726   ///
   727   GraphToEps<T> &undir(bool b=true) {_undir=b;return *this;}
   728   ///Sets whether the the graph is directed
   729 
   730   ///Sets whether the the graph is directed.
   731   ///Use it to show the undirected edges as a pair of directed ones.
   732   GraphToEps<T> &bidir(bool b=true) {_undir=!b;return *this;}
   733 
   734   ///Sets the title.
   735 
   736   ///Sets the title of the generated image,
   737   ///namely it inserts a <tt>%%Title:</tt> DSC field to the header of
   738   ///the EPS file.
   739   GraphToEps<T> &title(const std::string &t) {_title=t;return *this;}
   740   ///Sets the copyright statement.
   741 
   742   ///Sets the copyright statement of the generated image,
   743   ///namely it inserts a <tt>%%Copyright:</tt> DSC field to the header of
   744   ///the EPS file.
   745   ///\todo Multiline copyright notice could be supported.
   746   GraphToEps<T> &copyright(const std::string &t) {_copyright=t;return *this;}
   747 
   748 protected:
   749   bool isInsideNode(xy<double> p, double r,int t) 
   750   {
   751     switch(t) {
   752     case CIRCLE:
   753       return p.normSquare()<=r*r;
   754     case SQUARE:
   755       return p.x<=r&&p.x>=-r&&p.y<=r&&p.y>=-r;
   756     case DIAMOND:
   757       return p.x+p.y<=r && p.x-p.y<=r && -p.x+p.y<=r && -p.x-p.y<=r;
   758     }
   759     return false;
   760   }
   761 
   762 public:
   763   ~GraphToEps() { }
   764   
   765   ///Draws the graph.
   766 
   767   ///Like other functions using
   768   ///\ref named-templ-func-param "named template parameters",
   769   ///this function calles the algorithm itself, i.e. in this case
   770   ///it draws the graph.
   771   void run() {
   772     if(dontPrint) return;
   773     
   774     _NegY<typename T::CoordsMapType> mycoords(_coords,_negY);
   775 
   776     os << "%!PS-Adobe-2.0 EPSF-2.0\n";
   777     if(_title.size()>0) os << "%%Title: " << _title << '\n';
   778      if(_copyright.size()>0) os << "%%Copyright: " << _copyright << '\n';
   779 //        << "%%Copyright: XXXX\n"
   780     os << "%%Creator: LEMON, graphToEps()\n";
   781     
   782     {
   783       char cbuf[50];
   784       timeval tv;
   785       gettimeofday(&tv, 0);
   786       ctime_r(&tv.tv_sec,cbuf);
   787       os << "%%CreationDate: " << cbuf;
   788     }
   789 
   790     if (_autoEdgeWidthScale) {
   791       double max_w=0;
   792       for(EdgeIt e(g);e!=INVALID;++e)
   793 	max_w=std::max(double(_edgeWidths[e]),max_w);
   794       ///\todo better 'epsilon' would be nice here.
   795       if(max_w>1e-9) {
   796 	_edgeWidthScale/=max_w;
   797       }
   798     }
   799 
   800     if (_autoNodeScale) {
   801       double max_s=0;
   802       for(NodeIt n(g);n!=INVALID;++n)
   803 	max_s=std::max(double(_nodeSizes[n]),max_s);
   804       ///\todo better 'epsilon' would be nice here.
   805       if(max_s>1e-9) {
   806 	_nodeScale/=max_s;
   807       }
   808     }
   809 
   810 
   811     BoundingBox<double> bb;
   812     ///\bug: Chech whether the graph is empty.
   813     for(NodeIt n(g);n!=INVALID;++n) {
   814       double ns=_nodeSizes[n]*_nodeScale;
   815       xy<double> p(ns,ns);
   816       bb.add(p+mycoords[n]);
   817       bb.add(-p+mycoords[n]);
   818     }
   819     if (bb.empty()) {
   820       bb = BoundingBox<double>(xy<double>(0,0));
   821     }
   822     
   823     if(_scaleToA4)
   824       os <<"%%BoundingBox: 0 0 596 842\n%%DocumentPaperSizes: a4\n";
   825     else os << "%%BoundingBox: "
   826 	    << bb.left()   * _scale - _xBorder << ' '
   827 	    << bb.bottom() * _scale - _yBorder << ' '
   828 	    << bb.right()  * _scale + _xBorder << ' '
   829 	    << bb.top()    * _scale + _yBorder << '\n';
   830     
   831     os << "%%EndComments\n";
   832     
   833     //x1 y1 x2 y2 x3 y3 cr cg cb w
   834     os << "/lb { setlinewidth setrgbcolor newpath moveto\n"
   835        << "      4 2 roll 1 index 1 index curveto stroke } bind def\n";
   836     os << "/l { setlinewidth setrgbcolor newpath moveto lineto stroke } bind def\n";
   837     //x y r
   838     os << "/c { newpath dup 3 index add 2 index moveto 0 360 arc closepath } bind def\n";
   839     //x y r
   840     os << "/sq { newpath 2 index 1 index add 2 index 2 index add moveto\n"
   841        << "      2 index 1 index sub 2 index 2 index add lineto\n"
   842        << "      2 index 1 index sub 2 index 2 index sub lineto\n"
   843        << "      2 index 1 index add 2 index 2 index sub lineto\n"
   844        << "      closepath pop pop pop} bind def\n";
   845     //x y r
   846     os << "/di { newpath 2 index 1 index add 2 index moveto\n"
   847        << "      2 index             2 index 2 index add lineto\n"
   848        << "      2 index 1 index sub 2 index             lineto\n"
   849        << "      2 index             2 index 2 index sub lineto\n"
   850        << "      closepath pop pop pop} bind def\n";
   851     // x y r cr cg cb
   852     os << "/nc { 0 0 0 setrgbcolor 5 index 5 index 5 index c fill\n"
   853        << "     setrgbcolor " << 1+_nodeBorderQuotient << " div c fill\n"
   854        << "   } bind def\n";
   855     os << "/nsq { 0 0 0 setrgbcolor 5 index 5 index 5 index sq fill\n"
   856        << "     setrgbcolor " << 1+_nodeBorderQuotient << " div sq fill\n"
   857        << "   } bind def\n";
   858     os << "/ndi { 0 0 0 setrgbcolor 5 index 5 index 5 index di fill\n"
   859        << "     setrgbcolor " << 1+_nodeBorderQuotient << " div di fill\n"
   860        << "   } bind def\n";
   861     os << "/arrl " << _arrowLength << " def\n";
   862     os << "/arrw " << _arrowWidth << " def\n";
   863     // l dx_norm dy_norm
   864     os << "/lrl { 2 index mul exch 2 index mul exch rlineto pop} bind def\n";
   865     //len w dx_norm dy_norm x1 y1 cr cg cb
   866     os << "/arr { setrgbcolor /y1 exch def /x1 exch def /dy exch def /dx exch def\n"
   867        << "       /w exch def /len exch def\n"
   868       //	 << "       0.1 setlinewidth x1 y1 moveto dx len mul dy len mul rlineto stroke"
   869        << "       newpath x1 dy w 2 div mul add y1 dx w 2 div mul sub moveto\n"
   870        << "       len w sub arrl sub dx dy lrl\n"
   871        << "       arrw dy dx neg lrl\n"
   872        << "       dx arrl w add mul dy w 2 div arrw add mul sub\n"
   873        << "       dy arrl w add mul dx w 2 div arrw add mul add rlineto\n"
   874        << "       dx arrl w add mul neg dy w 2 div arrw add mul sub\n"
   875        << "       dy arrl w add mul neg dx w 2 div arrw add mul add rlineto\n"
   876        << "       arrw dy dx neg lrl\n"
   877        << "       len w sub arrl sub neg dx dy lrl\n"
   878        << "       closepath fill } bind def\n";
   879     os << "/cshow { 2 index 2 index moveto dup stringwidth pop\n"
   880        << "         neg 2 div fosi .35 mul neg rmoveto show pop pop} def\n";
   881 
   882     os << "\ngsave\n";
   883     if(_scaleToA4)
   884       if(bb.height()>bb.width()) {
   885 	double sc= std::min((A4HEIGHT-2*A4BORDER)/bb.height(),
   886 		  (A4WIDTH-2*A4BORDER)/bb.width());
   887 	os << ((A4WIDTH -2*A4BORDER)-sc*bb.width())/2 + A4BORDER << ' '
   888 	   << ((A4HEIGHT-2*A4BORDER)-sc*bb.height())/2 + A4BORDER << " translate\n"
   889 	   << sc << " dup scale\n"
   890 	   << -bb.left() << ' ' << -bb.bottom() << " translate\n";
   891       }
   892       else {
   893 	//\todo Verify centering
   894 	double sc= std::min((A4HEIGHT-2*A4BORDER)/bb.width(),
   895 		  (A4WIDTH-2*A4BORDER)/bb.height());
   896 	os << ((A4WIDTH -2*A4BORDER)-sc*bb.height())/2 + A4BORDER << ' '
   897 	   << ((A4HEIGHT-2*A4BORDER)-sc*bb.width())/2 + A4BORDER  << " translate\n"
   898 	   << sc << " dup scale\n90 rotate\n"
   899 	   << -bb.left() << ' ' << -bb.top() << " translate\n";	
   900 	}
   901     else if(_scale!=1.0) os << _scale << " dup scale\n";
   902     
   903     if(_showEdges) {
   904       os << "%Edges:\ngsave\n";      
   905       if(_enableParallel) {
   906 	std::vector<Edge> el;
   907 	for(EdgeIt e(g);e!=INVALID;++e)
   908 	  if((!_undir||g.source(e)<g.target(e))&&_edgeWidths[e]>0)
   909 	    el.push_back(e);
   910 	std::sort(el.begin(),el.end(),edgeLess(g));
   911 	
   912 	typename std::vector<Edge>::iterator j;
   913 	for(typename std::vector<Edge>::iterator i=el.begin();i!=el.end();i=j) {
   914 	  for(j=i+1;j!=el.end()&&isParallel(*i,*j);++j) ;
   915 
   916 	  double sw=0;
   917 	  for(typename std::vector<Edge>::iterator e=i;e!=j;++e)
   918 	    sw+=_edgeWidths[*e]*_edgeWidthScale+_parEdgeDist;
   919 	  sw-=_parEdgeDist;
   920 	  sw/=-2.0;
   921 	  xy<double> dvec(mycoords[g.target(*i)]-mycoords[g.source(*i)]);
   922 	  double l=std::sqrt(dvec.normSquare()); 
   923 	  ///\todo better 'epsilon' would be nice here.
   924 	  xy<double> d(dvec/std::max(l,1e-9));
   925  	  xy<double> m;
   926 // 	  m=xy<double>(mycoords[g.target(*i)]+mycoords[g.source(*i)])/2.0;
   927 
   928 //  	  m=xy<double>(mycoords[g.source(*i)])+
   929 // 	    dvec*(double(_nodeSizes[g.source(*i)])/
   930 // 	       (_nodeSizes[g.source(*i)]+_nodeSizes[g.target(*i)]));
   931 
   932  	  m=xy<double>(mycoords[g.source(*i)])+
   933 	    d*(l+_nodeSizes[g.source(*i)]-_nodeSizes[g.target(*i)])/2.0;
   934 
   935 	  for(typename std::vector<Edge>::iterator e=i;e!=j;++e) {
   936 	    sw+=_edgeWidths[*e]*_edgeWidthScale/2.0;
   937 	    xy<double> mm=m+rot90(d)*sw/.75;
   938 	    if(_drawArrows) {
   939 	      int node_shape;
   940 	      xy<double> s=mycoords[g.source(*e)];
   941 	      xy<double> t=mycoords[g.target(*e)];
   942 	      double rn=_nodeSizes[g.target(*e)]*_nodeScale;
   943 	      node_shape=_nodeShapes[g.target(*e)];
   944 	      Bezier3 bez(s,mm,mm,t);
   945 	      double t1=0,t2=1;
   946 	      for(int i=0;i<INTERPOL_PREC;++i)
   947 		if(isInsideNode(bez((t1+t2)/2)-t,rn,node_shape)) t2=(t1+t2)/2;
   948 		else t1=(t1+t2)/2;
   949 	      xy<double> apoint=bez((t1+t2)/2);
   950 	      rn = _arrowLength+_edgeWidths[*e]*_edgeWidthScale;
   951 	      rn*=rn;
   952 	      t2=(t1+t2)/2;t1=0;
   953 	      for(int i=0;i<INTERPOL_PREC;++i)
   954 		if((bez((t1+t2)/2)-apoint).normSquare()>rn) t1=(t1+t2)/2;
   955 		else t2=(t1+t2)/2;
   956 	      xy<double> linend=bez((t1+t2)/2);	      
   957 	      bez=bez.before((t1+t2)/2);
   958 // 	      rn=_nodeSizes[g.source(*e)]*_nodeScale;
   959 // 	      node_shape=_nodeShapes[g.source(*e)];
   960 // 	      t1=0;t2=1;
   961 // 	      for(int i=0;i<INTERPOL_PREC;++i)
   962 // 		if(isInsideNode(bez((t1+t2)/2)-t,rn,node_shape)) t1=(t1+t2)/2;
   963 // 		else t2=(t1+t2)/2;
   964 // 	      bez=bez.after((t1+t2)/2);
   965 	      os << _edgeWidths[*e]*_edgeWidthScale << " setlinewidth "
   966 		 << _edgeColors[*e].red() << ' '
   967 		 << _edgeColors[*e].green() << ' '
   968 		 << _edgeColors[*e].blue() << " setrgbcolor newpath\n"
   969 		 << bez.p1.x << ' ' <<  bez.p1.y << " moveto\n"
   970 		 << bez.p2.x << ' ' << bez.p2.y << ' '
   971 		 << bez.p3.x << ' ' << bez.p3.y << ' '
   972 		 << bez.p4.x << ' ' << bez.p4.y << " curveto stroke\n";
   973 	      xy<double> dd(rot90(linend-apoint));
   974 	      dd*=(.5*_edgeWidths[*e]*_edgeWidthScale+_arrowWidth)/
   975 		std::sqrt(dd.normSquare());
   976 	      os << "newpath " << psOut(apoint) << " moveto "
   977 		 << psOut(linend+dd) << " lineto "
   978 		 << psOut(linend-dd) << " lineto closepath fill\n";
   979 	    }
   980 	    else {
   981 	      os << mycoords[g.source(*e)].x << ' '
   982 		 << mycoords[g.source(*e)].y << ' '
   983 		 << mm.x << ' ' << mm.y << ' '
   984 		 << mycoords[g.target(*e)].x << ' '
   985 		 << mycoords[g.target(*e)].y << ' '
   986 		 << _edgeColors[*e].red() << ' '
   987 		 << _edgeColors[*e].green() << ' '
   988 		 << _edgeColors[*e].blue() << ' '
   989 		 << _edgeWidths[*e]*_edgeWidthScale << " lb\n";
   990 	    }
   991 	    sw+=_edgeWidths[*e]*_edgeWidthScale/2.0+_parEdgeDist;
   992 	  }
   993 	}
   994       }
   995       else for(EdgeIt e(g);e!=INVALID;++e)
   996 	if((!_undir||g.source(e)<g.target(e))&&_edgeWidths[e]>0)
   997 	  if(_drawArrows) {
   998 	    xy<double> d(mycoords[g.target(e)]-mycoords[g.source(e)]);
   999 	    double rn=_nodeSizes[g.target(e)]*_nodeScale;
  1000 	    int node_shape=_nodeShapes[g.target(e)];
  1001 	    double t1=0,t2=1;
  1002 	    for(int i=0;i<INTERPOL_PREC;++i)
  1003 	      if(isInsideNode((-(t1+t2)/2)*d,rn,node_shape)) t1=(t1+t2)/2;
  1004 	      else t2=(t1+t2)/2;
  1005 	    double l=sqrt(d.normSquare());
  1006 	    d/=l;
  1007 	    
  1008 	    os << l*(1-(t1+t2)/2) << ' '
  1009 	       << _edgeWidths[e]*_edgeWidthScale << ' '
  1010 	       << d.x << ' ' << d.y << ' '
  1011 	       << mycoords[g.source(e)].x << ' '
  1012 	       << mycoords[g.source(e)].y << ' '
  1013 	       << _edgeColors[e].red() << ' '
  1014 	       << _edgeColors[e].green() << ' '
  1015 	       << _edgeColors[e].blue() << " arr\n";
  1016 	  }
  1017 	  else os << mycoords[g.source(e)].x << ' '
  1018 		  << mycoords[g.source(e)].y << ' '
  1019 		  << mycoords[g.target(e)].x << ' '
  1020 		  << mycoords[g.target(e)].y << ' '
  1021 		  << _edgeColors[e].red() << ' '
  1022 		  << _edgeColors[e].green() << ' '
  1023 		  << _edgeColors[e].blue() << ' '
  1024 		  << _edgeWidths[e]*_edgeWidthScale << " l\n";
  1025       os << "grestore\n";
  1026     }
  1027     if(_showNodes) {
  1028       os << "%Nodes:\ngsave\n";
  1029       for(NodeIt n(g);n!=INVALID;++n) {
  1030 	os << mycoords[n].x << ' ' << mycoords[n].y << ' '
  1031 	   << _nodeSizes[n]*_nodeScale << ' '
  1032 	   << _nodeColors[n].red() << ' '
  1033 	   << _nodeColors[n].green() << ' '
  1034 	   << _nodeColors[n].blue() << ' ';
  1035 	switch(_nodeShapes[n]) {
  1036 	case CIRCLE:
  1037 	  os<< "nc";break;
  1038 	case SQUARE:
  1039 	  os<< "nsq";break;
  1040 	case DIAMOND:
  1041 	  os<< "ndi";break;
  1042 	}
  1043 	os<<'\n';
  1044       }
  1045       os << "grestore\n";
  1046     }
  1047     if(_showNodeText) {
  1048       os << "%Node texts:\ngsave\n";
  1049       os << "/fosi " << _nodeTextSize << " def\n";
  1050       os << "(Helvetica) findfont fosi scalefont setfont\n";
  1051       for(NodeIt n(g);n!=INVALID;++n) {
  1052 	switch(_nodeTextColorType) {
  1053 	case DIST_COL:
  1054 	  os << psOut(distantColor(_nodeColors[n])) << " setrgbcolor\n";
  1055 	  break;
  1056 	case DIST_BW:
  1057 	  os << psOut(distantBW(_nodeColors[n])) << " setrgbcolor\n";
  1058 	  break;
  1059 	case CUST_COL:
  1060 	  os << psOut(distantColor(_nodeTextColors[n])) << " setrgbcolor\n";
  1061 	  break;
  1062 	default:
  1063 	  os << "0 0 0 setrgbcolor\n";
  1064 	}
  1065 	os << mycoords[n].x << ' ' << mycoords[n].y
  1066 	   << " (" << _nodeTexts[n] << ") cshow\n";
  1067       }
  1068       os << "grestore\n";
  1069     }
  1070     if(_showNodePsText) {
  1071       os << "%Node PS blocks:\ngsave\n";
  1072       for(NodeIt n(g);n!=INVALID;++n)
  1073 	os << mycoords[n].x << ' ' << mycoords[n].y
  1074 	   << " moveto\n" << _nodePsTexts[n] << "\n";
  1075       os << "grestore\n";
  1076     }
  1077     
  1078     os << "grestore\nshowpage\n";
  1079 
  1080     //CleanUp:
  1081     if(_pleaseRemoveOsStream) {delete &os;}
  1082   } 
  1083 };
  1084 
  1085 template<class T>
  1086 const int GraphToEps<T>::INTERPOL_PREC = 20;
  1087 template<class T>
  1088 const double GraphToEps<T>::A4HEIGHT = 841.8897637795276;
  1089 template<class T>
  1090 const double GraphToEps<T>::A4WIDTH  = 595.275590551181;
  1091 template<class T>
  1092 const double GraphToEps<T>::A4BORDER = 15;
  1093 
  1094 
  1095 ///Generates an EPS file from a graph
  1096 
  1097 ///\ingroup io_group
  1098 ///Generates an EPS file from a graph.
  1099 ///\param g is a reference to the graph to be printed
  1100 ///\param os is a reference to the output stream.
  1101 ///By default it is <tt>std::cout</tt>
  1102 ///
  1103 ///This function also has a lot of
  1104 ///\ref named-templ-func-param "named parameters",
  1105 ///they are declared as the members of class \ref GraphToEps. The following
  1106 ///example shows how to use these parameters.
  1107 ///\code
  1108 /// graphToEps(g,os).scale(10).coords(coords)
  1109 ///              .nodeScale(2).nodeSizes(sizes)
  1110 ///              .edgeWidthScale(.4).run();
  1111 ///\endcode
  1112 ///\warning Don't forget to put the \ref GraphToEps::run() "run()"
  1113 ///to the end of the parameter list.
  1114 ///\sa GraphToEps
  1115 ///\sa graphToEps(G &g, const char *file_name)
  1116 template<class G>
  1117 GraphToEps<DefaultGraphToEpsTraits<G> > 
  1118 graphToEps(G &g, std::ostream& os=std::cout)
  1119 {
  1120   return 
  1121     GraphToEps<DefaultGraphToEpsTraits<G> >(DefaultGraphToEpsTraits<G>(g,os));
  1122 }
  1123  
  1124 ///Generates an EPS file from a graph
  1125 
  1126 ///\ingroup io_group
  1127 ///This function does the same as
  1128 ///\ref graphToEps(G &g,std::ostream& os)
  1129 ///but it writes its output into the file \c file_name
  1130 ///instead of a stream.
  1131 ///\sa graphToEps(G &g, std::ostream& os)
  1132 template<class G>
  1133 GraphToEps<DefaultGraphToEpsTraits<G> > 
  1134 graphToEps(G &g,const char *file_name)
  1135 {
  1136   return GraphToEps<DefaultGraphToEpsTraits<G> >
  1137     (DefaultGraphToEpsTraits<G>(g,*new std::ofstream(file_name),true));
  1138 }
  1139 
  1140 ///Generates an EPS file from a graph
  1141 
  1142 ///\ingroup io_group
  1143 ///This function does the same as
  1144 ///\ref graphToEps(G &g,std::ostream& os)
  1145 ///but it writes its output into the file \c file_name
  1146 ///instead of a stream.
  1147 ///\sa graphToEps(G &g, std::ostream& os)
  1148 template<class G>
  1149 GraphToEps<DefaultGraphToEpsTraits<G> > 
  1150 graphToEps(G &g,const std::string& file_name)
  1151 {
  1152   return GraphToEps<DefaultGraphToEpsTraits<G> >
  1153     (DefaultGraphToEpsTraits<G>(g,*new std::ofstream(file_name.c_str()),true));
  1154 }
  1155 
  1156 } //END OF NAMESPACE LEMON
  1157 
  1158 #endif // LEMON_GRAPH_TO_EPS_H