COIN-OR::LEMON - Graph Library

source: lemon-0.x/lemon/graph_to_eps.h @ 2000:ebcc93ead7da

Last change on this file since 2000:ebcc93ead7da was 1993:2115143eceea, checked in by Balazs Dezso, 14 years ago

utility, invalid and traits moved to bits

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