/*
 * Fig2dev: Translate Fig code to various Devices
 * Copyright (c) 1991 by Micah Beck
 * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1988 by Frank Schmuck
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 2015-2023 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies
 * of the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

/*
 * genlatex.c: convert fig to LaTeX
 *
 * Author: Frank Schmuck, Cornell University 6/88
 * Converted from fig2latex 5/89 by Micah Beck
 * Color, rotated text and ISO-chars added by Herbert Bauer 11/91
 * Arc boxes added by C. Scott Ananian 6/99
 *
 * Gabriel Zachmann <Gabriel.Zachmann@gmx.net>:
 *	- add option -F (for the PSTEX output language; it is not really an
 *	  option for latex output language!); this allows you to define the
 *	  font in the latex file before you \input the eepic file (except
 *	  the font size).  You must not mix eepic files generated with and
 *	  without the -F option.  However, I think this could be improved
 *	  easily, I just don't speak TeX well enough to feel confident
 *	  enough to change the way \SetFigFont is defined.
 *
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "genlatex.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <math.h>

#include "fig2dev.h"	/* includes bool.h and object.h */
#include "messages.h"
#include "pi.h"
#include "texfonts.h"


/*
 *  Installation dependent constants:
 *
 *  THINDOT	latex command for generating a dot if line width = \thinlines
 *  THICKDOT	latex command for generating a dot if line width = \thicklines
 *  MIN_LEN	shortest slanted line that latex can produce; shorter lines will
 *		we translated into a sequence of dots generated by \multiput.
 *  THICK_LDOT	latex command for generating the dot for making short slanted
 *		lines if line width = \thinlines
 *  THIN_LDOT	...  if line width = \thicklines
 */

#ifdef NFSS
#define THICKDOT	"\\normalsize."
#define THINDOT		"\\small."
#else
#define THICKDOT	"\\normalsize."
#define THINDOT		"\\small."
#endif
double	THIN_XOFF =	(0.1/72.0);
double	THIN_YOFF =	(0.7/72.0);
double	THICK_XOFF =	(0.4/72.0);
double	THICK_YOFF =	(0.6/72.0);
#define THICK_LDOT	"\\small."
#define THIN_LDOT	"\\tiny."
double	THIN_LXOFF =	(0.1/72.0);
double	THIN_LYOFF =	(0.7/72.0);
double	THICK_LXOFF =	(0.4/72.0);
double	THICK_LYOFF =	(0.6/72.0);
#define MIN_LEN		(13.0/72.27)	/* 13  points */

/*
 *  other constants and macros
 */
#define TOP		840
#define THINLINES	1
#define THICKLINES	2

#define MAXCIRCLEDIA	80
#define MAXCIRCLERAD	((MAXCIRCLEDIA-0.5)/(2*72.27))

#define	SWAP(x,y)	{tmp=x; x=y; y=tmp;}
#define TRANS(x,y)		(*translate_coordinates)(&x,&y)
#define TRANS2(x1,y1,x2,y2)	(*translate_coordinates)(&x1,&y1); \
				(*translate_coordinates)(&x2,&y2)
#define TRANSD(x,y)		(*translate_coordinates_d)(&x,&y)
#define	ABS(x)		(((x) >= 0)? (x): -(x))
#define round4(x)	((round(10000.0*(x))/10000.0))
#define round6(x)	((round(1000000.0*(x))/1000000.0))

char		thindot [] = THINDOT;
char		thickdot[] = THICKDOT;
char		thin_ldot [] = THIN_LDOT;
char		thick_ldot[] = THICK_LDOT;

static bool	select_fontname = true;
static	int	verbose = 0;
double		dash_mag = 1.0;
int		thick_width = 2;
double		tolerance = 2.0;
double		arc_tolerance = 1.0;
void		(*translate_coordinates)(int *x, int *y) = NULL;
void		(*translate_coordinates_d)(double *x, double *y) = NULL;
double		unitlength;
static int	cur_thickness = -1;
double		ldot_diameter = 1.0/72.0;
char		*dot_cmd = thindot;
char		*ldot_cmd = thin_ldot;
double		dot_xoffset;
double		dot_yoffset;
double		ldot_xoffset;
double		ldot_yoffset;
static int	border_margin = 0;
static double	rad2deg = 57.295779513082320877;

/*
 *	struct angle_table line_angles, arrow_angles and get_slope()
 *	from latex_line.c:
 *	    Subroutines for drawing and translating lines for the LaTeX
 *	    picture environment.
 *	Written by Frank Schmuck (schmuck@svax.cs.cornell.edu)
 *	June 1988
 *
 *	The LaTeX picture environment allows generating pictures in standard
 *	LaTeX.	However, some restrictions apply:  lines and vectors (a vector
 *	is a line with an arrow at the end) can only be drawn with a finite
 *	number of slopes.  The available slopes are given by dy/dx where
 *	dx and dy must be integers <= 6 for lines and <= 4 for vectors.
 *
 *	The subroutines in this file are used in fig2latex to approximate
 *	an arbitrary line or vector by a LaTeX line/vector, and in fig to
 *	restrict line drawing to slopes supported by LaTeX.
 */
