Logo Search packages:      
Sourcecode: plplot version File versions  Download package

plctrl.c

/* $Id: plctrl.c,v 1.76 2004/01/17 16:41:40 rlaboiss Exp $

      Misc. control routines, like begin, end, exit, change graphics/text
      mode, change color.  Includes some spillage from plcore.c.  If you
      don't know where it should go, put it here.

   Copyright (C) 2004  Joao Cardoso
   Copyright (C) 2004  Rafael Laboissiere

   This file is part of PLplot.

   PLplot is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Library Public License as published
   by the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   PLplot is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with PLplot; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#define DEBUG

#define NEED_PLDEBUG
#include "plplotP.h"
#ifdef macintosh
#include "mac.h"
/* for plMacLibOpen prototype; used in plLibOpen */
#endif

#ifdef DJGPP                  /* dos386/djgpp */
#ifdef __unix
#undef __unix
#endif
#endif

#ifdef __unix
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#endif

/* Static functions */

/* Used by any external init code to suggest a path */
char* plplotLibDir = 0;

static void
color_set(PLINT i, U_CHAR r, U_CHAR g, U_CHAR b, char *name );

static void
strcat_delim(char *dirspec);

static int
(*exit_handler) (char *errormsg);

void
(*abort_handler) (char *errormsg);

static void
plcmap0_def(int imin, int imax);

static void
plcmap1_def(void);

static PLFLT
value(double n1, double n2, double hue);

/* An additional hardwired location for lib files. */
/* I have no plans to change these again, ever. */

#if defined(DJGPP)
#ifndef PLLIBDEV
#define PLLIBDEV "c:/plplot/lib"
#endif

#elif defined(MSDOS)
#ifndef PLLIBDEV
#define PLLIBDEV "c:\\plplot\\lib"
#endif

#else

/* Anything else is assumed to be Unix */

#ifndef PLLIBDEV
#define PLLIBDEV "/usr/local/plplot/lib"
#endif

#endif

/*--------------------------------------------------------------------------*\
 *  Routines that deal with colors & color maps.
\*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*\
 * plcol0()
 *
 * Set color, map 0.  Argument is integer between 0 and plsc->ncol0.
\*--------------------------------------------------------------------------*/

void
c_plcol0(PLINT icol0)
{
    if (plsc->level < 1) {
      plabort("plcol0: Please call plinit first");
      return;
    }
    if (icol0 < 0 || icol0 >= plsc->ncol0) {
      char buffer[256];
      sprintf(buffer, "plcol0: Invalid color map entry: %d", (int) icol0);
      plabort(buffer);
      return;
    }

    plsc->icol0 = icol0;
    plsc->curcolor.r = plsc->cmap0[icol0].r;
    plsc->curcolor.g = plsc->cmap0[icol0].g;
    plsc->curcolor.b = plsc->cmap0[icol0].b;

    plsc->curcmap = 0;
    plP_state(PLSTATE_COLOR0);
}

/*--------------------------------------------------------------------------*\
 * plcol1()
 *
 * Set color, map 1.  Argument is a float between 0. and 1.
\*--------------------------------------------------------------------------*/

void
c_plcol1(PLFLT col1)
{
    PLINT icol1;

    if (plsc->level < 1) {
      plabort("plcol1: Please call plinit first");
      return;
    }
    if (col1 < 0 || col1 > 1) {
      char buffer[256];
      sprintf(buffer, "plcol1: Invalid color map position: %f", (PLFLT) col1);
      plabort(buffer);
      return;
    }

    icol1 = col1 * plsc->ncol1;
    icol1 = MIN(icol1, plsc->ncol1-1);

    plsc->icol1 = icol1;
    plsc->curcolor.r = plsc->cmap1[plsc->icol1].r;
    plsc->curcolor.g = plsc->cmap1[plsc->icol1].g;
    plsc->curcolor.b = plsc->cmap1[plsc->icol1].b;

    plsc->curcmap = 1;
    plP_state(PLSTATE_COLOR1);
}

/*--------------------------------------------------------------------------*\
 * plscolbg()
 *
 * Set the background color (cmap0[0]) by 8 bit RGB value
\*--------------------------------------------------------------------------*/

void
c_plscolbg(PLINT r, PLINT g, PLINT b)
{
    plscol0(0, r, g, b);
}

/*--------------------------------------------------------------------------*\
 * plgcolbg()
 *
 * Returns the background color (cmap0[0]) by 8 bit RGB value
\*--------------------------------------------------------------------------*/

void
c_plgcolbg(PLINT *r, PLINT *g, PLINT *b)
{
    plgcol0(0, r, g, b);
}

/*--------------------------------------------------------------------------*\
 * plscol0()
 *
 * Set a given color from color map 0 by 8 bit RGB value
 * Does not result in any additional cells to be allocated.
\*--------------------------------------------------------------------------*/

void
c_plscol0(PLINT icol0, PLINT r, PLINT g, PLINT b)
{
    if (plsc->cmap0 == NULL)
      plscmap0n(0);

    if (icol0 < 0 || icol0 >= plsc->ncol0) {
      char buffer[256];
      sprintf(buffer, "plscol0: Illegal color table value: %d", (int) icol0);
      plabort(buffer);
      return;
    }
    if ((r < 0 || r > 255) || (g < 0 || g > 255) || (b < 0 || b > 255)) {
      char buffer[256];
      sprintf(buffer, "plscol0: Invalid RGB color: %d, %d, %d",
            (int) r, (int) g, (int) b);
      plabort(buffer);
      return;
    }

    plsc->cmap0[icol0].r = r;
    plsc->cmap0[icol0].g = g;
    plsc->cmap0[icol0].b = b;

    if (plsc->level > 0)
      plP_state(PLSTATE_CMAP0);
}

/*--------------------------------------------------------------------------*\
 * plgcol0()
 *
 * Returns 8 bit RGB values for given color from color map 0
 * Values are negative if an invalid color id is given
\*--------------------------------------------------------------------------*/

void
c_plgcol0(PLINT icol0, PLINT *r, PLINT *g, PLINT *b)
{
    if (plsc->cmap0 == NULL)
      plscmap0n(0);

    *r = -1;
    *g = -1;
    *b = -1;

    if (icol0 < 0 || icol0 > plsc->ncol0) {
      char buffer[256];
      sprintf(buffer, "plgcol0: Invalid color index: %d", (int) icol0);
      plabort(buffer);
      return;
    }

    *r = plsc->cmap0[icol0].r;
    *g = plsc->cmap0[icol0].g;
    *b = plsc->cmap0[icol0].b;

    return;
}

