/*                                                                     --->  
<   Program "picture"          Version 11/29/1998                         >
<                                                                         >
<   Reads the files "dots & lineXX" to write postscript or LaTeX charts   >
<   (also includes gif support)                                           >
<                                                                         >
<   contact the author at                                                 >
<   nassau@nullhomotopie.de                                               >
<                                                                         >
<---                                                                     */

/* added dec 2002: GIF support probably doesn't work anymore */

/*
   Assuming that your C compiler is named "cc" -- which is the default on 
   most UNIX systems -- you should compile this file with the command line

    cc -o picture picture.c 

   If you get "parse errors" you should try gcc instead of cc. 

   If you get error messages about sqrt(...) or floor(...) that means
   that the maths library has to be included. Try it again with 
   "cc -o picture picture.c -lm" or "cc -o picture picture.c -lmath".
   If that doesn't work type "ls /usr/lib/libm*" for a list of the available
   libraries starting with "m". If this shows a file like "libmath-wizard.a"
   (for example) you should try "-lmath-wizard" instead of "-lm".

   After that, type "picture" for instructions. 
*/

/*
  just to remind myself: for (big) gifs use something like
  
      picture +biggif2000:4000 -dpp181 h2:3 h1:2 h0:1 +land 
              +dsz2 -nr1 -nu1 -crc +col1.185.16.0 +col2.201.73.80 
              +col3.185.178.178 +col0.0.0.0

  (by now, these colours here are the defaults)
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <math.h>   

void error(char *p) {
  printf("ERROR: %s\n",p);
  exit(1);
}

typedef enum { ps, xypic, mp, gif } pictype;
pictype base = ps;

#define NUMLT 50

int linetype[NUMLT];     
char extension[NUMLT][20];
unsigned numtypes = 0;

void newtype(p,n) char *p; unsigned n; {
  char *q = extension[numtypes];
  linetype[numtypes] = n;
  while ((*q++ = *p++)) ;
  if (++numtypes == NUMLT) {
	 printf("NUMLT too small; modify, recompile, and try again");
	 exit(1);
  }
}

/*
   This is what these int's mean:

      0 --- none
      1 --- plain
      2 --- dotted
      3 --- dashed
      4 --- dashed II
*/

/* Layout variables: */

int ox = 0;  /* (ox,oy) are the coordinates of the bottom left corner */
int oy = 0;
int nr = 1;  /* nr = number of pages to the right */
int nu = 1;  /* nu = number of pages upwards      */

int dpp = -1; /* number of "t-s"-values to display on one page */
int dpu = 0;  /* number of s-values that fit on one page       */

int sep = 0;  /* 1 = display empty pages too                   */
int sp  = 0;  /* 1 = make each page a file of its own          */

int ax = 1;   /* 1 = display grid                              */
int spl = 5;  /* space between labels                          */

int landscape = 0;  /* landscape mode ?                        */

float dsz = 1.5;  /* dot size (radius) */
float scy = 1;  /* scaling of the y - axis                     */

int ttl = 0;  /* 1 = print title                               */ 
char title[100];

int crc = 1;  /* 1 = use \circ instead of \bullet              */

int texspecials = 0;  /* 1 = macros for diffs, place for names */

unsigned pagecount   = 0;  /* number of pages we have already  */

int pic = 0;         /* are we inside a picture environment ?  */

FILE *out = NULL;     /* the output file                       */

int pageox, pagesheight;  /* needed for nice ps-output         */

char gif_colors[30][50];
int thegif, thefont;

void preface();
void endfile();
void newpage();
void endpage();

float space = 0;   /* space between dots          */
float XEXT  = 0;    /* width of the display area   */
float YEXT  = 0;    /* height of the display area  */

int TEXXEXT=180;
int TEXYEXT=220;
int  PSXEXT=500;
int  PSYEXT=740;
int  MPXEXT=400;
int  MPYEXT=700;
int GIFXEXT=400;
int GIFYEXT=540;

void setvalues() {
  int ok;
  ok = (-1 != ox) && (-1 != oy) && (-1 != nr) && (-1 != nu) && (-1 != dpp);
  if (!ok) {
	 printf("not all of -ox, -oy, -nr, -nu and -dpp were given. try again.\n");
	 exit(1);
  }
  switch (base) {
  case xypic:
	 XEXT = TEXXEXT;
	 YEXT = TEXYEXT;
	 break;
  case ps:
	 XEXT = PSXEXT;
	 YEXT = PSYEXT;
	 break;
  case mp:
	 XEXT = MPXEXT;
	 YEXT = MPYEXT;
	 break;
  case gif:
	 XEXT = 20+GIFXEXT;
	 YEXT = 20+GIFYEXT;
  }
  if (landscape) {
	 int aux = XEXT; XEXT = YEXT; YEXT = aux;
  }
  space = XEXT;
  space /= dpp;              /* space between dots                */
  dpu = (int) (YEXT/(scy*space));  /* number of dots in the y direction */
  YEXT = space * dpu;        /* adjust YEXT                       */
}

