/*******************************************************************************
+
+  LEDA 3.5
+
+  _ps_file.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/

#include <LEDA/ps_file.h>

#include <time.h>
/*
#include <sys/param.h>
*/

#if !defined(EXIT_FAILURE)
#define EXIT_FAILURE 1
#endif



// Static members of class ps_file:
const string ps_file::VERSION        ="PS-Adobe-2.0";
const short  ps_file::X_SHIFT        =60;
const short  ps_file::Y_SHIFT        =100;
#ifndef PI
const double ps_file::PI             =3.14159265358979323846;
#endif
const double ps_file::SIZE           =100.0;
const string ps_file::DEFAULT_FONT   ="Helvetica";
const double ps_file::DEFAULT_FACTOR =5.0/3.57;

// Auxiliary functions

void ps_file::scale_xcoord(double& x) {
  x=(x-X0)*scaling; }

void ps_file::scale_xcoord(double& x1,double& x2) {
  scale_xcoord(x1);scale_xcoord(x2); }

void ps_file::scale_ycoord(double& y) {
  y=(y-Y0)*scaling; }

void ps_file::scale_ycoord(double& y1,double& y2) {
  scale_ycoord(y1);scale_ycoord(y2); }

void ps_file::scale_radius(double& r) {
  r*=scaling; }

list_item ps_file::grayvalue_item(color c) {
  list_item gvi;
  forall_items(gvi,grayvalue_list)
    if (grayvalue_list.inf(gvi).c==c) return gvi;
  return NULL; }

inline int ps_file::round(double d) {
  return int(d+0.5); }

inline bool ps_file::exor(bool a, bool b) {
  if (a && !b || b && !a) return true;
  else                    return false; }

inline double ps_file::dist(double x1, double y1, double x2, double y2) {
  return hypot(fabs(x1-x2),fabs(y1-y2)); }

inline double ps_file::dist(point p1, point p2) {
  return dist(p1.xcoord(),p1.ycoord(),p2.xcoord(),p2.ycoord()); }