/*--------------------------------------------------------------------------*\
 * plscmap0()
 *
 * Set color map 0 colors by 8 bit RGB values.  This sets the entire color
 * map -- only as many colors as specified will be allocated.
\*--------------------------------------------------------------------------*/

void
c_plscmap0(PLINT *r, PLINT *g, PLINT *b, PLINT ncol0)
{
    int i;

    plscmap0n(ncol0);

    for (i = 0; i < plsc->ncol0; i++) {
      if ((r[i] < 0 || r[i] > 255) ||
          (g[i] < 0 || g[i] > 255) ||
          (b[i] < 0 || b[i] > 255)) {

          char buffer[256];
          sprintf(buffer, "plscmap0: Invalid RGB color: %d, %d, %d",
                (int) r[i], (int) g[i], (int) b[i]);
          plabort(buffer);
          return;
      }

      plsc->cmap0[i].r = r[i];
      plsc->cmap0[i].g = g[i];
      plsc->cmap0[i].b = b[i];
    }

    if (plsc->level > 0)
      plP_state(PLSTATE_CMAP0);
}

/*--------------------------------------------------------------------------*\
 * plscmap1()
 *
 * Set color map 1 colors by 8 bit RGB values
 * This also sets the number of colors.
\*--------------------------------------------------------------------------*/

void
c_plscmap1(PLINT *r, PLINT *g, PLINT *b, PLINT ncol1)
{
    int i;

    plscmap1n(ncol1);

    for (i = 0; i < plsc->ncol1; i++) {
      if ((r[i] < 0 || r[i] > 255) ||
          (g[i] < 0 || g[i] > 255) ||
          (b[i] < 0 || b[i] > 255)) {

          char buffer[256];
          sprintf(buffer, "plscmap1: Invalid RGB color: %d, %d, %d",
                (int) r[i], (int) g[i], (int) b[i]);
          plabort(buffer);
          return;
      }
      plsc->cmap1[i].r = r[i];
      plsc->cmap1[i].g = g[i];
      plsc->cmap1[i].b = b[i];
    }

    if (plsc->level > 0)
      plP_state(PLSTATE_CMAP1);
}

/*--------------------------------------------------------------------------*\
 * plscmap1l()
 *
 * Set color map 1 colors using a piece-wise linear relationship between
 * position in the color map (from 0 to 1) and position in HLS or RGB color
 * space.  May be called at any time.
 *
 * The idea here is to specify a number of control points that specify the
 * mapping between HLS (or RGB or CMY) and palette 1 value.  Between these
 * points, linear interpolation is used.  By mapping position in the color
 * map to function value, this gives a smooth variation of color with
 * intensity.  Any number of control points may be specified, located at
 * arbitrary positions (intensities), although typically 2 - 4 are enough.
 * Another way of stating this is that we are traversing a given number of
 * lines through HLS (or RGB) space as we move through cmap 1 entries.  The
 * control points at the minimum and maximum intensity (0 and 1) must
 * always be specified.  By adding more control points you can get more
 * variation.  One good technique for plotting functions that vary about
 * some expected average is to use an additional 2 control points in the
 * center (intensity ~= 0.5) that are the same color as the background
 * (typically white for paper output, black for crt), and same hue as the
 * boundary control points.  This allows the highs and lows to be very
 * easily distinguished.
 *
 * Each control point must specify the position in cmap 1 as well as three
 * coordinates in HLS or RGB space.  The first point MUST correspond to
 * position = 0, and the last to position = 1.
 *
 * The hue is interpolated around the "front" of the color wheel
 * (red<->green<->blue<->red) unless the "rev" flag is set, in which case
 * interpolation proceeds around the back (reverse) side.  Specifying
 * rev=NULL is equivalent to setting rev[]=0 for every control point.
 *
 * Bounds on RGB coordinates:
 *    R,G,B       [0, 1]            magnitude
 *
 * Bounds on HLS coordinates:
 *    hue         [0, 360]    degrees
 *    lightness   [0, 1]            magnitude
 *    saturation  [0, 1]            magnitude
 *
 * The inputs are:
 *    itype       0: HLS, 1: RGB
 *    npts        number of control points
 *    pos[]       position for each control point
 *    coord1[]    first coordinate for each control point
 *    coord2[]    second coordinate for each control point
 *    coord3[]    third coordinate for each control point
 *    rev[]       reverse flag for each control point
\*--------------------------------------------------------------------------*/

void
c_plscmap1l(PLINT itype, PLINT npts, PLFLT *pos,
          PLFLT *coord1, PLFLT *coord2, PLFLT *coord3, PLINT *rev)
{
    int n;
    PLFLT h, l, s, r, g, b;

    if (npts < 2) {
      plabort("plscmap1l: Must specify at least two control points");
      return;
    }

    if ( (pos[0] != 0) || (pos[npts-1] != 1)) {
      plabort("plscmap1l: First, last control points must lie on boundary");
      return;
    }

    if ( npts > PL_MAX_CMAP1CP ) {
      plabort("plscmap1l: exceeded maximum number of control points");
      return;
    }

/* Allocate if not done yet */

    if (plsc->cmap1 == NULL)
      plscmap1n(0);

/* Save control points */

    plsc->ncp1 = npts;

    for (n = 0; n < npts; n++) {

      if (itype == 0) {
          h = coord1[n];
          l = coord2[n];
          s = coord3[n];
      }
      else {
          r = coord1[n];
          g = coord2[n];
          b = coord3[n];
          plRGB_HLS(r, g, b, &h, &l, &s);
      }

      plsc->cmap1cp[n].h = h;
      plsc->cmap1cp[n].l = l;
      plsc->cmap1cp[n].s = s;
      plsc->cmap1cp[n].p = pos[n];

      if (rev == NULL)
          plsc->cmap1cp[n].rev = 0;
      else
          plsc->cmap1cp[n].rev = rev[n];
    }

/* Calculate and set color map */

    plcmap1_calc();
}

/*--------------------------------------------------------------------------*\
 * plcmap1_calc()
 *
 * Bin up cmap 1 space and assign colors to make inverse mapping easy.
 * Always do interpolation in HLS space.
\*--------------------------------------------------------------------------*/

