The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* Hey Emacs this is -*- C -*-
 * 
 * $Id: pstricks.trm,v 1.20 2002/07/26 16:42:29 mikulik Exp $
 */

/* GNUPLOT - pstricks.trm */

/*[
 * Copyright 1990 - 1993, 1998
 *
 * 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 complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/*
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *   The PSTricks macros for LaTeX.
 *
 * AUTHORS
 *   David Kotz
 *
 *   Raymond Toy	toy@soho.crd.ge.com
 *	Modified the eepic.trm file to use PSTricks macros instead.
 *
 *	20 Mar 93:
 *		Utilized many suggestions from Gisli Ottarsson
 *		(gisli@liapunov.eecs.umich.edu) to create a new version.
 *		Should also work with TeX as well as LaTeX.
 *
 *		If you have PSTricks version 0.91, #define OLD_PST to
 *		get the right dots.
 *
 *		Added a really ugly hack (enabled by default) to print
 *		"nice" numbers for axis labels.  This should really be at
 *		a higher level in the code, but I'm lazy right now.
 *
 * send your comments or suggestions to (info-gnuplot@dartmouth.edu).
 *
 */

/*
 *  This file contains the PSTricks terminal driver, intended for use with the
 *  pstricks.sty macro package for LaTeX. This is an alternative to the
 *  eepic and latex driver. You need pstricks.sty, and, of course, a printer
 *  that understands PostScript.  Ghostscript understands Postscript too.
 *
 *  PSTricks is available via anonymous ftp from the /pub directory
 *  at Princeton.EDU.  This driver definitely does not come close to
 *  using the full capability of the PSTricks package.
 */
/*
 * adapted to the new terminal layout by Stefan Bodewig (Dec. 1995)
 */

#include "driver.h"

#ifdef TERM_REGISTER
register_term(pstricks)
#endif

#ifdef TERM_PROTO
TERM_PUBLIC void PSTRICKS_options __PROTO((void));
TERM_PUBLIC void PSTRICKS_init __PROTO((void));
TERM_PUBLIC void PSTRICKS_graphics __PROTO((void));
TERM_PUBLIC void PSTRICKS_text __PROTO((void));
TERM_PUBLIC void PSTRICKS_linetype __PROTO((int linetype));
TERM_PUBLIC void PSTRICKS_move __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void PSTRICKS_point __PROTO((unsigned int x, unsigned int y, int number));
TERM_PUBLIC void PSTRICKS_vector __PROTO((unsigned int ux, unsigned int uy));
TERM_PUBLIC void PSTRICKS_arrow __PROTO((unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, TBOOLEAN head));
TERM_PUBLIC void PSTRICKS_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
TERM_PUBLIC int PSTRICKS_justify_text __PROTO((enum JUSTIFY mode));
TERM_PUBLIC int PSTRICKS_text_angle __PROTO((int ang));
TERM_PUBLIC void PSTRICKS_reset __PROTO((void));

#define PSTRICKS_XMAX 10000.0
#define PSTRICKS_YMAX 10000.0

#define PSTRICKS_HTIC	150
#define PSTRICKS_VTIC	200
#define PSTRICKS_HCHAR	160
#define PSTRICKS_VCHAR	420
#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY
static void PSTRICKS_endline __PROTO((void));
static char *PSTRICKS_hack_text __PROTO((const char *s));

static float PSTRICKS_posx;
static float PSTRICKS_posy;
static enum JUSTIFY PSTRICKS_justify = LEFT;
static int PSTRICKS_angle = 0;

#define	PSTRICKS_TINY_DOT	0.00025		/* A tiny dot */

/* POINTS */
#define PSTRICKS_POINT_TYPES 12	/* we supply more point types */

static const char *PSTRICKS_points[] = {
    "\\PST@Diamond",
    "\\PST@Plus",
    "\\PST@Square",
    "\\PST@Cross",
    "\\PST@Circle",
    "\\PST@Triangle",
    "\\PST@Pentagon",
    "\\PST@Filldiamond",
    "\\PST@Fillsquare",
    "\\PST@Filltriangle",
    "\\PST@Fillcircle",
    "\\PST@Fillpentagon"
};

