/*
 * $Id: latex.trm,v 3.26 92/03/24 22:35:37 woo Exp Locker: woo $
 */

/* GNUPLOT - latex.trm */
/*
 * Copyright (C) 1990, 1991, 1992   
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted, 
 * provided that the above copyright notice appear in all copies and 
 * that both that copyright notice and this permission notice appear 
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the modified code.  Modifications are to be distributed 
 * as patches to released version.
 *  
 * This software  is provided "as is" without express or implied warranty.
 * 
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *   LaTeX pictures (latex).
 *   LaTeX pictures with emTeX specials (emtex). 
 *
 * AUTHORS
 *   David Kotz, Russell Lang
 *
 * send your comments or suggestions to (info-gnuplot@ames.arc.nasa.gov).
 * 
 */

/* modified to optimize use of \rule for long lines */

/* the following LATEX driver has been modified by 
   Russell Lang, eln272v@monu1.cc.monash.oz from the
   GnuTeX 1.3 driver by David Kotz, David.Kotz@Dartmouth.edu.
   Since then it has been further extended by David Kotz.
   EmTeX driver by Russell Lang. */


#define LATEX_PTS_PER_INCH (72.27)
#define DOTS_PER_INCH (300)	/* resolution of printer we expect to use */
#define LATEX_UNIT (LATEX_PTS_PER_INCH/DOTS_PER_INCH) /* dot size in pt */

/* 5 inches wide by 3 inches high (default) */
#define LATEX_XMAX (5*DOTS_PER_INCH)  /* (LATEX_PTS_PER_INCH/LATEX_UNIT*5.0) */
#define LATEX_YMAX (3*DOTS_PER_INCH)  /* (LATEX_PTS_PER_INCH/LATEX_UNIT*3.0) */

#define LATEX_HTIC (5*DOTS_PER_INCH/72)		/* (5./LATEX_UNIT) */
#define LATEX_VTIC (5*DOTS_PER_INCH/72)		/* (5./LATEX_UNIT) */
#define LATEX_HCHAR (DOTS_PER_INCH*53/10/72)	/* (5.3/LATEX_UNIT) */
#define LATEX_VCHAR (DOTS_PER_INCH*11/72)	/* (11./LATEX_UNIT) */

static int LATEX_posx;
static int LATEX_posy;
static enum JUSTIFY latex_justify=LEFT;
static int latex_angle=0;

/* Default line-drawing character */
/* the definition of plotpoint varies with linetype */
#define LATEX_DOT "\\usebox{\\plotpoint}"
#define LATEX_TINY_DOT "\\rule{.1pt}{.1pt}" /* for dots plot style */

/* POINTS */
#define LATEX_POINT_TYPES 12	/* we supply more point types */
static char *LATEX_points[] = {
    "\\raisebox{-1.2pt}{\\makebox(0,0){$\\Diamond$}}",
    "\\makebox(0,0){$+$}",
    "\\raisebox{-1.2pt}{\\makebox(0,0){$\\Box$}}",
    "\\makebox(0,0){$\\times$}",
    "\\makebox(0,0){$\\triangle$}",
    "\\makebox(0,0){$\\star$}",
    "\\circle{12}", "\\circle{18}", "\\circle{24}",
    "\\circle*{12}", "\\circle*{18}", "\\circle*{24}"
};