char xxxps[] = "ps", xxxmp[] = "mp", xxxxypic[] = "tex", xxxgif[] = "perl";

int filecount = 0;

void newfile() {
  char fnme[20], *xxx = xxxps;

  if (NULL != out) {
	 if (pic) endpage();
	 endfile();
	 out = NULL;
  }

  if (base == ps) xxx = xxxps;
  if (base == mp) xxx = xxxmp;
  if (base == xypic) xxx = xxxxypic;
  if (base == gif) xxx = xxxgif;

  sprintf(fnme,"ext%d.%s",filecount,xxx);
  filecount++;

  if (base == gif) {
	 out = fopen(fnme,"wb");
  } else { 
	 out = fopen(fnme,"wt");
  }

  if (NULL == out) {
	 printf("cannot write file: %s\n",fnme);
	 exit(1);
  }

  printf("writing file: %s\n",fnme);

  preface();

}

void gifline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  int ai,bi,ci,di;
  if (e == 0) return;
  b *= scy; d *= scy;
  {    /*  clip lines to leave dots white */
	 double length,aux,xd,yd;
	 xd = c-a; yd = d-b;
	 length = sqrt(xd*xd + yd*yd);   /* length of the line */
	 aux = dsz/length;          /* percentage to cut from the ends */
	 if (aux <= 0.45) {
		if (sd) { /* move (a,b) */
		  a += xd*aux;
		  b += yd*aux;
		}
		if (td) { /* move (c,d) */
		  c -= xd*aux;
		  d -= yd*aux;
		}
	 }
  }
  ai = a; bi = b; ci = c; di = d;
  fprintf(out,"$im->line(%d,%d,%d,%d,%s);\n",
			 20+ai,(int) (YEXT-(10+bi)),20+ci,(int) (YEXT-(10+di)),gif_colors[e]);
}

void gifgrline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  int ai,bi,ci,di;
  if (e == 0) return;
  b *= scy; d *= scy;
  {    /*  clip lines to leave dots white */
	 double length,aux,xd,yd;
	 xd = c-a; yd = d-b;
	 length = sqrt(xd*xd + yd*yd);   /* length of the line */
	 aux = dsz/length;          /* percentage to cut from the ends */
	 if (aux <= 0.45) {
		if (sd) { /* move (a,b) */
		  a += xd*aux;
		  b += yd*aux;
		}
		if (td) { /* move (c,d) */
		  c -= xd*aux;
		  d -= yd*aux;
		}
	 }
  }
  ai = a; bi = b; ci = c; di = d;
  fprintf(out,"$im->line(%d,%d,%d,%d,$grcol);\n",
			 20+ai,(int) (YEXT-(10+bi)),20+ci,(int) (YEXT-(10+di)));
}

void pslineaux(a,b,c,d,e,sd,td,gr) float a,b,c,d; unsigned e,sd,td; int gr; {
  if (e == 0) return;
  b *= scy; d *= scy;
  {    /*  clip lines to leave dots white */
	 double length,aux,xd,yd;
	 xd = c-a; yd = d-b;
	 length = sqrt(xd*xd + yd*yd);   /* length of the line */
	 aux = dsz/length;          /* percentage to cut from the ends */
	 if (aux <= 0.45) {
		if (sd) { /* move (a,b) */
		  a += xd*aux;
		  b += yd*aux;
		}
		if (td) { /* move (c,d) */
		  c -= xd*aux;
		  d -= yd*aux;
		}
	 }
  }
  if (gr) fprintf(out,".9 setgray\n");
  fprintf(out,"%.2f %.2f moveto %.2f %.2f ",a,b,c,d);
  if (1 == e) fprintf(out,"la\n");
  if (2 == e) fprintf(out,"lb\n");
  if (3 == e) fprintf(out,"lc\n");
  if (4 == e) fprintf(out,"ld\n");
  if (5 <= e) fprintf(out,"lineto [%.2f] 0 setdash stroke\n",(float) 50/(e-1));
  if (gr) fprintf(out,"0 setgray\n");
}

int mpi=0;