void
plcmap1_calc(void)
{
    int i, n;
    PLFLT delta, dp, dh, dl, ds;
    PLFLT h, l, s, p, r, g, b;

/* Loop over all control point pairs */

    for (n = 0; n < plsc->ncp1-1; n++) {

      if ( plsc->cmap1cp[n].p == plsc->cmap1cp[n+1].p )
          continue;

    /* Differences in p, h, l, s between ctrl pts */

      dp = plsc->cmap1cp[n+1].p - plsc->cmap1cp[n].p;
      dh = plsc->cmap1cp[n+1].h - plsc->cmap1cp[n].h;
      dl = plsc->cmap1cp[n+1].l - plsc->cmap1cp[n].l;
      ds = plsc->cmap1cp[n+1].s - plsc->cmap1cp[n].s;

    /* Adjust dh if we are to go around "the back side" */

      if (plsc->cmap1cp[n].rev)
          dh = (dh > 0) ? dh-360 : dh+360;

    /* Loop over all color cells.  Only interested in cells located (in */
    /* cmap1 space)  between n_th and n+1_th control points */

      for (i = 0; i < plsc->ncol1; i++) {
          p = (double) i / (plsc->ncol1 - 1.0);
          if ( (p < plsc->cmap1cp[n].p) ||
             (p > plsc->cmap1cp[n+1].p) )
            continue;

      /* Interpolate based on position of color cell in cmap1 space */

          delta = (p - plsc->cmap1cp[n].p) / dp;

      /* Linearly interpolate to get color cell h, l, s values */

          h = plsc->cmap1cp[n].h + dh * delta;
          l = plsc->cmap1cp[n].l + dl * delta;
          s = plsc->cmap1cp[n].s + ds * delta;

          while (h >= 360.)
            h -= 360.;

          while (h < 0.)
            h += 360.;

          plHLS_RGB(h, l, s, &r, &g, &b);

          plsc->cmap1[i].r = MAX(0, MIN(255, (int) (256. * r)));
          plsc->cmap1[i].g = MAX(0, MIN(255, (int) (256. * g)));
          plsc->cmap1[i].b = MAX(0, MIN(255, (int) (256. * b)));
      }
    }

    if (plsc->level > 0)
      plP_state(PLSTATE_CMAP1);
}

/*--------------------------------------------------------------------------*\
 * plscmap0n()
 *
 * Set number of colors in cmap 0, (re-)allocate cmap 0, and fill with
 * default values for those colors not previously allocated (and less
 * than index 15, after that you just get grey).
 *
 * The driver is not guaranteed to support all of these.
\*--------------------------------------------------------------------------*/

void
c_plscmap0n(PLINT ncol0)
{
    int ncol, size, imin, imax;

/* No change */

    if (ncol0 > 0 && plsc->ncol0 == ncol0)
      return;

/* Handle all possible startup conditions */

    if (plsc->ncol0 <= 0 && ncol0 <= 0)
      ncol = 16;
    else if (ncol0 <= 0)
      ncol = plsc->ncol0;
    else
        ncol = ncol0;

    imax = ncol-1;
    size = ncol * sizeof(PLColor);

/* Allocate the space */

    if (plsc->cmap0 == NULL) {
      plsc->cmap0 = (PLColor *) calloc(1, size);
      imin = 0;
    }
    else {
      plsc->cmap0 = (PLColor *) realloc(plsc->cmap0, size);
      imin = plsc->ncol0;
    }

/* Fill in default entries */

    plsc->ncol0 = ncol;
    plcmap0_def(imin, imax);
}

/*--------------------------------------------------------------------------*\
 * plscmap1n()
 *
 * Set number of colors in cmap 1, (re-)allocate cmap 1, and set default
 * values if this is the first allocation.
 *
 * Note that the driver is allowed to disregard this number.
 * In particular, most use fewer than we use internally.
\*--------------------------------------------------------------------------*/

void
c_plscmap1n(PLINT ncol1)
{
    int ncol, size;

/* No change */

    if (ncol1 > 0 && plsc->ncol1 == ncol1)
      return;

/* Handle all possible startup conditions */

    if (plsc->ncol1 <= 0 && ncol1 <= 0)
      ncol = 128;
    else if (ncol1 <= 0)
      ncol = plsc->ncol1;
    else
        ncol = ncol1;

    size = ncol * sizeof(PLColor);

/* Allocate the space */

    if (plsc->ncol1 > 0)
      plsc->cmap1 = (PLColor *) realloc(plsc->cmap1, size);
    else
      plsc->cmap1 = (PLColor *) calloc(ncol, sizeof(PLColor));

/* Fill in default entries */

    plsc->ncol1 = ncol;
    if (plsc->ncp1 == 0)
      plcmap1_def();
    else
      plcmap1_calc();
}

/*--------------------------------------------------------------------------*\
 * color_set()
 *
 * Initializes color table entry by RGB values.
\*--------------------------------------------------------------------------*/

static void
color_set(PLINT i, U_CHAR r, U_CHAR g, U_CHAR b, char *name )
{
    plsc->cmap0[i].r = r;
    plsc->cmap0[i].g = g;
    plsc->cmap0[i].b = b;
    plsc->cmap0[i].name = name;
}

/*--------------------------------------------------------------------------*\
 * plcmap0_def()
 *
 * Initializes specified color map 0 color entry to its default.
 *
 * Initial RGB values for color map 0 taken from X11R6
 * (XFree86-3.3.6) X-windows
 * rgb.txt file, and may not accurately represent the described colors on
 * all systems.
\*--------------------------------------------------------------------------*/

#define color_def(i, r, g, b, n) \
if (i >= imin && i <= imax) color_set(i, r, g, b, n);

static void
plcmap0_def(int imin, int imax)
{
    int i;

    color_def(0,    0,   0,   0, "black" );     /* black */
    color_def(1,  255,   0,   0, "red");  /* red */
    color_def(2,  255, 255,   0, "yellow" );    /* yellow */
    color_def(3,    0, 255,   0, "green" );     /* green */
    color_def(4,  127, 255, 212, "aquamarine" );      /* aquamarine */
    color_def(5,  255, 192, 203, "pink" );      /* pink */
    color_def(6,  245, 222, 179, "wheat" );     /* wheat */
    color_def(7,  190, 190, 190, "grey" );      /* grey */
    color_def(8,  165,  42,  42, "brown" );     /* brown */
    color_def(9,    0,   0, 255, "blue" );      /* blue */
    color_def(10, 138,  43, 226, "BlueViolet" );      /* Blue Violet */
    color_def(11,   0, 255, 255, "cyan" );      /* cyan */
    color_def(12,  64, 224, 208, "turquoise" ); /* turquoise */
    color_def(13, 255,   0, 255, "magenta" );   /* magenta */
    color_def(14, 250, 128, 114, "salmon" );    /* salmon */
    color_def(15, 255, 255, 255, "white" );     /* white */

/* Any others are just arbitrarily set */

    for (i = 16; i <= imax; i++)
      color_def(i, 255, 0, 0, "red");     /* red */
}