/* LINES */
static float LATEX_size = 0;	/* current thick of line in points */
static float LATEX_dotspace = 0; /* current dotspace of line in points */
#define LATEX_LINE_TYPES 6	/* number of line types below */
#define LATEX_THIN_LINE 0	/* the thinnest solid line type */
static struct {
    float size;			/* size of dot, or thick of line in points */
    float dotspace;			/* inter-dot space in points; 0 for lines */
} LATEX_lines[] = {
    {.35, 0.0},				/* thin solid line */
    {.7, 0.0},				/* thick solid line */
    {1.0, 0.0},				/* Thick solid line */
    {.5, 5.0},				/* dotted line */
    {.5, 9.0},				/* widely dotted line */
    {.5, 13.0}				/* really widely dotted line */
};
/* for drawing dotted and solid lines */
static void LATEX_dot_line();
static void LATEX_solid_line();
static void LATEX_rule();
static void LATEX_flushdot();
#define LATEX_flushrule() LATEX_rule(2, 0.,0.,0.,0.) /* flush old rule */
static BOOLEAN LATEX_moved = TRUE;	/* pen is up after move */
static float LATEX_dotsize;	/* size of LATEX_DOT in units */
static BOOLEAN LATEX_needsdot = FALSE;/* does dotted line need termination? */

#ifdef EMTEX
BOOLEAN emtex=FALSE; /* not currently using emtex */
static void EMTEX_solid_line();
#endif

/* ARROWS */
/* the set of non-vertical/non-horizontal LaTeX vector slopes */
/* except negatives - they are handled specially */
static struct vslope {
    int dx, dy;
} LATEX_slopes[] = {
    {1,1}, {1,2}, {1,3}, {1,4},
    {2,1}, {2,3},
    {3,1}, {3,2}, {3,4},
    {4,1}, {4,3},
    {0,0}					/* terminator */
};
static void best_latex_arrow(); /* figure out the best arrow */

LATEX_init()
{
#ifdef EMTEX
    emtex = FALSE;
#endif
    LATEX_posx = LATEX_posy = 0;
    fprintf(outfile, "%% GNUPLOT: LaTeX picture\n");
    fprintf(outfile, "\\setlength{\\unitlength}{%fpt}\n", LATEX_UNIT);
    fprintf(outfile, 
		  "\\ifx\\plotpoint\\undefined\\newsavebox{\\plotpoint}\\fi\n");
    LATEX_linetype(-1);
	LATEX_size =0;
}


LATEX_scale(xs, ys)
	double xs, ys;			/* scaling factors */
{
    register struct termentry *t = &term_tbl[term];

    /* we change the table for use in graphics.c and LATEX_graphics */
    t->xmax = (unsigned int)(LATEX_XMAX * xs);
    t->ymax = (unsigned int)(LATEX_YMAX * ys);

    return(TRUE);
}

LATEX_graphics()
{
    register struct termentry *t = &term_tbl[term];

    fprintf(outfile, "\\begin{picture}(%d,%d)(0,0)\n", t->xmax, t->ymax);
    fprintf(outfile, "\\tenrm\n");
}


LATEX_text()
{
    LATEX_flushrule();
    LATEX_flushdot();
    fprintf(outfile, "\\end{picture}\n");
    LATEX_posx = LATEX_posy = 0; /* current position */
    LATEX_moved = TRUE;	/* pen is up after move */
}

LATEX_linetype(linetype)
	int linetype;
{
    float size;

    if (linetype >= LATEX_LINE_TYPES)
	 linetype %= LATEX_LINE_TYPES;

#ifdef EMTEX
    if (!emtex)
#endif
    LATEX_flushrule();
    LATEX_flushdot();

    /* Find the new desired line thickness. */
    /* negative linetypes (for axes) use a thin line */
    /* only relevant for drawing axes/border in 3d */
    size = (linetype >= 0 ? LATEX_lines[linetype].size 
		  : LATEX_lines[LATEX_THIN_LINE].size);

    /* If different from current size, redefine \plotpoint */
    if (size != LATEX_size) {
	   fprintf(outfile, 
			 "\\sbox{\\plotpoint}{\\rule[%.3fpt]{%.3fpt}{%.3fpt}}%%\n",
			 -size/2, size, size);
#ifdef EMTEX
        if (emtex)         /* change line width */
		fprintf(outfile, "\\special{em:linewidth %.1fpt}%%\n", size);
#endif
    }
    
    LATEX_size = size;
    LATEX_dotsize = size / LATEX_UNIT;
    LATEX_dotspace = (linetype >= 0) ? LATEX_lines[linetype].dotspace : 0;
    LATEX_moved = TRUE;			/* reset */
}

