COIN-OR::LEMON - Graph Library

source: lemon-0.x/lemon/graph_to_eps.h @ 1956:a055123339d5

Last change on this file since 1956:a055123339d5 was 1956:a055123339d5, checked in by Alpar Juttner, 18 years ago

Unified copyright notices

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