/*--------------------------------------------------------------------------*\
 * plcmap1_def()
 *
 * Initializes color map 1.
 *
 * The default initialization uses 6 control points in HLS space, the inner
 * ones being very close to one of the vertices of the HLS double cone.  The
 * vertex used (black or white) is chosen to be the closer to the background
 * color.  The 6 points were chosen over the older 4 points in order to make
 * weaker structures more easily visible, and give more control through the
 * palette editor.  If you don't like these settings.. change them!
\*--------------------------------------------------------------------------*/

static void
plcmap1_def(void)
{
    PLFLT i[6], h[6], l[6], s[6], midpt = 0., vertex = 0.;

/* Positions of control points */

    i[0] = 0;           /* left boundary */
    i[1] = 0.44;  /* a little left of center */
    i[2] = 0.50;  /* at center */
    i[3] = 0.50;  /* at center */
    i[4] = 0.56;  /* a little right of center */
    i[5] = 1;           /* right boundary */

/* For center control points, pick black or white, whichever is closer to bg */
/* Be carefult to pick just short of top or bottom else hue info is lost */

    if (plsc->cmap0 != NULL)
      vertex = ((PLFLT) plsc->cmap0[0].r +
              (PLFLT) plsc->cmap0[0].g +
              (PLFLT) plsc->cmap0[0].b) / 3. / 255.;

    if (vertex < 0.5) {
      vertex = 0.01;
      midpt  = 0.10;
    } else {
      vertex = 0.99;
      midpt  = 0.90;
    }

/* Set hue */

    h[0] = 260;         /* low: blue-violet */
    h[1] = 260;         /* only change as we go over vertex */
    h[2] = 260;         /* only change as we go over vertex */
    h[3] = 0;           /* high: red */
    h[4] = 0;           /* high: red */
    h[5] = 0;           /* keep fixed */

/* Set lightness */

    l[0] = 0.5;         /* low */
    l[1] = midpt; /* midpoint value */
    l[2] = vertex;      /* bg */
    l[3] = vertex;      /* bg */
    l[4] = midpt; /* midpoint value */
    l[5] = 0.5;         /* high */

/* Set saturation -- keep at maximum */

    s[0] = 1;
    s[1] = 1;
    s[2] = 1;
    s[3] = 1;
    s[4] = 1;
    s[5] = 1;

    c_plscmap1l(0, 6, i, h, l, s, NULL);
}

/*--------------------------------------------------------------------------*\
 * plscolor()
 *
 * Used to globally turn color output on/off
\*--------------------------------------------------------------------------*/

void
c_plscolor(PLINT color)
{
    plsc->colorset = 1;
    plsc->color = color;
}

/*--------------------------------------------------------------------------*\
 * plrgb()
 *
 * Set line color by red, green, blue from  0. to 1.
 * Do NOT use this.  Only retained for backward compatibility
\*--------------------------------------------------------------------------*/

void
c_plrgb(PLFLT r, PLFLT g, PLFLT b)
{
    if (plsc->level < 1) {
      plabort("plrgb: Please call plinit first");
      return;
    }

    plsc->icol0 = PL_RGB_COLOR;
    plsc->curcolor.r = MAX(0, MIN(255, (int) (256. * r)));
    plsc->curcolor.g = MAX(0, MIN(255, (int) (256. * g)));
    plsc->curcolor.b = MAX(0, MIN(255, (int) (256. * b)));

    plsc->curcmap = 0;
    plP_state(PLSTATE_COLOR0);
}

/*--------------------------------------------------------------------------*\
 * plrgb1()
 *
 * Set line color by 8 bit RGB values.
 * Do NOT use this.  Only retained for backward compatibility
\*--------------------------------------------------------------------------*/

void
c_plrgb1(PLINT r, PLINT g, PLINT b)
{
    if (plsc->level < 1) {
      plabort("plrgb1: Please call plinit first");
      return;
    }
    if ((r < 0 || r > 255) || (g < 0 || g > 255) || (b < 0 || b > 255)) {
      plabort("plrgb1: Invalid color");
      return;
    }

    plsc->icol0 = PL_RGB_COLOR;
    plsc->curcolor.r = r;
    plsc->curcolor.g = g;
    plsc->curcolor.b = b;

    plsc->curcmap = 0;
    plP_state(PLSTATE_COLOR0);
}

/*--------------------------------------------------------------------------*\
 * void plhls()
 *
 * Set current color by hue, lightness, and saturation.
 * Convert hls color coordinates to rgb, then call plrgb.
 * Do NOT use this.  Only retained for backward compatibility
\*--------------------------------------------------------------------------*/

void
c_plhls(PLFLT h, PLFLT l, PLFLT s)
{
    PLFLT r, g, b;

    plHLS_RGB(h, l, s, &r, &g, &b);
    plrgb(r, g, b);
}

/*--------------------------------------------------------------------------*\
 * void value()
 *
 * Auxiliary function used by plHLS_RGB().
\*--------------------------------------------------------------------------*/

static PLFLT
value(double n1, double n2, double hue)
{
    PLFLT val;

    while (hue >= 360.)
      hue -= 360.;
    while (hue < 0.)
      hue += 360.;

    if (hue < 60.)
      val = n1 + (n2 - n1) * hue / 60.;
    else if (hue < 180.)
      val = n2;
    else if (hue < 240.)
      val = n1 + (n2 - n1) * (240. - hue) / 60.;
    else
      val = n1;

    return (val);
}

/*--------------------------------------------------------------------------*\
 * void plHLS_RGB()
 *
 * Convert HLS color to RGB color.
 * Bounds on HLS (input):
 *    hue         [0., 360.]  degrees
 *    lightness   [0., 1.]    magnitude
 *    saturation  [0., 1.]    magnitude
 *
 * Hue is always mapped onto the interval [0., 360.] regardless of input.
 * Bounds on RGB (output) is always [0., 1.].  Convert to RGB color values
 * by multiplying by 2**nbits (nbits typically 8).
\*--------------------------------------------------------------------------*/

void
plHLS_RGB(PLFLT h, PLFLT l, PLFLT s, PLFLT *p_r, PLFLT *p_g, PLFLT *p_b)
{
    PLFLT m1, m2;

    if (l <= .5)
      m2 = l * (s + 1.);
    else
      m2 = l + s - l * s;

    m1 = 2 * l - m2;

    *p_r = value(m1, m2, h + 120.);
    *p_g = value(m1, m2, h);
    *p_b = value(m1, m2, h - 120.);
}