/*
 *  Tables of line and vector slopes supported by LaTeX
 */
struct angle_table {
	int    x, y;
	double angle;
};

#define N_LINE 25

static struct angle_table line_angles[N_LINE] = {
	{0, 1, 90.0},
	{1, 0,	0.0},
	{1, 1, 45.0},
	{1, 2, 63.434948822922010648},
	{1, 3, 71.565051177077989351},
	{1, 4, 75.963756532073521417},
	{1, 5, 78.690067525979786913},
	{1, 6, 80.537677791974382609},
	{2, 1, 26.565051177077989351},
	{2, 3, 56.309932474020213086},
	{2, 5, 68.198590513648188229},
	{3, 1, 18.434948822922010648},
	{3, 2, 33.690067525979786913},
	{3, 4, 53.130102354155978703},
	{3, 5, 59.036243467926478582},
	{4, 1, 14.036243467926478588},
	{4, 3, 36.869897645844021297},
	{4, 5, 51.340191745909909396},
	{5, 1, 11.309932474020213086},
	{5, 2, 21.801409486351811770},
	{5, 3, 30.963756532073521417},
	{5, 4, 38.659808254090090604},
	{5, 6, 50.194428907734805993},
	{6, 1, 9.4623222080256173906},
	{6, 5, 39.805571092265194006}
};

#define N_ARROW 13

static struct angle_table arrow_angles[N_ARROW] = {
	{0, 1, 90.0},
	{1, 0,	0.0},
	{1, 1, 45.0},
	{1, 2, 63.434948822922010648},
	{1, 3, 71.565051177077989351},
	{1, 4, 75.963756532073521417},
	{2, 1, 26.565051177077989351},
	{2, 3, 56.309932474020213086},
	{3, 1, 18.434948822922010648},
	{3, 2, 33.690067525979786913},
	{3, 4, 53.130102354155978703},
	{4, 1, 14.036243467926478588},
	{4, 3, 36.869897645844021297},
};

static void
translate2(int *xp, int *yp)
{
	*xp = *xp + 1;
	*yp = (double)(TOP - *yp -1);
}

static void
translate2_d(double *xp, double *yp)
{
	*xp = *xp + 1.0;
	*yp = (double)TOP - *yp -1.0;
}

void
genlatex_option(char opt, char *optarg)
{
	int	i;

	switch (opt) {
	case 'a':
		fprintf(stderr, "warning: latex option -a obsolete");
		break;

	case 'b':			/* border margin around figure */
		sscanf(optarg,"%d",&border_margin);
		break;

	case 'd':
		dash_mag = atof(optarg);	/* set dash magnification */
		break;

	case 'F':
		select_fontname = false;
		break;

	case 'f':			/* set default text font */
		for ( i = 1; i <= MAX_FONT; i++ )
			if ( !strcmp(optarg, texfontnames[i]) ) break;

		if ( i > MAX_FONT) {
			fprintf(stderr,
				"warning: non-standard font name %s ignored\n",
				optarg);
		} else {
			texfontnames[0] = texfontnames[i];
#ifdef NFSS
			texfontfamily[0] = texfontfamily[i];
			texfontseries[0] = texfontseries[i];
			texfontshape[0] = texfontshape[i];
#endif
		}
		break;

	case 'l':			/* set thin/thick line threshold */
		thick_width = atoi(optarg);
		break;

	case 'v':
		verbose = 1;		/* verbose mode */
		break;

	case 'G':
	case 'L':
		break;

	default:
		put_msg(Err_badarg, opt, "latex");
		exit(1);
	}
}

void
genlatex_start(F_compound *objects)
{
	int tmp;

	texfontsizes[0] = texfontsizes[1] =
		TEXFONTSIZE(font_size != 0.0? font_size : DEFAULT_FONT_SIZE);

	unitlength = mag/ppi;
	dash_mag /= unitlength*80.0;
	border_margin /= unitlength*72.0;

	/* adjust for any border margin */

	llx -= border_margin;
	lly -= border_margin;
	urx += border_margin;
	ury += border_margin;

	translate_coordinates = translate2;
	translate_coordinates_d = translate2_d;

	TRANS2(llx, lly, urx, ury);
	if (llx > urx) SWAP(llx, urx)
		if (lly > ury) SWAP(lly, ury)

			/* LaTeX start */

			/* print any whole-figure comments prefixed with "%" */
			if (objects->comments) {
				fprintf(tfp,"%%\n");
				print_comments("% ",objects->comments, "");
				fprintf(tfp,"%%\n");
			}

	fprintf(tfp, "\\setlength{\\unitlength}{%lisp}%%\n",
			(long) (round(4736286.72*unitlength)));
	/* define the SetFigFont macro */
	fprintf(tfp, "\\begin{picture}(%d,%d)(%d,%d)\n",
			urx-llx, ury-lly, llx, lly);
}