// Constructors, destructors, initializers
ps_file::ps_file(double w, double h, string name):
  filename(name), file(name), width(w), height(h) {
  if (!file) {
    cerr << "file name error: can't write to " << name << endl;
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  initialize(); }

ps_file::ps_file(string name):
  filename(name), file(name), width(WIDTH), height(HEIGHT) {
  initialize(); }

void ps_file::initialize() {
  bbx=width/PIXEL;bby=height/PIXEL;
  must_scale=true;
  page=1;
  set_defaults();

  time_t t;
  time(&t);

  file << "%!" << VERSION << endl;
  file << "%%Creator: LEDA-PS-Converter\n";
  file << "%%Title: " << filename << endl;
  file << "%%CreationDate: " << ctime(&t);
  file << "%%Pages: (atend)\n";
  file << "%%DocumentFonts: " << textfont << endl;
  file << "%%BoundingBox: " << X_SHIFT            << " " << Y_SHIFT << " "
                            << X_SHIFT+round(bbx) << " " << Y_SHIFT+round(bby) << endl;
  file << "%%EndComments\n\n";

  file << "% Abbreviations\n";
  file << "/BB {\n";
  file << "  newpath\n";
  file << "  " << X_SHIFT     << " " << Y_SHIFT     << " moveto\n";
  file << "  " << X_SHIFT+bbx << " " << Y_SHIFT     << " lineto\n";
  file << "  " << X_SHIFT+bbx << " " << Y_SHIFT+bby << " lineto\n";
  file << "  " << X_SHIFT     << " " << Y_SHIFT+bby << " lineto\n";
  file << "  closepath } def\n";
  file << "/dp {\n";
  file << "  newpath\n";
  file << "  moveto\n";
  file << "  closepath\n";
  file << "  1 setlinecap\n";
  file << "  stroke\n";
  file << "  0 setlinecap } def\n";
  file << "/ds {\n";
  file << "  newpath\n";
  file << "  moveto\n";
  file << "  lineto\n";
  file << "  stroke } def\n";
  file << "/dc {\n";
  file << "  newpath\n";
  file << "  0 360 arc\n";
  file << "  stroke } def\n";
  file << "/dd {\n";
  file << "  newpath\n";
  file << "  0 360 arc\n";
  file << "  fill } def\n";
  file << "/cg   { currentgray } def\n";
  file << "/cgsg { currentgray setgray } def\n";
  file << "/chp  { charpath } def\n";
  file << "/cp   { closepath } def\n";
  file << "/cpfi { closepath fill } def\n";
  file << "/cpst { closepath stroke } def\n";
  file << "/ff   { findfont } def\n";
  file << "/gr   { grestore } def\n";
  file << "/gs   { gsave } def\n";
  file << "/lt   { lineto } def\n";
  file << "/mt   { moveto } def\n";
  file << "/np   { newpath } def\n";
  file << "/sc   { scale } def\n";
  file << "/scf  { scalefont } def\n";
  file << "/sd   { setdash } def\n";
  file << "/sf   { setfont } def\n";
  file << "/sg   { setgray } def\n";
  file << "/slc  { setlinecap } def\n";
  file << "/slw  { setlinewidth } def\n";
  file << "/sp   { showpage } def\n";
  file << "/spds { stringwidth pop 2 div sub } def\n";
  file << "/srgb { setrgbcolor } def\n";
  file << "/st   { stroke } def\n";
  file << "/sw   { stringwidth } def\n";
  file << "/tl   { translate } def\n\n";

  new_font();
  file << "BB clip\n\n";
  file << "%%EndProlog\n\n";
  file << "%%Page: 1 1\n";
  file << "gs\n";
  if (X_SHIFT || Y_SHIFT) file << X_SHIFT << " " << Y_SHIFT << " tl\n";
  init(0.0,SIZE,0.0); }


void ps_file::set_defaults() {
  draw_bb=true;
  outputmode=colored_mode;
  fg_col=black;
  linewidth=1;
  unitlinewidth=PIXEL;
  linestyle=solid;
  nodewidth=20;
  crosswidth=0.2;
  arrowlength=0.5;
  arrowangle=PI/10;
  textfont=DEFAULT_FONT;
  fontsize=0.4;
  textmode=transparent; }

// When a new page is started, don't take defaults, but the old settings
// on the last page:
void ps_file::set_old_values() {
  if (fg_col!=black || outputmode==gray_mode) change_color(fg_col);
  if (unitlinewidth/PIXEL*linewidth!=1.0) file << unitlinewidth/PIXEL*linewidth << " slw\n";
  if (linestyle!=solid) {
    int on,off;
    int lw=round(unitlinewidth/PIXEL*linewidth);
    switch(linestyle) {
      case solid : break;
      case dashed: on =Max(lw*2,12);
		   off=Max(lw,6);
		   file << "[" << on << " " << off << "] 0";
		   break;
      case dotted: on =Max(lw,1);
		   off=Max(on,6);
		   file << "[" << on << " " << off << "] 0"; }
    file << " sd\n"; }
  /* Reinstall old textmode? */
  new_font(); }

void ps_file::init(double x0, double x1, double y0) {
  X0=x0;X1=x1;Y0=y0;
  Y1=Y0+height*(X1-X0)/width;
  scaling=bbx/(X1-X0);  /*  =bby/(Y1-Y0) */ }

void ps_file::newpage() {
  finish_old_page();
  page++;
  file << "%%Page: " << page << " " << page << "\n";
  file << "gs\n";
  if (X_SHIFT || Y_SHIFT) file << X_SHIFT << " " << Y_SHIFT << " tl\n";
  set_old_values(); }

void ps_file::finish_old_page() {
  if (draw_bb) file << "gr gs BB st\n";
  file << "sp gr\n\n"; }

void ps_file::newfile(double w, double h, string name) {
  if (page!=0) {       // User didn't close the old file
    cout << "closing current file: ";
    close(); }
  filename=name;
  file.open(name);
  width=w, height=h;
  if (!file) {
    cerr << "file name error: can't write to " << name << endl;
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  initialize(); }

void ps_file::newfile(string name) {
  if (page!=0) {       // User didn't close the old file
    cout << "closing current file: ";
    close(); }
  filename=name;
  file.open(name);
  width=WIDTH, height=HEIGHT;
  if (!file) {
    cerr << "file name error: can't write to " << name << endl;
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  initialize(); }

void ps_file::close() {
  finish_old_page();
  file << "%%Trailer\n";
  file << "%%Pages: " << page << endl;
  file.close();
  page=0;   // To recognize whether close has been called or not
  cout << "file " << filename << " has been created in " << flush;
  system("pwd"); }

ps_file::~ps_file() {
  if (page!=0) {       // User didn't close the last file
    cout << "closing current file: ";
    close(); }}


// Setting parameters
bool ps_file::set_draw_bb(bool flag) {
  bool temp=draw_bb;
  draw_bb=flag;
  return temp; }


output_mode ps_file::set_output_mode(output_mode new_mode) {
  output_mode temp=outputmode;
  if (exor(new_mode,outputmode)) {
    outputmode=new_mode;
    change_color(fg_col); }
  return temp; }

double ps_file::set_gray_value(color c,double d) {
  if ((int)c<0) {
    cerr << "file " << filename << ", page " << page
         << ": Error in ps_file::set_gray_value(color,double):\n"
         << "color index must be non-negative\n";
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  if (d<0 || d>1) {
    cerr << "file " << filename << ", page " << page
         << ": Error in ps_file::set_gray_value(color,double):\n"
         << "grayvalue must be in 0..1\n";
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  double temp;
  list_item gvi=grayvalue_item(c);
  if (gvi) {
    temp=grayvalue_list.inf(gvi).grayvalue;
    grayvalue_list[gvi].grayvalue=d; }
  else {
    temp=-1;
    grayvalue_list.append(ps_file_pair(c,d)); }
  if (c==fg_col && outputmode==gray_mode) change_color(fg_col);
  return temp; }

color ps_file::set_color(color c) {
  color temp=fg_col;
  if (c!=fg_col) {
    fg_col=c;
    change_color(c); }
  return temp; }

// This function is used internally to temporarily change the
// foreground color if a color is specified in drawing operations:
void ps_file::change_color(color c) {
  if (outputmode==colored_mode) change_rgb(c);
  else {
    list_item gvi=grayvalue_item(c);
    if (gvi) file << grayvalue_list.inf(gvi).grayvalue << " sg\n";
    else {
      change_rgb(c);
      file << "cgsg\n"; }}}

// PostScript uses a [0,1] range for rgb-values instead of [0,255]:
void ps_file::change_rgb(color c) {
  int r,g,b;
  c.get_rgb(r,g,b);
  file << double(r)/255.0 << " "
       << double(g)/255.0 << " "
       << double(b)/255.0 << " srgb\n"; }

double ps_file::set_line_width(double pix) {
  double temp=linewidth;
  if (pix!=linewidth) {
    linewidth=pix;
    file << unitlinewidth/PIXEL*linewidth << " slw\n";
    change_line_style(linestyle); }
  return temp; }

// cm specifications are converted into pixels:
double ps_file::set_unit_line_width(double cm) {
  double temp=unitlinewidth;
  if (cm!=unitlinewidth) {
    unitlinewidth=cm;
    file << unitlinewidth/PIXEL*linewidth << " slw\n";
    change_line_style(linestyle); }
  return temp; }

line_style ps_file::set_line_style(line_style s) {
  line_style temp=linestyle;
  if (s!=linestyle) change_line_style(s);
  return temp; }

void ps_file::change_line_style(line_style s) {
  linestyle=s;
  int on,off;
  int lw=round(unitlinewidth/PIXEL*linewidth);
  switch(s) {
    case solid : file << "[ ]   0";
		 break;
    case dashed: on =Max(lw*2,12);
		 off=Max(lw,6);
		 file << "[" << on << " " << off << "] 0";
		 break;
    case dotted: on =Max(lw,1);
		 off=Max(on,6);
		 file << "[" << on << " " << off << "] 0"; }
  file << " sd\n"; }

double ps_file::set_node_width(double pix) {
  double temp=nodewidth;
  nodewidth=pix;
  return temp; }

double ps_file::set_cross_width(double cm) {
  double temp=crosswidth;
  crosswidth=cm;
  return temp; }

double ps_file::set_arrow_length(double cm) {
  double temp=arrowlength;
  arrowlength=cm;
  return temp; }

double ps_file::set_arrow_angle(double angle) {
  double temp=arrowangle;
  arrowangle=angle;
  return temp; }

string ps_file::set_text_font(string s) {
  string temp=textfont;
  textfont=s;
  new_font();
  return temp; }

double ps_file::set_font_size(double fs) {
  double temp=fontsize;
  fontsize=fs;
  new_font();
  return temp; }

// Whenever a new font is set or the size of the current font is
// changed, the PostScript 'setfont' operation must be called. The
// size of a font is computed according to the size of the letter
// 'M'. This size is different for different PostScript fonts, even
// for the same fontsize. The 'fontfactor' tries to compensate these
// differences:
void ps_file::new_font() {
  double fontfactor;
  if (textfont(0,4)=="Times") fontfactor=5.0/3.3;
  else
    if (textfont(0,8)=="Helvetica") fontfactor=5.0/3.57;
    else
      if (textfont(0,6)=="Courier") fontfactor=5.0/2.91;
      else
	if (textfont(0,5)=="Symbol") fontfactor=5.0/3.3;
	else {
	  cerr << "file " << filename << ", page " << page
	       << ": Warning: " << textfont << ": unknown font; using "
	       << DEFAULT_FONT  << " instead\n";
	  file << "% Warning: " << textfont << ": unknown font; using "
	       << DEFAULT_FONT  << " instead\n";
	  textfont=DEFAULT_FONT;
	  fontfactor=DEFAULT_FACTOR; }
  file << "/" << textfont << " ff " << fontsize/PIXEL*fontfactor << " scf sf\n"; }

text_mode ps_file::set_text_mode(text_mode m) {
  text_mode temp=textmode;
  m=m; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::set_text_mode not implemented\n";
  file << "% Warning: ps_file::set_text_mode not implemented\n";
  return temp; }


// Reading parameters
bool        ps_file::get_draw_bb    () {return draw_bb; }
output_mode ps_file::get_output_mode() {return outputmode; }
double      ps_file::get_gray_value (color c) {
  if ((int)c<0) {
    cerr << "file " << filename << ", page " << page
         << ": Error in ps_file::get_gray_value(color):\n"
         << "color index must be non-negative\n";
    cerr << "exiting\n";
    exit(EXIT_FAILURE); }
  list_item gvi=grayvalue_item(c);
  if (gvi) return grayvalue_list.inf(gvi).grayvalue;
  else return -1; }
color       ps_file::get_color           () {return fg_col;        }
double      ps_file::get_line_width      () {return linewidth;     }
double      ps_file::get_unit_line_width () {return unitlinewidth; }
line_style  ps_file::get_line_style      () {return linestyle;     }
double      ps_file::get_node_width      () {return nodewidth;     }
double      ps_file::get_cross_width     () {return crosswidth;    }
double      ps_file::get_arrow_length    () {return arrowlength;   }
double      ps_file::get_arrow_angle     () {return arrowangle;    }
string      ps_file::get_text_font       () {return textfont;      }
double      ps_file::get_font_size       () {return fontsize;      }
text_mode   ps_file::get_text_mode       () {return textmode;      }

// Drawing operations
// Drawing points
void ps_file::draw_point(double x, double y, color c) {
  scale_xcoord(x);scale_ycoord(y);
  must_scale=false;
  double x0=x-crosswidth/PIXEL/2.0;double y0=x0+y-x;
  double x1=x+crosswidth/PIXEL/2.0;double y1=x1+y-x;
  draw_segment(x0,y0,x1,y1,c);
  y0=-x0+y+x;y1=-x1+y+x;
  draw_segment(x0,y0,x1,y1,c);
  must_scale=true; }
void ps_file::draw_point(point p, color c) {draw_point(p.xcoord(),p.ycoord(),c); }
void ps_file::draw_pixel(double x, double y, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << x << " " << y << " dp\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_pixel(point p, color c) {draw_pixel(p.xcoord(),p.ycoord(),c); }

// Drawing line segments
void ps_file::draw_segment(double x1, double y1, double x2, double y2, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x1,x2);scale_ycoord(y1,y2); }
  file << x1 << " " << y1 << " " << x2 << " " << y2 << " ds\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_segment(point p, point q, color c) {
  draw_segment(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),c); }
void ps_file::draw_segment(segment s, color c) {
  draw_segment(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),c); }

// Drawing lines
void ps_file::draw_line(double x1, double y1, double x2, double y2, color c) {
  if (x1==x2) draw_segment(x1,Y0,x2,Y1,c);
  else {
    double delta=(y2-y1)/(x2-x1);
    draw_segment(X0,delta*(X0-x1)+y1,X1,delta*(X1-x1)+y1,c); }}
void ps_file::draw_line(point p, point q, color c) {
  draw_line(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),c); }
void ps_file::draw_line(segment s, color c) {
  draw_line(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),c); }