void mplineaux(a,b,c,d,e,sd,td,gr) float a,b,c,d; unsigned e,sd,td; int gr; {
  if (e == 0) return;
  if (++mpi>1000) { endpage(); newpage(); preface(); mpi=0; }
  b *= scy; d *= scy;
  {    /*  clip lines to leave dots white */
	 double length,aux,xd,yd;
	 xd = c-a; yd = d-b;
	 length = sqrt(xd*xd + yd*yd);   /* length of the line */
	 aux = dsz/length;          /* percentage to cut from the ends */
	 if (aux <= 0.45) {
		if (sd) { /* move (a,b) */
		  a += xd*aux;
		  b += yd*aux;
		}
		if (td) { /* move (c,d) */
		  c -= xd*aux;
		  d -= yd*aux;
		}
	 }
  }
  fprintf(out,"draw (%.2f*uh,%.2f*uv) transformed T -- (%.2f*uh,%.2f*uv) transformed T withpen pencircle scaled .3",a,b,c,d);
  if (2 <= e) fprintf(out," dashed evenly scaled %.2f",.5/e);

  fprintf(out,";\n");
}
void mpline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  mplineaux(a,b,c,d,e,sd,td,0);
}
void mpgrline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  mplineaux(a,b,c,d,e,sd,td,1);
}
void psline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  pslineaux(a,b,c,d,e,sd,td,0);
}
void psgrline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  pslineaux(a,b,c,d,e,sd,td,1);
}
/*  in xyline:    (0 != sd) ? (reserve space for source dot) : don't   */
/*                same for target dot with (td != 0) ? ...             */
void xyline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  char s1[] = "i{\\circ}", s2[] = "{}";
  if (0 == e) return;
  b *= scy; d *= scy;
  fprintf(out,"(%.2f,%.2f) * %s; (%.2f,%.2f) * %s; **\\dir{",a,b,sd ? s1 : s2,c,d,td ? s1 : s2);
  if (1 == e) fprintf(out,"-};\n");
  if (2 == e) fprintf(out,".};\n");
  if (3 == e) fprintf(out,"--};\n");
  if (4 == e) fprintf(out,"~};\n");
  if (5 <= e) fprintf(out,"~~};\n");
}

int apeq(a,b) float a,b; {
  float x = a-b; 
  return (x <= 0.01) && (x >= -0.01);
}

void mkline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  if (apeq(a,c) && apeq(b,d)) return;
  if (base == xypic) xyline(a,b,c,d,e,sd,td);
  if (base == ps)    psline(a,b,c,d,e,sd,td);
  if (base == mp)    mpline(a,b,c,d,e,sd,td);
  if (base == gif)   gifline(a,b,c,d,e,sd,td);
}

void mkgrline(a,b,c,d,e,sd,td) float a,b,c,d; unsigned e,sd,td; {
  if (apeq(a,c) && apeq(b,d)) return;
  if (base == xypic) xyline(a,b,c,d,e,sd,td);
  if (base == ps)    psgrline(a,b,c,d,e,sd,td);
  if (base == mp)    mpgrline(a,b,c,d,e,sd,td);
  if (base == gif)   gifgrline(a,b,c,d,e,sd,td);
}

void mklineint(a,b,c,d,e) int a,b,c,d,e; {
  mkline((float) a, (float) b, (float) c, (float) d, e, 0, 0);
}

void xydot(a,b) float a,b; {
  b *= scy;
  if (crc) {
	 fprintf(out,"(%.2f,%.2f) * {\\circ};\n",a,b);
  } else {
	 fprintf(out,"(%.2f,%.2f) * {\\bullet};\n",a,b);
  }
}

void xyname(a,b) float a,b; {
  b -= (space / 2.5); b *= scy;
  fprintf(out,"(%.2f,%.2f) * {};\n",a,b);
}

void psdot(a,b) float a,b; {
  b *= scy;
  fprintf(out,"%.2f %.2f dot\n",a,b);
}

void mpdot(a,b) float a,b; {
  b *= scy;
  if (++mpi>1000) { endpage(); newpage(); preface(); mpi=0; }
  fprintf(out,"draw fullcircle scaled %f shifted (%.3f,%.3f) transformed T;\n",dsz,a,b);
}

void gifdot(a,b) float a,b; {
  int ai,bi,di; b *= scy; ai= a; bi = b; di = 2*dsz;
  do {
	 fprintf(out,"$im->arc(%d,%d,%d,%d,%d,%d,%s);\n",20+ai,(int) (YEXT-(10+bi)),di,di,0,360,gif_colors[0]);
  } while (!crc && di--);
}

void mkdot(a,b) float a,b; {
  if (base == xypic) xydot(a,b);
  if (base == ps) psdot(a,b);
  if (base == gif) gifdot(a,b);
  if (base == mp) mpdot(a,b);
}

#define MAXDOTS   100000      /* modify these values if you need to */
#define MAXLINES 1000000

typedef struct { float x,y; } dot;
typedef struct { dot source,target; int type; } line;

dot   dotlist[MAXDOTS];
unsigned numdots = 0;

line  linelist[MAXLINES];
unsigned numlines = 0;

void readdots() {
  FILE *fp; float x,y;

  fp = fopen("dots","rt");
  if (NULL == fp) {
	 printf("cannot open file: dots\n");
	 exit(1);
  }

  printf("reading: dots\n");

  while (!feof(fp)) {
	 if (2 == fscanf(fp," %f %f ",&x,&y)) {
		dotlist[numdots].x = x;
		dotlist[numdots].y = y;
		if (++numdots == MAXDOTS) {
		  printf("MAXDOTS too small. modify and recompile\n");
		  exit(1);
		}
	 }
  }

  fclose(fp);
}