int
genlatex_end(void)
{
	/* LaTeX ending */
	fprintf(tfp, "\\end{picture}%%\n");

	/* all ok */
	return 0;
}

static void
set_linewidth(int w)
{
	int		latex_w;

	if (w == 0) return;
	/* latex only knows thin lines or thick lines */
	latex_w = (w >= thick_width*THICK_SCALE)? THICKLINES: THINLINES;
	if (latex_w != cur_thickness) {
		cur_thickness = latex_w;
		if (cur_thickness == THICKLINES) {
			fprintf(tfp, "\\thicklines\n");
			dot_cmd = thickdot;
			dot_xoffset = round4(THICK_XOFF/unitlength);
			dot_yoffset = round4(THICK_YOFF/unitlength);
			ldot_cmd = thick_ldot;
			ldot_xoffset = round4(THICK_LXOFF/unitlength);
			ldot_yoffset = round4(THICK_LYOFF/unitlength);
		} else {
			fprintf(tfp, "\\thinlines\n");
			dot_cmd = thindot;
			dot_xoffset = round4(THIN_XOFF/unitlength);
			dot_yoffset = round4(THIN_YOFF/unitlength);
			ldot_cmd = thin_ldot;
			ldot_xoffset = round4(THIN_LXOFF/unitlength);
			ldot_yoffset = round4(THIN_LYOFF/unitlength);
		}
	}
}

/*
 * draw a solid line given latex slope
 */
static void
put_solidline(int x, int y, int sx, int sy, double l, int arrow, double val)
{
	(void)	val;
	double	cosine;		/* cosine of line angle */
	double	dx, dy;
	int	x2, y2, n;

	if (sx) {
		cosine = (double)abs(sx) / sqrt((double)sx*sx + (double)sy*sy);
		x2 = (sx >= 0)? x + round(l): x - round(l);
		y2 = y + round( ((sx>=0)? l: -l) * (double)sy / (double)sx);
	} else {
		cosine = 1.0;
		x2 = x;
		y2 = (sy >= 0)? y + round(l): y - round(l);
	}
	if (sx == 0  ||  sy == 0  ||  l*unitlength >= MIN_LEN) {
		switch (arrow) {
		case 0:  /* simple line */
			fprintf(tfp, "\\put(%3d,%3d){\\line(%2d,%2d)",
					x, y, sx,sy);
			break;
		case 1:  /* forward arrow */
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d)",
					x, y, sx,sy);
			break;
		case -1: /* backward arrow */
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d)",
					x2, y2, -sx,-sy);
			break;
		case 2:  /* double arrow */
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){  0}}\n",
					x,y,-sx,-sy);
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d)",
					x, y, sx, sy);
			break;
		}
		if (l == floor(l))
			fprintf(tfp, "{%3.0f}}\n", l);
		else
			fprintf(tfp, "{%7.3f}}\n", l);
	} else {
		n = 2 * (l/cosine) / (ldot_diameter/unitlength);
		if (n == 0)	/* missing */
			return;
		fprintf(stderr, "Line too short; will do %d dots\n", n);
		dx = l / (double)n;
		if (sx < 0) dx = -dx;
		dy = dx * (double)sy / (double)sx;
		fprintf(tfp, "\\multiput(%3d,%3d)(%.5f,%.5f){%d}"
				"{\\makebox(%.4f,%.4f){%s}}\n", x, y, dx, dy,
				n+1, ldot_xoffset, ldot_yoffset, ldot_cmd);
		if (arrow == 1 || arrow == 2)  /* forward arrow */
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
					x2,y2, sx,sy);
		if (arrow == -1 || arrow == 2) /* backward arrow */
			fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
					x,y, -sx,-sy);
	}
}

/*
 * draw a dashed line given latex slope
 */