void ps_file::draw_line(line l, color c) {
  if (l.is_horizontal()) draw_hline(l.y_proj(X0),c);
  else
    if (l.is_vertical()) draw_vline(l.x_proj(Y0),c);
    else draw_line(X0,l.y_proj(X0),X1,l.y_proj(X1),c); }
void ps_file::draw_hline(double y, color c) {draw_line(X0,y,X1,y,c); }
void ps_file::draw_vline(double x, color c) {draw_line(x,Y0,x,Y1,c); }
void ps_file::draw_arc(double x1, double y1, double x2, double y2, double r, color c) {
  x1=y1=x2=y2=r=c; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::draw_arc not implemented\n";
  file << "% Warning: ps_file::draw_arc not implemented\n"; }
void ps_file::draw_arc(point p, point q, double r, color c) {
  draw_arc(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),r,c); }
void ps_file::draw_arc(segment s, double r, color c) {
  draw_arc(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),r,c); }

// Drawing arrows
void ps_file::draw_arrow_head(point p, double dir, color c) {
  segment s1(p,PI+dir+arrowangle,arrowlength/PIXEL/scaling);
  segment s2(p,PI+dir-arrowangle,arrowlength/PIXEL/scaling);
  draw_segment(s1,c);
  draw_segment(s2,c); }