void readlines(n) unsigned n; {
  FILE *fp; float x1,y1,x2,y2; char fnme[20]; int type;

  sprintf(fnme,"line%s",extension[n]);
  type = linetype[n];

  fp = fopen(fnme,"rt");
  if (NULL == fp) {
	 printf("cannot open file: %s\n",fnme);
	 exit(1);
  }
   
  printf("reading: %s\n",fnme);

  while (!feof(fp)) {
	 if (4 == fscanf(fp," %f %f %f %f ",&x1,&y1,&x2,&y2)) {
		linelist[numlines].source.x = x1;
		linelist[numlines].source.y = y1;
		linelist[numlines].target.x = x2;
		linelist[numlines].target.y = y2;
		linelist[numlines].type = type;
		if (++numlines == MAXLINES) {
		  printf("MAXLINES too small. modify and recompile\n");
		  exit(1);
		}
	 }
  }

  fclose(fp);
}

void gdlabelyax(a,b) int a,b; {
  int o;
  o = 6 / 2;
  fprintf(out,"$im->string(gdSmallFont,%d,%d,\"%d\",$tcol);\n",1,(int) (YEXT-(10+a+o)),b);
}

void gdlabelxax(a,b) int a,b; {
  int o,p;
  o = p = 6 / 2;
  if (b >= 10) o += p;
  if (b >= 100) o += p;
  fprintf(out,"$im->string(gdSmallFont,%d,%d,\"%d\",$tcol);\n",20+a-o,(int) (YEXT-7),b);
}

void coordsystem(x,y) int x,y; {
  unsigned i;

  if (ax) {
	 mkline(0.0,0.0,dpp*space,0.0,1,0,0); /* x axis */
	 mkline(0.0,0.0,0.0,dpu*space,1,0,0); /* y axis */

	 for (i=0;i<=dpp;i++) 
		mkgrline(i*space,0.00*space,i*space,1.0*dpu*space*scy,1,0,0);
	 for (i=0;i<=dpu;i++) 
		mkgrline(0.0*space,i*space,1.0*dpp*space,i*space,1,0,0);
  }
  
  /* mark upper right corner */

  mkline(dpp*space,(0.1 + (float) dpu)*space,dpp*space,(-0.1 + (float) dpu)*space,1,0,0);
  mkline((0.1 + (float) dpp)*space,dpu*space,(-0.1 + (float) dpp)*space,dpu*space,1,0,0);

  /* display tickmarks and labels */

  for (i=0;i<=dpp;i++) {
	 if (((i+x) % spl) != 0) {
		mkline(i*space,0.05*space,i*space,-0.05*space,1,0,0);
	 } else {
		mkline(i*space,0.1*space,i*space,-0.1*space,1,0,0);
		switch (base) {
		case xypic:
		  fprintf(out,"(%.2f,-10) * {%d};\n",i*space,i+x);
		  break;
		case ps:
		  fprintf(out,"%.2f -10 moveto (%d) writex\n",i*space,i+x);
		  break;
		case mp:
		  fprintf(out,"label.bot(btex %d etex,(%.2f*uh,-10*uv)) transformed T;\n",i+x,i*space);
		  break;
		case gif:
		  { 
			 int ai,bi; ai = i*space; bi = i+x;
			 gdlabelxax(ai,bi);
		  }
		  break;
		}
	 }
  }

  for (i=0;i<=dpu;i++) {
	 if (((i+y) % spl) != 0) {
		mkline(0.05*space,i*space,-0.05*space,i*space,1,0,0);
	 } else {
		mkline(0.1*space,i*space,-0.1*space,i*space,1,0,0);
		switch (base) {
		case xypic:
		  fprintf(out,"(-10,%.2f) * {%d};\n",i*space*scy,i+y);
		  break;
		case ps:
		  fprintf(out,"-10 %.2f moveto (%d) writey\n",i*space*scy,i+y);
		  break;
		case mp:
		  fprintf(out,"label.lft(btex %d etex,(-10*uh,%.2f*uv)) transformed T;\n",i+y,i*space*scy);
		  break;
		case gif:
		  { 
			 int ai,bi; ai = i*space*scy; bi = i+y;
			 gdlabelyax(ai,bi);
		  }
		  break;
		}
	 }
  }

}

int leq(a,b) float a,b; { /* return "approximately a <= b" */ 
  return (b-a) >= -0.01;
}

