/*                         l2ps.c

	transformiert den output von bernds lindenmayer-grammatik
			in postscript

		sep-1992 (c) ingo kloeckl jr.
		jan-1993 (c) ingo kloeckl jr.
*/

#include <stdio.h>
#include <string.h>
#include <math.h>

typedef struct all_inf { int    a_pos_x, a_pos_y,       /* aktuelle pos */
				l_pos_x, l_pos_y;       /* letzte pos */
			 double act_deg, last_deg;      /* drehwinkel */
		       };

typedef struct sym_entry { char sym;                    /* symbol */
			   int type;                    /* 0-3 */
			   double val;          
			 };            

#define MIN(a,b)        ((a)<(b) ? (a):(b))
#define MAX(a,b)        ((a)<(b) ? (b):(a))
#define abs(a)          ((a)<0. ? (-a):(a))
#define ANZ             30                     
		       
struct sym_entry symbol[ANZ];                  
int maxcnt = 0;
double pi=3.1415927, deg=15, scale;
extern double sin(), cos(), atof();
double factor1=1, width=1, dfaktor=1, rfaktor=1;
char *rindex(), *ps;
FILE *in, *out;
double x_min, x_max, y_max;
#define MAX_CM  5
char *com[MAX_CM+1] = { "-t", "-a", "-s", "-w", "-t", "-dummy" };

main(argc, argv)
int argc;
char **argv;
{       char i_file[80], o_file[80], **l_ptr = argv+argc-1;
	struct all_inf inf;
	double factor=1.;
	long insert_pos;
	int l_com=0, f_n=0;

	char *buf;
	buf = malloc(32768);

	printf("****  l2ps - (c) 1992 by IK  ****\n\n");
	printf("reading definition file...\n");
	read_info();    /* reads symbol description file */

	inf.a_pos_x = inf.a_pos_y = 0;
	inf.l_pos_x = inf.l_pos_y = 0;
	inf.act_deg = inf.last_deg = 0;

	if ( argc<2 )
	  { printf("usage is l2ps infile [ofile]\n\n");
	    exit(1);
	  }

	argv++;
	while ( !f_n )
	  {  l_com = 0;
	     while ( strcmp(com[l_com],*argv) && l_com < MAX_CM )
	       l_com++;
	     if ( l_com==MAX_CM )  /* noch ein kommando ? */
	       f_n = 1;            /* nein, des ist der filename */
	     else
	       {  switch( l_com )
		    {  case 0 :  sscanf(*++argv,"%lf",&factor1);
				 break;
		       case 1 :  sscanf(*++argv,"%lf",&deg);
				 break;
		       case 2 :  sscanf(*++argv,"%lf",&dfaktor);
				 break;
		       case 3 :  sscanf(*++argv,"%lf",&width);
				 break;
		       default: ;
		    }
		  argv++;
	       }
	  }
	strcpy(i_file,*argv++);
	if ( argv==l_ptr)
	  strcpy(o_file,*argv);
	else
	  {  strcpy(o_file,i_file);
	     if ( (ps=rindex(o_file,'.')) )
	       *ps = '_';
	     strcat(o_file,".ps");
	  }

	printf("translating symbols...\n");

	printf("Infile %s  Outfile %s\n",i_file,o_file);
	printf("factor1:%lf  deg:%lf dfaktor:%lf\n",factor1,deg,dfaktor);
	in = fopen(i_file,"r");
	out = fopen(o_file,"w");

    setbuf(out,buf);
	fprintf(out,"\%%!PS-Adobe-2.0\n\%% beschreibt eine graphtale pflanze\n");
	fprintf(out,"\%% output made by l2ps\n");
	fprintf(out,"/lcs { lineto closepath stroke } def  /m { moveto } def  ");
	fprintf(out,"/sl { setlinewidth } bind def\n");
	fprintf(out,"/n { newpath } def\n");
	fprintf(out,"/blatt1 { gsave 1 0 0 setrgbcolor 2 0 360 arc closepath fill grestore x y moveto } def\n");
	fprintf(out,"300 100 translate\n");
	insert_pos = ftell(out);        /* for back-patching */
	fprintf(out,"%12.5e %12.5e scale\n0 0 m\n",1,1);

	transform(&inf, factor);
	fprintf(out,"showpage\n");
	fseek(out, insert_pos, 0);      /* back-patching for auto-scaling */
	scale = MIN(280/MAX(abs(x_max),abs(x_min)), 700/abs(y_max));
	fprintf(out,"%12.5e %12.5e",scale,scale);

	fclose(in);
	fclose(out);
}