void ps_file::draw_arrow(double x1, double y1, double x2, double y2, color c) {
  draw_arrow(segment(x1,y1,x2,y2),c); }
void ps_file::draw_arrow(point p, point q, color c) {
  draw_arrow(segment(p,q),c); }
void ps_file::draw_arrow(segment s, color c) {
  draw_segment(s,c);
  draw_arrow_head(s.target(),s.direction(),c); }
void ps_file::draw_arc_arrow(double x1, double y1, double x2, double y2, double r, color c) {
  x1=y1=x2=y2=r=c; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::draw_arc_arrow not implemented\n";
  file << "% Warning: ps_file::draw_arc_arrow not implemented\n"; }
void ps_file::draw_arc_arrow(point p, point q, double r, color c) {
  draw_arc_arrow(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),r,c); }
void ps_file::draw_arc_arrow(segment s, double r, color c) {
  draw_arc_arrow(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),r,c); }

// Drawing circles
void ps_file::draw_circle(double x, double y, double r, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y);scale_radius(r); }
  file << x << " " << y << " " << r << " dc\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_circle(point p, double r, color c) {
  draw_circle(p.xcoord(),p.ycoord(),r,c); }
void ps_file::draw_circle(circle C, color c) {
  draw_circle(C.center().xcoord(),C.center().ycoord(),C.radius(),c); }