void writepage(n,m) unsigned n,m; {
  float nx = n*dpp+ox, ny = m*dpu+oy;
  unsigned i;

  pageox = ox + n*dpp; pagesheight = m;

  if (sep) { 
	 int nnx, nny; nnx = nx; nny = ny;
	 newpage(); coordsystem(nnx,nny); 
  }

  for (i=0;i<numdots;i++) {
	 float x = dotlist[i].x;
	 float y = dotlist[i].y;
	 if (x+0.1>=nx && y+0.1>=ny) {
		float xbak = x, ybak = y;
		x -= nx; y -= ny;
		if (x-0.1<=dpp && y-0.1<=dpu) {
		  x *= space;
		  y *= space;
		  if (NULL == out) newfile();
		  if (!pic) { 
			 int nnx, nny; nnx = nx; nny = ny;
			 newpage(); coordsystem(nnx,nny); 
		  }
		  if (texspecials && base == xypic) 
			 fprintf(out,"%% next: (x,y) = (%.1f,%.1f)\n",xbak,ybak);
		  mkdot(x,y);
		  if (texspecials && base == xypic)
			 xyname(x,y);
		}
	 }
  }
   
  for (i=0;i<numlines;i++) {
	 float xs = linelist[i].source.x;
	 float ys = linelist[i].source.y;
	 float xt = linelist[i].target.x;
	 float yt = linelist[i].target.y;
	 float xmi,ymi,xma,yma;
	 int z = linelist[i].type;
      
	 int linevisible = 1;
	 int sunclipped = 1, tunclipped = 1;

	 xmi = nx; ymi = ny; xma = nx + dpp; yma = ymi + dpu;


	 /*
		Clipping algorithm rewritten 09/29/1998:

		The clipping algorithm that I used in previous versions seems to be
		faulty. We probably need a detailed plan, so here it is:

		1)  Build a tolerance into xmi, xma, ymi and yma by making the displayed
		region slightly bigger.

		2)  Use a boolean variable "linevisible".

		3)  Clip lines in four steps, corresponding to right, left, top, bottom.

		4)  For right/left: Exchange endpoints to make sure xs <= xt.
		For top/bottom: Exchange endpoints to make sure ys <= yt.

		5r) For clipping at the right page border:  (assume xs <= xt)
		(xt <  xmi)  -->   not visible
		(xs >  xma)  -->   not visible
		(xt <= xma)  -->   do nothing
		(xt >  xma)  -->   clip the right end

		5l), 5t), 5b) Same for left, top, bottom.

		6)  If (linevisible == 1) print line.

	 */
	 xma -= xmi; xs -= xmi; xt -= xmi; xmi -= xmi; /* move page */
	 yma -= ymi; ys -= ymi; yt -= ymi; ymi -= ymi; 
      
	 xmi -= 0.1; xma += 0.1; ymi -= 0.1; yma += 0.1;  /* tolerance */

	 xmi *= space; xma *= space; xs *= space; xt *= space;  /* scaling */
	 ymi *= space; yma *= space; ys *= space; yt *= space;

	 if (xt < xs) {
		float aux; int bux;
		aux = xs; xs = xt; xt = aux;
		aux = ys; ys = yt; yt = aux;
		bux = sunclipped; sunclipped = tunclipped; tunclipped = bux;
	 }
	 /* now: may assume xs <= xt */

	 /* clipping at the right pageborder starts here */

	 if (xt < xmi) linevisible = 0;
	 if (xs > xma) linevisible = 0;
	 if (xt > xma) { /* clip at the right end */
		float t; 
		t = (xma-xs); t /= (xt-xs); 
		xt = xma;
		yt = ys + (yt-ys)*t;
		tunclipped = 0; 
	 }

	 if (xt < xs) {
		float aux; int bux;
		aux = xs; xs = xt; xt = aux;
		aux = ys; ys = yt; yt = aux;
		bux = sunclipped; sunclipped = tunclipped; tunclipped = bux;
	 }
	 /* now: may assume xs <= xt */

	 /* clipping at the left pageborder starts here */

	 if (xt < xmi) linevisible = 0;
	 if (xs > xma) linevisible = 0;
	 if (xs < xmi) { /* clip at the left end */
		float t; 
		t = (xmi-xs); t /= (xt-xs); 
		xs = xmi;
		ys = ys + t * (yt-ys);
		sunclipped = 0; 
	 }

	 if (yt < ys) {
		float aux; int bux;
		aux = xs; xs = xt; xt = aux;
		aux = ys; ys = yt; yt = aux;
		bux = sunclipped; sunclipped = tunclipped; tunclipped = bux;
	 }
	 /* now: may assume ys <= yt */

	 /* clipping at the top pageborder starts here */

	 if (yt < ymi) linevisible = 0;
	 if (ys > yma) linevisible = 0;
	 if (yt > yma) { /* clip at the upper end */
		float t; 
		t = (yma-ys); t /= (yt-ys); 
		yt = yma;
		xt = xs + t * (xt-xs);
		tunclipped = 0; 
	 }

	 if (yt < ys) {
		float aux; int bux;
		aux = xs; xs = xt; xt = aux;
		aux = ys; ys = yt; yt = aux;
		bux = sunclipped; sunclipped = tunclipped; tunclipped = bux;
	 }
	 /* now: may assume ys <= yt */

	 /* clipping at the bottom pageborder starts here */

	 if (yt < ymi) linevisible = 0;
	 if (ys > yma) linevisible = 0;
	 if (ys < ymi) { /* clip at the lower end */
		float t; 
		t = (ymi-ys); t /= (yt-ys); 
		ys = xmi;
		xs = xs + t * (xt-xs);
		sunclipped = 0; 
	 }
         
	 if (linevisible) {
		if (!pic) {
		  int nnx, nny; nnx = nx; nny = ny;
		  newpage(); coordsystem(nnx,nny); 
		}
		mkline(xs,ys,xt,yt,z,sunclipped,tunclipped);
	 }

  }
      
  if (base == gif) sp = 1;  /* single page mode for gifs */

  if (pic) endpage();
  if (sp) endfile();

}