transform(inf, factor)
struct all_inf *inf;
double factor;
{       int i, j, cnt, val;
	char cc[2], cmp[2];
	double i_factor, ddeg;
	struct all_inf neuinf;

	i_factor = factor*factor1;
	neuinf.l_pos_x = neuinf.a_pos_x = inf->a_pos_x;
	neuinf.l_pos_y = neuinf.a_pos_y = inf->a_pos_y;
	neuinf.last_deg = inf->last_deg;
	neuinf.act_deg = inf->act_deg;
	*(cc+1) = *(cmp+1) = '\0';

	fprintf(out,"%f sl\n",width*factor);
	while ( !feof(in) && (*cc = (char)fgetc(in)) )
	  {  cnt = 1;
	     while ( strcmp(cc,(*cmp = symbol[cnt].sym,cmp)) && cnt<=maxcnt )
	       cnt++;
	     if ( cnt>maxcnt )
	       printf("cannot transform %c\n",*cc);
	     else
	       {  val = symbol[cnt].val;
		  switch(symbol[cnt].type)
		    {  case 0 : val *= i_factor;
				neuinf.a_pos_x += val*sin(pi/180*neuinf.act_deg);
				neuinf.a_pos_y += val*cos(pi/180*neuinf.act_deg);
				x_min = MIN(x_min,neuinf.a_pos_x);
				x_max = MAX(x_max,neuinf.a_pos_x);
				y_max = MAX(y_max,neuinf.a_pos_y);
				fprintf(out,"/x %d def /y %d def x y lcs",neuinf.a_pos_x,neuinf.a_pos_y);
				fprintf(out,"   n x y m\n");
				break;
		       case 1 : neuinf.last_deg = neuinf.act_deg;
				neuinf.act_deg += val*dfaktor;
				neuinf.l_pos_x = neuinf.a_pos_x;
				neuinf.l_pos_y = neuinf.a_pos_y;
				transform(&neuinf, i_factor);
				neuinf.a_pos_x = neuinf.l_pos_x;
				neuinf.a_pos_y = neuinf.l_pos_y;
				if ( val<0 ) ddeg=deg; else ddeg = (-deg);
				neuinf.act_deg = neuinf.last_deg + ddeg;
				fprintf(out,"n %d %d m\n",neuinf.a_pos_x, neuinf.a_pos_y);
				break;
		       case 2 : blatt(val, &neuinf);
				break;
		       case 3 : return;
		       default: printf("zu dumm, um zahlen einzutragen !!\n");
		    }
	       }
	  }
}


blatt(form, inf)
int form;
struct all_inf *inf;
{       switch(form)
	  { case 1 : sp();
		     fprintf(out,"blatt1\n");
		     break;
	    case 2 : sp();
		     fprintf(out,"3 0 360 arc closepath stroke x y moveto\n");
		     break;
	  }
}


sp()
{       fprintf(out,"currentpoint /y exch def /x exch def x y\n");
}


read_info()
{       int type = 0;
	char sym;
	double val;

	in = fopen("grammar.dat","r");
	while ( type < 4 )
	  {  while ( !feof(in) && ( sym = (char)fgetc(in) ) != '#' )
	       if ( sym != ' ' && sym != '\n' && sym != '\r' )
		 {  symbol[++maxcnt].sym = sym;
		    fscanf(in,"%lf",&symbol[maxcnt].val);
		    symbol[maxcnt].type = type;
		    printf("read entry %2d : sym %c  val = %lf\n",maxcnt,sym,symbol[maxcnt].val);
		 }
	     type++;
	  }
	fclose(in);
}


char *rindex(str, such)
char *str, such;
{       char *hlp;

	hlp = str+strlen(str);
	while ( hlp >= str && ( *hlp != such ) ) hlp--;
	return( (hlp < str) ? 0 : hlp);
}