void ps_file::draw_ellipse(double x, double y, double r1, double r2, color c) {
  file << "gs\n";
  file << r1/r2 << " 1 sc\n";
  scale_xcoord(x);x*=r2/r1;scale_ycoord(y);scale_radius(r2);
  must_scale=false;
  draw_circle(x,y,r2,c);
  must_scale=true;
  file << "gr\n"; }
void ps_file::draw_ellipse(point p, double r1, double r2, color c) {
  draw_ellipse(p.xcoord(),p.ycoord(),r1,r2,c); }

// Drawing discs
void ps_file::draw_disc(double x, double y, double r, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y);scale_radius(r); }
  file << x << " " << y << " " << r << " dd\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_disc(point p, double r, color c) {
  draw_disc(p.xcoord(),p.ycoord(),r,c); }
void ps_file::draw_disc(circle C, color c) {
  draw_disc(C.center().xcoord(),C.center().ycoord(),C.radius(),c); }
void ps_file::draw_filled_ellipse(double x, double y, double r1, double r2, color c) {
  file << "gs\n";
  file << r1/r2 << " 1 sc\n";
  scale_xcoord(x);x*=r2/r1;scale_ycoord(y);scale_radius(r2);
  must_scale=false;
  draw_disc(x,y,r2,c);
  must_scale=true;
  file << "gr\n"; }