/*--------------------------------------------------------------------------*\
 * void plRGB_HLS()
 *
 * Convert RGB color to HLS color.
 * Bounds on RGB (input) is always [0., 1.].
 * Bounds on HLS (output):
 *    hue         [0., 360.]  degrees
 *    lightness   [0., 1.]    magnitude
 *    saturation  [0., 1.]    magnitude
\*--------------------------------------------------------------------------*/

void
plRGB_HLS(PLFLT r, PLFLT g, PLFLT b, PLFLT *p_h, PLFLT *p_l, PLFLT *p_s)
{
    PLFLT h, l, s, d, rc, gc, bc, rgb_min, rgb_max;

    rgb_min = MIN( r, MIN( g, b ));
    rgb_max = MAX( r, MAX( g, b ));

    l = (rgb_min+rgb_max) / 2.0;

    if (rgb_min == rgb_max) {
      s = 0;
      h = 0;
    }
    else {
      d = rgb_max - rgb_min;
      if (l < 0.5)
          s = 0.5 * d / l;
      else
          s = 0.5* d / (1.-l);

      rc = (rgb_max-r) / d;
      gc = (rgb_max-g) / d;
      bc = (rgb_max-b) / d;

      if (r == rgb_max)
          h = bc-gc;
      else if (g == rgb_max)
          h = rc-bc+2;
      else
          h = gc-rc-2;

      h = h*60;
      if (h <  0)
          h = h+360;
      else if (h >= 360)
          h = h-360;
    }
    *p_h = h;
    *p_l = l;
    *p_s = s;
}

/*--------------------------------------------------------------------------*\
 * A grab-bag of various control routines.
\*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*\
 * void plwarn()
 *
 * A handy way to issue warnings, if need be.
\*--------------------------------------------------------------------------*/

void
plwarn(char *errormsg)
{
    int was_gfx = 0;

    if (plsc->graphx == 1) {
      was_gfx = 1;
      pltext();
    }

    fprintf(stderr, "\n*** PLPLOT WARNING ***\n");
    if (*errormsg != '\0')
      fprintf(stderr, "%s\n", errormsg);

    if (was_gfx == 1)
      plgra();
}

/*--------------------------------------------------------------------------*\
 * void plabort()
 *
 * Much the same as plwarn(), but appends ", aborting operation" to the
 * error message.  Helps to keep source code uncluttered and provides a
 * convention for error aborts.
 *
 * If cleanup needs to be done in the main program, the user should write
 * his/her own exit handler and pass it in via plsabort().
\*--------------------------------------------------------------------------*/

void
plabort(char *errormsg)
{

    if (abort_handler != NULL)
         (*abort_handler)(errormsg);

    if (plsc->errcode != NULL)
      *(plsc->errcode) = 1;

    if (plsc->errmsg != NULL) {
      sprintf(plsc->errmsg, "\n*** PLPLOT ERROR ***\n");
      if (*errormsg != '\0')
          sprintf(plsc->errmsg, "%s, aborting operation\n", errormsg);

    } else {
      int was_gfx = 0;

      if (plsc->graphx == 1) {
          was_gfx = 1;
          pltext();
      }

      fprintf(stderr, "\n*** PLPLOT ERROR ***\n");
      if (*errormsg != '\0')
          fprintf(stderr, "%s, aborting operation\n", errormsg);

      if (was_gfx == 1)
          plgra();
    }
}


/*--------------------------------------------------------------------------*\
 * void plsabort()
 *
 * Sets an optional user abort handler.
\*--------------------------------------------------------------------------*/

void
plsabort(void (*handler) (char *))
{
    abort_handler = handler;
}

/*--------------------------------------------------------------------------*\
 * void plexit()
 *
 * In case of an abort this routine is called.  It just prints out an error
 * message and tries to clean up as much as possible.  It's best to turn
 * off pause and then restore previous setting before returning.
 *
 * If cleanup needs to be done in the main program, the user should write
 * his/her own exit handler and pass it in via plsexit().  This function
 * should should either call plend() before exiting, or simply return.
\*--------------------------------------------------------------------------*/

void
plexit(char *errormsg)
{
    int status = 1;

    if (exit_handler != NULL)
      status = (*exit_handler)(errormsg);

    plsc->nopause = 1;
    if (*errormsg != '\0') {
      fprintf(stderr, "\n*** PLPLOT ERROR ***\n");
      fprintf(stderr, "%s\n", errormsg);
    }
    plend();

    fprintf(stderr, "Program aborted\n");
    exit(status);
}

/*--------------------------------------------------------------------------*\
 * void plsexit()
 *
 * Sets an optional user exit handler.
\*--------------------------------------------------------------------------*/

void
plsexit(int (*handler) (char *))
{
    exit_handler = handler;
}

/*--------------------------------------------------------------------------*\
 * void plgra()
 *
 * Switches to graphics screen.
 *
 * Here and in pltext() it's a good idea to return silently if plinit()
 * hasn't yet been called, since plwarn() calls pltext() and plgra(), and
 * plwarn() may be called at any time.
\*--------------------------------------------------------------------------*/

void
c_plgra(void)
{
    if (plsc->level > 0)
      plP_esc(PLESC_GRAPH, NULL);
}

void
c_plxormod(PLINT mode, PLINT *status)     /* xor mode */
{
  static int ostate = 0;

  if (!plsc->dev_xor) {
    *status = 0;
    return;
  }

  if (plsc->level > 0) {
    plP_esc(PLESC_XORMOD, &mode);
    if (mode) {
      ostate = plsc->plbuf_write;
      plsc->plbuf_write = 0;
    } else
      plsc->plbuf_write = ostate;
  }
  *status = 1;
}

/*--------------------------------------------------------------------------*\
 * void pltext()
 *
 * Switches to text screen.
\*--------------------------------------------------------------------------*/

void
c_pltext(void)
{
    if (plsc->level > 0)
      plP_esc(PLESC_TEXT, NULL);
}

/*--------------------------------------------------------------------------*\
 * void pl_cmd()
 *
 * Front-end to driver escape function.
 * In principle this can be used to pass just about anything directly
 * to the driver.
\*--------------------------------------------------------------------------*/

void
pl_cmd(PLINT op, void *ptr)
{
    plP_esc(op, ptr);
}