LATEX_move(x,y)
	unsigned int x,y;
{
    LATEX_flushdot();

    LATEX_posx = x;
    LATEX_posy = y;
    LATEX_moved = TRUE;			/* reset */
}


LATEX_point(x,y, number)		/* version of line_and_point */
	unsigned int x,y;
	int number;				/* type of point */
{
    LATEX_move(x,y);
    
    /* Print the character defined by 'number'; number < 0 means 
	  to use a dot, otherwise one of the defined points. */
    fprintf(outfile, "\\put(%d,%d){%s}\n", x, y, 
		  (number < 0 ? LATEX_TINY_DOT
		   : LATEX_points[number % LATEX_POINT_TYPES]));
}


LATEX_vector(ux,uy)
	unsigned int ux,uy;
{
    if (LATEX_dotspace == 0.0) {
	   /* solid line */
#ifdef EMTEX
	   if (emtex)
		EMTEX_solid_line(LATEX_posx, (int)ux, LATEX_posy, (int)uy);
	   else
#endif
		LATEX_solid_line(LATEX_posx, (int)ux, LATEX_posy, (int)uy);
    } else
	 /* dotted line */
	 LATEX_dot_line(LATEX_posx, (int)ux, LATEX_posy, (int)uy);

    LATEX_posx = ux;
    LATEX_posy = uy;
}

static void
LATEX_solid_line(x1,x2, y1,y2)
	int x1,x2, y1,y2;
{
    float slope;
    int inc;
    float next;
    float x,y;
    int code;				/* possibly combine with previous rule */

    /* we draw a solid line using the current line thickness (size) */
    /* we do it with lots of \\rules */

    if (x1 == x2 && y1 == y2) { /* zero-length line - just a dot */
	   if (LATEX_moved) {
		  LATEX_flushrule();
		  /* plot a dot */
		  fprintf(outfile, "\\put(%u,%u){%s}\n", x1, y1, LATEX_DOT);
	   }
    } else {
	   code = (LATEX_moved ? 0 : 1); /* no combine after move */
	   if (x1 == x2)		/* vertical line - special case */
		LATEX_rule(code, (double)x1, (double)y1,
			   LATEX_dotsize, (double)y2-y1);
	   else if (y1 == y2)	/* horizontal line - special case */
		LATEX_rule(code, (double)x1, (double)y1, (double)x2-x1,
			   LATEX_dotsize);
	   else {
		  slope = ((float)y2-y1)/((float)x2-x1);
		  if (abs(slope) <= 1.0) {
			 /* longer than high */
			 inc = sign(y2-y1);
			 for (x = x1, y = y1; (y2-y)*inc >= 0; y += inc) {
				next = inc/slope + x;
				if ((x2>x1 && next > x2) || (x2<x1 && next < x2)) {
				    LATEX_rule(code, x,y, x2-x, LATEX_dotsize);
				    break;
				} else {
				    LATEX_rule(code, x,y, next-x, LATEX_dotsize);
				    x = next;
				}
				code = 0;
			 }
		  } else {
			 /* higher than long */
			 inc = sign(x2-x1);
			 for (x = x1, y = y1; (x2-x)*inc >= 0; x += inc) {
				next = inc*slope + y;
				if ((y2>y1 && next > y2) || (y2<y1 && next < y2)) {
				    LATEX_rule(code, x,y, LATEX_dotsize, y2-y);
				    break;
				} else {
				    LATEX_rule(code, x,y, LATEX_dotsize, next-y);
				    y = next;
				}
				code = 0;
			 }
		  }
	   }
    }
    LATEX_moved = FALSE;
}