/* LINES */
#define PSTRICKS_NUMLINES 6	/* number of linetypes below */

static const char *PSTRICKS_lines[] = {
    "\\PST@Border",
    "\\PST@Axes",
    "\\PST@Solid",
    "\\PST@Dashed",
    "\\PST@Dotted",
    "\\PST@LongDash"
};

/* current line type */
static int PSTRICKS_type;

/* are we in the middle of a line */
static TBOOLEAN PSTRICKS_inline = FALSE;

/* terminate any line in progress */
static void PSTRICKS_endline __PROTO((void));

/* number of points in line so far */
static int PSTRICKS_linecount = 0;

/* max value for linecount */
#define PSTRICKS_LINEMAX 100

/*
 * Handle options
 */

static int PST_hack_text = TRUE;	/* Hack text on */
static int PST_unit_plot = FALSE;	/* Unit-sized plot off */

TERM_PUBLIC void
PSTRICKS_options()
{
    if (!END_OF_COMMAND) {
	if (almost_equals(c_token, "no$hacktext")) {
	    PST_hack_text = FALSE;
	    c_token++;
	} else if (almost_equals(c_token, "u$nit")) {
	    PST_unit_plot = TRUE;
	    c_token++;
	}
    }
}

TERM_PUBLIC void
PSTRICKS_init()
{
    PSTRICKS_posx = PSTRICKS_posy = 0;
    PSTRICKS_linetype(-1);
    fputs("% GNUPLOT: LaTeX picture using PSTRICKS macros\n", gpoutfile);
}


TERM_PUBLIC void
PSTRICKS_graphics()
{
    fputs("\
% Define new PST objects, if not already defined\n\
\\ifx\\PSTloaded\\undefined\n\
\\def\\PSTloaded{t}\n\
\\psset{arrowsize=.01 3.2 1.4 .3}\n\
\\psset{dotsize=.01}\n\
\\catcode`@=11\n\n", gpoutfile);

    /* Define line type objects */
    fputs("\
\\newpsobject{PST@Border}{psline}{linewidth=.0015,linestyle=solid}\n\
\\newpsobject{PST@Axes}{psline}{linewidth=.0015,linestyle=dotted,dotsep=.004}\n\
\\newpsobject{PST@Solid}{psline}{linewidth=.0015,linestyle=solid}\n\
\\newpsobject{PST@Dashed}{psline}{linewidth=.0015,linestyle=dashed,dash=.01 .01}\n\
\\newpsobject{PST@Dotted}{psline}{linewidth=.0025,linestyle=dotted,dotsep=.008}\n\
\\newpsobject{PST@LongDash}{psline}{linewidth=.0015,linestyle=dashed,dash=.02 .01}\n", gpoutfile);

    /* Define point objects */

#ifdef	OLD_PST
    /* PSTricks version 0.91 had x and diamond dot types */
    fputs("\
\\newpsobject(PST@Diamond}{psdots}{linewidth=.001,linestyle=solid,dotstyle=diamond}\n\
\\newpsobject(PST@Filldiamond}{psdots}{linewidth=.001,linestyle=solid,dotstyle=diamond*}\n\
\\newpsobject{PST@Cross}{psdots}{linewidth=.001,linestyle=solid,dotstyle=x}\n", gpoutfile);
#else
    /* Newer versions use rotated plus and square to get the x and diamond dots */
    fputs("\
\\newpsobject{PST@Diamond}{psdots}{linewidth=.001,linestyle=solid,dotstyle=square,dotangle=45}\n\
\\newpsobject{PST@Filldiamond}{psdots}{linewidth=.001,linestyle=solid,dotstyle=square*,dotangle=45}\n\
\\newpsobject{PST@Cross}{psdots}{linewidth=.001,linestyle=solid,dotstyle=+,dotangle=45}\n", gpoutfile);
#endif

    fputs("\
\\newpsobject{PST@Plus}{psdots}{linewidth=.001,linestyle=solid,dotstyle=+}\n\
\\newpsobject{PST@Square}{psdots}{linewidth=.001,linestyle=solid,dotstyle=square}\n\
\\newpsobject{PST@Circle}{psdots}{linewidth=.001,linestyle=solid,dotstyle=o}\n\
\\newpsobject{PST@Triangle}{psdots}{linewidth=.001,linestyle=solid,dotstyle=triangle}\n\
\\newpsobject{PST@Pentagon}{psdots}{linewidth=.001,linestyle=solid,dotstyle=pentagon}\n\
\\newpsobject{PST@Fillsquare}{psdots}{linewidth=.001,linestyle=solid,dotstyle=square*}\n\
\\newpsobject{PST@Fillcircle}{psdots}{linewidth=.001,linestyle=solid,dotstyle=*}\n\
\\newpsobject{PST@Filltriangle}{psdots}{linewidth=.001,linestyle=solid,dotstyle=triangle*}\n\
\\newpsobject{PST@Fillpentagon}{psdots}{linewidth=.001,linestyle=solid,dotstyle=pentagon*}\n", gpoutfile);

    /* Define arrow object */
    fputs("\
\\newpsobject{PST@Arrow}{psline}{linewidth=.001,linestyle=solid}\n\
\\catcode`@=12\n\n\
\\fi\n", gpoutfile);

    /* Set the scaled plot size, if it's not a unit plot */
    if (!PST_unit_plot) {
	fputs("\\psset{unit=5.0in,xunit=5.0in,yunit=3.0in}\n", gpoutfile);
    }
    /* HBB 20001027: fix bounding box bug by letting the currently
     * active 'size' and 'offset' setting influence the area used by
     * the picture environment */
    fprintf(gpoutfile, "\
\\pspicture(%f,%f)(%f,%f)\n\
\\ifx\\nofigs\\undefined\n\
\\catcode`@=11\n\n",
	    xoffset,
	    yoffset,
	    (xoffset + xsize),
	    (yoffset + ysize)
    );
}