/*--------------------------------------------------------------------------*\
 * char *plFindCommand
 *
 * Looks for the specified executable file.  Search path:
 *      if command invoked in the build tree:
 *         build_tree/tk (plserver lies there - needed for the tk driver)
 *         build_tree/scripts (plpr lies there - needed for the tk driver)
 *      else
 *    PLPLOT_BIN_ENV = $(PLPLOT_BIN)
 *    current directory
 *    PLPLOT_HOME_ENV/bin = $(PLPLOT_HOME)/bin
 *    BIN_DIR
 *
 * The caller must free the returned pointer (points to malloc'ed memory)
 * when finished with it.
\*--------------------------------------------------------------------------*/

char *
plFindCommand(char *fn)
{
    char *fs = NULL, *dn;

    /**** see if in build tree ***/
    if (plInBuildTree() == 1) {
        plGetName(BUILD_DIR, "bindings/tk", fn, &fs);
        if ( ! plFindName(fs))
            return fs;
      else {
        plGetName(BUILD_DIR, "scripts", fn, &fs);
        if ( ! plFindName(fs))
            return fs;
      }
    }

/* PLPLOT_BIN_ENV = $(PLPLOT_BIN) */

#if defined(PLPLOT_BIN_ENV)
    if ((dn = getenv(PLPLOT_BIN_ENV)) != NULL) {
        plGetName(dn, "", fn, &fs);
        if ( ! plFindName(fs))
            return fs;
        fprintf(stderr, PLPLOT_BIN_ENV"=\"%s\"\n", dn); /* what IS set? */
    }
#endif  /* PLPLOT_BIN_ENV */

/* Current directory */

    plGetName(".", "", fn, &fs);
    if ( ! plFindName(fs))
      return fs;

/* PLPLOT_HOME_ENV/bin = $(PLPLOT_HOME)/bin */

#if defined(PLPLOT_HOME_ENV)
    if ((dn = getenv(PLPLOT_HOME_ENV)) != NULL) {
        plGetName(dn, "bin", fn, &fs);
        if ( ! plFindName(fs))
            return fs;
        fprintf(stderr, PLPLOT_HOME_ENV"=\"%s\"\n",dn); /* what IS set? */
    }
#endif  /* PLPLOT_HOME_ENV */

/* BIN_DIR */

#if defined (BIN_DIR)
    plGetName(BIN_DIR, "", fn, &fs);
    if ( ! plFindName(fs))
      return fs;
#endif

/* Crapped out */

    free_mem(fs);
    fprintf(stderr, "plFindCommand: cannot locate command: %s\n", fn);
#if defined (BIN_DIR)
    fprintf(stderr, "bin dir=\"" BIN_DIR "\"\n" );      /* what WAS set? */
#endif  /* BIN_DIR */
    return NULL;
}

/*--------------------------------------------------------------------------*\
 * FILE *plLibOpen(fn)
 *
 * Return file pointer to lib file.
 * Locations checked:
 *    PLPLOT_LIB_ENV = $(PLPLOT_LIB)
 *    current directory
 *    PLPLOT_HOME_ENV/lib = $(PLPLOT_HOME)/lib
 *    DATA_DIR
 *    PLLIBDEV
\*--------------------------------------------------------------------------*/

FILE *
plLibOpen(char *fn)
{
    FILE *ret = NULL;

    PDFstrm *pdfs = plLibOpenPdfstrm(fn);
    if (pdfs == NULL) {
        return NULL;
    }
    if (pdfs->file != NULL) {
        ret = pdfs->file;
      pdfs->file = NULL;
    }
    pdf_close(pdfs);
    return ret;
}

PDFstrm *
plLibOpenPdfstrm(char *fn)
{
    PDFstrm *file;
    char *fs = NULL, *dn = NULL;

/****   search build tree               ****/

    if (plInBuildTree() == 1) {
      plGetName(BUILD_DIR, "data", fn, &fs);

      if ((file = pdf_fopen(fs, "rb")) != NULL)
        goto done;
    }

/**** search PLPLOT_LIB_ENV = $(PLPLOT_LIB)     ****/

#if defined(PLPLOT_LIB_ENV)
    if ((dn = getenv(PLPLOT_LIB_ENV)) != NULL) {
        plGetName(dn, "", fn, &fs);

        if ((file = pdf_fopen(fs, "rb")) != NULL)
            goto done;

        fprintf(stderr, PLPLOT_LIB_ENV"=\"%s\"\n", dn); /* what IS set? */
    }
#endif  /* PLPLOT_LIB_ENV */

/**** search current directory      ****/

    if ((file = pdf_fopen(fn, "rb")) != NULL)
        goto done;

/**** search PLPLOT_HOME_ENV/lib = $(PLPLOT_HOME)/lib ****/

#if defined (PLPLOT_HOME_ENV)
    if ((dn = getenv(PLPLOT_HOME_ENV)) != NULL) {
        plGetName(dn, "lib", fn, &fs);

        if ((file = pdf_fopen(fs, "rb")) != NULL)
            goto done;
        fprintf(stderr, PLPLOT_HOME_ENV"=\"%s\"\n",dn); /* what IS set? */
    }
#endif  /* PLPLOT_HOME_ENV/lib */

/****       search installed location     ****/

#if defined (DATA_DIR)
    plGetName(DATA_DIR, "", fn, &fs);

    if ((file = pdf_fopen(fs, "rb")) != NULL)
        goto done;
#endif  /* DATA_DIR */

/****       search hardwired location     ****/

#ifdef PLLIBDEV
    plGetName(PLLIBDEV, "", fn, &fs);

    if ((file = pdf_fopen(fs, "rb")) != NULL)
      goto done;
#endif      /* PLLIBDEV */

#ifdef macintosh
    file = plMacLibOpen(fn);
    if (file != NULL)
        goto done;
#endif /* macintosh */

    if (plplotLibDir != NULL) {
      plGetName(plplotLibDir, "", fn, &fs);
      if ((file = pdf_fopen(fs, "rb")) != NULL)
          goto done;

    }

/****       not found, give up      ****/
    pldebug("plLibOpenPdfstr", "File %s not found.\n", fn);
    return NULL;

 done:
    pldebug("plLibOpenPdfstr", "Found file %s\n", fs);
    free_mem(fs);
    return (file);
}

/*--------------------------------------------------------------------------*\
 * int plFindName
 *
 * Authors: Paul Dubois (LLNL), others?
 * This function is in the public domain.
 *
 * Given a pathname, determine if it is a symbolic link.  If so, continue
 * searching to the ultimate terminus - there may be more than one link.
 * Use the error value to determine when the terminus is reached, and to
 * determine if the pathname really exists.  Then stat it to determine
 * whether it's executable.  Return 0 for an executable, errno otherwise.
 * Note that 'p' _must_ have at least one '/' character - it does by
 * construction in this program.  The contents of the array pointed to by
 * 'p' are changed to the actual pathname if findname is successful.
 *
 * This function is only defined under Unix for now.
\*--------------------------------------------------------------------------*/