/*  
   The following xypic - macro (or "<decor>ation") draws d_2 differentials
   for "-dpp30", and is the prototype for the others:

\def\dtwo{\save<-6mm,14.4mm>+p%
  \POS*i{\circ}**\dir{-}
;}

*/

void TeXdiff(n) int n; {
  float xd = space, yd = space;
  char num[][18] = {
	 "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
	 "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
	 "seventeen", "eighteen", "nineteen", "twenty"
  };
  yd *= n; yd *= scy; xd = -xd;
  fprintf(out,"\\def\\d%s{\\save<%.2fmm,%.2fmm>+p%%\n",num[n-2],xd,yd);
  fprintf(out,"\\POS*i{\\circ}**\\dir{-}\n;}\n");
}

void TeXdiffs() {
  int n;
  fprintf(out,"%% use the following macros for diffs\n");
  for (n=2;n<=20;n++) TeXdiff(n);
}

void TeXpreface() {
  fprintf(out,"\\documentclass{article}\n");
  fprintf(out,"\\usepackage{a4}\n");
  fprintf(out,"\\usepackage{xy}\n");
  if (texspecials) TeXdiffs();
  fprintf(out,"\\begin{document}\n");
  fprintf(out,"\\pagestyle{empty}\n");
  fprintf(out,"\\setlength{\\oddsidemargin}{-1.5cm}\n");
  fprintf(out,"\\setlength{\\evensidemargin}{-1.5cm}\n");
  fprintf(out,"\\setlength{\\topmargin}{-1.5cm}\n");
  fprintf(out,"\\addtolength{\\textheight}{1.5cm}\n");
  fprintf(out,"\\addtolength{\\textwidth}{1.5cm}\n");
  fprintf(out,"\n\n");
}

void PSprocdefs();

void PSpreface() {
  time_t aux; aux = time(NULL);
  fprintf(out,"%%!PS-Adobe-3.0\n");
  fprintf(out,"%%%%BoundingBox: -20 -20 400 300\n");
  fprintf(out,"%%%%Title: Ext chart\n");
  fprintf(out,"%%%%Creator: picture.c\n");
  fprintf(out,"%%%%CreationDate:%s",ctime(&aux));
  fprintf(out,"%%%%Pages: (atend)\n");
  fprintf(out,"%%%%PageOrder: Ascend\n");
  fprintf(out,"%%%%EndComments\n");
  fprintf(out,"%%%%BeginProlog\n");
  PSprocdefs();
  fprintf(out,"%%%%EndProlog\n");
  fprintf(out,"%%%%BeginSetup\n");
  fprintf(out,"%%%%PaperSize: A4\n");
  fprintf(out,"%%%%Orientation: %s\n",landscape ? "Landscape" : "Portrait");
  fprintf(out,"%%%%EndSetup\n");
  fprintf(out,"\n");
  fprintf(out,"\n\n");
}

int fc[20][3] = {  /* favorite colours (dots, linetype I, linetype II, etc.) */
  {0,0,0},{185,16,0},{201,73,80},{185,178,178},{0,0,0},{0,0,0},{0,0,0},
  {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0},
  {0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}
};

void GIFpreface() {
  int i;
  fprintf(out,"#!/usr/bin/perl\n"
			 "use GD;\n" 
			 "$im = new GD::Image(%d,%d);\n",(int) (20+XEXT),(int) (10+YEXT));
  fprintf(out,"$white = $im->colorAllocate(255,255,255);\n"
			 "# $im->transparent($white);\n"
			 "$im->interlaced('true');\n");
  
  fprintf(out,"$tcol = $im->colorAllocate(0,0,0);\n");
  fprintf(out,"$grcol = $im->colorAllocate(200,200,200);\n");
  for (i=0;i<=20;i++) {
	 sprintf(gif_colors[i],"$cl%d",i);
	 fprintf(out,"%s = $im->colorAllocate(%d,%d,%d);\n",
				gif_colors[i],fc[i][0],fc[i][1],fc[i][2]);
  }
}

int haveuvh=0;
void mppreface() {
  fprintf(out,"beginfig(%d);\n",pagecount);
  if (!haveuvh) { haveuvh=1; fprintf(out,"uv=1; uh=1;\n"); }
  fprintf(out,"transform T; T := identity rotated -90 scaled .7;\n");
  fprintf(out,"\n");
}