static void
put_dashline(int x, int y, int sx, int sy, double l, int arrow, double val)
{
	double	cosine;		/* cosine of line angle */
	double	nd;		/* number of dashes and gaps fitting on line */
	int	n;		/* nd rounded to the nearest odd integer */
	double	dl;		/* actual x-length of each dash */
	double	dg;		/* actual x-length of each gap */
	double	dx, dy;		/* step between dashes */
	int	x2, y2;

	if (sx) {
		cosine = (double)abs(sx) / sqrt((double)sx*sx + (double)sy*sy);
		x2 = (sx >= 0)? x + round(l): x - round(l);
		y2 = y + round( ((sx>=0)? l: -l) * (double)sy / (double)sx );
	} else {
		cosine = 1.0;
		x2 = x;
		y2 = (sy >= 0)? y + round(l): y - round(l);
	}
	/*** compute number of dashes, length of dashes and gaps ***/
	nd = l / (val*dash_mag*cosine);
	n = round((nd + 1.0)/2.0)*2 - 1;
	dl = l / (double)n;
	if (sx	&&  sy	&&  dl*unitlength < MIN_LEN) {
		fprintf(stderr, "Dash too small; using larger dash\n");
		dl = MIN_LEN/unitlength * cosine;
		nd = l / dl;
		n = round((nd + 1.0)/2.0)*2 - 1;
	}
	if (2*dl >= l  ||  (sx	&&  sy	&&  l*unitlength < MIN_LEN)) {
		fprintf(stderr, "Dashed line too short; drawing solid line\n");
		put_solidline (x, y, sx, sy, l, arrow, (double) 0.0);
		return;
	}
	dg = (l - (n/2+1)*dl) / (double)(n/2);
	if (sx) {
		dx = dl+dg;
		if (sx < 0) dx = -dx;
		dy = dx * (double)sy / (double)sx;
	}
	else {
		dx = 0.0;
		dy = dl+dg;
		if (sy < 0) dy = -dy;
	}
	/*** draw dashed line ***/
	fprintf(tfp, "\\multiput(%3d,%3d)(%.5f,%.5f){%d}{\\line(%2d,%2d){%7.3f}}\n",
			x, y, dx, dy, n/2+1, sx, sy, dl);
	/*** draw arrow heads ***/
	if (arrow == 1 || arrow == 2)
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
				x2, y2, sx, sy);
	if (arrow == -1 || arrow == 2)
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
				x, y, -sx, -sy);
}

/*
 * draw a dotted line given latex slope
 */
static void
put_dotline(int x, int y, int sx, int sy, double l, int arrow, double val)
{
	double	cosine;		/* cosine of line angle */
	double	nd;		/* number of dots fitting on line */
	int	n;		/* nd rounded to the nearest integer */
	double	dx, dy;		/* step between dashes */
	int	x2, y2;


	cosine = (sx)? (double)abs(sx) / sqrt((double)sx*sx+(double)sy*sy): 1.0;
	/*** compute step width ***/
	nd = l / (3*val*cosine);
	n = round(nd);
	if (n == 0)
		n = 1;		/* sanity check */
	if (sx) {
		dx = l / (double)n;
		if (sx < 0) dx = -dx;
		dy = dx * (double)sy / (double)sx;
	}
	else {
		dx = 0.0;
		dy = l / (double)n;
		if (sy < 0) dy = -dy;
	}
	/*** draw arrow heads ***/
	if (arrow == 1	||  arrow == 2) {
		/* forward arrow */
		if (sx) {
			x2 = (sx >= 0)? x + round(l): x - round(l);
			y2 = y + round( ((sx>=0)? l: -l) * (double)sy / sx );
		}
		else {
			x2 = x;
			y2 = (sy >= 0)? y + round(l): y - round(l);
		}
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
				x2, y2, sx, sy);
		n--;
	}
	if (arrow == -1  ||  arrow == 2) {
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%2d,%2d){0}}\n",
				x, y, -sx, -sy);
		x = round(x + dx);
		y = round(y + dy);
		n--;
	}
	/*** draw dotted line ***/
	fprintf(tfp, "\\multiput(%3d,%3d)(%.5f,%.5f){%d}"
			"{\\makebox(%.4f,%.4f){%s}}\n", x, y, dx, dy, n+1,
			dot_xoffset, dot_yoffset, dot_cmd);
}

void
get_slope(int dx, int dy, int *sxp, int *syp, int arrow)
/*
 *  Find the LaTeX line slope that is closest to the one given by dx, dy.
 *  Result is returned in *sxp, *syp.  If (arrow != 0) the closest LaTeX
 *  vector slope is selected.
 */
{
	double angle;
	int    i, s, max;
	double d, d1;
	struct angle_table *st;

	if (dx == 0) {
		*sxp = 0;
		*syp = (dy < 0)? -1: 1;
		return;
	}
	angle = atan((double)abs(dy) / (double)abs(dx)) * rad2deg;
	if (arrow) {
		st = arrow_angles;
		max = N_ARROW;
	} else {
		st = line_angles;
		max = N_LINE;
	}
	s = 0;
	d = 9.9e9;
	for (i = 0; i < max; i++) {
		d1 = fabs(angle - st[i].angle);
		if (d1 < d) {
			s = i;
			d = d1;
		}
	}
	*sxp = st[s].x;
	if (dx < 0) *sxp = -*sxp;
	*syp = st[s].y;
	if (dy < 0) *syp = -*syp;
}