#ifdef __unix
int
plFindName(char *p)
{
    int n;
    char buf[1024], *cp;
    extern int errno;
    struct stat sbuf;

    pldebug("plFindName", "Trying to find %s\n", p);
    while ((n = readlink(p, buf, 1024)) > 0) {
      pldebug("plFindName", "Readlink read %d chars at: %s\n", n, p);
      if (buf[0] == '/') {
      /* Link is an absolute path */

          strncpy(p, buf, n);
          p[n] = '\0';
          pldebug("plFindName", "Link is absolute: %s\n", p);
      }
      else {
      /* Link is relative to its directory; make it absolute */

          cp = 1 + strrchr(p, '/');
          strncpy(cp, buf, n);
          cp[n] = '\0';
          pldebug("plFindName",
                "Link is relative: %s\n\tTotal path:%s\n", cp, p);
      }
    }

/* This macro not defined on the NEC SX-3 */

#ifdef SX
#define S_ISREG(mode)   (mode & S_IFREG)
#endif

/* SGI machines return ENXIO instead of EINVAL Dubois 11/92 */

    if (errno == EINVAL || errno == ENXIO) {
      pldebug("plFindName", "%s may be the one...\n", p);
      if ((stat(p, &sbuf) == 0) && S_ISREG(sbuf.st_mode)) {
          pldebug("plFindName", "%s is a regular file\n", p);
          return (access(p, X_OK));
      }
    }
    pldebug("plFindName", "%s found but is not executable\n", p);
    return (errno ? errno : -1);
}

#else
int
plFindName(char *p)
{
    return 1;
}
#endif

/*--------------------------------------------------------------------------*\
 * void plGetName()
 *
 * Gets search name for file by concatenating the dir, subdir, and file
 * name, allocating memory as needed.  The appropriate delimiter is added
 * after the dir specification as necessary.  The caller is responsible
 * for freeing the malloc'ed memory.
\*--------------------------------------------------------------------------*/

void
plGetName(char *dir, char *subdir, char *filename, char **filespec)
{
    int lfilespec;

/* Malloc space for filespec */

    free_mem(*filespec);
    lfilespec = 10;
    lfilespec = strlen(dir) + strlen(subdir) + strlen(filename) + 10;
    *filespec = (char *) malloc(lfilespec);

    strcpy(*filespec, dir);

    if (*subdir != '\0') {
      strcat_delim(*filespec);
      strcat(*filespec, subdir);
    }
    if (*filename != '\0') {
      strcat_delim(*filespec);
      strcat(*filespec, filename);
    }
}

/*--------------------------------------------------------------------------*\
 * void strcat_delim()
 *
 * Append path name deliminator if necessary (does not add one if one's
 * there already, or if dealing with a colon-terminated device name).
\*--------------------------------------------------------------------------*/

static void
strcat_delim(char *dirspec)
{
    int ldirspec = strlen(dirspec);
#if defined (MSDOS)
    if (dirspec[ldirspec-1] != '\\')
      strcat(dirspec, "\\");
#elif defined (macintosh)
    if (dirspec[ldirspec-1] != ':')
        strcat(dirspec, ":");
#else           /* unix is the default */
    if (dirspec[ldirspec-1] != '/')
      strcat(dirspec, "/");
#endif
}

/*--------------------------------------------------------------------------*\
 * plcol_interp()
 *
 * Initializes device cmap 1 entry by interpolation from pls->cmap1
 * entries.  Returned PLColor is supposed to represent the i_th color
 * out of a total of ncol colors in the current color scheme.
\*--------------------------------------------------------------------------*/

void
plcol_interp(PLStream *pls, PLColor *newcolor, int i, int ncol)
{
    PLFLT x, delta;
    int il, ir;

    x = (double) (i * (pls->ncol1-1)) / (double) (ncol-1);
    il = x;
    ir = il + 1;
    delta = x - il;

    if (ir > pls->ncol1 || il < 0)
      fprintf(stderr, "Invalid color\n");

    else if (ir == pls->ncol1 || (delta == 0.)) {
      newcolor->r = pls->cmap1[il].r;
      newcolor->g = pls->cmap1[il].g;
      newcolor->b = pls->cmap1[il].b;
    }
    else {
      newcolor->r = (1.-delta) * pls->cmap1[il].r + delta * pls->cmap1[ir].r;
      newcolor->g = (1.-delta) * pls->cmap1[il].g + delta * pls->cmap1[ir].g;
      newcolor->b = (1.-delta) * pls->cmap1[il].b + delta * pls->cmap1[ir].b;
    }
}

/*--------------------------------------------------------------------------*\
 * plOpenFile()
 *
 * Opens file for output, prompting if not set.
 * Prints extra newline at end to make output look better in batch runs.
 * A file name of "-" indicates output to stdout.
\*--------------------------------------------------------------------------*/

#define MAX_NUM_TRIES   10
void
plOpenFile(PLStream *pls)
{
    int i = 0, count = 0;
    size_t len;
    char line[256];

    while (pls->OutFile == NULL) {

/* Setting pls->FileName = NULL forces creation of a new family member */
/* You should also free the memory associated with it if you do this */

      if (pls->family && pls->BaseName != NULL)
          plP_getmember(pls);

/* Prompt if filename still not known */

      if (pls->FileName == NULL) {
          do {
            fprintf(stdout, "Enter graphics output file name: ");
            fgets(line, sizeof(line), stdin);
            len = strlen(line);
            if (len)
                len--;
            line[len] = '\0'; /* strip new-line */
            count++;          /* count zero entries */
          } while (!len && count < MAX_NUM_TRIES);
          plP_sfnam(pls, line);
      }

/* If name is "-", send to stdout */

      if ( ! strcmp(pls->FileName, "-")) {
          pls->OutFile = stdout;
          pls->output_type = 1;
          break;
      }

/* Need this here again, for prompted family initialization */

      if (pls->family && pls->BaseName != NULL)
          plP_getmember(pls);

      if (i++ > 10)
          plexit("Too many tries.");

      if ((pls->OutFile = fopen(pls->FileName, "wb+")) == NULL)
          fprintf(stderr, "Can't open %s.\n", pls->FileName);
      else
          pldebug("plOpenFile", "Opened %s\n", pls->FileName);
    }
}

/*--------------------------------------------------------------------------*\
 * plP_getmember()
 *
 * Sets up next file member name (in pls->FileName), but does not open it.
\*--------------------------------------------------------------------------*/