/* Draw a \rule. Width or height may be negative; we can correct.
 * The rule is never output immediately. The previous rule is output
 * as-is if code is 0, and the previous rule is
 * combined with the current rule (if possible) if code is 1.
 * The previous rule is output, and the new one ignored, if code is 2.
 */
static void
LATEX_rule(code, x,y, width, height)
	int code;				/* how do we treat this rule? */
	double x, y;
	double width;
	double height;
{  
    static float lastx, lasty;
    static float lastw, lasth;
    static BOOLEAN valid = FALSE; /* is 'last' data valid? */
    BOOLEAN combine = (code == 1);
    BOOLEAN flush = (code == 2);

    if (!flush)
	 if (width == 0 || height == 0)
	   return;			/* ignore this rule */

    if (valid && combine) {
	   /* try to combine new rule with old rule */
	   if ((int)lastx == (int)x && lastw == width) { /* vertical rule */
		  if (lasth * height >= 0) { /* same sign */
			 lasth += height;
			 return;
		  }
	   } else if ((int)lasty == (int)y && lasth == height){ /* horiz rule */
		  if (lastw * width >= 0) { /* same sign */
			 lastw += width;
			 return;
		  }
	   }
	   /* oh well, output last and remember the new one */
    }

    if (valid) {
	   /* output the rule */
	   if (lastw < 0) {
		  lastx += lastw;
		  lastw = -lastw;
	   }
	   if (lasth < 0) {
		  lasty += lasth;
		  lasth = -lasth;
	   }

	   /* if very small use canned dot */
	   if (lastw < LATEX_dotsize || lasth < LATEX_dotsize)
		fprintf(outfile, "\\put(%d,%d){%s}\n",	   
			   (int)lastx, (int)lasty, LATEX_DOT);
	   else
		fprintf(outfile, "\\put(%d,%d){\\rule[%.3fpt]{%.3fpt}{%.3fpt}}\n",
			   (int)lastx, (int)lasty, -LATEX_dotsize*LATEX_UNIT/2,
			   lastw*LATEX_UNIT, lasth*LATEX_UNIT);
    }
    
    if (flush) {
	   valid = FALSE;
    } else {
	   lastx = x; lasty = y;
	   lastw = width; lasth = height;
	   valid = TRUE;
    }
}

static void
LATEX_dot_line(x1,x2, y1,y2)
	int x1,x2, y1,y2;
{
    static float LATEX_left;	/* fraction of space left after last dot */
#ifndef AMIGA_AC_5
    extern double sqrt();
#endif
    /* we draw a dotted line using the current dot spacing */

    if (LATEX_moved)
	 LATEX_left = 1.0;		/* reset after a move */

    /* zero-length line? */
    if (x1 == x2 && y1 == y2) {
	   if (LATEX_moved)
		/* plot a dot */
		fprintf(outfile, "\\put(%u,%u){%s}\n", x1, y1, LATEX_DOT);
    } else {
	   float dotspace = LATEX_dotspace / LATEX_UNIT;
	   float x,y;			/* current position */
	   float xinc, yinc;	/* increments */
	   float slope;		/* slope of line */
	   float lastx = -1;	/* last x point plotted */
	   float lasty = -1;	/* last y point plotted */

	   /* first, figure out increments for x and y */
	   if (x2 == x1) {
		  xinc = 0.0;
		  yinc = (y2-y1>0)?dotspace:-dotspace;
	   } else {
		  slope = ((float)y2-y1)/((float)x2-x1);
		  xinc = dotspace / sqrt(1 + slope*slope) * sign(x2-x1);
		  yinc = slope * xinc;
	   }
	   
	   /* now draw the dotted line */
	   /* we take into account where we last placed a dot */
	   for (x=x1 + xinc*(1-LATEX_left), y=y1 + yinc*(1-LATEX_left);
		   (x2-x)*xinc >= 0 && (y2-y)*yinc >= 0; /* same sign or zero */
		   lastx = x, x += xinc, 
		   lasty = y, y += yinc)
		fprintf(outfile, "\\put(%d,%d){%s}\n", (int)x, (int)y, LATEX_DOT);

	   /* how much is left over, as a fraction of dotspace? */
	   if (xinc != 0.0)			/* xinc must be nonzero */
		if (lastx >= 0)
		  LATEX_left = abs(x2 - lastx) / abs(xinc);
		else
		  LATEX_left += abs(x2-x1) / abs(xinc);
	   else
		if (lasty >= 0)
		  LATEX_left = abs(y2 - lasty) / abs(yinc);
		else
		  LATEX_left += abs(y2-y1) / abs(yinc);
    }

    LATEX_needsdot = (LATEX_left > 0);

    LATEX_moved = FALSE;
}