TERM_PUBLIC void
PSTRICKS_text()
{
    PSTRICKS_endline();
    fputs("\
\\catcode`@=12\n\
\\fi\n\
\\endpspicture\n", gpoutfile);
}


TERM_PUBLIC void
PSTRICKS_linetype(linetype)
int linetype;
{
    PSTRICKS_endline();

    if (linetype >= PSTRICKS_NUMLINES - 2)
	linetype %= (PSTRICKS_NUMLINES - 2);

    PSTRICKS_type = linetype;
}



TERM_PUBLIC void
PSTRICKS_move(x, y)
unsigned int x;
unsigned int y;
{
    PSTRICKS_endline();

    PSTRICKS_posx = x / PSTRICKS_XMAX;
    PSTRICKS_posy = y / PSTRICKS_YMAX;
}


TERM_PUBLIC void
PSTRICKS_point(x, y, number) /* version of line_and_point */
unsigned x;
unsigned y;
int number;
{
    PSTRICKS_move(x, y);

    /* Print the character defined by 'number'; number < 0 means
       to use a dot, otherwise one of the defined points. */

    if (number < 0) {
	fprintf(gpoutfile, "\\qdisk(%.4f,%.4f){%.4f}\n",
		x / PSTRICKS_XMAX,
		y / PSTRICKS_YMAX,
		PSTRICKS_TINY_DOT);
    } else {
	fprintf(gpoutfile, "%s(%.4f,%.4f)\n",
		PSTRICKS_points[number % PSTRICKS_POINT_TYPES],
		x / PSTRICKS_XMAX,
		y / PSTRICKS_YMAX);
    }
}