static void
single_line(int x1, int y1, int x2, int y2, int arrow, int style, double val)
{
	int    dx, dy, sx, sy;
	double l, m, deviation;

	TRANS2(x1, y1, x2, y2);
	dx = x2-x1;
	dy = y2-y1;
	/*** compute direction vector ***/
	get_slope(dx, dy, &sx, &sy, arrow);
	/*** compute line length in x-direction ***/
	if (sx == 0) {
		l = (double)abs(dy);
	} else {
		m = (double)abs(sy) / (double)abs(sx);
		l = ((double)abs(dx) + m*(double)abs(dy)) / (1.0 + m*m);
		deviation = fabs(l-abs(dx)) + fabs(m*l-abs(dy));
		if (deviation > tolerance)
			fprintf(stderr, "Not a LaTeX slope (%d, %d), "
					"deviation %.1f pixels\n",
					dx, dy, deviation);
	}
	l = round4(l);
	/*** output letex command ***/
	switch (style) {
	case SOLID_LINE:
		put_solidline(x1, y1, sx, sy, l, arrow, val);
		break;
	case DASH_LINE:
		put_dashline(x1, y1, sx, sy, l, arrow, val);
		break;
	case DOTTED_LINE:
		put_dotline(x1, y1, sx, sy, l, arrow, val);
		break;
	default:
		fprintf(stderr, "Unknown line style -- approximating "
				"with solid lines\n");
		break;
	}
}

/*
 * draw arc box
 */
static void
put_arc_box(int llx, int lly, int urx, int ury, int radius, int style,
	    double val)
{
	int	radius2= 2*radius;
	double	swidth = (double)(urx-llx-radius2);
	double	sheight= (double)(ury-lly-radius2);
	void (*put_line)(int x, int y, int sx, int sy,
			double l, int arrow, double val);

	switch (style) {
	case SOLID_LINE:
		put_line = put_solidline;
		break;
	case DASH_LINE:
		fprintf(stderr,
			"Dashed arc boxes approximated with solid arcs\n");
		put_line = put_dashline;
		break;
	case DOTTED_LINE:
		fprintf(stderr,
			"Dotted arc boxes approximated with solid arcs\n");
		put_line = put_dotline;
		break;
	default:
		fprintf(stderr,
			"Unknown line style -- approximating with solid lines\n");
		break;
	}
	put_line = put_solidline;
	fprintf(tfp,
			"\\put(%3d,%3d){\\oval(%3d,%3d)[bl]}\n"
			"\\put(%3d,%3d){\\oval(%3d,%3d)[tl]}\n"
			"\\put(%3d,%3d){\\oval(%3d,%3d)[br]}\n"
			"\\put(%3d,%3d){\\oval(%3d,%3d)[tr]}\n",
			llx+radius,lly+radius,radius2,radius2,
			llx+radius,ury-radius,radius2,radius2,
			urx-radius,lly+radius,radius2,radius2,
			urx-radius,ury-radius,radius2,radius2);
	put_line (llx+radius, lly, 1, 0, swidth, 0, val);
	put_line (llx+radius, ury, 1, 0, swidth, 0, val);
	put_line (llx, lly+radius, 0, 1, sheight, 0, val);
	put_line (urx, lly+radius, 0, 1, sheight, 0, val);
	return;
}

/*
 * draw box
 */
static void
put_box(int llx, int lly, int urx, int ury, int style, double val)
{
	int	dlen;

	switch (style) {
	case SOLID_LINE:
		fprintf(tfp, "\\put(%3d,%3d){\\framebox(%d,%d){}}\n",
				llx, lly, urx-llx, ury-lly);
		break;
	case DASH_LINE:
		dlen = round(val*dash_mag);
		fprintf(tfp, "\\put(%3d,%3d){\\dashbox{%d}(%d,%d){}}\n",
				llx, lly, dlen, urx-llx, ury-lly);
		break;
	case DOTTED_LINE:
		put_dotline (llx, lly, 1, 0, (double)(urx-llx), 0, val);
		put_dotline (llx, ury, 1, 0, (double)(urx-llx), 0, val);
		put_dotline (llx, lly, 0, 1, (double)(ury-lly), 0, val);
		put_dotline (urx, lly, 0, 1, (double)(ury-lly), 0, val);
		break;
	default:
		fprintf(stderr, "Unknown line style -- approximating "
				"with solid lines\n");
		break;
	}
	return;
}