void preface() {
  if (base == xypic) TeXpreface();
  if (base == ps) PSpreface();
  if (base == mp) mppreface();
  if (base == gif) GIFpreface();
}

void PSprocdefs() {
  fprintf(out,"/dot { newpath %.2f 0 360 arc %s } def\n",dsz,crc ? "stroke" : "fill");
  fprintf(out,"/pagewidth 550 def\n");
  fprintf(out,"/pageheight 750 def\n");
  fprintf(out,"/slw { 0.5 setlinewidth } def\n");
  fprintf(out,"/trl { %d 50 translate } def\n",landscape ? 550 : 50);
  fprintf(out,"/la { lineto [] 0 setdash stroke } def\n");
  fprintf(out,"/lb { lineto [%.2f] 0 setdash stroke } def\n", (float) 25/20);
  fprintf(out,"/lc { lineto [%.2f] 0 setdash stroke } def\n", (float) 25/10);
  fprintf(out,"/ld { lineto [%.2f] 0 setdash stroke } def\n", (float) 25/5);
  fprintf(out,"/writex { dup stringwidth pop 2 div neg -3 rmoveto show } def\n");
  fprintf(out,"/writey { -10 -3 rmoveto show } def\n");
}

void PSnewpage() {
  char pagename[40];
  sprintf(pagename,"%3d-%3d %d",pageox,pageox+dpp,pagesheight+1);
  fprintf(out,"\n\n");
  fprintf(out,"%%%%Page: (%s) : %d\n",pagename,pagecount);
  fprintf(out,"%%%%BeginPageSetup\n");
  fprintf(out,"%%%%EndPageSetup\n");
  fprintf(out,"gsave\n");
  fprintf(out,"slw\n");
  fprintf(out,"/Times-Roman findfont 8 scalefont setfont\n");
  fprintf(out,"trl\n");
  if (landscape) fprintf(out,"90 rotate\n");
  if (ttl) {
	 fprintf(out,"0 750 moveto (%s) show\n",title);
  }
}

void PSendpage() {
  fprintf(out,"grestore\n");
  fprintf(out,"showpage\n");
}

void TeXnewpage() {
  if (pagecount) fprintf(out,"\n\n");
  fprintf(out,"\n\n");
  fprintf(out,"\\unitlength 1mm\n");
  fprintf(out,"\\begin{xy}\n");
}

void mpnewpage() {
  mppreface();
}

void TeXendpage() {
  fprintf(out,"\\end{xy}\n");
  fprintf(out,"\\newpage\n");
  fprintf(out,"\n\n%% end of page %d\n\n\n",pagecount);
}

void mpendpage() {
  if (pic) {
	 fprintf(out,"endfig;\n");
  }
}

void PSendfile() {
  fprintf(out,"\n%%%%Trailer:\n");
  fprintf(out,"%%%%Pages: %d\n",pagecount);
  fprintf(out,"\n%%%%EOF\n");
  pagecount = 0;
}

void mpendfile() {
  fprintf(out,"end\n");
}

void TeXendfile() {
  fprintf(out,"\n\n\\end{document}\n");
  pagecount = 0;
}

void GIFendfile() {
  fprintf(out,"binmode STDOUT;\nprint $im->png;\n");
}

void endfile() {
  if (NULL != out) {
	 if (base == xypic) TeXendfile();
	 if (base == ps) PSendfile();
	 if (base == mp) mpendfile();
	 if (base == gif) GIFendfile();
	 fclose(out);
	 out = NULL;
  }
}

void newpage() {
  if (out == NULL) newfile();
  pagecount++;
  if (base == xypic) TeXnewpage();
  if (base == ps) PSnewpage();
  if (base == mp) mpendpage();
  pic = 1;
}

void endpage() {
  if (base == xypic) TeXendpage();
  if (base == ps) PSendpage();
  if (base == mp) mpendpage();
  pic = 0;
}

