Several new named parameters and documentation added to graphToEps().
authoralpar
Tue, 04 Jan 2005 22:16:46 +0000
changeset 1050bcc0766a7b86
parent 1049 e27446e1deda
child 1051 4ebe32765b48
Several new named parameters and documentation added to graphToEps().
src/work/Doxyfile
src/work/alpar/graph_to_eps.cc
     1.1 --- a/src/work/Doxyfile	Tue Jan 04 22:14:42 2005 +0000
     1.2 +++ b/src/work/Doxyfile	Tue Jan 04 22:16:46 2005 +0000
     1.3 @@ -407,6 +407,7 @@
     1.4  			 jacint/graph_gen.h \
     1.5  			 marci/max_bipartite_matching.h \
     1.6  			 marci/bipartite_graph_wrapper.h \
     1.7 +			 alpar/graph_to_eps.cc \
     1.8                           deba \
     1.9                           
    1.10  
     2.1 --- a/src/work/alpar/graph_to_eps.cc	Tue Jan 04 22:14:42 2005 +0000
     2.2 +++ b/src/work/alpar/graph_to_eps.cc	Tue Jan 04 22:16:46 2005 +0000
     2.3 @@ -1,32 +1,41 @@
     2.4 +#include<math.h>
     2.5  #include<lemon/xy.h>
     2.6  #include<lemon/maps.h>
     2.7  #include<lemon/list_graph.h>
     2.8  
     2.9 -///\file
    2.10 +
    2.11 +///\file \ingroup misc
    2.12  ///Simple graph drawer
    2.13  
    2.14  namespace lemon {
    2.15  
    2.16 -  ///\e
    2.17 +///Data structure representing RGB colors.
    2.18 +
    2.19 +///Data structure representing RGB colors.
    2.20 +///\ingroup misc
    2.21  class Color
    2.22  {
    2.23    double _r,_g,_b;
    2.24  public:
    2.25 -  ///\e
    2.26 +  ///Default constructor
    2.27    Color() {}
    2.28 -  ///\e
    2.29 +  ///Constructor
    2.30    Color(double r,double g,double b) :_r(r),_g(g),_b(b) {};
    2.31 -  ///\e
    2.32 +  ///Returns the red component
    2.33    double getR() {return _r;}
    2.34 -  ///\e
    2.35 +  ///Returns the green component
    2.36    double getG() {return _g;}
    2.37 -  ///\e
    2.38 +  ///Returns the blue component
    2.39    double getB() {return _b;}
    2.40 -  ///\e
    2.41 +  ///Set the color components
    2.42    void set(double r,double g,double b) { _r=r;_g=g;_b=b; };
    2.43  };
    2.44    
    2.45 -  ///\e
    2.46 +///Default traits class of \ref GraphToEps
    2.47 +
    2.48 +///Default traits class of \ref GraphToEps
    2.49 +///
    2.50 +///\c G is the type of the underlying graph.
    2.51  template<class G>
    2.52  struct DefaultGraphToEpsTraits
    2.53  {
    2.54 @@ -40,21 +49,42 @@
    2.55    
    2.56  
    2.57    const Graph &g;
    2.58 -  ConstMap<typename Graph::Node,xy<double> > coords;
    2.59 -  ConstMap<typename Graph::Node,double > nodeSizes;
    2.60 +  ConstMap<typename Graph::Node,xy<double> > _coords;
    2.61 +  ConstMap<typename Graph::Node,double > _nodeSizes;
    2.62  
    2.63 -  ConstMap<typename Graph::Node,Color > nodeColors;
    2.64 -  ConstMap<typename Graph::Edge,Color > edgeColors;
    2.65 -  double nodeSizeScalar;
    2.66 -  double xBorder, yBorder;
    2.67 -    
    2.68 -  DefaultGraphToEpsTraits(G &_g) :
    2.69 -    g(_g), coords(xy<double>(1,1)), nodeSizes(1.0),
    2.70 -    nodeColors(Color(1,1,1)), edgeColors(Color(0,0,0)),
    2.71 -    nodeSizeScalar(1.0), xBorder(10), yBorder(10) {}
    2.72 +  ConstMap<typename Graph::Node,Color > _nodeColors;
    2.73 +  ConstMap<typename Graph::Edge,Color > _edgeColors;
    2.74 +
    2.75 +  ConstMap<typename Graph::Edge,double > _edgeWidths;
    2.76 +  
    2.77 +  double _edgeWidthScale;
    2.78 +  
    2.79 +  double _nodeScale;
    2.80 +  double _xBorder, _yBorder;
    2.81 +  double _scale;
    2.82 +  double _nodeBorderQuotient;
    2.83 +  
    2.84 +  bool _drawArrows;
    2.85 +  double _arrowLength, _arrowWidth;
    2.86 +  
    2.87 +  ///Constructor
    2.88 +
    2.89 +  ///Constructor
    2.90 +  ///\param _g is a reference to the underlying graph
    2.91 +  DefaultGraphToEpsTraits(const G &_g) :
    2.92 +    g(_g), _coords(xy<double>(1,1)), _nodeSizes(1.0),
    2.93 +    _nodeColors(Color(1,1,1)), _edgeColors(Color(0,0,0)),
    2.94 +    _edgeWidths(1), _edgeWidthScale(0.3),
    2.95 +    _nodeScale(1.0), _xBorder(10), _yBorder(10), _scale(1.0),
    2.96 +    _nodeBorderQuotient(.1),
    2.97 +    _drawArrows(false), _arrowLength(1), _arrowWidth(0.3) {}
    2.98  };
    2.99  
   2.100 -  ///\e
   2.101 +///Helper class to implement the named parameters of \ref graphToEps()
   2.102 +
   2.103 +///Helper class to implement the named parameters of \ref graphToEps()
   2.104 +///\todo Is 'helper class' a good name for this?
   2.105 +///
   2.106  template<class T> class GraphToEps : public T 
   2.107  {
   2.108    typedef typename T::Graph Graph;
   2.109 @@ -70,49 +100,115 @@
   2.110  public:
   2.111    GraphToEps(const T &t) : T(t), dontPrint(false) {};
   2.112    
   2.113 -  template<class X> struct SetCoordsTraits : public T {
   2.114 -    const X &coords;
   2.115 -    SetCoordsTraits(const T &t,const X &x) : T(t), coords(x) {}
   2.116 +  template<class X> struct CoordsTraits : public T {
   2.117 +    const X &_coords;
   2.118 +    CoordsTraits(const T &t,const X &x) : T(t), _coords(x) {}
   2.119    };
   2.120 -  ///\e
   2.121 -  template<class X> GraphToEps<SetCoordsTraits<X> > setCoords(const X &x) {
   2.122 +  ///Sets the map of the node coordinates
   2.123 +
   2.124 +  ///Sets the map of the node coordinates.
   2.125 +  ///\param x must be a node map with xy<double> or xy<int> values. 
   2.126 +  template<class X> GraphToEps<CoordsTraits<X> > coords(const X &x) {
   2.127      dontPrint=true;
   2.128 -    return GraphToEps<SetCoordsTraits<X> >(SetCoordsTraits<X>(*this,x));
   2.129 +    return GraphToEps<CoordsTraits<X> >(CoordsTraits<X>(*this,x));
   2.130    }
   2.131 -  template<class X> struct SetNodeSizesTraits : public T {
   2.132 -    const X &nodeSizes;
   2.133 -    SetNodeSizesTraits(const T &t,const X &x) : T(t), nodeSizes(x) {}
   2.134 +  template<class X> struct NodeSizesTraits : public T {
   2.135 +    const X &_nodeSizes;
   2.136 +    NodeSizesTraits(const T &t,const X &x) : T(t), _nodeSizes(x) {}
   2.137    };
   2.138 -  ///\e
   2.139 -  template<class X> GraphToEps<SetNodeSizesTraits<X> > setNodeSizes(const X &x)
   2.140 +  ///Sets the map of the node sizes
   2.141 +
   2.142 +  ///Sets the map of the node sizes
   2.143 +  ///\param x must be a node map with \c double (or convertible) values. 
   2.144 +  template<class X> GraphToEps<NodeSizesTraits<X> > nodeSizes(const X &x)
   2.145    {
   2.146      dontPrint=true;
   2.147 -    return GraphToEps<SetNodeSizesTraits<X> >(SetNodeSizesTraits<X>(*this,x));
   2.148 +    return GraphToEps<NodeSizesTraits<X> >(NodeSizesTraits<X>(*this,x));
   2.149    }
   2.150 -  template<class X> struct SetNodeColorsTraits : public T {
   2.151 -    const X &nodeColors;
   2.152 -    SetNodeColorsTraits(const T &t,const X &x) : T(t), nodeColors(x) {}
   2.153 +   template<class X> struct EdgeWidthsTraits : public T {
   2.154 +    const X &_edgeWidths;
   2.155 +    EdgeWidthsTraits(const T &t,const X &x) : T(t), _edgeWidths(x) {}
   2.156    };
   2.157 -  ///\e
   2.158 -  template<class X> GraphToEps<SetNodeColorsTraits<X> >
   2.159 -  setNodeColors(const X &x)
   2.160 +  ///Sets the map of the edge widths
   2.161 +
   2.162 +  ///Sets the map of the edge widths
   2.163 +  ///\param x must be a edge map with \c double (or convertible) values. 
   2.164 +  template<class X> GraphToEps<EdgeWidthsTraits<X> > edgeWidths(const X &x)
   2.165    {
   2.166      dontPrint=true;
   2.167 -    return GraphToEps<SetNodeColorsTraits<X> >(SetNodeColorsTraits<X>(*this,x));
   2.168 +    return GraphToEps<EdgeWidthsTraits<X> >(EdgeWidthsTraits<X>(*this,x));
   2.169    }
   2.170 -  template<class X> struct SetEdgeColorsTraits : public T {
   2.171 -    const X &edgeColors;
   2.172 -    SetEdgeColorsTraits(const T &t,const X &x) : T(t), edgeColors(x) {}
   2.173 +
   2.174 +  template<class X> struct NodeColorsTraits : public T {
   2.175 +    const X &_nodeColors;
   2.176 +    NodeColorsTraits(const T &t,const X &x) : T(t), _nodeColors(x) {}
   2.177    };
   2.178 -  ///\e
   2.179 -  template<class X> GraphToEps<SetEdgeColorsTraits<X> >
   2.180 -  setEdgeColors(const X &x)
   2.181 +  ///Sets the map of the node colors
   2.182 +
   2.183 +  ///Sets the map of the node colors
   2.184 +  ///\param x must be a node map with \ref Color values. 
   2.185 +  template<class X> GraphToEps<NodeColorsTraits<X> >
   2.186 +  nodeColors(const X &x)
   2.187    {
   2.188      dontPrint=true;
   2.189 -    return GraphToEps<SetEdgeColorsTraits<X> >(SetEdgeColorsTraits<X>(*this,x));
   2.190 +    return GraphToEps<NodeColorsTraits<X> >(NodeColorsTraits<X>(*this,x));
   2.191    }
   2.192 -  ///\e
   2.193 -  GraphToEps<T> &scaleNodeSize(double d) {nodeSizeScalar=d;return *this;}
   2.194 +  template<class X> struct EdgeColorsTraits : public T {
   2.195 +    const X &_edgeColors;
   2.196 +    EdgeColorsTraits(const T &t,const X &x) : T(t), _edgeColors(x) {}
   2.197 +  };
   2.198 +  ///Sets the map of the edge colors
   2.199 +
   2.200 +  ///Sets the map of the edge colors
   2.201 +  ///\param x must be a edge map with \ref Color values. 
   2.202 +  template<class X> GraphToEps<EdgeColorsTraits<X> >
   2.203 +  edgeColors(const X &x)
   2.204 +  {
   2.205 +    dontPrint=true;
   2.206 +    return GraphToEps<EdgeColorsTraits<X> >(EdgeColorsTraits<X>(*this,x));
   2.207 +  }
   2.208 +  ///Sets a global scale factor for node sizes
   2.209 +
   2.210 +  ///Sets a global scale factor for node sizes
   2.211 +  ///
   2.212 +  GraphToEps<T> &nodeScale(double d) {_nodeScale=d;return *this;}
   2.213 +  ///Sets a global scale factor for edge widths
   2.214 +
   2.215 +  ///Sets a global scale factor for edge widths
   2.216 +  ///
   2.217 +  GraphToEps<T> &edgeWidthScale(double d) {_edgeWidthScale=d;return *this;}
   2.218 +  ///Sets a global scale factor for the whole picture
   2.219 +
   2.220 +  ///Sets a global scale factor for the whole picture
   2.221 +  ///
   2.222 +  GraphToEps<T> &scale(double d) {_scale=d;return *this;}
   2.223 +  ///Sets the width of the border around the picture
   2.224 +
   2.225 +  ///Sets the width of the border around the picture
   2.226 +  ///
   2.227 +  GraphToEps<T> &border(double b) {_xBorder=_yBorder=b;return *this;}
   2.228 +  ///Sets the width of the border around the picture
   2.229 +
   2.230 +  ///Sets the width of the border around the picture
   2.231 +  ///
   2.232 +  GraphToEps<T> &border(double x, double y) {
   2.233 +    _xBorder=x;_yBorder=y;return *this;
   2.234 +  }
   2.235 +  ///Sets whether to draw arrows
   2.236 +
   2.237 +  ///Sets whether to draw arrows
   2.238 +  ///
   2.239 +  GraphToEps<T> &drawArrows(bool b=true) {_drawArrows=b;return *this;}
   2.240 +  ///Sets the length of the arrowheads
   2.241 +
   2.242 +  ///Sets the length of the arrowheads
   2.243 +  ///
   2.244 +  GraphToEps<T> &arrowLength(double d) {_arrowLength*=d;return *this;}
   2.245 +  ///Sets the width of the arrowheads
   2.246 +
   2.247 +  ///Sets the width of the arrowheads
   2.248 +  ///
   2.249 +  GraphToEps<T> &arrowWidth(double d) {_arrowWidth*=d;return *this;}
   2.250    
   2.251    ~GraphToEps() 
   2.252    {
   2.253 @@ -121,44 +217,98 @@
   2.254      cout << "%!PS-Adobe-2.0 EPSF-2.0\n";
   2.255      //\todo: Chech whether the graph is empty.
   2.256      BoundingBox<double> bb;
   2.257 -    for(NodeIt n(g);
   2.258 -	n!=INVALID;
   2.259 -	++n) {
   2.260 -      xy<double> p(nodeSizes[n]*nodeSizeScalar,nodeSizes[n]*nodeSizeScalar);
   2.261 -      bb+=coords[n]+p;
   2.262 -      bb+=coords[n]-p;
   2.263 +    for(NodeIt n(g);n!=INVALID;++n) {
   2.264 +      double ns=_nodeSizes[n]*_nodeScale;
   2.265 +      xy<double> p(ns,ns);
   2.266 +      bb+=p+_coords[n];
   2.267 +      bb+=-p+_coords[n];
   2.268        }
   2.269      cout << "%%BoundingBox: "
   2.270 -	 << bb.left()-xBorder << ' ' << bb.bottom()-yBorder << ' '
   2.271 -	 << bb.right()+xBorder << ' ' << bb.top()+yBorder << '\n';
   2.272 -    //x1 y1 x2 y2 cr cg cb
   2.273 -    cout << "/l { setrgbcolor newpath moveto lineto stroke } bind def\n";
   2.274 +	 << bb.left()*  _scale-_xBorder << ' '
   2.275 +	 << bb.bottom()*_scale-_yBorder << ' '
   2.276 +	 << bb.right()* _scale+_xBorder << ' '
   2.277 +	 << bb.top()*   _scale+_yBorder << '\n';
   2.278 +    //x1 y1 x2 y2 cr cg cb w
   2.279 +    cout << "/l { setlinewidth setrgbcolor newpath moveto lineto stroke } bind def\n";
   2.280      cout << "/c { newpath dup 3 index add 2 index moveto 0 360 arc } bind def\n";
   2.281      // x y r cr cg cb
   2.282      cout << "/n { setrgbcolor 2 index 2 index 2 index c fill\n"
   2.283 -	 << "     0 0 0 setrgbcolor dup 10 div setlinewidth c stroke\n"
   2.284 +	 << "     0 0 0 setrgbcolor dup "
   2.285 +	 << _nodeBorderQuotient << " mul setlinewidth "
   2.286 +	 << 1+_nodeBorderQuotient/2 << " div c stroke\n"
   2.287  	 << "   } bind def\n";
   2.288 -    
   2.289 +    cout << "/arrl " << _arrowLength << " def\n";
   2.290 +    cout << "/arrw " << _arrowWidth << " def\n";
   2.291 +    // l dx_norm dy_norm
   2.292 +    cout << "/lrl { 2 index mul exch 2 index mul exch rlineto pop} bind def\n";
   2.293 +    //len w dx_norm dy_norm x1 y1 cr cg cb
   2.294 +    cout << "/arr { setrgbcolor /y1 exch def /x1 exch def /dy exch def /dx exch def\n"
   2.295 +	 << "       /w exch def /len exch def\n"
   2.296 +      //	 << "       0.1 setlinewidth x1 y1 moveto dx len mul dy len mul rlineto stroke"
   2.297 +	 << "       newpath x1 dy w 2 div mul add y1 dx w 2 div mul sub moveto\n"
   2.298 +	 << "       len w sub arrl sub dx dy lrl\n"
   2.299 +	 << "       arrw dy dx neg lrl\n"
   2.300 +	 << "       dx arrl w add mul dy w 2 div arrw add mul sub\n"
   2.301 +	 << "       dy arrl w add mul dx w 2 div arrw add mul add rlineto\n"
   2.302 +	 << "       dx arrl w add mul neg dy w 2 div arrw add mul sub\n"
   2.303 +	 << "       dy arrl w add mul neg dx w 2 div arrw add mul add rlineto\n"
   2.304 +	 << "       arrw dy dx neg lrl\n"
   2.305 +	 << "       len w sub arrl sub neg dx dy lrl\n"
   2.306 +	 << "       closepath fill } bind def\n";
   2.307 +    cout << "\ngsave\n";
   2.308 +    if(_scale!=1.0) cout << _scale << " dup scale\n";
   2.309      cout << "%Edges:\ngsave\n";
   2.310      for(NodeIt n(g);n!=INVALID;++n)
   2.311        for(OutEdgeIt e(g,n);e!=INVALID;++e)
   2.312 -	cout << coords[g.source(e)].x << ' ' << coords[g.source(e)].y << ' '
   2.313 -	     << coords[g.target(e)].x << ' ' << coords[g.target(e)].y << ' '
   2.314 -	     << edgeColors[e].getR() << ' '
   2.315 -	     << edgeColors[e].getG() << ' '
   2.316 -	     << edgeColors[e].getB() << " l\n";
   2.317 +	if(_drawArrows) {
   2.318 +	  xy<double> d(_coords[g.target(e)]-_coords[g.source(e)]);
   2.319 +	  double l=sqrt(d.normSquare());
   2.320 +	  d/=l;
   2.321 +	  xy<double> x1(d*_nodeScale*_nodeSizes[g.source(e)]+
   2.322 +			_coords[g.source(e)]);
   2.323 +	  cout << l-(_nodeSizes[g.source(e)]+
   2.324 +		     _nodeSizes[g.target(e)])*_nodeScale << ' '
   2.325 +	       << _edgeWidths[e]*_edgeWidthScale << ' '
   2.326 +	       << d.x << ' ' << d.y << ' '
   2.327 +	       << x1.x << ' ' << x1.y << ' '
   2.328 +	       << _edgeColors[e].getR() << ' '
   2.329 +	       << _edgeColors[e].getG() << ' '
   2.330 +	       << _edgeColors[e].getB() << " arr\n";
   2.331 +	}
   2.332 +    	else cout << _coords[g.source(e)].x << ' '
   2.333 +		  << _coords[g.source(e)].y << ' '
   2.334 +		  << _coords[g.target(e)].x << ' '
   2.335 +		  << _coords[g.target(e)].y << ' '
   2.336 +		  << _edgeColors[e].getR() << ' '
   2.337 +		  << _edgeColors[e].getG() << ' '
   2.338 +		  << _edgeColors[e].getB() << ' '
   2.339 +		  << _edgeWidths[e]*_edgeWidthScale << " l\n";
   2.340      cout << "grestore\n%Nodes:\ngsave\n";
   2.341      for(NodeIt n(g);n!=INVALID;++n)
   2.342 -      cout << coords[n].x << ' ' << coords[n].y << ' '
   2.343 -	   << nodeSizes[n]*nodeSizeScalar << ' '
   2.344 -	   << nodeColors[n].getR() << ' '
   2.345 -	   << nodeColors[n].getG() << ' '
   2.346 -	   << nodeColors[n].getB() << " n\n"; 
   2.347 -    cout << "grestore\n";
   2.348 +      cout << _coords[n].x << ' ' << _coords[n].y << ' '
   2.349 +	   << _nodeSizes[n]*_nodeScale << ' '
   2.350 +	   << _nodeColors[n].getR() << ' '
   2.351 +	   << _nodeColors[n].getG() << ' '
   2.352 +	   << _nodeColors[n].getB() << " n\n"; 
   2.353 +    cout << "grestore\ngrestore\n";
   2.354    } 
   2.355  };
   2.356  
   2.357  
   2.358 +///Generates an EPS file from a graph
   2.359 +
   2.360 +///\ingroup misc
   2.361 +///Generates an EPS file from a graph.
   2.362 +///
   2.363 +///This function has a lot of \ref named-templ-param "named parameters",
   2.364 +///they are declared as the members of class \ref GraphToEps. The following
   2.365 +///example shows how to use these parameters.
   2.366 +///\code
   2.367 +/// graphToEps(g).scale(10).coords(coords)
   2.368 +///              .nodeScale(2).nodeSizes(sizes)
   2.369 +///              .edgeWidthScale(.4);
   2.370 +///\endcode
   2.371 +///\sa GraphToEps
   2.372  template<class G>
   2.373  GraphToEps<DefaultGraphToEpsTraits<G> > graphToEps(G &g)
   2.374  {
   2.375 @@ -194,7 +344,7 @@
   2.376    typedef ListGraph::Node Node;
   2.377    typedef ListGraph::NodeIt NodeIt;
   2.378    typedef ListGraph::Edge Edge;
   2.379 -  typedef xy<double> Xy;
   2.380 +  typedef xy<int> Xy;
   2.381    
   2.382    Node n1=g.addNode();
   2.383    Node n2=g.addNode();
   2.384 @@ -206,6 +356,7 @@
   2.385    ListGraph::NodeMap<double> sizes(g);
   2.386    ListGraph::NodeMap<int> colors(g);
   2.387    ListGraph::EdgeMap<int> ecolors(g);
   2.388 +  ListGraph::EdgeMap<int> widths(g);
   2.389    
   2.390    coords[n1]=Xy(50,50);  sizes[n1]=1; colors[n1]=1;
   2.391    coords[n2]=Xy(50,70);  sizes[n2]=2; colors[n2]=2;
   2.392 @@ -215,16 +366,19 @@
   2.393    
   2.394    Edge e;
   2.395  
   2.396 -  e=g.addEdge(n1,n2); ecolors[e]=0;
   2.397 -  e=g.addEdge(n2,n3); ecolors[e]=0;
   2.398 -  e=g.addEdge(n3,n5); ecolors[e]=0;
   2.399 -  e=g.addEdge(n5,n4); ecolors[e]=0;
   2.400 -  e=g.addEdge(n4,n1); ecolors[e]=0;
   2.401 -  e=g.addEdge(n2,n4); ecolors[e]=1;
   2.402 -  e=g.addEdge(n3,n4); ecolors[e]=2;
   2.403 +  e=g.addEdge(n1,n2); ecolors[e]=0; widths[e]=1;
   2.404 +  e=g.addEdge(n2,n3); ecolors[e]=0; widths[e]=1;
   2.405 +  e=g.addEdge(n3,n5); ecolors[e]=0; widths[e]=3;
   2.406 +  e=g.addEdge(n5,n4); ecolors[e]=0; widths[e]=1;
   2.407 +  e=g.addEdge(n4,n1); ecolors[e]=0; widths[e]=1;
   2.408 +  e=g.addEdge(n2,n4); ecolors[e]=1; widths[e]=2;
   2.409 +  e=g.addEdge(n3,n4); ecolors[e]=2; widths[e]=1;
   2.410    
   2.411 -  graphToEps(g).setCoords(coords).
   2.412 -    scaleNodeSize(2).setNodeSizes(sizes).
   2.413 -    setNodeColors(composeMap(colorSet,colors)).
   2.414 -    setEdgeColors(composeMap(colorSet,ecolors));
   2.415 +  graphToEps(g).scale(10).coords(coords).
   2.416 +    nodeScale(2).nodeSizes(sizes).
   2.417 +    nodeColors(composeMap(colorSet,colors)).
   2.418 +    edgeColors(composeMap(colorSet,ecolors)).
   2.419 +    edgeWidthScale(.4).edgeWidths(widths).
   2.420 +    drawArrows().arrowWidth(1).arrowLength(1)
   2.421 +    ;
   2.422  }