void
genlatex_line(F_line *l)
{
	F_point		*p, *q;
	int		x, y, llx, lly, urx, ury, arrow;

	if (verbose)
		fprintf(tfp, "%%\n%% Fig POLYLINE object\n%%\n");

	/* print any comments prefixed with "%" */
	print_comments("% ",l->comments, "");

	set_linewidth(l->thickness);
	set_color(l->pen_color);

	p = l->points;
	q = p->next;

	if (q == NULL) { /* A single point line */
		x = p->x; y = p->y;
		TRANS(x, y);
		fprintf(tfp, "\\put(%3d,%3d){\\makebox(%.4f,%.4f){%s}}\n",
				x, y, dot_xoffset, dot_yoffset, dot_cmd);
		reset_color(l->pen_color);
		return;
	}

	/* a box, perhaps with rounded corners */
	if (l->type == T_BOX || l->type == T_ARC_BOX) {
		x = p->x; y = p->y;
		TRANS(x, y);
		llx = urx = x;
		lly = ury = y;
		while (q != NULL) {
			x = q->x; y = q->y;
			TRANS(x, y);
			if (x < llx) llx = x;
			if (y < lly) lly = y;
			if (x > urx) urx = x;
			if (y > ury) ury = y;
			q = q->next;
		}
		if (l->type == T_ARC_BOX)
			put_arc_box (llx, lly, urx, ury, l->radius, l->style,
					l->style_val);
		else
			put_box (llx, lly, urx, ury, l->style, l->style_val);
		reset_color(l->pen_color);
		return;
	}

	while (q != NULL) {
		arrow = 0;
		if (l->for_arrow  &&  q->next == NULL)
			arrow = 1;
		if (l->back_arrow  &&  p == l->points)
			arrow = (arrow)? 2: -1;
		single_line(p->x, p->y, q->x, q->y, arrow, l->style,
				l->style_val);
		p = q;
		q = q->next;
	}

	if (l->fill_style != UNFILLED)
		fprintf(stderr, "Line area fill not implemented\n");
	reset_color(l->pen_color);
}


void
genlatex_spline(F_spline *s)
{
	(void)	s;
	fprintf(stderr, "Can't generate spline; omitting object\n");
}

void
genlatex_ellipse(F_ellipse *e)
{
	int  x, y, d, dx, dy;

	if (verbose)
		fprintf(tfp, "%%\n%% Fig ELLIPSE\n%%\n");

	/* print any comments prefixed with "%" */
	print_comments("% ",e->comments, "");

	set_color(e->pen_color);

	set_linewidth(e->thickness);
	switch (e->style) {
	case SOLID_LINE:
		break;
	case DASH_LINE:
		fprintf(stderr, "Dashed circles and elipses not supported\n");
		break;
	case DOTTED_LINE:
		fprintf(stderr, "Dotted circles and elipses not supported\n");
		break;
	default:
		fprintf(stderr, "Unknown line style -- approximating with "
				"solid lines\n");
		break;
	}

	x = e->center.x;
	y = e->center.y;
	TRANS(x, y);
	if ((e->type == T_CIRCLE_BY_RAD || e->type == T_CIRCLE_BY_DIA)
			&& e->radiuses.x*unitlength <= MAXCIRCLERAD) {

		d = 2 * e->radiuses.x;
		if (e->fill_style == BLACK_FILL)
			fprintf(tfp, "\\put(%3d,%3d){\\circle*{%d}}\n",
					x, y, d);
		else {
			fprintf(tfp, "\\put(%3d,%3d){\\circle{%d}}\n", x, y, d);
			if (e->fill_style != UNFILLED)
				fprintf(stderr,
					"Circle area fill not implemented\n");
		}

	} else {
		dx = 2 * e->radiuses.x;
		dy = 2 * e->radiuses.y;
		fprintf(tfp, "\\put(%3d,%3d){\\oval(%d,%d)}\n", x, y, dx, dy);
		if (e->fill_style != UNFILLED)
			fprintf(stderr, "Ellipse area fill not implemented\n");
	}
	reset_color(e->pen_color);

}

void
genlatex_text(F_text *t)
{
	int		x, y;
	const char	*tpos;

	if (verbose)
		fprintf(tfp, "%%\n%% Fig TEXT object\n%%\n");

	/* print any comments prefixed with "%" */
	print_comments("% ",t->comments, "");

	x = t->base_x;
	y = t->base_y;
	TRANS(x, y);

	switch (t->type) {

	case T_LEFT_JUSTIFIED:
	case DEFAULT:
		tpos = "[lb]";
		break;

	case T_CENTER_JUSTIFIED:
		tpos = "[b]";
		break;

	case T_RIGHT_JUSTIFIED:
		tpos = "[rb]";
		break;

	default:
		fprintf(stderr, "Text incorrectly positioned\n");
		tpos = "[lb]";	/* make left in this case */
	}

	fprintf(tfp, "\\put(%3d,%3d){", x, y);

#ifdef LATEX2E_GRAPHICS
	if(t->angle)
		fprintf(tfp, "\\rotatebox{%.1f}{", t->angle*180/M_PI);
#endif

	/* smash is used to position text at baseline */
	fprintf(tfp, "\\makebox(0,0)%s{\\smash{", tpos);

	select_font(t, true, select_fontname, false);

	set_color(t->color);

	put_string(t->cstring, t->font, special_text(t));

	reset_color(t->color);

#ifdef LATEX2E_GRAPHICS
	if(t->angle)
		fprintf(tfp, "}");
#endif
	fprintf(tfp, "}}}\n");
}