void
plP_getmember(PLStream *pls)
{
    char tmp[256];

    if (pls->FileName == NULL)
      pls->FileName = (char *) malloc(10 + strlen(pls->BaseName));

    sprintf(tmp, "%s.%%0%1ii", pls->BaseName, (int) pls->fflen);
    sprintf(pls->FileName, tmp, pls->member);
}

/*--------------------------------------------------------------------------*\
 * plP_sfnam()
 *
 * Sets up file name & family stem name.
 * Reserve some extra space (5 chars) to hold an optional member number.
\*--------------------------------------------------------------------------*/

void
plP_sfnam(PLStream *pls, const char *fnam)
{
    pls->OutFile = NULL;

    if (pls->FileName != NULL)
      free((void *) pls->FileName);

    pls->FileName = (char *) malloc(10 + strlen(fnam));

    strcpy(pls->FileName, fnam);

    if (pls->BaseName != NULL)
      free((void *) pls->BaseName);

    pls->BaseName = (char *) malloc(10 + strlen(fnam));

    strcpy(pls->BaseName, fnam);
}

/*--------------------------------------------------------------------------*\
 * plFamInit()
 *
 * Initializes family file parameters.
\*--------------------------------------------------------------------------*/

void
plFamInit(PLStream *pls)
{
    if (pls->family) {
      pls->bytecnt = 0;
      if ( ! pls->member)
          pls->member = 1;
      if ( ! pls->finc)
          pls->finc = 1;
      if ( ! pls->fflen)
          pls->fflen = 1;
      if ( ! pls->bytemax)
          pls->bytemax = PL_FILESIZE_KB * 1000;
    }
}

/*--------------------------------------------------------------------------*\
 * plGetFam()
 *
 * Starts new member file of family file set if necessary.
 *
 * Note each member file is a complete graphics file (can be printed
 * individually), although 'plrender' will treat a family as a single
 * logical file if given the family name instead of the member name.
\*--------------------------------------------------------------------------*/

void
plGetFam(PLStream *pls)
{
    PLFLT xpmm_loc, ypmm_loc;
    if (pls->family) {
      if (pls->bytecnt > pls->bytemax || pls->famadv) {
          plP_tidy();
          pls->member += pls->finc;
          pls->famadv = 0;
          plP_init();
         /* Apply compensating factor to original xpmm and ypmm so that
          * character aspect ratio is preserved when overall aspect ratio
          * is changed. */
          plP_gpixmm(&xpmm_loc, &ypmm_loc);
          plP_setpxl(xpmm_loc*plsc->caspfactor, ypmm_loc/plsc->caspfactor);
          return;
      }
    }
}

/*--------------------------------------------------------------------------*\
 * plRotPhy()
 *
 * Rotates physical coordinates if necessary for given orientation.
 * Each time orient is incremented, the plot is rotated 90 deg clockwise.
 * Note: this is now used only to rotate by 90 degrees for devices that
 * expect portrait mode.
\*--------------------------------------------------------------------------*/

void
plRotPhy(PLINT orient, PLINT xmin, PLINT ymin, PLINT xmax, PLINT ymax,
       int *px, int *py)
{
    int x, y;

    x = *px;
    y = *py;

    switch (orient%4) {

    case 1:
      *px = xmin + (y - ymin);
      *py = ymin + (xmax - x);
      break;

    case 2:
      *px = xmin + (xmax - x);
      *py = ymin + (ymax - y);
      break;

    case 3:
      *px = xmin + (ymax - y);
      *py = ymin + (x - xmin);
      break;

    default:
      break;                  /* do nothing */
    }
}

/*--------------------------------------------------------------------------*\
 * plAllocDev()
 *
 * Allocates a standard PLDev structure for device-specific data, stores
 * the address in pls->dev, and returns the address as well.
\*--------------------------------------------------------------------------*/

PLDev *
plAllocDev(PLStream *pls)
{
    if (pls->dev != NULL)
      free((void *) pls->dev);

    pls->dev = calloc(1, (size_t) sizeof(PLDev));
    if (pls->dev == NULL)
      plexit("plAllocDev: cannot allocate memory\n");

    return (PLDev *) pls->dev;
}

/*--------------------------------------------------------------------------*\
 * plGinInit()
 *
 * Just fills in the PLGraphicsIn with appropriate initial values.
\*--------------------------------------------------------------------------*/

void
plGinInit(PLGraphicsIn *gin)
{
    gin->type = 0;
    gin->state = 0;
    gin->keysym = 0;
    gin->button = 0;
    gin->string[0] = '\0';
    gin->pX = gin->pY = -1;
    gin->dX = gin->dY = 0.;
    gin->wX = gin->wY = 0.;
}

/*--------------------------------------------------------------------------*\
 * plGetInt()
 *
 * Prompts human to input an integer in response to given message.
\*--------------------------------------------------------------------------*/

PLINT
plGetInt(char *s)
{
    int m;
    int i = 0;
    char line[256];

    while (i++ < 10) {
      fprintf(stdout, s);
      fgets(line, sizeof(line), stdin);
#ifdef MSDOS
      m = atoi(line);
      return (m);
#else
      if (sscanf(line, "%d", &m) == 1)
          return (m);
      fprintf(stdout, "No value or value out of range; please try again\n");
#endif
    }
    plexit("Too many tries.");
    return (0);
}

/*--------------------------------------------------------------------------*\
 * plGetFlt()
 *
 * Prompts human to input a float in response to given message.
\*--------------------------------------------------------------------------*/

PLFLT
plGetFlt(char *s)
{
    PLFLT m;
    double m1;
    int i = 0;
    char line[256];

    while (i++ < 10) {
      fprintf(stdout, s);
      fgets(line, sizeof(line), stdin);
#ifdef MSDOS
      m = atof(line);
      return (m);
#else
      if (sscanf(line, "%lf", &m1) == 1) {
          m = (PLFLT) m1;
          return (m);
      }
      fprintf(stdout, "No value or value out of range; please try again\n");
#endif
    }
    plexit("Too many tries.");
    return (0.);
}

/*--------------------------------------------------------------------------*\
 * plstrdup()
 *
 * A replacement for strdup(), which isn't portable.
 * Caller responsible for freeing the allocated memory.
\*--------------------------------------------------------------------------*/

char *
plstrdup(const char *src)
{
    char *dest = (char *) malloc( (strlen(src) + 1) * sizeof(char) );
    if (dest != NULL)
      strcpy(dest, src);
    else
      plabort("Out of memory");

    return dest;
}

Generated by  Doxygen 1.6.0   Back to index