void usage() {
  printf("picture -- from dots & lineXX to LaTeX, postscript or gif charts\n");
  printf("usage:  picture [options]\n");
  printf("available options are:\n");
  printf("  -ox<value>                x coordinate of the bottom left corner\n");
  printf("  -oy<value>                y coordinate of the bottom left corner\n");
  printf("  -nr<value>                number of pages to the right\n");
  printf("  -nu<value>                number of pages upwards\n");
  printf("  -dpp<value>               number of dimensions to display per page\n");
  printf("  +latex                    LaTeX output using xypic (**not recommended**)\n");
  printf("  +ps                       postscript output                >default<\n");  
  printf("  +mp                       metapost output\n");
#if defined GIF_SUPPORT
  printf("  +gif                      output as gif\n");
  printf("  +biggif<value1>:<value2>  output as gif of size <val1> x <val2> pixel\n"); 
  printf("  +col<n>.<r>.<g>.<b>       set rgb value of colour #<n> (for gif mode)\n");
#endif
  printf("  -sp  [neg = +sp]          put all pages into the same file >default<\n");
  printf("  -sep [neg = +sep]         don't show empty pages           >default<\n");
  printf("  +ax  [neg = -ax]          display grid                     >default<\n");
  printf("  +crc  [neg = -crc]        unfilled dots, instead of TeX - \\bullets >default<\n");
  printf("  +texspecials [neg = -..]  useful for nicely edited charts >off<\n");
  printf("  +portrait/+landscape      set page orientation >default: portrait<\n");
  printf("  -spl<value>               space between labels\n");
  printf("  -scy<value>               scaling of the y - axis\n");
  printf("  -dsz<value>               size of dots in Postscript points (default: %.1f)\n",dsz);
  printf("  -ttl:<string>             print <string> as title\n");
  printf("  h<digit1>:<digit2>        display multiplication by h<digit1> with\n"); 
  printf("                            line type <digit2>\n");
  printf("  v<digit1>:<digit2>        same for v<digit1>\n");
  printf("line types are: 1 - plain / 2 - dotted / 3 - dashed / 4 - dashed II\n");
  printf("You have to provide at least the -dpp option.\n");
  printf("Call picture in the directory that contains the files dots & lineXX.\n");
#if defined GIF_SUPPORT
  printf("The GIF part uses Quest's gd library, see http://www.boutell.com/gd\n");
#endif
  printf("Author's address:  nassau@nullhomotopie.de\n");
  printf("Author's homepage: http://www.nullhomotopie.de\n");
}

int badopt = 0;

void complain(p) char *p; {
  printf("unknown option: %s\n",p);
  badopt = 1;
}

int streq(p,q) char *p,*q; {
  for (;*p && *q;p++,q++) if (*p != *q) return 0;
  return *p == *q;
}

int streqint(p,q,n) char *p,*q; int n; {
  while (n--) if (*p++ != *q++) return 0;
  return 1;
}

void handleoption(p) char *p; {
  int a1,a2,a3,a4;
  if (('+' == *p) || ('-' == *p)) {
    if (sscanf(p+1,"ox%d",&ox)) return;
    if (sscanf(p+1,"oy%d",&oy)) return;
    if (sscanf(p+1,"nr%d",&nr)) return;
    if (sscanf(p+1,"nu%d",&nu)) return;
    if (sscanf(p+1,"dpp%d",&dpp)) return;
    if (sscanf(p+1,"spl%d",&spl)) return;
    if (sscanf(p+1,"scy%f",&scy)) return;
    if (sscanf(p+1,"dsz%f",&dsz)) return;
    if (sscanf(p+1,"ttl:%70[^*]",title)) { ttl = 1; return; }
    if (2 == sscanf(p+1,"biggif%d:%d",&GIFXEXT,&GIFYEXT)) {
      base = gif; return;
    }
    if (4 == sscanf(p+1,"col%d.%d.%d.%d",&a1,&a2,&a3,&a4)) {
      fc[a1][0] = a2;
      fc[a1][1] = a3;
      fc[a1][2] = a4;
      return;
    }
  }
  switch (*p) { 
  case '+':
  case '-':
	 {
		p++;
		if (streqint("por",p,3)) { landscape = 0; return; }
		if (streqint("lan",p,3)) { landscape = 1; return; }
		if (streq("ps",p)) { base = ps; return; };
		if (streq("latex",p)) { base = xypic; return; };
		if (streq("mp",p)) { base = mp; return; };
		if (streq("gif",p)) { base = gif; return; };
		p--;
	 }
	 {
		int z = (*p == '+') ? 1 : 0;
		if (streq("sp",p+1)) { sp = z; return; };
		if (streq("sep",p+1)) { sep = z; return; };
		if (streq("ax",p+1)) { ax = z; return; };
		if (streq("crc",p+1)) { crc = z; return; };
		if (streq("texspecials",p+1)) { texspecials = z; return; };
	 }
	 break;
  default :
	 {  
		char nme[20]; unsigned lt;
		if (2 == sscanf(p,"%20[^:]:%d",nme,&lt)) {
		  newtype(nme,lt); return;
		} else complain(p);
	 }
	 break;
  }
  complain(p);
}

int main(argc,argv) int argc; char **argv; {
  unsigned n,m;
   
  if (argc == 1) { usage(); exit(0); }

  for (n=1;n<argc;n++) handleoption(argv[n]);

  if (badopt) {
	 usage();
	 exit(1);
  }
   
  setvalues();

  readdots();

  for (n=0;n<numtypes;n++) {
	 readlines(n);
  }

  out = NULL;

  for (m=0;m<nu;m++)
	 for (n=0;n<nr;n++)
		writepage(n,m);

  if (out != NULL) {
	 if (pic) endpage();
	 endfile();
  }

  return 0;

}