static void
put_quarter(F_pos p1, F_pos p2, int q)
/*
 *  Draw quarter oval from p1 to p2 in quadrant q
 */
{
	char	*opt;
	int	px, py, dx, dy;

	dx = 2*ABS(p1.x - p2.x);
	dy = 2*ABS(p1.y - p2.y);
	if (dx == 0  &&  dy == 0)
		return;
	switch (q) {
	case 0:
		px = MIN(p1.x, p2.x);
		py = MIN(p1.y, p2.y);
		opt = "tr";
		break;
	case 1:
		px = MAX(p1.x, p2.x);
		py = MIN(p1.y, p2.y);
		opt = "tl";
		break;
	case 2:
		px = MAX(p1.x, p2.x);
		py = MAX(p1.y, p2.y);
		opt = "bl";
		break;
	case 3:
		px = MIN(p1.x, p2.x);
		py = MAX(p1.y, p2.y);
		opt = "br";
		break;
	}
	fprintf(tfp, "\\put(%3d,%3d){\\oval(%3d,%3d)[%s]}\n",
			px, py, dx, dy, opt);
}

void
genlatex_arc(F_arc *a)
/*
 *  Approximates an arc by a sequence of quarter ovals.
 *
 *  Example:
 *
 *	Arc with center at (0,0) and radius 10 from +45 degree to +225 degree
 *	(arc from p1 = (7.07, 7.07) to p2 = (-7.07, -7.07) counterclockwise).
 *	This arc is approximated by three quarter ovals, one for each quadrant
 *	through which the arc goes:
 *
 *	 1. quarter oval from p1 to the intersection of arc and y-axis,
 *	    i.e., from (7.07, 7.07) to (0, 10) in quadrant 0
 *
 *		\put(0, 7.07){\oval(14.14, 5.86)[tr]}
 *
 *	 2. quarter oval from intersection arc/y-axis to intersection arc/x-axis
 *	    i.e., from (0, 10) to (-10, 0) in quadrant 1
 *
 *		\put(0, 0){\oval(20,20)[tl]}
 *
 *	 3. quarter oval from p1 to the intersection of arc and y-axis,
 *	    i.e., from (-10, 0) to (-7.07, -7.07) in quadrant 2
 *
 *		\put(-7.07, 0){\oval(5.86, 14.14)[bl]}
 */
{
	F_pos		p1, p2, pq[4];
	double		cx, cy;
	double		v1x, v1y, v2x, v2y;
	double		r, angle1, angle2;
	int		q1, q2;
	int		p1_arrow, p2_arrow;
	static char	*ad1[4] = { " 0,-1", " 1, 0", " 0, 1", "-1, 0" };
	static char	*ad2[4] = { "-1, 0", " 0,-1", " 1, 0", " 0, 1" };

	if (verbose)
		fprintf(tfp, "%%\n%% Fig ARC object\n%%\n");

	/* print any comments prefixed with "%" */
	print_comments("% ",a->comments, "");

	set_linewidth(a->thickness);
	set_color(a->pen_color);
	switch (a->style) {
	case SOLID_LINE:
		break;
	case DASH_LINE:
		fprintf(stderr, "Dashed arcs not supported\n");
		break;
	case DOTTED_LINE:
		fprintf(stderr, "Dotted arcs not supported\n");
		break;
	default:
		fprintf(stderr, "Unknown line style -- approximating with "
				"solid lines\n");
		break;
	}
	if (a->direction == 1) {
		p1 = a->point[0];
		p2 = a->point[2];
		p1_arrow = (a->back_arrow != NULL);
		p2_arrow = (a->for_arrow != NULL);
	} else {
		p1 = a->point[2];
		p2 = a->point[0];
		p1_arrow = (a->for_arrow != NULL);
		p2_arrow = (a->back_arrow != NULL);
	}
	cx = a->center.x;
	cy = a->center.y;
	TRANS2(p1.x, p1.y, p2.x, p2.y);
	TRANSD(cx, cy);
	/*** compute vectors and angles from arc center to p1, p2 ***/
	v1x = (double)p1.x - cx;
	v1y = (double)p1.y - cy;
	v2x = (double)p2.x - cx;
	v2y = (double)p2.y - cy;
	angle1 = atan2(v1y, v1x) * rad2deg;
	angle2 = atan2(v2y, v2x) * rad2deg;
	if (angle1 < 0.0)
		angle1 += 360.0;
	if (angle2 < 0.0)
		angle2 += 360.0;
	/* compute arc radius */
	r = sqrt(v1x*v1x+v1y*v1y);
	/*** compute intersection of arc with x and y axis (origin at cx, cy) */
	pq[0].x = round(cx);
	pq[0].y = round(cy + r);
	pq[1].x = round(cx - r);
	pq[1].y = round(cy);
	pq[2].x = round(cx);
	pq[2].y = round(cy - r);
	pq[3].x = round(cx + r);
	pq[3].y = round(cy);
	/*** compute in which quadrants p1 and p2 are located ***/
	q1 = (int)(angle1/90.0);
	q2 = (int)(angle2/90.0);
	if (fabs(angle1 - 90.0*q1) > arc_tolerance
			|| fabs(angle2 - 90.0*q2) > arc_tolerance)
		fprintf(stderr, "Approximating arc by ovals\n");
	/*** Draw arc ***/
	if (a->type == T_OPEN_ARC && a->thickness != 0 && p1_arrow)
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%s){0}}\n",
				p1.x, p1.y, ad1[q1]);
	while (q1 != q2) {
		put_quarter(p1, pq[q1], q1);
		p1 = pq[q1];
		q1 = (q1 + 1) % 4;
	}
	put_quarter(p1, p2, q1);
	if (a->type == T_OPEN_ARC && a->thickness != 0 && p2_arrow)
		fprintf(tfp, "\\put(%3d,%3d){\\vector(%s){0}}\n",
				p2.x, p2.y, ad2[q2]);

	if (a->fill_style != UNFILLED)
		fprintf(stderr, "Arc area fill not implemented\n");
	reset_color(a->pen_color);
}