void ps_file::draw_filled_ellipse(point p, double r1, double r2, color c) {
  draw_filled_ellipse(p.xcoord(),p.ycoord(),r1,r2,c); }

// Drawing polygons
void ps_file::draw_polygon(const list<point>& lp, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  point p;
  double x=lp.head().xcoord(),y=lp.head().ycoord();
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << "np\n";
  file << x << " " << y << " mt\n";
  for(int i=1;i<lp.length();i++) {
    p=lp.contents(lp.get_item(i));
    x=p.xcoord();y=p.ycoord();
    if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
    file << x << " " << y << " lt\n"; }
  file << "cpst\n";
  if (c!=fg_col) change_color(fg_col); }

void ps_file::draw_polygon(polygon P, color c) {
  draw_polygon(P.vertices(),c); }

void ps_file::draw_filled_polygon(const list<point>& lp, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  point p;
  double x=lp.head().xcoord(),y=lp.head().ycoord();
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << "np\n";
  file << x << " " << y << " mt\n";
  for(int i=1;i<lp.length();i++) {
    p=lp.contents(lp.get_item(i));
    x=p.xcoord();y=p.ycoord();
    if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
    file << x << " " << y << " lt\n"; }
  file << "cpfi\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_filled_polygon(polygon P, color c) {
  draw_filled_polygon(P.vertices(),c); }
void ps_file::draw_rectangle(double x0, double y0, double x1, double y1, color c) {
  list<point> lp;
  lp.append(point(x0,y0));lp.append(point(x1,y0));
  lp.append(point(x1,y1));lp.append(point(x0,y1));
  draw_polygon(lp,c); }
void ps_file::draw_rectangle(point p, point q, color c) {
  draw_rectangle(p.xcoord(),p.ycoord(),q.xcoord(),q.ycoord(),c); }