TERM_PUBLIC void
PSTRICKS_vector(ux, uy)
unsigned ux;
unsigned uy;
{
    if (!PSTRICKS_inline) {
	PSTRICKS_inline = TRUE;

	/* Start a new line. This depends on line type */
	fprintf(gpoutfile, "%s(%.4f,%.4f)\n",
		PSTRICKS_lines[PSTRICKS_type + 2],
		PSTRICKS_posx, PSTRICKS_posy);
	PSTRICKS_linecount = 1;
    } else {
	/*
	 * Even though we are in middle of a path,
	 * we may want to start a new path command.
	 * If they are too long then latex will choke.
	 */
	if (PSTRICKS_linecount++ >= PSTRICKS_LINEMAX) {
	    /* fprintf(gpoutfile, "\n"); */
	    fprintf(gpoutfile, "%s(%.4f,%.4f)\n",
		    PSTRICKS_lines[PSTRICKS_type + 2],
		    PSTRICKS_posx, PSTRICKS_posy);
	    PSTRICKS_linecount = 1;
	}
    }
    PSTRICKS_posx = ux / PSTRICKS_XMAX;
    PSTRICKS_posy = uy / PSTRICKS_YMAX;
    fprintf(gpoutfile, "(%.4f,%.4f)\n", PSTRICKS_posx, PSTRICKS_posy);
}

static void
PSTRICKS_endline()
{
    if (PSTRICKS_inline) {
	putc('\n', gpoutfile);
	PSTRICKS_inline = FALSE;
    }
}


TERM_PUBLIC void
PSTRICKS_arrow(sx, sy, ex, ey, head)
unsigned int sx;
unsigned int sy;
unsigned int ex;
unsigned int ey;
TBOOLEAN head;
{
    fprintf(gpoutfile, "\\PST@Arrow%s(%.4f,%.4f)(%.4f,%.4f)\n",
	    head ? "{->}" : "",
	    sx / PSTRICKS_XMAX,
	    sy / PSTRICKS_YMAX,
	    ex / PSTRICKS_XMAX,
	    ey / PSTRICKS_YMAX);

    PSTRICKS_posx = ex / PSTRICKS_XMAX;
    PSTRICKS_posy = ey / PSTRICKS_YMAX;
}

/*
 * A really ugly hack!!!
 *
 * This function takes an input string and hacks it up.  If the
 * input string starts with a number, it converts the number into a
 * TeX style number including exponential notation.  Thus, if
 * the input is the string "3.14159e3 is a number", then
 * the output is "$3.14159\cdot 10^{3}$ is a number", so that TeX
 * will produce something nice.
 *
 * This is basically meant for producing axis labels that look nice.
 *
 * What a hack!
 */


static char *
PSTRICKS_hack_text(s)
const char *s;
{
    double value;
    char *ends;
    static char hack[BUFSIZ];

    /*
     * Does the string start with a number?
     */

    value = strtod(s, &ends);

    if (s == ends) {
	/*
	 * This doesn't start a number, so just copy the string over
	 */

	strcpy(hack, s);
    } else {
	char *ptr;

	/*
	 * We have a number!  Check to see if the number
	 * is in scientific notation
	 */

	safe_strncpy(hack, s, ends - s + 1);
	/* hack[ends - s] = '\0'; */

	ptr = strchr(hack, 'e');
	if (ptr == NULL) {
	    ptr = strchr(hack, 'E');
	}
	if (ptr != NULL) {
	    /*
	     * Exponential notation!  Let's get the mantissa and exponent separately
	     */

	    double man_val;
	    int expo_val;

	    *ptr = NUL;

	    man_val = atof(hack);
	    expo_val = atoi(ptr + 1);

	    if (man_val == 0) {
		sprintf(hack, "0");
	    } else if (man_val == 1) {
		sprintf(hack, "$10^{%d}$", expo_val);
	    } else if (man_val == (int) man_val) {
		if (expo_val == 1) {
		    sprintf(hack, "$%d$", (int) man_val);
		} else {
		    sprintf(hack, "$%d \\times 10^{%d}$", (int) man_val, expo_val);
		}
	    } else {
		if (expo_val == 1) {
		    sprintf(hack, "$%f$", man_val);
		} else {
		    sprintf(hack, "$%f \\times 10^{%d}$", man_val, expo_val);
		}
	    }
	}
	/*
	 * Copy anything that's left of the string
	 */

	strcat(hack, ends);
    }

    return hack;
}