static void
LATEX_flushdot()
{
    if (LATEX_needsdot) 
	 fprintf(outfile, "\\put(%d,%d){%s}\n", 
		    LATEX_posx, LATEX_posy, LATEX_DOT);
    LATEX_needsdot = FALSE;
}

LATEX_arrow(sx,sy, ex,ey, head)
	int sx,sy, ex,ey;
	BOOLEAN head;
{
    best_latex_arrow(sx,sy, ex,ey, 1, head);

    LATEX_posx = ex;
    LATEX_posy = ey;
}

static void best_latex_arrow(sx,sy, ex,ey, who, head)
	int sx,sy, ex,ey;		/* start and end points */
	int who;				/* 1=LATEX, 2=EEPIC */
	BOOLEAN head;
{
    int dx = ex - sx;
    int dy = ey - sy;
    int x, y;				/* points near sx,sy */
    float m;				/* slope of line */
    float arrowslope;		/* slope of arrow */
    float minerror = 0;		/* best-case error */
    struct vslope *slope;	/* one of the slopes */
    struct vslope *bestslope;	/* the slope with min error */
    BOOLEAN horiz;			/* was it the horiz line that was best? */

    /* We try to draw a real arrow (ie, \vector). If we can't get
	* a slope that is close, we draw a bent arrow.
	*/

    if (dx == 0) {
	   /* vertical arrow */
	   fprintf(outfile, "\\put(%d,%d){\\%s(0,%d){%d}}\n",
			 sx, sy, head ? "vector":"line", 
			 sign(ey-sy), abs(ey-sy));
    } else if (dy == 0) {
	   /* horizontal arrow */
	   fprintf(outfile, "\\put(%d,%d){\\%s(%d,0){%d}}\n",
			 sx, sy, head ? "vector":"line",
			 sign(ex-sx), abs(ex-sx));
    } else {
	   /* Slanted arrow. We'll give it a try.
	    * we try to find the closest-slope arrowhead.
	    */
	   bestslope = NULL;
	   minerror = 0; /* to shut up turbo C */
	   m = abs((float)dy/dx); /* the slope we want */
	   for (slope = LATEX_slopes; slope->dx != 0.0; slope++) {
		  /* find the slope of the arrow */
		  arrowslope = (float) slope->dy / slope->dx;
		  if (bestslope == NULL || abs(m-arrowslope) < minerror) {
			 minerror = abs(m-arrowslope);
			 bestslope = slope;
		  }
	   }

	   /* now we have the best slope arrow */
	   /* maybe it's exactly the right slope! */
	   if (minerror == 0.0)	/* unlikely but possible */
		fprintf(outfile, "\\put(%d,%d){\\%s(%d,%d){%d}}\n",
			   sx, sy, head ? "vector" : "line",
			   bestslope->dx*sign(ex-sx), bestslope->dy*sign(ey-sy),
			   abs(ex-sx));
	   else {
		  /* we draw the line the usual way, with thin lines */
#ifdef EMTEX
		  if (emtex) {
			 LATEX_linetype(LATEX_THIN_LINE);
			 EMTEX_solid_line(sx,ex,sy,ey);
		  } else 
#endif
			if (who == 1) {
			   LATEX_linetype(LATEX_THIN_LINE);
			   LATEX_solid_line(sx,ex,sy,ey);
			}
#ifdef EEPIC
			else {
			   EEPIC_move(sx,sy);
			   EEPIC_vector(ex,ey);
			}
#endif /* EEPIC */
		  /* and then draw an arrowhead (a short vector) there */
			if (head)
		  		fprintf(outfile, "\\put(%d,%d){\\vector(%d,%d){0}}\n",
				ex, ey, 
				bestslope->dx*sign(ex-sx), bestslope->dy*sign(ey-sy));
	   }
    }
}