void ps_file::draw_box(double x0, double y0, double x1, double y1, color c) {
  list<point> lp;
  lp.append(point(x0,y0));lp.append(point(x1,y0));
  lp.append(point(x1,y1));lp.append(point(x0,y1));
  draw_filled_polygon(lp,c); }
void ps_file::draw_box(point p, point q, color c) {
  draw_box(p.xcoord(),p.ycoord(),q.xcoord(),q.ycoord(),c); }
void ps_file::draw_triangle(point A, point B, point C, color c) {
  list<point> lp;
  lp.append(A);lp.append(B);lp.append(C);
  draw_polygon(lp,c); }
void ps_file::draw_filled_triangle(point A, point B, point C, color c) {
  list<point> lp;
  lp.append(A);lp.append(B);lp.append(C);
  draw_filled_polygon(lp,c); }

// Drawing functions
void ps_file::plot_xy(double x0, double x1, win_draw_func F, color c) {
  x0=x1=c;F=F; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::plot_xy not implemented\n";
  file << "% Warning: ps_file::plot_xy not implemented\n"; }
void ps_file::plot_yx(double y0, double y1, win_draw_func F, color c) {
  y0=y1=c;F=F; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::plot_yx not implemented\n";
  file << "% Warning: ps_file::plot_yx not implemented\n"; }

// Drawing text
void ps_file::draw_text(double x, double y, string s, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << x << " " << y-fontsize/PIXEL << " mt\n";
  file << "(" << s << ") show\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_text(point p, string s, color c) {
  draw_text(p.xcoord(),p.ycoord(),s,c); }
void ps_file::draw_ctext(double x, double y, string s, color c) {
  if (c==DEF_COLOR) c=fg_col;
  if (c!=fg_col) change_color(c);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << x << " (" << s << ") spds\n";
  file << y-fontsize/PIXEL/2.0 << " mt\n";
  file << "(" << s << ") show\n";
  if (c!=fg_col) change_color(fg_col); }
void ps_file::draw_ctext(point p, string s, color c) {
  draw_ctext(p.xcoord(),p.ycoord(),s,c); }

// Drawing nodes
void ps_file::draw_node(double x0, double y0, color c) {
  draw_circle(x0,y0,nodewidth/2.0/scaling,c); }
void ps_file::draw_node(point p, color c) {
  draw_node(p.xcoord(),p.ycoord(),c); }
void ps_file::draw_filled_node(double x0, double y0, color c) {
  draw_disc(x0,y0,nodewidth/2.0/scaling,c); }
void ps_file::draw_filled_node(point p, color c) {
  draw_filled_node(p.xcoord(),p.ycoord(),c); }
void ps_file::draw_text_node(double x, double y, string s, color c) {
  if (c==white) draw_node(x,y,black);else draw_filled_node(x,y,c);
  if (c==DEF_COLOR) c=fg_col;
  color c1;
  if (c==black) c1=white;else c1=black;
  if (c1!=fg_col) change_color(c1);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << "np\n";
  file << x << " (" << s << ") spds\n";
  file << y-fontsize/PIXEL/2.0 << " mt\n";
  file << "(" << s << ") false chp\n";
  file << "fill\n";
  if (c1!=fg_col) change_color(fg_col); }
void ps_file::draw_text_node(point p, string s, color c) {
  draw_text_node(p.xcoord(),p.ycoord(),s,c); }
void ps_file::draw_int_node(double x, double y, int i, color c) {
  draw_filled_node(x,y,c);
  if (c==DEF_COLOR) c=fg_col;
  color c1;
  if (c==black) c1=white;else c1=black;
  if (c1!=fg_col) change_color(c1);
  if (must_scale) {scale_xcoord(x);scale_ycoord(y); }
  file << "np\n";
  file << x << " (" << i << ") spds\n";
  file << y-fontsize/PIXEL/2.0 << " mt\n";
  file << "(" << i << ") false chp\n";
  file << "fill\n";
  if (c1!=fg_col) change_color(fg_col); }