TERM_PUBLIC void
PSTRICKS_put_text(x, y, str)
unsigned int x;
unsigned int y;
const char str[];
{
    PSTRICKS_endline();

    /* Skip this if the string is empty */

    if (strlen(str) > 0) {
	fputs("\\rput", gpoutfile);

	/* Set justification */

	switch (PSTRICKS_justify) {
	case LEFT:
	    fputs("[l]", gpoutfile);
	    break;
	case CENTRE:
	    break;
	case RIGHT:
	    fputs("[r]", gpoutfile);
	    break;
	}

	/* Set text angle */

	switch (PSTRICKS_angle) {
	case 0:
	    break;
	case 1:
	    fputs("{L}", gpoutfile);
	    break;
	}

	/* Set reference position and text */

	fprintf(gpoutfile, "(%.4f,%.4f)",
		x / PSTRICKS_XMAX,
		y / PSTRICKS_YMAX);
	if (PST_hack_text) {
	    char *hack;

	    /* Hack leading numbers to something nice for TeX */

	    hack = PSTRICKS_hack_text(str);
	    fprintf(gpoutfile, "{%s}\n", hack);
	} else {
	    fprintf(gpoutfile, "{%s}\n", str);
	}
    }
}



TERM_PUBLIC int
PSTRICKS_justify_text(mode)
enum JUSTIFY mode;
{
    PSTRICKS_justify = mode;
    return (TRUE);
}

TERM_PUBLIC int
PSTRICKS_text_angle(ang)
int ang;
{
    PSTRICKS_angle = (ang ? 1 : 0);
    return (TRUE);
}

TERM_PUBLIC void
PSTRICKS_reset()
{
    PSTRICKS_endline();
    PSTRICKS_posx = PSTRICKS_posy = 0;
}

#endif /* TERM_BODY */

#ifdef TERM_TABLE

TERM_TABLE_START(pstricks_driver)
    "pstricks", "LaTeX picture environment with PSTricks macros",
    PSTRICKS_XMAX, PSTRICKS_YMAX, PSTRICKS_VCHAR, PSTRICKS_HCHAR,
    PSTRICKS_VTIC, PSTRICKS_HTIC, PSTRICKS_options, PSTRICKS_init, PSTRICKS_reset,
    PSTRICKS_text, null_scale, PSTRICKS_graphics, PSTRICKS_move, PSTRICKS_vector,
    PSTRICKS_linetype, PSTRICKS_put_text, PSTRICKS_text_angle,
    PSTRICKS_justify_text, PSTRICKS_point, PSTRICKS_arrow, set_font_null
TERM_TABLE_END(pstricks_driver)

#undef LAST_TERM
#define LAST_TERM pstricks_driver

#endif /* TERM_TABLE */
#endif /* TERM_PROTO_ONLY */

#ifdef TERM_HELP
START_HELP(pstricks)
"1 pstricks",
"?commands set terminal pstricks",
"?set terminal pstricks",
"?set term pstricks",
"?terminal pstricks",
"?term pstricks",
"?pstricks",
" The `pstricks` driver is intended for use with the \"pstricks.sty\" macro",
" package for LaTeX.  It is an alternative to the `eepic` and `latex` drivers.",
" You need \"pstricks.sty\", and, of course, a printer that understands",
" PostScript, or a converter such as Ghostscript.",
"",
" PSTricks is available via anonymous ftp from the /pub directory at",
" Princeton.EDU.  This driver definitely does not come close to using the full",
" capability of the PSTricks package.",
"",
" Syntax:",
"       set terminal pstricks {hacktext | nohacktext} {unit | nounit}",
"",
" The first option invokes an ugly hack that gives nicer numbers; the second",
" has to do with plot scaling.  The defaults are `hacktext` and `nounit`."
END_HELP(pstricks)
#endif /* TERM_HELP */