LATEX_put_text(x, y, str)
	int x,y;				/* reference point of string */
	char str[];			/* the text */
{
    /* ignore empty strings */
    if (str[0] == '\0')
	 return;

    fprintf(outfile, "\\put(%d,%d)",x,y);
    switch(latex_angle) {  
 	   case 0: {
		  switch(latex_justify) {
			 case LEFT: {
				fprintf(outfile,
					   "{\\makebox(0,0)[l]{%s}}\n", str);
				break;
			 }
			 case CENTRE: {
				fprintf(outfile,
					   "{\\makebox(0,0){%s}}\n", str);
				break;
			 }
			 case RIGHT: {
				fprintf(outfile,
					   "{\\makebox(0,0)[r]{%s}}\n", str);
				break;
			 }
		  }
		  break;
	   }
	   case 1: {			/* put text in a short stack */
		  switch(latex_justify) {
			 case LEFT: {
				fprintf(outfile,
					   "{\\makebox(0,0)[lb]{\\shortstack{%s}}}\n", str);
				break;
			 }
			 case CENTRE: {
				fprintf(outfile,
					   "{\\makebox(0,0)[l]{\\shortstack{%s}}}\n", str);
				break;
			 }
			 case RIGHT: {
				fprintf(outfile,
					   "{\\makebox(0,0)[lt]{\\shortstack{%s}}}\n", str);
				break;
			 }
		  }
		  break;
	   }	
    }
}



int LATEX_justify_text(mode)
	enum JUSTIFY mode;
{
    latex_justify = mode;
    return (TRUE);
}

int LATEX_text_angle(angle)
	int angle;
{
    /* we can't really write text vertically, but this will 
	  put the ylabel centred at the left of the plot, and
	  then we'll make a \shortstack */
    latex_angle = angle;
    return (TRUE);
}

LATEX_reset()
{
    LATEX_posx = LATEX_posy = 0; /* current position */
    LATEX_moved = TRUE;	/* pen is up after move */
}


#ifdef EMTEX

EMTEX_init()
{
    emtex=TRUE;
    LATEX_posx = LATEX_posy = 0;
    fprintf(outfile, "%% GNUPLOT: LaTeX picture with emtex specials\n");
    fprintf(outfile, "\\setlength{\\unitlength}{%fpt}\n", LATEX_UNIT);
    fprintf(outfile, 
		  "\\ifx\\plotpoint\\undefined\\newsavebox{\\plotpoint}\\fi\n");
    LATEX_linetype(-1);
}


EMTEX_reset()
{
    emtex=FALSE;
    LATEX_posx = LATEX_posy = 0;
}


EMTEX_text()
{
    fprintf(outfile, "\\end{picture}\n");
}


static void
EMTEX_solid_line(x1,x2, y1,y2)
	int x1,x2, y1,y2;
{
    /* emtex special solid line */
	if (LATEX_moved)
		fprintf(outfile, "\\put(%d,%d){\\special{em:moveto}}\n", x1, y1);
	if ( (x1!=x2) || (y1!=y2) )
		fprintf(outfile, "\\put(%d,%d){\\special{em:lineto}}\n", x2, y2);
    LATEX_posx = x2;
    LATEX_posy = y2;
    LATEX_moved = FALSE;
}


#endif /* EMTEX */