void ps_file::draw_int_node(point p, int i, color c) {
  draw_int_node(p.xcoord(),p.ycoord(),i,c); }

// Drawing edges
void ps_file::draw_edge(double x1, double y1, double x2, double y2, color c) {
  scale_xcoord(x1,x2);scale_ycoord(y1,y2);
  must_scale=false;
  line l(segment(x1,y1,x2,y2));
  double d=dist(x1,y1,x2,y2),dx=fabs(x2-x1),r=nodewidth/2.0;
  if (d<nodewidth) draw_segment(x1,y1,x2,y2,c);
  else
    if (dx==0) draw_segment(x1,Min(y1,y2)+r,x1,Max(y1,y2)-r,c);
    else {
      double delta_x=r/d*dx;
      if (x1<x2) draw_segment(x1+delta_x,l.y_proj(x1+delta_x),x2-delta_x,l.y_proj(x2-delta_x),c);
      else       draw_segment(x1-delta_x,l.y_proj(x1-delta_x),x2+delta_x,l.y_proj(x2+delta_x),c); }
  must_scale=true; }
void ps_file::draw_edge(point p, point q, color c) {
  draw_edge(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),c); }
void ps_file::draw_edge(segment s, color c) {
  draw_edge(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),c); }
void ps_file::draw_edge_arrow(double x1, double y1, double x2, double y2, color c) {
  scale_xcoord(x1,x2);scale_ycoord(y1,y2);
  must_scale=false;
  double temp=scaling;scaling=1;
  line l(segment(x1,y1,x2,y2));
  double d=dist(x1,y1,x2,y2),dx=fabs(x2-x1),r=nodewidth/2.0;
  if (d<nodewidth) draw_arrow(x1,y1,x2,y2,c);
  else
    if (dx==0) draw_arrow(x1,Min(y1,y2)+r,x2,Max(y1,y2)-r,c);
    else {
      double delta_x=r/d*dx;
      if (x1<x2) draw_arrow(x1+delta_x,l.y_proj(x1+delta_x),x2-delta_x,l.y_proj(x2-delta_x),c);
      else	 draw_arrow(x1-delta_x,l.y_proj(x1-delta_x),x2+delta_x,l.y_proj(x2+delta_x),c); }
  must_scale=true;
  scaling=temp; }
void ps_file::draw_edge_arrow(point p, point q, color c) {
  draw_edge_arrow(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),c); }
void ps_file::draw_edge_arrow(segment s, color c) {
  draw_edge_arrow(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),c); }
void ps_file::draw_arc_edge(double x1, double y1, double x2, double y2, double r, color c) {
  x1=y1=x2=y2=r=c; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::draw_arc_edge not implemented\n";
  file << "% Warning: ps_file::draw_arc_edge not implemented\n"; }
void ps_file::draw_arc_edge(point p, point q, double r, color c) {
  draw_arc_edge(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),r,c); }
void ps_file::draw_arc_edge(segment s, double r, color c) {
  draw_arc_edge(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),r,c); }
void ps_file::draw_arc_edge_arrow(double x1, double y1, double x2, double y2, double r, color c) {
  x1=y1=x2=y2=r=c; // avoid "unused"-warning
  cerr << "file " << filename << ", page " << page
       << ": Warning: ps_file::draw_arc_edge_arrow not implemented\n";
  file << "% Warning: ps_file::draw_arc_edge_arrow not implemented\n"; }
void ps_file::draw_arc_edge_arrow(point p, point q, double r, color c) {
  draw_arc_edge_arrow(p.xcoord (),p.ycoord (),q.xcoord (),q.ycoord (),r,c); }
void ps_file::draw_arc_edge_arrow(segment s, double r, color c) {
  draw_arc_edge_arrow(s.xcoord1(),s.ycoord1(),s.xcoord2(),s.ycoord2(),r,c); }