#define  MAXCOLORS 32

/* need this for communication between color routines. Sorry */
static int lastcolor=-1;

void
set_color(int col)
{
	static char *colors[] = {
		"0,0,0",    /* black */
		"0,0,1",    /* blue */
		"0,1,0",    /* green */
		"0,1,1",    /* cyan */
		"1,0,0",    /* red */
		"1,0,1",    /* magenta */
		"1,1,0",    /* yellow */
		"1,1,1",    /* white */
		"0,0,.56",	/* dk blue */
		"0,0,.69",	/* md blue */
		"0,0,.82",	/* lt blue */
		".53,.81,1",	/* blue4 */
		"0,.56,0",	/* dk green */
		"0,.69,0",	/* md green */
		"0,.82,0",	/* lt green */
		"0,.56,.56",	/* dk cyan */
		"0,.69,.69",	/* md cyan */
		"0,.82,.82",	/* lt cyan */
		".56,0,0",	/* dk red */
		".69,0,0",	/* md red */
		".82,0,0",	/* lt red */
		".56,0,.56",	/* dk magenta */
		".69,0,.69",	/* md magenta */
		".82,0,.82",	/* lt magenta */
		".5,.17,0",	/* dk brown */
		".63,.25,0",	/* md brown1 */
		".75,.38,0",	/* md brown1 */
		"1,.5,.5",	/* dk pink */
		"1,.63,.63",	/* md pink1 */
		"1,.75,.75",	/* md pink2 */
		"1,.88,.88",	/* lt pink */
		"1,.84,0",	/* gold */
	};


#ifdef LATEX2E_GRAPHICS
	if (col != -1) {
		/* we do not support nested colors, although LaTeX would */
		if (lastcolor == -1)
			fprintf(tfp, "{");
		/* switch to (other) color */
		if (col < NUM_STD_COLS)
			fprintf(tfp, "\\color[rgb]{%s}",colors[col]);
		else
			fprintf(tfp, "\\color[rgb]{%.3f,%.3f,%.3f}",
					user_colors[col-NUM_STD_COLS].r/255.0,
					user_colors[col-NUM_STD_COLS].g/255.0,
					user_colors[col-NUM_STD_COLS].b/255.0);
		lastcolor = col;
	}
#endif
	return;
}

void
reset_color(int col)
{
#ifdef LATEX2E_GRAPHICS
	if (col != -1 && col < NUM_STD_COLS + MAX_USR_COLS) {
		/* end using the last color */
		fprintf(tfp, "}%%\n");
	}
	/* make sure the color indicator is reset */
	lastcolor = -1;
#endif
	return;
}


struct driver dev_latex = {
	genlatex_option,
	genlatex_start,
	gendev_nogrid,
	genlatex_arc,
	genlatex_ellipse,
	genlatex_line,
	genlatex_spline,
	genlatex_text,
	genlatex_end,
	EXCLUDE_TEXT
};
