
#define VERSION  "1.0"                    /* don't touch this */
#define VDATE    "November 1, 1991"

/*
Name:
 vs2ps


---------------------------------------------------------------------------
Purpose:
---------------------------------------------------------------------------
 To produce a PostScript file containing one of the following:
 1. a contour plot of data defined on a two-dimensional "rectangular" grid 
    and/or a line drawing of this grid.  Input data is given in a single
    HDF file.
 2. a contour plot of data defined on a two-dimensional "curvilinear" grid
    and/or a line drawing of this grid.  Input data is given by two
    straight HDF files (grid only) or three HDF files (contour plot).
 3. a contour plot of data defined on a two-dimensional "finite element"
    grid consisting of triangles and/or quadrangles.  Also a line drawing of
    the vertex set can be produced.  Input data is given in a single
    HDF Vset file.

                                   ---------
       floating point data        |         |         PostScript contour plot
       (single HDF Vset file ---> |  vs2ps  | ------> and/or line drawing of
        or two/three HDF files    |         |         grid
        or single HDF file)        ---------


 The main advantages of this tool over others are (I think):
 a. The accuracy of the (linear) interpolation used for contouring.
    The tool rules out an artificial directional bias common in most other
    contouring packages by a special "saddle point" strategy.  Especially
    sharp/steep "ridges" in the data (not aligned with the grid) are handled
    more accurate than usual for linear methods. (Don't expect miracles from
    a linear technique though.)
 b. Contourplots of data defined on curvilinear or finite element grids are
    generated directly from this grid/data. No artificial interpolation to
    an intermediate rectangular grid is used (nor an artificial clipping polygon). 
    The result will be much more accurate for "difficult" data.
 c. The resulting (Encapsulated) PostScript file can be EDITED (etc.) with 
    Adobe's Illustrator 3.0. Direct routing to a PostScript printer is
    possible also.

---------------------------------------------------------------------------
Source Availability:
---------------------------------------------------------------------------
 This software was developed and made available by:

    Fred Walsteijn
    Institute for Marine and Atmospheric research
    University of Utrecht
    Princetonplein 5
    3584 CC Utrecht
    The Netherlands

    E-mail:  walsteyn@fys.ruu.nl

 You are free to use this software, however all commercial rights are reserved
 by Fred Walsteijn.  This software may not be sold or distributed for profit,
 or included with other software which is sold or distributed for profit
 without the prior permission of the author Fred Walsteijn.

 HDF and HDF Vset file format and interface developed at the National 
 Center for Supercomputing Applications at the University of Illinois at 
 Urbana-Champaign.

 FRED WALSTEIJN AND THE UNIVERSITIES OF ILLINOIS AND UTRECHT GIVE NO WARRANTY,
 EXPRESSED OR IMPLIED, FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED,
 INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF
 FITNESS FOR A PARTICULAR PURPOSE.


---------------------------------------------------------------------------
Syntax:
---------------------------------------------------------------------------
Read the source of function usage(), or compile this tool and execute it
without arguments.


---------------------------------------------------------------------------
Implementation Notes -1- :
---------------------------------------------------------------------------
The interpretation of the scale arrays in a common HDF file (to be used
for generation of a rectangular grid) is ambiguous.  vs2ps follows the 
interpretation as made in other NCSA tools if the -asris option is used.

--- About the "bug" concerning the -vs & -grid options --- 
The closepath-and-stroke PostScript macro ('s') as defined in the Adobe 
resources (version 1.1 and 3.0) is not handled correctly by Illustrator 3.0:
for each triangle and quadrangle drawn the closing line segment generated
by Illustrator is a spurious Bezier curve which gobbles up the last straight
line segment plotted.
Therefore the preprocessor macros PLOT_TRIANGLE and PLOT_QUADRANGLE given
below should explicitly draw four line segments if Illustrator readable/editable
PostScript is wanted.
I don't favour this solution since it would increase the output file size,
therefore let's wait for an Illustrator upgrade ...
Of course the current output files contain correct PostScript and print OK.

Note: Aldus Freehand 2.02 is able to correctly read/edit an Illustrator 1.1 
version of the file (if its creator is set to 'ARTY') !

---------------------------------------------------------------------------
Implementation Notes -2- : (copied from vs2ris.c)
---------------------------------------------------------------------------
The current "read_vset" can handle both LOCAL_INTTYPE and LOCAL_LONGTYPE
connectivity lists.  For small Vsets one can still use LOCAL_INTTYPE lists;
to read pre version 2.0 Vsets one certainly needs this capability, whereas
for large files LOCAL_LONGTYPE storage is mandatory.  Nowadays simulations
with more than 32767 gridpoints/vertices are quite common.  Such data sets
cannot be stored in a HDF Vset file without the use of LOCAL_LONGTYPE
connectivity lists.

I have used the undocumented but PUBLIC function VSsizeof() to distinguish
LOCAL_INTTYPE from LOCAL_LONGTYPE lists.  Without using this function only
the total vdata element size returned in arg vsize by VSinquire() is
available.  This however includes the size of other fields if present.
Thus the list element size is obtained only if the list were the only field
present in its vdata.  I consider this too restrictive, and therefore have
used VSsizeof() instead, and urge the NCSA developers to include this
function in the documentation.
 

---------------------------------------------------------------------------
"To do":
---------------------------------------------------------------------------
1. True line clipping (=expensive) instead of PS clip (=easy, but expensive
   ---and yieds large files---if only a tiny part of domain is visualized).
2. (vset_)contour:
   a. Handle non-convex quadrangles correctly (test as in vs2ris).
   b. New option -accu  ->  subdivides each quad into 4 tri. 
      Then saddle points coinciding with gridlines contoured most accurate.
      Leads to huge output files !
3. (vset_)labels:  Labeling with values not implemented.
4. captions:  Strings are not centered but flushed left.
5. (rect_,curvi_)grid:  -iskip, -jskip options not implemented.
6. captions, inittext, centertext:  Check if PS output needs polishing.
      Options -fontsize <npts> -labelfontsize <npts> ?  -> then automatic
      fontsize-dependent (Hor & Vert) centering needed also !
7. Allow specification of different penwidths for grid/boundary/contours.
8. Option -verbose: write contour levels and CI to stdout.
9. Option -zero <val>
   Draw contours with value>val in black; other contours in grey.
   This needs another contouring order: loop over contour value instead
   of over grid-cells.

---------------------------------------------------------------------------
Limitations & Bugs & ...:
---------------------------------------------------------------------------
1. The -vs option combined with -grid yields a PostScript file which is read
   incorrectly by Illustrator 3.0 (= Illustrator bug !?).
   Cf. "Implementation Notes 1". File prints OK.
2. Boundary of vertex set (option -boundary <greyvalue>) is drawn correctly
   only for sensible Vsets. See function vset_boundary(). Will not be modified.
3. Plateaux are not labeled. Will not be modified.

If you encounter a bug, report it (and a fix) to:
   Fred Walsteijn
   walsteyn@fys.ruu.nl
 

---------------------------------------------------------------------------
History:
---------------------------------------------------------------------------
Oct   5, 1991 -- last development version
Oct  23, 1991 -- second beta version
Nov   1, 1991 -- declaration of connectivity lists modified: now runs on Cray

      (by  Fred Walsteijn -- walsteyn@fys.ruu.nl)

*/

/* ---------------------------- includes --------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
#include <string.h>
#include <time.h>

/* Addition made by Pekka Janhunen (for UNICOS 6.0): */
#ifdef UNICOS
#include <sys/types.h>
#endif
/* 2nd addition made by Pekka Janhunen (for SUN): */
#ifdef SUN
#define FILENAME_MAX 256
#define EXIT_FAILURE -1
#endif

#include "vg.h"

/* ---------------------------- macros --------------------------- */

#undef malloc
#undef free      
       /* ^ the NCSA def of free(p) for the Mac fails if p==NULL */

#define DATA(i,j)  *(in.data + (i)*in.dim1 + (j))
#define X(i,j)     *(in.x + (i)*in.dim1 + (j))
#define Y(i,j)     *(in.y + (i)*in.dim1 + (j))

/* These macros are efficient for complex args.
 * Calling routines should define float tmp.
 */

#define ROUNDUP( x )     ( tmp=(x), tmp<0? (int32)tmp   :(int32)tmp+1 )
#define ROUNDDOWN( x )   ( tmp=(x), tmp<0? (int32)tmp-1 :(int32)tmp   )
#define SMALL            0.001       /* Needed to avoid rounding problems */

/* These min/max macros are efficient only if invoked with simple first args
 * (second may be complex):
 */

#define MIN(a,b)         ( tmp=(b), (a)<tmp?(a):tmp )
#define MAX(a,b)         ( tmp=(b), (a)>tmp?(a):tmp )

/* These min/max macros should only be invoked with simple args: */

#define MIN2(a,b)        ( (a)<(b)?(a):(b) )
#define MAX2(a,b)        ( (a)>(b)?(a):(b) )
#define MIN3(a,b,c)      ( (a)<(b)?   MIN2(a,c)  :  MIN2(b,c) )
#define MAX3(a,b,c)      ( (a)>(b)?   MAX2(a,c)  :  MAX2(b,c) )
#define MIN4(a,b,c,d)    ( (a)<(b)?                                                     \
                                   ((a)<(c)?  ((a)<(d)?(a):(d))  :  ((c)<(d)?(c):(d)) ) \
                                  :                                                     \
                                   ((b)<(c)?  ((b)<(d)?(b):(d))  :  ((c)<(d)?(c):(d)) ) )
#define MAX4(a,b,c,d)    ( (a)>(b)?                                                     \
                                   ((a)>(c)?  ((a)>(d)?(a):(d))  :  ((c)>(d)?(c):(d)) ) \
                                  :                                                     \
                                   ((b)>(c)?  ((b)>(d)?(b):(d))  :  ((c)>(d)?(c):(d)) ) )


/* ---------------------------- types, consts --------------------------- */

#define FNAME_MAX  FILENAME_MAX    /* maximum number of chars in filenames */
#define STR_MAX    64              /* maximum number of chars in strings */
#define CMAX       10              /* maximum number of connectivity lists to look for */

#define LOC_INT    int             /* the LOCAL_INTTYPE */
#define LOC_LONG   long            /* the LOCAL_LONGTYPE */

enum boolean  { FALSE, TRUE };
enum filetype { RECT, CRV, VSET };   /* input file(s) type: RECT==1*HDF, CRV==3*HDF, VSET==1*HDF_Vset */
enum elmtype  { INTTYPE, LONGTYPE }; /* integral type of HDF Vset connectivity lists:
                                        INTTYPE == LOCAL_INTTYPE, LONGTYPE == LOCAL_LONGTYPE */
struct Box {
  int xlo, xhi, ylo, yhi;   /* bounding box in integer default PS coords */
};


/*
 * structure definition for the input data
 */
struct Input {
  enum filetype ftyp;       /* ftyp == RECT then rectangular grid/data (from single HDF file)
                               ftyp == CRV then curvilinear grid/data (from two/three HDF files)
                               ftyp == VSET then vertex set (from single HDF Vset file) */
  int32 dim0, dim1;         /* input dimensions - ncols, nrows; RECT and CRV only */
  float32 *x;               /* horizontal coords of vertices */
  float32 *y;               /* vertical coords of vertices */
  float32 *data;            /* vertex data values */
                              /* RECT: x[dim0],       y[dim1],        data[dim0][dim1]
                               * CRV:  x[dim0][dim1], y[dim0][dim1],  data[dim0][dim1]
                               * VSET: x[nv],         y[nv],          data[nv]
                               */

  int32 nv,                 /* number of vertices, VSET only */
        ntri, nquad;        /* number of triangles, quadrangles in (s)plist3, (s)plist4 */
  enum elmtype etyp3,       /* etyp3 == INTTYPE then triangles in splist3; otherwise plist3 */
               etyp4;       /* etyp4 == INTTYPE then quadrangles in splist4; otherwise plist4 */
  LOC_LONG *plist3, *plist4;   /* pointers to LOCAL_LONGTYPE connectivity lists */
  LOC_INT *splist3, *splist4;  /* pointers to LOCAL_INTTYPE connectivity lists */
};


/*
 * structure definition for command line options
 */
struct Options {
  char infile[FNAME_MAX], 
       gridx[FNAME_MAX], 
       gridy[FNAME_MAX];      /* input HDF/HDF Vset file names */
  char outfile[FNAME_MAX];    /* output PostScript file name */
  FILE *psfile;               /* output file pointer */
  int ncon;                   /* number of connectivity lists specified, VSET only */
  char groupname[STR_MAX],  
       pxname[STR_MAX],
       pyname[STR_MAX],
       connectname[CMAX][STR_MAX],   
       scalarname[STR_MAX];   /* vgroup and vdata names to use, VSET only */
  enum boolean domain,        /* spatial (x,y-) domain is specified */
               asris,         /* put SDS[0][0] in upper left corner, RECT only */
               minmax,        /* datamin and datamax specified, contour==TRUE only */
               ratio,         /* output domain ratio of PostScript output is specified */
               connect,       /* connectivity list(s) was specified by user */
               contour,       /* generate contour plot */
               grid,          /* generate line drawing of grid */
               boundary,      /* draw boundary in contourplot */
               labels,        /* draw 'L','H'-label at local min,max in contourplot */
               values,        /* draw value-label at local extremes in contourplot */
               threshold,     /* threshold values for labeling specified by user */
               caption;       /* draw caption strings & box around line drawing/contourplot */
  char *topstring, *botstring; /* caption strings */
  int32 ncontours,            /* number of contourlevels to plot */
        iskip, jskip;         /* number of gridlines to skip in grid linedrawing */
  float grid_grey,            /* PostScript greylevel of grid line drawing */
        boun_grey;            /* PostScript greylevel of boundary line drawing */
  float32 xmin, xmax, 
          ymin, ymax,         /* domain geometry if domain==TRUE */
          ydivx,              /* requested domain ratio if ratio==TRUE */
          min_threshold,
          max_threshold,      /* thresholds for contour labeling */
          scalefactor;        /* multiplicative scalefactor for values to be plotted at extremes */
};

/* ---------------------------- globals --------------------------- */

float32 datamin, datamax;  /* selected data range */

struct Box bound,  /* union of plot,tops,bots: == PostScript %%BoundingBox */
           plot,   /* plot domain BB */
           tops,   /* topstring BB */
           bots;   /* botstring BB */



/* ---------------------------- prototypes --------------------------- */

void quit( char *msg );
void clear( struct Input *in, struct Options *opt );
void atexit_free( struct Input *in );
void free_mem( void );
void usage( char *tool );
void parse_cmd( int argc, char *argv[], struct Input *in, struct Options *opt );

#ifdef DEBUGGING
  void write_opt( struct Options opt );
  void write_in( struct Input in );
#endif

void rotate( struct Input *in, struct Options *opt );
void read_rect( struct Input *in, struct Options *opt );
void read_crv( struct Input *in, struct Options *opt );
void read_vset( struct Input *in, struct Options *opt );
void gminmax( float32 *xmin, float32 *xmax, float32 *x, int32 dim );

void rect_grid( struct Input in, struct Options opt );
void rect_boundary( struct Input in, struct Options opt );
void curvi_grid( struct Input in, struct Options opt );
void curvi_boundary( struct Input in, struct Options opt );
void contour( struct Input in, struct Options opt );
void vset_grid( struct Input in, struct Options opt );
void vset_contour( struct Input in, struct Options opt );
void vset_boundary( struct Input in, struct Options opt );
int listcomp( int32 a[2], int32 b[2] );

void labels( struct Input in, struct Options opt );
void vset_labels( struct Input in, struct Options opt );
void inittext( FILE *psfile );
void centertext( FILE *psfile, float32 x, float32 y, char *text );


void pshead( struct Options opt );
void pstail( struct Options opt );
void captions( struct Options opt );
void beginobject( FILE *psfile, char *objname );
void endobject( FILE *psfile );
void ps_coords( struct Input in, struct Options opt );

void adobe( FILE *psfile );
void adobe_part1( FILE *psfile );
void adobe_part2( FILE *psfile );



/* ---------------------------- HDF 3.10 prototypes --------------------------- */

/*  DF *DFopen( char *name, int access, int ndds ); ---  already oldstyle prototyped in df.h !!! */
int DFclose( DF *dfile );
int DFSDgetdims( char *filename, int *prank, int32 sizes[], int maxrank );
int DFSDgetmaxmin( float32 *pmax, float32 *pmin );
int DFSDgetdata( char *filename, int rank, int32 maxsizes[], float32 data[] );
int DFSDgetdimscale();                             /* <--- MAKE THIS NEWSTYLE !!! */



/* ---------------------------- HDF Vset 2.0 prototypes --------------------------- */

VGROUP *Vattach( DF *f, int vgid, char *accesstype );  
void Vdetach( VGROUP *vg );
void Vgetname( VGROUP *vg, char *vgname );
int Visvs( VGROUP *vg, int id );
int Vgetid( DF* f, int vgid );
int Vgetnext( VGROUP *vg, int id );
VDATA *VSattach( DF *f, int vsid, char *accesstype );
void VSdetach( VDATA *vs );
int VSread( VDATA *vs, unsigned char *buf, int32 nelt, int interlace );
int VSfexist( VDATA *vs, char *fields );
int VSinquire( VDATA *vs, int *nelt, int *interlace, char *fields, int *eltsize, char *vsname);
int VSsetfields( VDATA *vs, char *fields );
int VSsizeof( VDATA *vs, char *fields );



/*************************************************************************************************
 * main: Read a rectangular grid/data from a single HDF file, or a curvilinear grid/data         *
 * ----  from two/three HDF input files, or a finite element grid from a single HDF Vset file.   *
 *       Write a contourplot of the data and/or a line drawing of the grid to a PostScript file. *
 *************************************************************************************************/


main( int argc, char *argv[] )
{ 
  static struct Input in;             /* rectangular, curvilinear, or vset input data */
  struct Options opt;                 /* command line options */
  
  clear( &in, &opt );                 /* set all fields to 0, NULL, ... */
  atexit_free( &in );                 /* remember to free malloc-ed stuff */

  parse_cmd( argc, argv, &in, &opt ); /* get switches, filenames, ... */

#ifdef DEBUGGING
  write_opt( opt );
#endif

  if      ( in.ftyp == RECT )  read_rect( &in, &opt );      /* read 1 HDF file */
  else if ( in.ftyp == CRV )   read_crv(  &in, &opt );      /* read 2/3 HDF files */
  else if ( in.ftyp == VSET )  read_vset( &in, &opt );      /* read single HDF Vset file */
  else
    quit("main: Unknown 'in.ftyp' value.");


  ps_coords( in, opt );          /* scale the input grid coords to output PS coordinate system */
  pshead( opt );                 /* write header of PS file: comment, prolog, setup sections
                                    including caption strings if requested */


  if ( opt.grid == TRUE )        /* generate linedrawing of grid */
  {
    beginobject( opt.psfile, "Grid Line Drawing" );
    if      ( in.ftyp == RECT )  rect_grid( in, opt );      
    else if ( in.ftyp == CRV )  curvi_grid( in, opt );
    else if ( in.ftyp == VSET )  vset_grid( in, opt );
    endobject( opt.psfile );
  }

  if ( opt.contour == TRUE )     /* generate contourplot of data */
  {
    beginobject( opt.psfile, "Contour Plot" );
    if      ( in.ftyp == RECT )      contour( in, opt );
    else if ( in.ftyp == CRV )       contour( in, opt );
    else if ( in.ftyp == VSET ) vset_contour( in, opt );
    endobject( opt.psfile );
  }


  if ( opt.boundary == TRUE )    /* draw boundary of domain */
  {
    beginobject( opt.psfile, "Grid Boundary Line Drawing" );
    if      ( in.ftyp == RECT )   rect_boundary( in, opt );
    else if ( in.ftyp == CRV )   curvi_boundary( in, opt );
    else if ( in.ftyp == VSET )   vset_boundary( in, opt );
    endobject( opt.psfile );
  }

  if ( opt.labels == TRUE || opt.values == TRUE )   /* label maxima and minima of contourplot */
  {
    beginobject( opt.psfile, "Contour Labels" );
    if      ( in.ftyp == RECT )        labels( in, opt );
    else if ( in.ftyp == CRV )         labels( in, opt );
    else if ( in.ftyp == VSET )   vset_labels( in, opt );
    endobject( opt.psfile );
  }


  pstail( opt );                 /* write trailer of PostScript file, and close it */

#ifdef DEBUGGING
  write_in( in );
#endif
  return 0;
}

/****************************************************
 *  quit:  Write message and terminate program.     *
 ****************************************************/

void quit( char *msg )
{
  printf("%s\n", msg );
  exit(EXIT_FAILURE);
}

/*******************************************************
 *  clear:  Sets struct fields and globals to 'zero'.  *
 *******************************************************/

void clear( struct Input *in, struct Options *opt )
{
  static char empty[] = "*EMPTY*";

  in->ftyp = CRV;
  in->x = in->y = in->data  = NULL;
  in->plist3  = in->plist4  = NULL;
  in->splist3 = in->splist4 = NULL;
  in->etyp3 = in->etyp4 = INTTYPE;
  in->dim0 = in->dim1 = in->ntri = in->nquad = in->nv = 0;

  opt->psfile = NULL;
/*  opt->topstring = opt->botstring = NULL; */

  opt->topstring = opt->botstring = empty;

  (void) strcpy( opt->infile,  "" );  /* just in case these are printed (DEBUGGING) but */
  (void) strcpy( opt->gridx,   "" );  /* are not applicable ... */
  (void) strcpy( opt->gridy,   "" );
  (void) strcpy( opt->outfile, "" );

  opt->scalefactor = opt->xmin = opt->xmax = opt->ymin = opt->ymax = opt->ydivx = 
                     opt->min_threshold = opt->max_threshold = 0.0;
}

/****************************************************************************
 *  atexit_free:  Store pointers to struct which will contain malloc-ed     *
 *                data.  By an atexit() call these data will automatically  *
 *                be free-ed on exit.                                       *
 ****************************************************************************/

/*  Global for atexit_free() and free_mem():  */

   struct Input *ptr_in;

void atexit_free( struct Input *in )
{
  extern struct Input *ptr_in;

  ptr_in = in;
  if ( 0 != atexit( free_mem ) )
    quit("atexit_free: atexit() failed.");
}

/**************************************************************************
 *  free_mem:  Free malloc-ed stuff.                                      *
 *             ASSUMES: every pointer has been malloc-ed, or equals NULL. *
 **************************************************************************/

void free_mem( void )
{
  extern struct Input *ptr_in;

  free( (void *) ptr_in->data );
  free( (void *) ptr_in->y );
  free( (void *) ptr_in->x );

  free( (void *) ptr_in->splist3 );
  free( (void *) ptr_in->plist3 );
  free( (void *) ptr_in->splist4 );
  free( (void *) ptr_in->plist4 );
}


/***************************************************************
 *  usage:  Give command line syntax and terminate program.    *
 ***************************************************************/

void usage( char *tool )
{
  printf( "\n\n");
  printf( "This is vs2ps (version: %s, dated: %s).\n", VERSION, VDATE );
  printf( "Generates a PostScript contourplot of data defined on a two-dimensional\n");
  printf( "finite element, curvilinear, or rectangular grid and/or a line drawing of\n");
  printf( "the grid.\n");
  printf( "By:  Fred Walsteijn,  University of Utrecht,  The Netherlands\n");
  printf( "                      (walsteyn@fys.ruu.nl)\n\n");

  printf( "SYNTAX:\n" );
  printf( "  %s  -vs <infile>  \n", tool );
  printf( "         -o <outfile>  [OPTIONS ...]  [VS-OPTIONS ...]\n\n" );

  printf( "OR:\n" );
  printf( "  %s  -crv2 <gridxfile> <gridyfile>\n", tool );
  printf( "         -o <outfile>  [OPTIONS ...]  [CR-OPTIONS ...]\n\n" );

  printf( "OR:\n" );
  printf( "  %s  -crv <datafile> <gridxfile> <gridyfile>\n", tool );
  printf( "         -o <outfile>  [OPTIONS ...]  [CR-OPTIONS ...]\n\n" );

  printf( "OR:\n" );
  printf( "  %s  -rect <infile> \n", tool );
  printf( "         -o <outfile>  [OPTIONS ...]  [CR-OPTIONS ...] [-asris]\n\n" );

  printf( "where:\n" );
  printf( "    -vs <infile>\n" );
  printf( "           Input HDF Vset file containing two-dimensional vertex grid/data.\n" );
  printf( "    -crv2 <gridxfile> <gridyfile>\n" );
  printf( "           Two HDF input files specifying a two-dimensional curvilinear grid.\n" );
  printf( "           The first file contains a single SDS describing the x coordinates,\n" );
  printf( "           the second file specifies the y coordinates of the gridpoints.\n" );
  printf( "    -crv <datafile> <gridxfile> <gridyfile>\n" );
  printf( "           Three HDF input files specifying a two-dimensional curvilinear grid/data.\n" );
  printf( "           Each file contains a single SDS describing respectively the data values,\n" );
  printf( "           x coordinates, and y coordinates of the gridpoints.\n" );
  printf( "    -rect <infile>\n" );
  printf( "           Single HDF input files specifying a two-dimensional rectangular grid/data.\n" );
  printf( "           If scale arrays are present these are used to define the rectangular grid.\n" );
  printf( "    -asris\n" );
  printf( "           Always put first element of SDS in upper left corner, as in NCSA tools\n");
  printf( "           generating a RIS. Default: interpret scale arrays as true x,y coords.\n" );
  printf( "    -o <outfile>\n" );
  printf( "           PostScript output file containing contour plot and/or grid line drawing.\n" );

  printf( "OPTIONS:\n" );
  printf( "    -domain <xmin> <xmax> <ymin> <ymax>\n" );
  printf( "           Specifies the domain part to be visualized. Allows zooming in/out.\n" );
  printf( "           Also determines the output line drawing aspect ratio, if this\n" );
  printf( "           ratio is not set explicitly using the option -ratio.\n" );
  printf( "           Default: min/max of coordinates in input file(s).\n" );
  printf( "    -ratio <ydivx>\n" );
  printf( "           Ratio of output plot domain/line drawing. Default: (ymax-ymin)/(xmax-xmin).\n" );
  printf( "    -range <datamin> <datamax>\n" );
  printf( "           Minimum resp maximum contour level. Default: min/max of input data.\n" );
  printf( "    -boundary <greylevel> | -noboundary\n" );
  printf( "           Draw grid boundary with PostScript greylevel | do not draw it.\n" );
  printf( "           Default: -boundary 0.0 (=black).\n" );
  printf( "    -grid <greylevel> | -nogrid\n" );
  printf( "           Draw grid with PostScript greylevel | do not draw it.\n" );
  printf( "           Default: for the -crv2 option: -grid 0.0 (=black), otherwise -nogrid.\n" );
  printf( "    -contour <ncontours> | -nocontour\n" );
  printf( "           Draw 'ncontours' contours | do not generate contour plot.\n" );
  printf( "           Default: the -crv2 option always implies -nocontour, otherwise -contour 11\n" );
  printf( "    -labels\n" );
  printf( "           Put labels 'L', 'H' at local minima, maxima outside the threshold-range.\n" );
  printf( "    -values <scalefactor>\n" );
  printf( "    -threshold <min_threshold> <max_threshold>\n" );
  printf( "           The threshold floats are used to determine if an extremum is to be labeled.\n" );
  printf( "           Default: datamin+0.3*(datamax-datamin),  datamin+0.7*(datamax-datamin)\n" );
  printf( "    -caption <topstring> <botstring>\n" );
  printf( "           Puts plot in box with a string above and below it.\n\n" );

  printf( "CR-OPTIONS:\n" );
  printf( "    -iskip <nri>\n" );
  printf( "    -jskip <nrj>\n" );
  printf( "           Number of gridlines to skip. Default: -iskip 0 -jskip 0\n\n" );

  printf( "VS-OPTIONS:\n" );
  printf( "    -group <groupname>\n" );
  printf( "           Name of vgroup in the Vset input file to read the vertex vdata(s)\n" );
  printf( "           from. Default: the first vgroup encountered in the Vset file.\n" );
  printf( "    -px <px_vdataname>\n" );
  printf( "           Name of vdata field containing x coords. Default: 'px'.\n" );
  printf( "    -py <py_vdataname>\n" );
  printf( "           Name of vdata field containing y coords. Default: 'py'.\n" );
  printf( "  { -connect <connect_vdataname> } ...\n" );
  printf( "           Name(s) of vdata field(s) containing a 'triangular' and/or\n" );
  printf( "           'quadrangular' connectivity list. Each field name must be\n" );
  printf( "           preceded by the flag -connect. From the specified connectivity\n" );
  printf( "           lists only the first two of different type encountered in the\n" );
  printf( "           Vset file are read: one list of triangles and/or a list of\n" );
  printf( "           quadrangles, which may contain triangles also (as in PolyView\n" );
  printf( "           the 4th vertex id should be zero). Other field names are skipped.\n" );
  printf( "           Default: -connect plist -connect plist3 -connect plist4\n" );
  printf( "    -scalar <scalar_vdataname>\n" );
  printf( "           Name of vdata field containing scalar values. Default: 'scalar'.\n\n" );

  printf( "NOTE: all options may be specified in an arbitrary order.\n\n" );
  exit(EXIT_FAILURE);
}


/*********************************************************
 *  parse_cmd:   Parse the command line. Defines:        *
 *                opt:     ALL fields,                   *
 *                in:      ftyp field only.              *
 *                globals: datamin, datamax.             *
 *********************************************************/

void parse_cmd( int argc, char *argv[], struct Input *in, struct Options *opt )
{
  enum boolean is_rect, is_crv, is_vs, outp;    /* mandatory switches */
  int          i, ibot, itop;              
  double       x0, x1, y0, y1, d0, d1, ydivx;   /* for sscanf() */

/* 
 *  Option defaults:
 */
  opt->domain = opt->minmax = opt->ratio  = opt->connect = opt->grid = opt->asris =
                opt->values = opt->labels = opt->caption = opt->threshold = FALSE;    
  opt->contour = opt->boundary = TRUE;

  opt->ncontours = 11;                    /* odd number so that zero contour will be plotted
                                             if datamin == -datamax */
  opt->iskip = opt->jskip = 0;            /* draw all gridlines */
  opt->grid_grey = opt->boun_grey = 0.0;  /* PostScript black */
  
/*
 *  Default vgroup and vdata names similar (not identical) to PolyView defaults:
 */
  (void) strcpy( opt->groupname, "" );                /* "the 1st vgroup" */
  (void) strcpy( opt->pxname, "px" );
  (void) strcpy( opt->pyname, "py" );
  (void) strcpy( opt->scalarname, "scalar" );
  (void) strcpy( opt->connectname[0], "plist" );
  (void) strcpy( opt->connectname[1], "plist3" );
  (void) strcpy( opt->connectname[2], "plist4" );
  opt->ncon = 0;                           /* init for loop below; final assignment is done later */

/*
 *  Local var init:
 */
  is_rect = is_crv = is_vs = outp = FALSE;

  if (argc<2) usage( argv[0] );

  for ( i=1; i<argc; i++ ) 
  {
    if ( !strcmp(argv[i],"-crv") &&  i<argc-3 )
    { is_crv = TRUE;
      (void) strcpy(opt->infile, argv[++i]);
      (void) strcpy(opt->gridx, argv[++i]);
      (void) strcpy(opt->gridy, argv[++i]);
    }

    else if ( !strcmp(argv[i],"-crv2") &&  i<argc-2 )
    { is_crv = TRUE;
      opt->contour = FALSE;
      opt->grid = TRUE;
      (void) strcpy(opt->gridx, argv[++i]);
      (void) strcpy(opt->gridy, argv[++i]);
    }

    else if ( !strcmp(argv[i],"-vs") &&  i<argc-1 )
    { is_vs = TRUE;
      (void) strcpy(opt->infile, argv[++i]);
    }

    else if ( !strcmp(argv[i],"-rect") &&  i<argc-1 )
    { is_rect = TRUE;
      (void) strcpy(opt->infile, argv[++i]);
    }

    else if ( !strcmp(argv[i],"-o") &&  i<argc-1 )
    { outp = TRUE;
      (void) strcpy(opt->outfile, argv[++i]);
      if ( (opt->psfile = fopen( opt->outfile, "w")) == NULL )
        quit("parse_cmd: Cannot create PostScript file.");
    }

    else if ( !strcmp(argv[i],"-domain") &&  i<argc-4 )
    { opt->domain = TRUE;
      if ( sscanf(argv[++i], "%lf", &x0) != 1  ||
           sscanf(argv[++i], "%lf", &x1) != 1  ||
           sscanf(argv[++i], "%lf", &y0) != 1  ||
           sscanf(argv[++i], "%lf", &y1) != 1  )    /* more checks below */
        quit("parse_cmd: Error in domain spec.");
      opt->xmin = x0;  opt->xmax = x1;
      opt->ymin = y0;  opt->ymax = y1;
    }

    else if ( !strcmp(argv[i],"-ratio") &&  i<argc-1 )
    { opt->ratio = TRUE;
      if ( sscanf(argv[++i], "%lf", &ydivx) != 1 )
        quit("parse_cmd: Error in ratio spec.");
      opt->ydivx = ydivx;
    }

    else if ( !strcmp(argv[i],"-range") &&  i<argc-2 )
    { opt->minmax = TRUE;
      if ( sscanf(argv[++i], "%lf", &d0) != 1  ||
           sscanf(argv[++i], "%lf", &d1) != 1  ||
           d0 >= d1                            )
        quit("parse_cmd: Error in range spec.");
      datamin = d0;
      datamax = d1;
    }

    else if ( !strcmp(argv[i],"-boundary") &&  i<argc-1 )
    { opt->boundary = TRUE;
      if ( sscanf(argv[++i], "%lf", &d0) != 1  ||
           d0 < 0.0  || d0 > 1.0               )
        quit("parse_cmd: Error in boundary grey level.");
      opt->boun_grey = d0;
    }

    else if ( !strcmp(argv[i],"-noboundary") )
      opt->boundary = FALSE;

    else if ( !strcmp(argv[i],"-grid") &&  i<argc-1 )
    { opt->grid = TRUE;
      if ( sscanf(argv[++i], "%lf", &d0) != 1  ||
           d0 < 0.0  || d0 > 1.0               )
        quit("parse_cmd: Error in grid grey level.");
      opt->grid_grey = d0;
    }

    else if ( !strcmp(argv[i],"-nogrid") )
      opt->grid = FALSE;

    else if ( !strcmp(argv[i],"-contour") &&  i<argc-1 )
    { opt->contour = TRUE;
      if ( sscanf(argv[++i], "%d", &opt->ncontours) != 1  ||
           opt->ncontours < 1  || opt->ncontours > 1000   )
        quit("parse_cmd: Error in contour spec.");
    }

    else if ( !strcmp(argv[i],"-nocontour") )
      opt->contour = FALSE;

    else if ( !strcmp(argv[i],"-asris") )
      opt->asris = TRUE;

    else if ( !strcmp(argv[i],"-labels") )
      opt->labels = TRUE;

    else if ( !strcmp(argv[i],"-values") &&  i<argc-1 )
    { opt->values = TRUE;
      if ( sscanf(argv[++i], "%lf", &d0) != 1  ||
           d0 == 0.0                            )
        quit("parse_cmd: Error in scalefactor spec.");
      opt->scalefactor = d0;
    }

    else if ( !strcmp(argv[i],"-threshold") &&  i<argc-2 )
    { opt->threshold = TRUE;
      if ( sscanf(argv[++i], "%lf", &d0) != 1  ||
           sscanf(argv[++i], "%lf", &d1) != 1  ||
           d0 >= d1                            )
        quit("parse_cmd: Error in threshold spec.");
      opt->min_threshold = d0;
      opt->max_threshold = d1;
    }

    else if ( !strcmp(argv[i],"-caption") &&  i<argc-2 )
    { opt->caption = TRUE;
      itop = strlen( argv[i+1] ) + 1;
      ibot = strlen( argv[i+2] ) + 1;
      if ( (opt->topstring = (char *) malloc( (size_t)(itop*sizeof(char)))) == NULL ||
           (opt->botstring = (char *) malloc( (size_t)(ibot*sizeof(char)))) == NULL )
        quit("parse_cmd: Cannot allocate mem for caption strings.");
      (void) strcpy( opt->topstring, argv[++i]);
      (void) strcpy( opt->botstring, argv[++i]);
    }

    else if ( !strcmp(argv[i],"-iskip") &&  i<argc-1 )
    { if ( sscanf(argv[++i], "%d", &opt->iskip) != 1  || opt->iskip < 0  )
        quit("parse_cmd: Error in iskip spec.");
    }

    else if ( !strcmp(argv[i],"-jskip") &&  i<argc-1 )
    { if ( sscanf(argv[++i], "%d", &opt->jskip) != 1  || opt->jskip < 0  )
        quit("parse_cmd: Error in jskip spec.");
    }

    else if (!strcmp(argv[i],"-group") && i<argc-1 )
      (void) strcpy(opt->groupname,argv[++i]);
    else if (!strcmp(argv[i],"-px") && i<argc-1 )
      (void) strcpy(opt->pxname,argv[++i]);
    else if (!strcmp(argv[i],"-py") && i<argc-1 )
      (void) strcpy(opt->pyname,argv[++i]);
    else if (!strcmp(argv[i],"-scalar") && i<argc-1 )
      (void) strcpy(opt->scalarname,argv[++i]);
    else if (!strcmp(argv[i],"-connect") && i<argc-1 && opt->ncon < CMAX )
    { opt->connect = TRUE;
      (void) strcpy(opt->connectname[opt->ncon++],argv[++i]);
    }

    else
    { printf("parse_cmd: Unknown flag(s) encountered.\n");
      usage( argv[0] );
    }
  }

/*
 *  Do final checks on the input:
 */

  if ( outp == FALSE )
    quit("parse_cmd: Output file is mandatory. Use option -o.\n");

  if ( opt->connect == FALSE ) 
    opt->ncon = 3;            /* use the three default connectivity lists */
  else if ( opt->ncon > 2 )
    printf("parse_cmd: Warning: at most 2 connectivity lists will be used.\n");


  if ( is_crv == TRUE )       /* Two/three straight HDF files specified. */
  { if ( is_vs == TRUE || is_rect == TRUE )      /* Oops ... */
      quit("parse_cmd: Use only one of -vs, -crv, -crv2, -rect.\n");
    in->ftyp = CRV;
  }
  else if ( is_vs == TRUE )   /* Single HDF Vset file specified. */
  { if ( is_rect == TRUE )                       /* Oops ... */
      quit("parse_cmd: Use only one of -vs, -crv, -crv2, -rect.\n");
    in->ftyp = VSET;
  }
  else if ( is_rect == TRUE )   /* Single HDF file specified. */
    in->ftyp = RECT;
  else
    quit("parse_cmd: Input file(s) are mandatory. Use one of -vs, -crv, -crv2, -rect.\n");

  if ( opt->domain == TRUE &&
       (in->ftyp != RECT  ||  opt->asris == FALSE) &&    /* case asris==TRUE: checked in read_rect() */
       (opt->xmin >= opt->xmax  ||  opt->ymin >= opt->ymax)  ) 
    quit("parse_cmd: Error in domain spec.");
}





#ifdef DEBUGGING
       /* ^ needed since DEC Ultrix C cannot handle the macros below ... (sigh) */

/***********************************************************************
 * write_opt:  Write contents of 'opt' and some global vars to stdout. *
 *             For debugging.                                          *
 ***********************************************************************/

#define pr_bool( arg )   printf(#arg " = %s\n", (arg==FALSE)?"FALSE":((arg==TRUE)?"TRUE":"*TRASH*") )
#define pr_float( arg )  printf(#arg " = %lf\n", (double)arg )
#define pr_int( arg )    printf(#arg " = %ld\n", (long)arg )
#define pr_string( arg ) printf(#arg " = %s\n", arg )
#define pr_ftyp( arg )   printf(#arg " = %s\n", (arg==RECT)?"RECT":((arg==CRV)?"CRV":((arg==VSET)?"VSET":"*TRASH*")) )
#define pr_etyp( arg )   printf(#arg " = %s\n", (arg==INTTYPE)?"INTTYPE":((arg==LONGTYPE)?"LONGTYPE":"*TRASH*") )

void write_opt( struct Options opt )
{
  int i;

  printf("\n\n");
  printf("write_opt: --- globals ---\n");
  pr_float( datamin );
  pr_float( datamax );

  printf("\n\n");
  printf("write_opt: --- contents of 'struct Options opt' ---\n");

  pr_string( opt.infile );
  pr_string( opt.gridx );
  pr_string( opt.gridy );
  pr_string( opt.outfile );

  pr_string( opt.groupname );
  pr_string( opt.pxname );
  pr_string( opt.pyname );
  pr_string( opt.scalarname );
  pr_int( opt.ncon );
  for (i=0; i<opt.ncon; i++)
    printf("opt.connectname[%d] = %s\n", i, opt.connectname[i] );

  pr_bool( opt.domain );
  pr_bool( opt.asris );
  pr_bool( opt.minmax );
  pr_bool( opt.ratio );
  pr_bool( opt.connect );
  pr_bool( opt.contour );
  pr_bool( opt.grid );
  pr_bool( opt.boundary );
  pr_bool( opt.labels );
  pr_bool( opt.values );
  pr_bool( opt.threshold );
  pr_bool( opt.caption );

  pr_string( opt.topstring );
  pr_string( opt.botstring ); 

  pr_int( opt.ncontours );
  pr_int( opt.iskip );
  pr_int( opt.jskip );

  pr_float( opt.grid_grey );
  pr_float( opt.boun_grey );
  pr_float( opt.xmin );
  pr_float( opt.xmax );
  pr_float( opt.ymin );
  pr_float( opt.ymax );
  pr_float( opt.ydivx );
  pr_float( opt.min_threshold );
  pr_float( opt.max_threshold );
  pr_float( opt.scalefactor );

  printf("\n\n");
}

/************************************************************
 * write_in:   Write contents of 'in' to stdout.            *
 *             For debugging.                               *
 ************************************************************/

void write_in( struct Input in )
{
  printf("\n\n");
  printf("write_in: --- contents of 'struct Input in' ---\n");

  pr_ftyp( in.ftyp );

  pr_int( in.dim0 );
  pr_int( in.dim1 );

  pr_int( in.nv );
  pr_int( in.ntri );
  pr_int( in.nquad );

  pr_etyp( in.etyp3 );
  pr_etyp( in.etyp4 );

/*  if ( in.ftyp==VSET && in.etyp4==LONGTYPE )
 *  {
 *    int32 i, p1, p2, p3, p4;

 *    printf("ARRAY --- int32 *in.plist4:\n");
 *    for ( i=0; i<in.nquad; i++ )
 *    { p1 = *in.plist4++; 
 *      p2 = *in.plist4++; 
 *      p3 = *in.plist4++; 
 *      p4 = *in.plist4++; 
 *      printf("%ld,%ld,%ld,%ld\n", (long)p1, (long)p2, (long)p3, (long)p4 );
 *    }
 *  }
 */

  printf("\n\n");
}

#endif /* DEBUGGING */



/*********************************************************************************
 * rotate:  Rotates struct in, and the 'domain' reals of struct opt. RECT only ! * 
 *********************************************************************************/

#define  ROT(i,j)  *(in->data + (newdim1-j-1)*newdim0 + (i))   /* == DATA(newdim1-j-1,i) */

void rotate( struct Input *in, struct Options *opt )
{
  float32 *rot, *rotcp, *datcp, *swap, s;
  int     incr;
  int32   newdim0, newdim1, i, j;

  if ( in->ftyp != RECT ) quit("rotate: Illegal input data type.");

  newdim0 = in->dim1;
  newdim1 = in->dim0;

  if ( opt->contour == TRUE )
  {
    /* 
     *  Step 1:  Rotate 'in->data' such that element [0][0] becomes [...][...]
     */

    /* rotation needs temp array: */
   
    if ((rot = (float32 *) malloc((size_t) (in->dim0*in->dim1*sizeof(float32)) )) == NULL)
      quit("read_rect: Cannot allocate mem for temporary array 'rot'.");

    rotcp = rot;

    for ( i=0; i<newdim0; i++ )             /* store rotated in->data in rot */
      for ( j=0; j<newdim1; j++ )
        *rotcp++ = ROT(i,j);           

    rotcp = rot;
    datcp = in->data;

    for ( i=0; i<newdim0*newdim1; i++ )     /* copy it back to in->data */
      *datcp++ = *rotcp++;

    free( (void *) rot );
  }


  in->dim0 = newdim0;
  in->dim1 = newdim1;

  /*
   *  Step 2:  'rotate' the scale arrays (exchange them; modify their monotonicity properties)
   */

  /* first, simply swap pointers (dim0 and dim1 fields have been swapped already): */

  swap  = in->x;
  in->x = in->y;
  in->y = swap;

  /* only the y-scale must be physically 'mirrored': */

  for ( j=0; j<in->dim1/2; j++ )
  {
    s                   = in->y[j];                  /* s: swap var */
    in->y[j]            = in->y[in->dim1-j-1];
    in->y[in->dim1-j-1] = s;
  }


  /* If either scale array is decreasing with its index, flip its sign such that
     it will be monotonically increasing.
     Modify xmin, xmax, ymin, ymax -fields of opt correspondingly:
   */

  incr = in->x[0] < in->x[1];

  for ( i=1; i<in->dim0-1; i++ )              /* this check should be redundant */
    if ( incr != (in->x[i] < in->x[i+1]) )
      quit("read_rect: Hor-scale array not strictly monotonic.");

  if ( !incr )
  { for ( i=0; i<in->dim0; i++ )
      in->x[i] = - in->x[i];                  /* now the x-array is monot.increasing */
    if ( opt->domain == TRUE )
    { opt->xmin = - opt->xmin;
      opt->xmax = - opt->xmax;
    }
  }

  incr = in->y[0] < in->y[1];

  for ( j=1; j<in->dim1-1; j++ )
    if ( incr != (in->y[j] < in->y[j+1]) )
      quit("read_rect: Vert-scale array not strictly monotonic.");

  if ( !incr )
  { for ( j=0; j<in->dim1; j++ )
      in->y[j] = - in->y[j];
    if ( opt->domain == TRUE )
    { opt->ymin = - opt->ymin;
      opt->ymax = - opt->ymax;
    }
  }
}


/**************************************************************************
 * read_rect:  Reads rectangular grid from single HDF file containing     *
 *             two scale arrays.                                          *
 **************************************************************************/

void read_rect( struct Input *in, struct Options *opt )
{
  int   rank;
  int32 hdfdims[2], i, j;
  
  if ( DFSDgetdims(opt->infile, &rank, hdfdims, 2) ) 
    quit("read_rect: Unable to get dimensions from input file.");

  if( rank != 2)
    quit("read_rect: Invalid array rank in input file.");

  if ((hdfdims[0] < 2) || (hdfdims[1] < 2)) 
    quit("read_rect: Dimension(s) less than '2' in input file.");

  in->dim0 = hdfdims[0];
  in->dim1 = hdfdims[1];

  if ( opt->contour == TRUE && 
       opt->minmax == FALSE && 
       !DFSDgetmaxmin(&datamax, &datamin) &&
       datamax > datamin)  
    opt->minmax = TRUE;

  if ((in->x    = (float32 *) malloc((size_t) (in->dim0*sizeof(float32)) )) == NULL || 
      (in->y    = (float32 *) malloc((size_t) (in->dim1*sizeof(float32)) )) == NULL || 
      opt->contour == TRUE && 
      (in->data = (float32 *) malloc((size_t) (in->dim0*in->dim1*sizeof(float32)) )) == NULL)
    quit("read_rect: Cannot allocate mem for scale arrays and/or data.");

  if (DFSDgetdimscale(1, in->dim0, in->x)  ||
      DFSDgetdimscale(2, in->dim1, in->y)  )
  { 
    printf("read_rect: Warning: Unable to get TWO scale arrays from input file. Using equidistant scales instead." );
    for ( i=0; i<in->dim0; i++ )  in->x[i] = i;
    for ( j=0; j<in->dim1; j++ )  in->y[j] = j; 
  }

  if ( opt->contour == TRUE  &&
       DFSDgetdata(opt->infile, 2, hdfdims, in->data) ) 
    quit("read_rect: Unable to get SDS from input file." );

  if ( opt->asris == TRUE )   /* ensures that contourplot/griddrawing has orientation of NCSA-RIS output */
    rotate( in, opt );

  if ( opt->domain == FALSE )
  { gminmax( &opt->xmin, &opt->xmax, in->x, in->dim0 );
    gminmax( &opt->ymin, &opt->ymax, in->y, in->dim1 );
  }
  if ( opt->contour == TRUE  &&  opt->minmax == FALSE )  
    gminmax( &datamin, &datamax, in->data, in->dim0*in->dim1 );

  if (datamin >= datamax)
    quit("read_rect: Data max/min err.");

  if (opt->xmin >= opt->xmax || opt->ymin >= opt->ymax) 
    quit("read_rect: Domain spec err."); 

  if ( opt->ratio == FALSE )
    opt->ydivx = (opt->ymax - opt->ymin) / (opt->xmax - opt->xmin);

  if ( opt->ydivx < 1E-3 || opt->ydivx > 1E3) 
    quit("read_rect: Ratio too small/large.");

  if ( opt->threshold == FALSE )
  { opt->min_threshold = datamin + 0.3*(datamax-datamin);
    opt->max_threshold = datamin + (1-0.3)*(datamax-datamin);
  }

}

/***********************************************************************************
 * read_crv:  Reads curvilinear data from two/three straight HDF files containing  *
 *            one SDS each:  data and/or gridx, gridy.                             *
 ***********************************************************************************/

void read_crv( struct Input *in, struct Options *opt )
{
  int   rank;
  int32 hdfdims[2];
  
  if ( DFSDgetdims(opt->gridx, &rank, hdfdims, 2) ) 
    quit("read_crv: Unable to get dimensions from gridx file.");

  if( rank != 2)
    quit("read_crv: Invalid array rank in gridx file.");

  if ((hdfdims[0] < 2) || (hdfdims[1] < 2)) 
    quit("read_crv: Dimension(s) less than '2' in gridx file.");

  in->dim0 = hdfdims[0];
  in->dim1 = hdfdims[1];

  if ( DFSDgetdims(opt->gridy, &rank, hdfdims, 2) ||
       rank != 2              || 
       hdfdims[0] != in->dim0 || 
       hdfdims[1] != in->dim1    ) 
    quit("read_crv: Gridy file incompatible with gridx file.");

  if ( opt->contour == TRUE &&
       DFSDgetdims(opt->infile, &rank, hdfdims, 2) ||
       rank != 2              || 
       hdfdims[0] != in->dim0 ||
       hdfdims[1] != in->dim1    ) 
    quit("read_crv: Data file incompatible with grid files.");

  if ( opt->contour == TRUE && 
       opt->minmax == FALSE &&
       !DFSDgetmaxmin(&datamax, &datamin) &&
       datamax > datamin )  
    opt->minmax = TRUE;

  if ((in->x = (float32 *) malloc((size_t) (in->dim0*in->dim1*sizeof(float32)) )) == NULL || 
      (in->y = (float32 *) malloc((size_t) (in->dim0*in->dim1*sizeof(float32)) )) == NULL || 
      opt->contour == TRUE &&
      (in->data = (float32 *) malloc((size_t) (in->dim0*in->dim1*sizeof(float32)) )) == NULL)
    quit("read_crv: Cannot allocate mem for grid/data.");

  if (opt->contour == TRUE &&
      DFSDgetdata(opt->infile, 2, hdfdims, in->data) ||
      DFSDgetdata(opt->gridx, 2, hdfdims, in->x)     ||
      DFSDgetdata(opt->gridy, 2, hdfdims, in->y)     ) 
    quit("read_crv: Unable to get SDS from input file(s)." );

  if ( opt->domain == FALSE )
  { gminmax( &opt->xmin, &opt->xmax, in->x, in->dim0*in->dim1 );
    gminmax( &opt->ymin, &opt->ymax, in->y, in->dim0*in->dim1 );
  }
  if ( opt->contour == TRUE && opt->minmax == FALSE )  
    gminmax( &datamin, &datamax, in->data, in->dim0*in->dim1 );

  if ( opt->contour == TRUE && datamin >= datamax)
    quit("read_crv: Data max/min err.");

  if (opt->xmin >= opt->xmax || opt->ymin >= opt->ymax)
    quit("read_crv: Domain spec err.");

  if ( opt->ratio == FALSE )
    opt->ydivx = (opt->ymax - opt->ymin) / (opt->xmax - opt->xmin);

  if ( opt->ydivx < 1E-3 || opt->ydivx > 1E3)
    quit("read_crv: Ratio too small/large.");

  if ( opt->threshold == FALSE )
  { opt->min_threshold = datamin + 0.3*(datamax-datamin);
    opt->max_threshold = datamin + (1-0.3)*(datamax-datamin);
  }
}

/****************************************************
 *  read_vset:  Read HDF Vset file.                 *
 ****************************************************/

void read_vset( struct Input *in, struct Options *opt )
{
  VGROUP *vg;    
  VDATA  *vs;
  DF     *f;
  char   vgname[STR_MAX], vsname[STR_MAX], fields[STR_MAX];
  int    vgid, vsid;
  enum boolean 
         found, pxfound, pyfound, scalarfound,
         connectfound[CMAX], confound, oneconfound, con3found, con4found;
  int    i, csize, vsize, interlace;
  int    nelements;            /* --- hopefully int==int32 ... --- */
  int32  npx, npy, nscalar;   


  if ( (f = DFopen( opt->infile, DFACC_ALL, 0 )) == NULL)
    quit("read_vset: Cannot open input file.");

  /*
   * Now try to find vgroup with name opt->groupname.  
   * If it equals "" (empty string) then the first vgroup is taken.
   */

  found = FALSE;
  vgid  = -1;

  while ((vgid = Vgetid(f,vgid)) != -1)
  { if ((vg = Vattach(f, vgid, "r")) != NULL)
    { Vgetname(vg, vgname);
      if (strcmp(vgname,opt->groupname) == 0 || strcmp(opt->groupname,"") == 0 )
      { found = TRUE;
        break;
      }
    }
    Vdetach(vg);
  }

  if ( found == FALSE )
    quit("read_vset: Group not found.");

  /*
   * Now scan all the vdatas in the vgroup found.
   * Read the px, py, connect, scalar fields if encountered.
   */

  pxfound = pyfound = scalarfound = 
            confound = oneconfound = con3found = con4found = FALSE;
  for (i=0; i<CMAX; i++)  connectfound[i] = FALSE;
  vsid = -1;

  while ((vsid = Vgetnext(vg, vsid)) != -1 &&
         (pxfound == FALSE || pyfound == FALSE || 
          (opt->contour == TRUE && scalarfound == FALSE) || confound == FALSE) )
  {
    if (Visvs(vg,vsid))             /* the vsid is indeed referring to a vdata */
    {
      if ((vs = VSattach( f, vsid, "r" )) != NULL &&
          VSinquire(vs, &nelements, &interlace, fields, &vsize, vsname) == 1 &&
          nelements > 0 )
      {
#ifdef DEBUGGING
        printf("vdata %s has %d elements of size %d, and fields %s\n",
                vsname, nelements, vsize, fields );
#endif
        if ( pxfound == FALSE &&
             VSfexist(vs, opt->pxname) == 1 && 
             VSsetfields(vs, opt->pxname) == 1 )
        {
          if ((in->x = (float32 *) malloc((size_t) (nelements*sizeof(float32)))) == NULL)
            quit("read_vset: Cannot allocate mem for px.");
          VSread(vs, (unsigned char *)in->x, nelements, NO_INTERLACE );
          pxfound = TRUE;
          npx = nelements;
#ifdef DEBUGGING
          printf("   px read\n" );
#endif
        }

        if ( pyfound == FALSE &&
             VSfexist(vs, opt->pyname) == 1 && 
             VSsetfields(vs, opt->pyname) == 1 )
        {
          if ((in->y = (float32 *) malloc((size_t) (nelements*sizeof(float32)))) == NULL)
            quit("read_vset: Cannot allocate mem for py.");
          VSread(vs, (unsigned char *)in->y, nelements, NO_INTERLACE );
          pyfound = TRUE;
          npy = nelements;
#ifdef DEBUGGING
          printf("   py read\n" );
#endif
        }

        if ( opt->contour == TRUE && 
             scalarfound == FALSE &&
             VSfexist(vs, opt->scalarname) == 1 && 
             VSsetfields(vs, opt->scalarname) == 1 )
        {
          if ((in->data = (float32 *) malloc((size_t) (nelements*sizeof(float32)))) == NULL)
            quit("read_vset: Cannot allocate mem for scalar.");
          VSread(vs, (unsigned char *)in->data, nelements, NO_INTERLACE );
          scalarfound = TRUE;
          nscalar = nelements;
#ifdef DEBUGGING
          printf("   scalar read\n" );
#endif
        }

        for (i=0; i < opt->ncon; i++)
          if ( connectfound[i] == FALSE &&
               VSfexist(vs, opt->connectname[i]) == 1 && 
               VSsetfields(vs, opt->connectname[i]) == 1 &&
               (csize = VSsizeof(vs, opt->connectname[i])) > 0)
          {
#ifdef DEBUGGING
            printf("   attempting conn list named %s\n", opt->connectname[i] );
            printf("              size found %d\n", csize );
#endif
            if ( con3found == FALSE &&
                 csize == 3*sizeof(LOC_INT) )       /* LOCAL_INTTYPE triangle conn list */
            {
              if ((in->splist3 = (LOC_INT *) malloc((size_t) ((int32)nelements*csize))) == NULL)
                quit("read_vset: Cannot allocate mem for connectivity list.");
              VSread(vs, (unsigned char *)in->splist3, nelements, NO_INTERLACE );
              in->etyp3 = INTTYPE;
              in->ntri  = nelements;
              con3found = TRUE;
#ifdef DEBUGGING
              printf("              it's a tri-INTTYPE\n" );
#endif
            }
            else if ( con4found == FALSE &&
                      csize == 4*sizeof(LOC_INT) )  /* LOCAL_INTTYPE quadrangle conn list */
            {
              if ((in->splist4 = (LOC_INT *) malloc((size_t) ((int32)nelements*csize))) == NULL)
                quit("read_vset: Cannot allocate mem for connectivity list.");
              VSread(vs, (unsigned char *)in->splist4, nelements, NO_INTERLACE );
              in->etyp4 = INTTYPE;
              in->nquad = nelements;
              con4found = TRUE;
#ifdef DEBUGGING
              printf("              it's a quad-INTTYPE\n" );
#endif
            }
            else if ( con3found == FALSE &&
                      csize == 3*sizeof(LOC_LONG) )  /* LOCAL_LONGTYPE triangle conn list */
            {
              if ((in->plist3 = (LOC_LONG *) malloc((size_t) ((int32)nelements*csize))) == NULL)
                quit("read_vset: Cannot allocate mem for connectivity list.");
              VSread(vs, (unsigned char *)in->plist3, nelements, NO_INTERLACE );
              in->etyp3 = LONGTYPE;
              in->ntri  = nelements;
              con3found = TRUE;
#ifdef DEBUGGING
              printf("              it's a tri-LONGTYPE\n" );
#endif
            }
            else if ( con4found == FALSE &&
                      csize == 4*sizeof(LOC_LONG) )  /* LOCAL_LONGTYPE quadrangle conn list */
            {
              if ((in->plist4 = (LOC_LONG *) malloc((size_t) ((int32)nelements*csize))) == NULL)
                quit("read_vset: Cannot allocate mem for connectivity list.");
              VSread(vs, (unsigned char *)in->plist4, nelements, NO_INTERLACE );
              in->etyp4 = LONGTYPE;
              in->nquad = nelements;
              con4found = TRUE;
#ifdef DEBUGGING
              printf("              it's a quad-LONGTYPE\n" );
#endif
            }
            else
              quit("read_vset: Illegal or duplicate connectivity list.");
            connectfound[i] = TRUE;
          }
        confound = TRUE;
        oneconfound = FALSE;
        for (i=0; i < opt->ncon; i++)
        { confound = confound && connectfound[i];
          oneconfound = oneconfound || connectfound[i];
        }
        confound = confound || ( con3found && con4found );
      }
      VSdetach(vs);
    }
  }

  Vdetach(vg);
  DFclose(f);

  if ( pxfound == FALSE )  quit("read_vset: Cannot find px.");
  if ( pyfound == FALSE )  quit("read_vset: Cannot find py.");

  if ( opt->contour == TRUE && scalarfound == FALSE )  quit("read_vset: Cannot find scalar.");
  if ( oneconfound == FALSE )  quit("read_vset: Cannot find connect.");
  if ( confound == FALSE && opt->connect == TRUE )  
    printf("read_vset: Warning: Couldn't load/find all connect lists you specified.\n");

  if ( npx != npy )
    quit("read_vset: (px, py) have unequal lengths.");
  else if ( opt->contour == TRUE && npx != nscalar )
    quit("read_vset: (px, py, scalar) have unequal lengths.");
  else
    in->nv = npx;

  if ( opt->domain == FALSE )
  { gminmax( &opt->xmin, &opt->xmax, in->x, in->nv );
    gminmax( &opt->ymin, &opt->ymax, in->y, in->nv );
  }
  if ( opt->contour == TRUE && opt->minmax == FALSE )  
    gminmax( &datamin, &datamax, in->data, in->nv );
  if ( opt->contour == TRUE && datamin >= datamax)
    quit("read_vset: Data max/min err.");

  if (opt->xmin >= opt->xmax || opt->ymin >= opt->ymax)
    quit("read_vset: Domain spec err.");

  if ( opt->ratio == FALSE )
    opt->ydivx = (opt->ymax - opt->ymin) / (opt->xmax - opt->xmin);

  if ( opt->ydivx < 1E-3 || opt->ydivx > 1E3)
    quit("read_vset: Ratio too small/large.");

  if ( opt->threshold == FALSE )
  { opt->min_threshold = datamin + 0.3*(datamax-datamin);
    opt->max_threshold = datamin + (1-0.3)*(datamax-datamin);
  }
}

/*****************************************************************
 *  gminmax:  Determines min and max of float32 array x[dim].    *
 *****************************************************************/

void gminmax( float32 *xmin, float32 *xmax, float32 *x, int32 dim )
{
  for ( *xmin = *x, *xmax = *x; dim>1; dim-- )  
  {
    x++;
    if (*x < *xmin) *xmin = *x;
    else if (*x > *xmax) *xmax = *x;
  }
}


/***********************************************************************
 *               PostScript (ILLUSTRATOR) defines                      *
 ***********************************************************************/

#if ( defined(MAC) && defined(MPW) )
#pragma segment PSpart
#endif

/*
 * BEGIN_LN, LN, END_LN: flushes textlines to PS file. (for resources, etc)
 */

#define BEGIN_LN   (void)( 0                    /* dirty "do nothing" */
#define LN         ); (void) fprintf( psfile, "%s\n", 
#define END_LN     );

#define NEWPATH          /* NEWPATH == nothing for ILLUSTRATOR */
#define MOVETO( x, y )   (void) fprintf(psfile, "%3.2f %3.2f m\n", x, y )
#define LINETO( x, y )   (void) fprintf(psfile, "%3.2f %3.2f l\n", x, y )
#define CLOSE_AND_STROKE (void) fprintf(psfile, "s\n")
#define STROKE           (void) fprintf(psfile, "S\n")
#define SET_GREY( val )  (void) fprintf(psfile, "%3.3f G\n", val )

/*-----------------------------------------------------------------
   SEG: Draws the line xp,yp[i]--xp,yp[j].  
  -----------------------------------------------------------------*/
#define SEG( i,j )  { NEWPATH;                \
                      MOVETO( xp[i], yp[i] ); \
                      LINETO( xp[j], yp[j] ); \
                      STROKE;                 \
                    }

/*-----------------------------------------------------------------
   SEG2: Draws the lines xp,yp[i]--xp,yp[j]--xp,yp[k].  
  -----------------------------------------------------------------*/
#define SEG2( i,j,k )  { NEWPATH;                \
                         MOVETO( xp[i], yp[i] ); \
                         LINETO( xp[j], yp[j] ); \
                         LINETO( xp[k], yp[k] ); \
                         STROKE;                 \
                       }

/*-----------------------------------------------------------------
   PLOT_TRIANGLE: Draws the triangle specified by x[3],y[3].  
  -----------------------------------------------------------------*/
#define PLOT_TRIANGLE( x, y ) { NEWPATH;              \
                                MOVETO( x[0], y[0] ); \
                                LINETO( x[1], y[1] ); \
                                LINETO( x[2], y[2] ); \
                                CLOSE_AND_STROKE;     \
                              }

/*-----------------------------------------------------------------
   PLOT_QUADRANGLE: Draws the quadrangle specified by x[4],y[4].
                ASSUMES: THE FOUR VERTICES ARE ORDERED.
  -----------------------------------------------------------------*/

#define PLOT_QUADRANGLE( x, y ) { NEWPATH;              \
                                  MOVETO( x[0], y[0] ); \
                                  LINETO( x[1], y[1] ); \
                                  LINETO( x[2], y[2] ); \
                                  LINETO( x[3], y[3] ); \
                                  CLOSE_AND_STROKE;     \
                                }

/* Note: using a semi-colon (;) after invocations of PLOT_TRIANGLE
         or PLOT_QUADRANGLE is fatal in the context below. */


/************************************************************************
 * rect_grid:   Generates PostScript line drawing of rectangular grid.  *                                        *
 ************************************************************************/

void rect_grid( struct Input in, struct Options opt )
{
  FILE *psfile=opt.psfile;
  int32 i, j;

  /* 
   *  First draw the gridlines  i = const:
   */  

  for ( i=0; i<in.dim0; i++ ) 
  { 
    NEWPATH;
    MOVETO( in.x[i], in.y[0] );
    LINETO( in.x[i], in.y[in.dim1-1] );
    STROKE;
  }

  /* 
   *  Finally draw the gridlines  j = const:
   */  

  for ( j=0; j<in.dim1; j++ )
  {
    NEWPATH;
    MOVETO( in.x[0], in.y[j] );
    LINETO( in.x[in.dim0-1], in.y[j] );
    STROKE;
  }
}


/************************************************************************
 * curvi_grid:   Generates PostScript line drawing of curvilinear grid. *                                        *
 ************************************************************************/

void curvi_grid( struct Input in, struct Options opt )
{
  FILE *psfile=opt.psfile;
  int32 i, j;

  /* 
   *  First draw the gridlines  i = const:
   */  

  for ( i=0; i<in.dim0; i++ ) 
  { 
    NEWPATH;
    MOVETO( X(i,0), Y(i,0) );

    for ( j=1; j<in.dim1; j++ )
      LINETO( X(i,j), Y(i,j) );

    STROKE;
  }

  /* 
   *  Finally draw the gridlines  j = const:
   */  

  for ( j=0; j<in.dim1; j++ )
  {
    NEWPATH;
    MOVETO( X(0,j), Y(0,j) );

    for ( i=1; i<in.dim0; i++ )
      LINETO( X(i,j), Y(i,j) );

    STROKE;
  }
}

/***************************************************************************
 * vset_grid:   Generates PostScript line drawing of finite element grid.  *
 ***************************************************************************/

void vset_grid( struct Input in, struct Options opt )
{  
  FILE *psfile=opt.psfile;
  int32   i, j, ptr;
  float32 x[4], y[4];

  /*
   *  First the triangles.
   */

  for (i=0; i < in.ntri; i++)
  {
    for (j=0; j<3; j++)
    {
      ptr = (in.etyp3==INTTYPE)? in.splist3[3*i+j] - 1
                               : in.plist3[3*i+j] - 1;
      if ( ptr < 0  ||  ptr > in.nv-1 )
        quit("vset_grid: Connectivity list element out of range.");
      x[j] = in.x[ptr];
      y[j] = in.y[ptr];
    }

    PLOT_TRIANGLE( x, y )
  }

  /*
   *  Finally the quadrangles.
   */

  for (i=0; i < in.nquad; i++)
  {
    for (j=0; j<4; j++)
    {
      ptr = (in.etyp4==INTTYPE)? in.splist4[4*i+j] - 1
                               : in.plist4[4*i+j] - 1;
      if ( ptr < -1  ||  (ptr==-1 && j!=3)  ||  ptr > in.nv-1 )
        quit("vset_grid: Connectivity list element out of range.");
      x[j] = in.x[ptr];
      y[j] = in.y[ptr];
    }

    if ( ptr != -1 )
      PLOT_QUADRANGLE( x, y )
    else       /* last ptr == -1 means triangle stored in quadr conn list */
      PLOT_TRIANGLE( x, y )
  }
}


/**************************************************************************************
 * vset_contour:                                                                      *
 *          Generates contourplot of two dimensional vertex set.                      *
 *          USES GLOBALS:                                                             *
 *            float32 datamin, datamax;   (requested min/max contour level)           *
 *          ASSUMES:                                                                  *
 *            quadrangles (if present) are convex.                                    *
 **************************************************************************************/

void vset_contour( struct Input in, struct Options opt )
{
  FILE *psfile=opt.psfile;
  int32   ptr, ilo, ihi, i, j, ip, np, n, interv = opt.ncontours - 1;
  float32 xp[6], yp[6], x[6], y[6], c[6];
  float32 dz, clo, chi, elev, pdif, tmp;

/*
 * First some checks:
 */

  if (in.ftyp != VSET)             quit("vset_contour: Illegal input data type.");
  if (in.ntri < 1 && in.nquad < 1) quit("vset_contour: Not enough triangles/quadrangles.");
  if (datamax <= datamin)          quit("vset_contour: datamax <= datamin.");
  if (interv < 0)                  quit("vset_contour: Nr contours < 1.");

  dz = (datamax-datamin) / MAX2(1,interv);

  SET_GREY( 0.0 );  /* all contours are drawn black */

  /*
   *  First the triangles.
   */

  for (i=0; i < in.ntri; i++)
  {
    for (j=0; j<3; j++)
    {
      ptr = (in.etyp3==INTTYPE)? in.splist3[3*i+j] - 1
                               : in.plist3[3*i+j] - 1;
      if ( ptr < 0  ||  ptr > in.nv-1 )
        quit("vset_contour: Connectivity list element out of range.");
      x[j] = in.x[ptr];
      y[j] = in.y[ptr];
      c[j] = in.data[ptr];
    }
 
    /* --------------------- contour the TRIANGLE ------------------- */

    x[3] = x[0];
    y[3] = y[0];
    c[3] = c[0];

    clo = MIN3(c[0],c[1],c[2]);
    chi = MAX3(c[0],c[1],c[2]);
    ilo = MAX(0,      ROUNDUP(  (clo - datamin)/dz - SMALL) );  /* CHECK NECESSITY OF SMALL */
    ihi = MIN(interv, ROUNDDOWN((chi - datamin)/dz + SMALL) );

    for ( ip=ilo; ip<=ihi; ip++ )
    {
      elev = ip*dz + datamin;
      np = 0;             /* number of contour intersections with the triangle perimeter */
                                  
      /* Calculate the x,y coords of the contour intersections with the triangle perimeter:
       */

      for ( n=0; n<3; n++ )
      { 
        if ( elev > MIN2(c[n],c[n+1])  &&  elev <= MAX2(c[n],c[n+1]) )  /* what about rounding ??? */
        { pdif = ( elev - c[n] )/( c[n+1] - c[n] );
          if ( pdif < 0.0 ) pdif = 0.0;
          if ( pdif > 1.0 ) pdif = 1.0; /* force pdif in [0..1] -- copes with severe rounding problems */
          xp[np] = x[n] + ( x[n+1] - x[n] ) * pdif;  
          yp[np] = y[n] + ( y[n+1] - y[n] ) * pdif;
          np++;
        }
      }

      if (np==2) SEG( 0,1 )
#ifdef DEBUGGING
      else if (np != 0)  quit("vset_contour: This cannot happen (np not in {0,2,4}).");
#endif
    } 
  }

  /*
   *  Finally the quadrangles.
   */

  for (i=0; i < in.nquad; i++)
  {
    for (j=0; j<4; j++)
    {
      ptr = (in.etyp4==INTTYPE)? in.splist4[4*i+j] - 1
                               : in.plist4[4*i+j] - 1;
      if ( ptr < -1  ||  (ptr==-1 && j!=3)  ||  ptr > in.nv-1 )
        quit("vset_contour: Connectivity list element out of range.");
      x[j] = in.x[ptr];
      y[j] = in.y[ptr];
      c[j] = in.data[ptr];
    }

    if ( ptr != -1 )
    { /* ---------------- contour the QUADRANGLE (comments: see contour() block) ------- */

      x[4] = x[0];
      x[5] = (x[0]+x[1]+x[2]+x[3])/4;
      y[4] = y[0];
      y[5] = (y[0]+y[1]+y[2]+y[3])/4;
      c[4] = c[0];
      c[5] = (c[0]+c[1]+c[2]+c[3])/4;

      clo = MIN4(c[0],c[1],c[2],c[3]);
      chi = MAX4(c[0],c[1],c[2],c[3]);
      ilo = MAX(0,      ROUNDUP(  (clo - datamin)/dz - SMALL) ); 
      ihi = MIN(interv, ROUNDDOWN((chi - datamin)/dz + SMALL) );

      for ( ip=ilo; ip<=ihi; ip++ )
      {
        elev = ip*dz + datamin;
        np = 0; 
                                           
        for ( n=0; n<4; n++ )
        { 
          if ( elev > MIN2(c[n],c[n+1])  &&  elev <= MAX2(c[n],c[n+1]) ) 
          { pdif = ( elev - c[n] )/( c[n+1] - c[n] );
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0; 
            xp[np] = x[n] + ( x[n+1] - x[n] ) * pdif;  
            yp[np] = y[n] + ( y[n+1] - y[n] ) * pdif;
            np++;
          }
        }

        if (np==2) SEG( 0,1 )
        else if (np==4)
        {
          if( MIN2(c[5],c[1]) >= elev || MAX2(c[5],c[1]) < elev )
          {
            pdif = ( elev - c[0] )/( c[5] - c[0] );  
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0;
            xp[4] = x[0] + ( x[5] - x[0] ) * pdif;
            yp[4] = y[0] + ( y[5] - y[0] ) * pdif;
            pdif = ( elev - c[2] )/( c[5] - c[2] );
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0;
            xp[5] = x[2] + ( x[5] - x[2] ) * pdif;
            yp[5] = y[2] + ( y[5] - y[2] ) * pdif;

            SEG2( 0,4,3 )
            SEG2( 1,5,2 )
          }
          else
          {
            pdif = ( elev - c[1] )/( c[5] - c[1] );
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0;
            xp[4] = x[1] + ( x[5] - x[1] ) * pdif;
            yp[4] = y[1] + ( y[5] - y[1] ) * pdif;
            pdif = ( elev - c[3] )/( c[5] - c[3] );
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0;
            xp[5] = x[3] + ( x[5] - x[3] ) * pdif;
            yp[5] = y[3] + ( y[5] - y[3] ) * pdif;

            SEG2( 0,4,1 )
            SEG2( 2,5,3 )
          }
        }
#ifdef DEBUGGING
        else if (np != 0)  quit("vset_contour: This cannot happen (np not in {0,2,4}).");
#endif
      }
    }

    else       /* last ptr == -1 means triangle stored in quadr conn list */
    { 
      /* ------------------ contour the TRIANGLE (comments: see tri-block above) ------- */

      x[3] = x[0];
      y[3] = y[0];
      c[3] = c[0];

      clo = MIN3(c[0],c[1],c[2]);
      chi = MAX3(c[0],c[1],c[2]);
      ilo = MAX(0,      ROUNDUP(  (clo - datamin)/dz - SMALL) );
      ihi = MIN(interv, ROUNDDOWN((chi - datamin)/dz + SMALL) );

      for ( ip=ilo; ip<=ihi; ip++ )
      {
        elev = ip*dz + datamin;
        np = 0;
                                  
        for ( n=0; n<3; n++ )
        { 
          if ( elev > MIN2(c[n],c[n+1])  &&  elev <= MAX2(c[n],c[n+1]) ) 
          { pdif = ( elev - c[n] )/( c[n+1] - c[n] );
            if ( pdif < 0.0 ) pdif = 0.0;
            if ( pdif > 1.0 ) pdif = 1.0;   
            xp[np] = x[n] + ( x[n+1] - x[n] ) * pdif;  
            yp[np] = y[n] + ( y[n+1] - y[n] ) * pdif;
            np++;
          }
        }

        if (np==2) SEG( 0,1 )
#ifdef DEBUGGING
        else if (np != 0)  quit("vset_contour: This cannot happen (np not in {0,2}).");
#endif
      }
    }
  }
}

/**************************************************************************************
 * vset_boundary:                                                                     *
 *          Draws the boundary of two dimensional vertex set.                         *
 *          ASSUMES:                                                                  *
 *          - Each vertex (px,py) occurs only once in the coordinate vdatas.          *
 *          - The interior sides (called "line-segments" in this function) of any two *
 *            elements are EITHER disjunct OR identical.                              *
 **************************************************************************************/

#define EMPTY  -99   /* value of empty segment-entry in 'list' */


void vset_boundary( struct Input in, struct Options opt )
{
  FILE  *psfile=opt.psfile;
  int32 i, j, iseg, nseg, ptr[4];
  int32 (*list)[2];               /* array of line-segment indices */
  enum boolean interior;          /* "current line-segment is interior to domain" */

  nseg = 3*in.ntri + 4*in.nquad;  /* max number of line segments we can expect */

  if ( (list = (int32 (*)[2]) malloc( (size_t) (2*nseg*sizeof(int32)) )) == NULL )
    quit("vset_boundary: Cannot allocate mem for boundary connectivity list.");

  /*
   *  Step 1: Fill 'list' with ALL segments as specified in the connectivity list(s):
   */

  iseg = 0;  /* true nr of segments  --  iseg in [0..nseg]
                Differs from nseg iff triangles are specified in the quadrangle connectivity list */

  for (i=0; i < in.ntri; i++)    /* First the triangles. */
  {
    for (j=0; j<3; j++)
    {
      ptr[j] = (in.etyp3==INTTYPE)? in.splist3[3*i+j] - 1
                                  : in.plist3[3*i+j] - 1;
      if ( ptr[j] < 0  ||  ptr[j] > in.nv-1 )
        quit("vset_boundary: Connectivity list element out of range.");
    }
    list[iseg][0]   = MIN2( ptr[0], ptr[1] );    /* order each pair of list -- facilitates listcomp() */
    list[iseg++][1] = MAX2( ptr[0], ptr[1] );
    list[iseg][0]   = MIN2( ptr[1], ptr[2] );
    list[iseg++][1] = MAX2( ptr[1], ptr[2] );
    list[iseg][0]   = MIN2( ptr[2], ptr[0] );
    list[iseg++][1] = MAX2( ptr[2], ptr[0] );
  }

  for (i=0; i < in.nquad; i++)   /* Finally the quadrangles. */
  {
    for (j=0; j<4; j++)
    {
      ptr[j] = (in.etyp4==INTTYPE)? in.splist4[4*i+j] - 1
                                  : in.plist4[4*i+j] - 1;
      if ( ptr[j] < -1  ||  (ptr[j]==-1 && j!=3)  ||  ptr[j] > in.nv-1 )
        quit("vset_boundary: Connectivity list element out of range.");
    }

    if ( ptr[3] != -1 )  /* indeed a quadrangle */
    {
      list[iseg][0]   = MIN2( ptr[0], ptr[1] );
      list[iseg++][1] = MAX2( ptr[0], ptr[1] );
      list[iseg][0]   = MIN2( ptr[1], ptr[2] );
      list[iseg++][1] = MAX2( ptr[1], ptr[2] );
      list[iseg][0]   = MIN2( ptr[2], ptr[3] );
      list[iseg++][1] = MAX2( ptr[2], ptr[3] );
      list[iseg][0]   = MIN2( ptr[3], ptr[0] );
      list[iseg++][1] = MAX2( ptr[3], ptr[0] );
    }
    else       /* ptr[3] == -1 means triangle stored in quadr conn list */
               /* The difference between nseg and iseg originates here */
    {
      list[iseg][0]   = MIN2( ptr[0], ptr[1] );
      list[iseg++][1] = MAX2( ptr[0], ptr[1] );
      list[iseg][0]   = MIN2( ptr[1], ptr[2] );
      list[iseg++][1] = MAX2( ptr[1], ptr[2] );
      list[iseg][0]   = MIN2( ptr[2], ptr[0] );
      list[iseg++][1] = MAX2( ptr[2], ptr[0] );
    }
  }

  /*
   *  Step 2:  Sort the list containing iseg segments.
   */

#ifdef PRE_C89   /* some compilers cannot handle "const void *" casts */
  qsort( (void *)list, iseg, 2*sizeof(int32), (int (*)(void*,void*))listcomp );
#else
  qsort( (void *)list, iseg, 2*sizeof(int32), (int (*)(const void*,const void*))listcomp );
#endif

  /*
   *  Step 3:  Strip the list from all multiply occuring identical segments.
   */

  interior = FALSE;   /* loop init */

  for ( i=0; i<iseg-1; i++ )
    if ( listcomp( list[i], list[i+1] ) == 0 ) 
    { 
      list[i][0] = EMPTY;
      interior = TRUE;
    }
    else if ( interior == TRUE )
    { 
      list[i][0] = EMPTY;
      interior = FALSE;
    }

  if ( interior == TRUE ) list[iseg-1][0] = EMPTY;    /* last segment may be interior too */

  /*
   *  Step 4:  Plot all non-EMPTY segments in 'list'.  (Equals the set of boundaries
   *           of the vertex set if the coord vdatas of this vset do not contain
   *           multiple occurences of a single vertex.)
   */

  SET_GREY( opt.boun_grey );

  for ( i=0; i<iseg; i++ )
    if ( list[i][0] != EMPTY )  
    {
      NEWPATH;
      MOVETO( in.x[list[i][0]], in.y[list[i][0]] );
      LINETO( in.x[list[i][1]], in.y[list[i][1]] );
      STROKE;
    }

  SET_GREY( 0.0 );   /* restore to black */
}

/************************************************************************
 * listcomp:    Comparison function for qsort call in vset_boundary().  *
 ************************************************************************/

int listcomp( int32 a[2], int32 b[2] )
{
  if ( a[0]<b[0] )
    return -1;
  else if ( a[0]>b[0] )
    return 1;
  else if ( a[1]<b[1] )
    return -1;
  else if ( a[1]>b[1] )
    return 1;
  else
    return 0;
}

/**************************************************************************************
 * contour:                                                                           *
 *          Generates contourplot of two dimensional rectangular or curvilinear data. *
 *          USES GLOBALS:                                                             *
 *            float32 datamin, datamax;   (requested min/max contour level)           *
 *          ASSUMES:                                                                  *
 *            quadrangles are convex.                                                 *
 **************************************************************************************/

void contour( struct Input in, struct Options opt )
{
  FILE    *psfile = opt.psfile;
  int32   ilo, ihi, i, j, ip, np, n, interv = opt.ncontours - 1;
  float32 xp[6], yp[6], x[6], y[6], c[6];
  float32 dz, clo, chi, elev, pdif, tmp;

/*
 * First some checks:
 */

  if (in.ftyp != RECT && in.ftyp != CRV)  quit("contour: Illegal input data type.");
  if (in.dim0 < 2 || in.dim1 < 2)         quit("contour: Dim(s) too small.");
  if (datamax <= datamin)                 quit("contour: datamax <= datamin.");
  if (interv < 0)                         quit("contour: Nr contours < 1.");

  dz = (datamax-datamin) / MAX2(1,interv);

/*
 * Loop on cells to draw contours:
 */

  SET_GREY( 0.0 );  /* all contours are drawn black */

  for ( i=0; i<in.dim0-1; i++ )
    for ( j=0; j<in.dim1-1; j++ )
    {
      /*
       * First find lowest and highest corners.
       *
       * INDEX NOTATION USED BELOW FOR CURRENT QUADRANGLE:  
       *
       *      3 ----------- 2     
       *      !\           /!
       *      !  \       /  !
       *      !    \   /    !
       *      !      5      !
       *      !    /   \    !
       *      !  /       \  !
       *      !/           \!
       *     0==4 --------- 1
       *
       * (5= artificially constructed midpoint,
       *     to subdivide a quadrangle into four triangles.
       *     This is done/needed only if the data has a 
       *     saddle point in the current quadrangle.)
       */

      c[0] = DATA(i  ,j  );
      c[1] = DATA(i+1,j  );
      c[2] = DATA(i+1,j+1);
      c[3] = DATA(i  ,j+1);
      c[4] = c[0];
      c[5] = (c[0]+c[1]+c[2]+c[3])/4;

      clo = MIN4(c[0],c[1],c[2],c[3]);
      chi = MAX4(c[0],c[1],c[2],c[3]);
      ilo = MAX(0,      ROUNDUP(  (clo - datamin)/dz - SMALL) );  /* CHECK NECESSITY OF SMALL */
      ihi = MIN(interv, ROUNDDOWN((chi - datamin)/dz + SMALL) );

      /*
       * See if range of data values warrants any contour lines in this quad:
       */
      if (ihi >= ilo)
      {
        if (in.ftyp==RECT)
        { x[0] = in.x[i];
          x[1] = in.x[i+1];
          x[2] = x[1];
          x[3] = x[0];
          x[4] = x[0];
          x[5] = (x[0]+x[1]+x[2]+x[3])/4;
          y[0] = in.y[j];
          y[1] = y[0];
          y[2] = in.y[j+1]; 
          y[3] = y[2];
          y[4] = y[0];
          y[5] = (y[0]+y[1]+y[2]+y[3])/4;
        }
        else
        { x[0] = X(i  ,j  );
          x[1] = X(i+1,j  );
          x[2] = X(i+1,j+1);
          x[3] = X(i  ,j+1);
          x[4] = x[0];
          x[5] = (x[0]+x[1]+x[2]+x[3])/4;
          y[0] = Y(i  ,j  );
          y[1] = Y(i+1,j  );
          y[2] = Y(i+1,j+1);
          y[3] = Y(i  ,j+1);
          y[4] = y[0];
          y[5] = (y[0]+y[1]+y[2]+y[3])/4;
        }

        for ( ip=ilo; ip<=ihi; ip++ )
        {
          /* Contours in greylevels: 0. -- 0.5, use something like:
           * SET_GREY( 0.5 * ip/(float)interv );
           */
          elev = ip*dz + datamin;
          np = 0;    /* number of contour intersections with the quadrangle perimeter */
                                           
          /* Calculate the x,y coords of the contour intersections with the quadrangle perimeter:
           */

          for ( n=0; n<4; n++ )
          { 
            if ( elev > MIN2(c[n],c[n+1])  &&  elev <= MAX2(c[n],c[n+1]) )  /* what about rounding ??? */
            { pdif = ( elev - c[n] )/( c[n+1] - c[n] );
              if ( pdif < 0.0 ) pdif = 0.0;
              if ( pdif > 1.0 ) pdif = 1.0; /* force pdif in [0..1] -- copes with severe rounding problems */
              xp[np] = x[n] + ( x[n+1] - x[n] ) * pdif;  
              yp[np] = y[n] + ( y[n+1] - y[n] ) * pdif;
              np++;
            }
          }

          if (np==2) SEG( 0,1 )
          else if (np==4)
          {
            /* Saddle point strategy: subdivide the quadrangle into
             * four triangles: (centre point has index '5')
             */

            if( MIN2(c[5],c[1]) >= elev || MAX2(c[5],c[1]) < elev )
            {
              /* Interconnect point on side 0/1 with point on side 3/0
               * via an intersection on side 0/5, point on side 1/2 with
               * point on side 2/3 via an intersection on side 2/5:
               */
              pdif = ( elev - c[0] )/( c[5] - c[0] );  
              if ( pdif < 0.0 ) pdif = 0.0;
              if ( pdif > 1.0 ) pdif = 1.0;           /* force pdif in [0..1] */
              xp[4] = x[0] + ( x[5] - x[0] ) * pdif;
              yp[4] = y[0] + ( y[5] - y[0] ) * pdif;
              pdif = ( elev - c[2] )/( c[5] - c[2] );
              if ( pdif < 0.0 ) pdif = 0.0;
              if ( pdif > 1.0 ) pdif = 1.0;           /* force pdif in [0..1] */
              xp[5] = x[2] + ( x[5] - x[2] ) * pdif;
              yp[5] = y[2] + ( y[5] - y[2] ) * pdif;

              SEG2( 0,4,3 )
              SEG2( 1,5,2 )
            }
            else
            {
              /* Interconnect point on side 0/1 with point on side 1/2
               * via an intersection on side 1/5, point on side 2/3 with
               * point on side 3/0 via an intersection on side 3/5:
               */
              pdif = ( elev - c[1] )/( c[5] - c[1] );
              if ( pdif < 0.0 ) pdif = 0.0;
              if ( pdif > 1.0 ) pdif = 1.0;           /* force pdif in [0..1] */
              xp[4] = x[1] + ( x[5] - x[1] ) * pdif;
              yp[4] = y[1] + ( y[5] - y[1] ) * pdif;
              pdif = ( elev - c[3] )/( c[5] - c[3] );
              if ( pdif < 0.0 ) pdif = 0.0;
              if ( pdif > 1.0 ) pdif = 1.0;           /* force pdif in [0..1] */
              xp[5] = x[3] + ( x[5] - x[3] ) * pdif;
              yp[5] = y[3] + ( y[5] - y[3] ) * pdif;

              SEG2( 0,4,1 )
              SEG2( 2,5,3 )
            }
          }
#ifdef DEBUGGING
          else if (np != 0)
            quit("contour: This cannot happen (np not in {0,2,4}).");
            /* This may happen due to rounding problems ??? */
#endif
        }
      } 
    }
}

/*********************************************************************
 * rect_boundary:  Draws two dimensional rectangular grid.           *
 *********************************************************************/

void rect_boundary( struct Input in, struct Options opt )
{
  FILE  *psfile = opt.psfile;
  int32 i, j;

  SET_GREY( opt.boun_grey );

  NEWPATH;
  MOVETO( in.x[0], in.y[0] );
  for ( j=1; j<in.dim1; j++ )     /* Of course, this is a straight line.  But drawing it by */
    LINETO( in.x[0], in.y[j] );   /*   segments allows partial removal in Illustrator, etc. */
  STROKE;                         /*   I need this in some of my applications ... */

  NEWPATH;
  MOVETO( in.x[in.dim0-1], in.y[0] );
  for ( j=1; j<in.dim1; j++ )
    LINETO( in.x[in.dim0-1], in.y[j] ); 
  STROKE;

  NEWPATH;
  MOVETO( in.x[0], in.y[0] );
  for ( i=1; i<in.dim0; i++ )
    LINETO( in.x[i], in.y[0] );
  STROKE;

  NEWPATH;
  MOVETO( in.x[0], in.y[in.dim1-1] );
  for ( i=1; i<in.dim0; i++ )
    LINETO( in.x[i], in.y[in.dim1-1] );
  STROKE;

  SET_GREY( 0.0 );   /* restore to black */
}

/*********************************************************************
 * curvi_boundary:  Draws two dimensional curvilinear grid.          *
 *********************************************************************/

void curvi_boundary( struct Input in, struct Options opt )
{
  FILE  *psfile = opt.psfile;
  int32 i, j;

  SET_GREY( opt.boun_grey );

  NEWPATH;
  MOVETO( X(0,0), Y(0,0) );
  for ( j=1; j<in.dim1; j++ )
    LINETO( X(0,j), Y(0,j) );
  STROKE;

  NEWPATH;
  MOVETO( X(in.dim0-1,0), Y(in.dim0-1,0) );
  for ( j=1; j<in.dim1; j++ )
    LINETO( X(in.dim0-1,j), Y(in.dim0-1,j) );
  STROKE;

  NEWPATH;
  MOVETO( X(0,0), Y(0,0) );
  for ( i=1; i<in.dim0; i++ )
    LINETO( X(i,0), Y(i,0) );
  STROKE;

  NEWPATH;
  MOVETO( X(0,in.dim1-1), Y(0,in.dim1-1) );
  for ( i=1; i<in.dim0; i++ )
    LINETO( X(i,in.dim1-1), Y(i,in.dim1-1) );
  STROKE;

  SET_GREY( 0.0 );   /* restore to black */
}

/***********************************************************************************
 * labels:  Draws labels at local extremes for ftyp==RECT or CRV.                  *
 ***********************************************************************************/

void labels( struct Input in, struct Options opt )
{
  FILE    *psfile=opt.psfile;
  int32   i, j;
  float32 x, y;

  if ( opt.values == TRUE )  printf( "labels: Labeling with values not implemented.\n" );
  if ( opt.labels == FALSE ) return;

  if ( in.ftyp != RECT && in.ftyp != CRV )     quit( "labels: Incorrect datatype." );
  if ( opt.max_threshold < opt.min_threshold ) quit( "labels: Thresholds corrupt." );

  fprintf( psfile, "%s\n",  "q   % Illustrator equiv of: gsave" );
  inittext( psfile );

  for( i=1; i<in.dim0-1; i++ )
    for( j=1; j<in.dim1-1; j++ )
      if ( DATA(i,j) > opt.max_threshold )
      { if ( DATA(i,j) > DATA(i+1,j)   && 
             DATA(i,j) > DATA(i,j+1)   && 
             DATA(i,j) > DATA(i+1,j+1) && 
             DATA(i,j) > DATA(i+1,j-1) && 
             DATA(i,j) > DATA(i-1,j)   && 
             DATA(i,j) > DATA(i,j-1)   && 
             DATA(i,j) > DATA(i-1,j+1) && 
             DATA(i,j) > DATA(i-1,j-1) )
        {
          if ( in.ftyp == RECT )
          {
            x = in.x[i];
            y = in.y[j];
          }
          else
          {
            x = X(i,j);
            y = Y(i,j);
          }
          centertext( psfile, x, y, "H" );     /* does horizontal and vertical alignment */
        }
      }
      else if ( DATA(i,j) < opt.min_threshold )
      { if ( DATA(i,j) < DATA(i+1,j) && 
             DATA(i,j) < DATA(i,j+1) && 
             DATA(i,j) < DATA(i+1,j+1) && 
             DATA(i,j) < DATA(i+1,j-1) && 
             DATA(i,j) < DATA(i-1,j) && 
             DATA(i,j) < DATA(i,j-1) && 
             DATA(i,j) < DATA(i-1,j+1) && 
             DATA(i,j) < DATA(i-1,j-1) )
        {
          if ( in.ftyp == RECT )
          {
            x = in.x[i];
            y = in.y[j];
          }
          else
          {
            x = X(i,j);
            y = Y(i,j);
          }
          centertext( psfile, x, y, "L" );     /* does horizontal and vertical alignment */
        }
      }

  fprintf( psfile, "%s\n",  "Q   % Illustrator equiv of: grestore" );
}

/*********************************************************************
 * vset_labels:  Draws labels at local extremes of vertex set.       *
 *********************************************************************/

#define DAT(i)  in.data[ptr[i]]     /* handy abbreviations */
#define PTYP(i) ptyp[ptr[i]]

void vset_labels( struct Input in, struct Options opt )
{
  FILE  *psfile=opt.psfile;
  int32 i, j, ptr[4];
  enum point_typ { IS_MIN, NONE, IS_MAX };
  enum point_typ *ptyp;                     /* array */

  if ( opt.values == TRUE )  printf( "vset_labels: Labeling with values not implemented.\n" );
  if ( opt.labels == FALSE ) return;

  if ( in.ftyp != VSET )                       quit( "vset_labels: Incorrect datatype." );
  if ( opt.max_threshold < opt.min_threshold ) quit( "vset_labels: Thresholds corrupt." );

  if ( (ptyp = (enum point_typ *) malloc( (size_t) (in.nv*sizeof(enum point_typ)) )) == NULL )
    quit("vset_labels: Cannot allocate mem for ptyp array.");

  for ( i=0; i<in.nv; i++ )         /* init */
  {
    if      ( in.data[i] > opt.max_threshold )
      ptyp[i] = IS_MAX;
    else if ( in.data[i] < opt.min_threshold )
      ptyp[i] = IS_MIN;
    else
      ptyp[i] = NONE;
  }

  for (i=0; i < in.ntri; i++)    /* First the triangles. */
  {
    for (j=0; j<3; j++)
    {
      ptr[j] = (in.etyp3==INTTYPE)? in.splist3[3*i+j] - 1
                                  : in.plist3[3*i+j] - 1;
      if ( ptr[j] < 0  ||  ptr[j] > in.nv-1 )
        quit("vset_labels: Connectivity list element out of range.");
    }
    if      ( PTYP(0)==IS_MAX ) { if ( DAT(0) <= DAT(1) || DAT(0) <= DAT(2) )  PTYP(0) = NONE; }
    else if ( PTYP(0)==IS_MIN ) { if ( DAT(0) >= DAT(1) || DAT(0) >= DAT(2) )  PTYP(0) = NONE; }

    if      ( PTYP(1)==IS_MAX ) { if ( DAT(1) <= DAT(0) || DAT(1) <= DAT(2) )  PTYP(1) = NONE; }
    else if ( PTYP(1)==IS_MIN ) { if ( DAT(1) >= DAT(0) || DAT(1) >= DAT(2) )  PTYP(1) = NONE; }

    if      ( PTYP(2)==IS_MAX ) { if ( DAT(2) <= DAT(0) || DAT(2) <= DAT(1) )  PTYP(2) = NONE; }
    else if ( PTYP(2)==IS_MIN ) { if ( DAT(2) >= DAT(0) || DAT(2) >= DAT(1) )  PTYP(2) = NONE; }
  }

  for (i=0; i < in.nquad; i++)   /* Finally the quadrangles. */
  {
    for (j=0; j<4; j++)
    {
      ptr[j] = (in.etyp4==INTTYPE)? in.splist4[4*i+j] - 1
                                  : in.plist4[4*i+j] - 1;
      if ( ptr[j] < -1  ||  (ptr[j]==-1 && j!=3)  ||  ptr[j] > in.nv-1 )
        quit("vset_labels: Connectivity list element out of range.");
    }

    if ( ptr[3] != -1 )  /* indeed a quadrangle */
    {
      if      ( PTYP(0)==IS_MAX ) 
      { if ( DAT(0) <= DAT(1) || DAT(0) <= DAT(2) || DAT(0) <= DAT(3) )  PTYP(0) = NONE; }
      else if ( PTYP(0)==IS_MIN )
      { if ( DAT(0) >= DAT(1) || DAT(0) >= DAT(2) || DAT(0) >= DAT(3) )  PTYP(0) = NONE; }

      if      ( PTYP(1)==IS_MAX )
      { if ( DAT(1) <= DAT(0) || DAT(1) <= DAT(2) || DAT(1) <= DAT(3) )  PTYP(1) = NONE; }
      else if ( PTYP(1)==IS_MIN )
      { if ( DAT(1) >= DAT(0) || DAT(1) >= DAT(2) || DAT(1) >= DAT(3) )  PTYP(1) = NONE; }

      if      ( PTYP(2)==IS_MAX )
      { if ( DAT(2) <= DAT(0) || DAT(2) <= DAT(1) || DAT(2) <= DAT(3) )  PTYP(2) = NONE; }
      else if ( PTYP(2)==IS_MIN )
      { if ( DAT(2) >= DAT(0) || DAT(2) >= DAT(1) || DAT(2) >= DAT(3) )  PTYP(2) = NONE; }

      if      ( PTYP(3)==IS_MAX )
      { if ( DAT(3) <= DAT(0) || DAT(3) <= DAT(1) || DAT(3) <= DAT(2) )  PTYP(3) = NONE; }
      else if ( PTYP(3)==IS_MIN )
      { if ( DAT(3) >= DAT(0) || DAT(3) >= DAT(1) || DAT(3) >= DAT(2) )  PTYP(3) = NONE; }
    }
    else       /* ptr[3] == -1 means triangle stored in quadr conn list */
    {
      if      ( PTYP(0)==IS_MAX ) { if ( DAT(0) <= DAT(1) || DAT(0) <= DAT(2) )  PTYP(0) = NONE; }
      else if ( PTYP(0)==IS_MIN ) { if ( DAT(0) >= DAT(1) || DAT(0) >= DAT(2) )  PTYP(0) = NONE; }

      if      ( PTYP(1)==IS_MAX ) { if ( DAT(1) <= DAT(0) || DAT(1) <= DAT(2) )  PTYP(1) = NONE; }
      else if ( PTYP(1)==IS_MIN ) { if ( DAT(1) >= DAT(0) || DAT(1) >= DAT(2) )  PTYP(1) = NONE; }

      if      ( PTYP(2)==IS_MAX ) { if ( DAT(2) <= DAT(0) || DAT(2) <= DAT(1) )  PTYP(2) = NONE; }
      else if ( PTYP(2)==IS_MIN ) { if ( DAT(2) >= DAT(0) || DAT(2) >= DAT(1) )  PTYP(2) = NONE; }
    }
  }

/*
 *  Now the extremes can be labeled:
 */

  fprintf( psfile, "%s\n",  "q   % Illustrator equiv of: gsave" );
  inittext( psfile );

  for ( i=0; i<in.nv; i++ )
    if      ( ptyp[i] == IS_MAX )
      centertext( psfile, in.x[i], in.y[i], "H" );     /* does horizontal and vertical alignment */
    else if ( ptyp[i] == IS_MIN )
      centertext( psfile, in.x[i], in.y[i], "L" );

  fprintf( psfile, "%s\n",  "Q   % Illustrator equiv of: grestore" );

  free( (void *) ptyp );
}

#define FONTSIZE 12
#define VALIGN 4    /* for vertical centering of labels (unit=points) */
#define HALIGN 5    /* for horizontal centering of labels (unit=points) */

void inittext( FILE *psfile )
{
BEGIN_LN
LN  "0 O"
LN  "0 g"
LN  "0 To                % begin dummy text"
LN  "1 0 0 1 19 19 0 Tp  % begin dummy text path"
LN  "TP                  % end dummy text path"
LN  "0 Tr                % begin render"
LN  "0 Ts                %   set super/subscripting (rise)"
LN  "100 Tz              %   set horizontal scaling"
LN  "0 Tt                %   set user tracking"
LN  "0 TA                %   set pairwise kerning"
LN  "0 0 5 TC            %   set character spacing"
LN  "100 100 200 TW      %   set word spacing"
LN  "0 0 0 Ti            %   set indentation"
LN  "0 Ta                %   set alignment, 0=flushleft, 1=center, 2=flushright"
LN  "0 Tq                %   set hanging quotes"
LN  "0 0 Tl              %   set leading"
LN  "0 Tc                %   set computed char spacing"
LN  "0 Tw                %   set computed word spacing"
END_LN

  fprintf( psfile, "%s %d %s\n", "/_Times-BoldItalic", FONTSIZE, "Tf  %   set font name and size" );
  fprintf( psfile, "%s\n", "(\\r) Tx   % dummytext" );
  fprintf( psfile, "%s\n", "TO        % end dummy text" );
}

/***************************************************************************
 *  centertext:  Puts text string at position x,y.  H & V centering of     *
 *               first line in 'text' is correct only if it consists of    *
 *               a single char.                                            *
 ***************************************************************************/

void centertext( FILE *psfile, float32 x, float32 y, char *text )
{
  fprintf( psfile, "%s\n", "0 To  % begin text" );
  fprintf( psfile, "%s %3.3f %3.3f %s\n",  "1 0 0 1", x-HALIGN, y-VALIGN, "0 Tp   % begin text path" );
  fprintf( psfile, "%s\n", "TP    % end text path" );
  fprintf( psfile, "%s\n", "0 Tr  % begin render" );

  fprintf( psfile, "%s%s%s\n", "(", text, "\\r) Tx   % text" );
  fprintf( psfile, "%s\n", "TO    % end text" );
}


/***********************************************************************************
 * pshead: Creates Encapsulated PostScript file, and writes its comment header,    *
 *         prolog, setup section, and init.                                        *
 *         The Adobe Illustrator 3.0 resources are pasted into the prolog section. *
 *         The script and trailing sections should be written by the calling       *
 *         routine.  If that routine uses only macros from the Adobe resources,    *
 *         then the resulting PostScript output file can be read and               *
 *         edited with Adobe Illustrator.                                          *
 *         USES GLOBALS:  struct Box bound;                                        *
 ***********************************************************************************/


#ifdef UNIX  /* %%For: ... uses: getlogin(), getuid(), getpwuid() */
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#endif

void pshead( struct Options opt )
{
  FILE      *psfile=opt.psfile;
  time_t    tim;
  struct tm t;
  char      timestr[STR_MAX];
  char      *username;
#ifdef UNIX
  struct passwd *pw;
#endif


/*
 * Write the first part of the comment header:
 */

  fprintf( psfile, "%s\n",     "%!PS-Adobe-3.0 EPSF-3.0" );
  fprintf( psfile, "%s%s%s%s%s\n",     
                               "%%Creator: (Fred Walsteijn's vs2ps -- version: ", VERSION, ", dated: ", VDATE, ")" );
                                             /* ^ don't change the line above */
  fprintf( psfile, "%s%s%s\n", "%%Title: (", opt.outfile, ")" );

/* 
 * This should find your username (UNIX and MAC/MPW systems only):
 */

#ifdef UNIX_ALT
  if ( (username = cuserid(NULL)) != NULL )
    fprintf( psfile, "%s %s\n", "%%For:", username );
  else
#elif ( defined(UNIX) )                         /* is "UNIX"-version better than "UNIX_ALT" ? */
  if ( (username = getlogin()) != NULL )
    fprintf( psfile, "%s %s\n", "%%For:", username );
  else if ( (pw = getpwuid(getuid())) != NULL )
    fprintf( psfile, "%s %s\n", "%%For:", (*pw).pw_name );
  else
#elif ( defined(MAC) && defined(MPW) )
  if ( (username = getenv("user")) != NULL )
    fprintf( psfile, "%s %s\n", "%%For:", username );
  else
#endif

  fprintf( psfile, "%s\n", "%%For: (Fred Walsteijn) (University of Utrecht, The Netherlands)" );
    /* ^ default username in case I cannot get it from OS --- you may change this line */

/* 
 * Write the date and time:
 */

if ( (tim = time(NULL)) == -1   ||
     localtime(&tim) == NULL    ||
     ( t = *localtime(&tim), 
       (strftime( timestr, (size_t)STR_MAX, "(%a, %b %d, %Y) (%H:%M:%S)", &t ) == 0 )) )
  printf("pshead: Cannot get the date/time.\n");
else
  fprintf( psfile, "%s %s\n", "%%CreationDate:", timestr );

/*
 * Write the rest of the comment header:
 */

BEGIN_LN
LN  "%%DocumentProcessColors: Black"
LN  "%%DocumentFonts: Times-Bold"
LN  "%%+ Times-BoldItalic"
LN  "%%DocumentSuppliedResources: procset Adobe_packedarray 2.0 0"
LN  "%%+ procset Adobe_cmykcolor 1.1 0"
LN  "%%+ procset Adobe_cshow 1.1 0"
LN  "%%+ procset Adobe_customcolor 1.0 0"
LN  "%%+ procset Adobe_typography_AI3 1.0 0"
LN  "%%+ procset Adobe_IllustratorA_AI3 1.0 0"
END_LN

fprintf( psfile, "%s %1d %1d %1d %1d\n",  
                          "%%BoundingBox:", bound.xlo, bound.ylo, bound.xhi, bound.yhi );
fprintf( psfile, "%s %1d %1d %1d %1d\n",  
                          "%AI3_TemplateBox:", (bound.xhi+bound.xlo)/2, (bound.yhi+bound.ylo)/2,
                                               (bound.xhi+bound.xlo)/2, (bound.yhi+bound.ylo)/2  );
BEGIN_LN
LN  "%AI3_TileBox: 0 0 0 0"
LN  "%AI3_DocumentPreview: None"
LN  "%AI3_ColorUsage: Black&White"
LN  "%%EndComments"
LN  "%%BeginProlog"
END_LN

/*
 * The prolog contains Adobe Illustrator resources:
 */

  adobe( psfile );
   
/*
 * The setup section:
 */

BEGIN_LN
LN  "%%EndProlog"
LN  "%%BeginSetup"
LN  "%%IncludeFont: Times-Bold"
LN  "%%IncludeFont: Times-BoldItalic"
LN  "Adobe_cmykcolor /initialize get exec"
LN  "Adobe_cshow /initialize get exec"
LN  "Adobe_customcolor /initialize get exec"
LN  "Adobe_typography_AI3 /initialize get exec"
LN  "Adobe_IllustratorA_AI3 /initialize get exec"
LN  "["
LN  "39/quotesingle 96/grave 128/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis"
LN  "/Udieresis/aacute/agrave/acircumflex/adieresis/atilde/aring/ccedilla/eacute"
LN  "/egrave/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis/ntilde"
LN  "/oacute/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex"
LN  "/udieresis/dagger/degree/cent/sterling/section/bullet/paragraph/germandbls"
LN  "/registered/copyright/trademark/acute/dieresis/.notdef/AE/Oslash"
LN  "/.notdef/plusminus/.notdef/.notdef/yen/mu/.notdef/.notdef"
LN  "/.notdef/.notdef/.notdef/ordfeminine/ordmasculine/.notdef/ae/oslash"
LN  "/questiondown/exclamdown/logicalnot/.notdef/florin/.notdef/.notdef"
LN  "/guillemotleft/guillemotright/ellipsis/.notdef/Agrave/Atilde/Otilde/OE/oe"
LN  "/endash/emdash/quotedblleft/quotedblright/quoteleft/quoteright/divide"
LN  "/.notdef/ydieresis/Ydieresis/fraction/currency/guilsinglleft/guilsinglright"
LN  "/fi/fl/daggerdbl/periodcentered/quotesinglbase/quotedblbase/perthousand"
LN  "/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex"
LN  "/Idieresis/Igrave/Oacute/Ocircumflex/.notdef/Ograve/Uacute/Ucircumflex"
LN  "/Ugrave/dotlessi/circumflex/tilde/macron/breve/dotaccent/ring/cedilla"
LN  "/hungarumlaut/ogonek/caron"
LN  "TE"
LN  "%AI3_BeginEncoding: _Times-Bold Times-Bold"
LN  "[/_Times-Bold/Times-Bold 0 0 1 TZ"
LN  "%AI3_EndEncoding"
LN  "%AI3_BeginEncoding: _Times-BoldItalic Times-BoldItalic"
LN  "[/_Times-BoldItalic/Times-BoldItalic 0 0 1 TZ"
LN  "%AI3_EndEncoding"
LN  "%%EndSetup"
END_LN

/*
 * Illustrator's default init:
 */

BEGIN_LN
LN  "0 A"
LN  "0 R"
LN  "0 G"
LN  "0 i 0 J 0 j 0.5 w 4 M []0 d"
LN  "%AI3_Note:"
LN  "0 D   % Illustrator needs this"
END_LN

  if ( opt.caption == TRUE )  captions( opt );

  fprintf( psfile, "%s\n",  "q   % Illustrator equiv of: gsave" );

  beginobject( psfile, "Plot domain clipping path" );
  fprintf( psfile, "%1d %1d %s\n",  plot.xlo, plot.ylo, "m" );
  fprintf( psfile, "%1d %1d %s\n",  plot.xhi, plot.ylo, "L" );
  fprintf( psfile, "%1d %1d %s\n",  plot.xhi, plot.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  plot.xlo, plot.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  plot.xlo, plot.ylo, "L" );
  fprintf( psfile, "%s\n",  "h     % Illustrator equiv of: closepath" );
  endobject( psfile );

  fprintf( psfile, "%s\n",  "W n   % Illustrator equiv of: clip newpath" );
}

/***************************************************************************
 * pstail:   Generates (Illustrator 3.0) PostScript file trailer.          * 
 ***************************************************************************/

void pstail( struct Options opt )
{
  FILE *psfile=opt.psfile;

BEGIN_LN
LN  "Q   % Illustrator equiv of: grestore  (This matches the gsave just before the plot domain clip)"
LN  "%%PageTrailer"
LN  "gsave annotatepage grestore showpage"
LN  "%%Trailer"
LN  "Adobe_IllustratorA_AI3 /terminate get exec"
LN  "Adobe_typography_AI3 /terminate get exec"
LN  "Adobe_customcolor /terminate get exec"
LN  "Adobe_cshow /terminate get exec"
LN  "Adobe_cmykcolor /terminate get exec"
LN  "Adobe_packedarray /terminate get exec"
LN  "%%EOF"
END_LN

  if ( fclose( opt.psfile ) != 0 )
    quit("pstail: Cannot close PostScript file.");

#if ( defined(MAC) && defined(MPW) )
  fsetfileinfo( opt.outfile, 'ART3', 'TEXT' );  /* creator resp type of Illustrator 3.0 EPS */
#endif
}

/******************************************************************************
 * captions:  Puts the bot and top strings, as specified in 'opt', below      *
 *            and atop the plot domain. Should this function do some parsing  *
 *            of these strings ?  (\flushleft, \flushright, \psi_1, etc ...)  *
 ******************************************************************************/

#define DEPTH 8  /* Margin of baseline above clip-box lower side (unit=points) */

void captions( struct Options opt )
{
  FILE *psfile=opt.psfile;

  if ( opt.caption == FALSE ) return;

/*
 * First the topstring. For safety, clip it:
 */

  fprintf( psfile, "%s\n",  "q   % Illustrator equiv of: gsave" );

  beginobject( psfile, "Top string clipping path" );
  fprintf( psfile, "%1d %1d %s\n",  tops.xlo, tops.ylo, "m" );
  fprintf( psfile, "%1d %1d %s\n",  tops.xhi, tops.ylo, "L" );
  fprintf( psfile, "%1d %1d %s\n",  tops.xhi, tops.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  tops.xlo, tops.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  tops.xlo, tops.ylo, "L" );
  fprintf( psfile, "%s\n",  "h   % Illustrator equiv of: closepath" );
  endobject( psfile );

  fprintf( psfile, "%s\n",  "W n % Illustrator equiv of: clip newpath" );

  beginobject( psfile, "Top string" );

BEGIN_LN
LN  "0 O"
LN  "0 g"
LN  "0 To  % begin text"
END_LN

  fprintf( psfile, "%s %1d %1d %s\n",  "1 0 0 1", tops.xlo, tops.ylo+DEPTH, "0 Tp    % begin text path" );

BEGIN_LN
LN  "TP                  % end text path"
LN  "0 Tr                % begin render"
LN  "0 Ts                %   set super/subscripting (rise)"
LN  "100 Tz              %   set horizontal scaling"
LN  "0 Tt                %   set user tracking"
LN  "0 TA                %   set pairwise kerning"
LN  "0 0 5 TC            %   set character spacing"
LN  "100 100 200 TW      %   set word spacing"
LN  "0 0 0 Ti            %   set indentation"
LN  "0 Ta                %   set alignment, 0=flushleft, 1=center, 2=flushright"
LN  "0 Tq                %   set hanging quotes"
LN  "0 0 Tl              %   set leading"
LN  "0 Tc                %   set computed char spacing"
LN  "0 Tw                %   set computed word spacing"
LN  "/_Times-Bold 24 Tf  %   set font name and size"
END_LN

  fprintf( psfile, "%s%s%s\n", "(", opt.topstring, "\\r) Tx   % text" );
  fprintf( psfile, "%s\n", "TO   % end text" );

  endobject( psfile );

  fprintf( psfile, "%s\n",  "Q   % Illustrator equiv of: grestore  (This matches the gsave just before the topstring clip)" );



/*
 * Finally the botstring. For safety, clip it:
 */

  fprintf( psfile, "%s\n",  "q   % Illustrator equiv of: gsave" );

  beginobject( psfile, "Bot string clipping path" );
  fprintf( psfile, "%1d %1d %s\n",  bots.xlo, bots.ylo, "m" );
  fprintf( psfile, "%1d %1d %s\n",  bots.xhi, bots.ylo, "L" );
  fprintf( psfile, "%1d %1d %s\n",  bots.xhi, bots.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  bots.xlo, bots.yhi, "L" );
  fprintf( psfile, "%1d %1d %s\n",  bots.xlo, bots.ylo, "L" );
  fprintf( psfile, "%s\n",  "h   % Illustrator equiv of: closepath" );
  endobject( psfile );

  fprintf( psfile, "%s\n",  "W n % Illustrator equiv of: clip newpath" );

  beginobject( psfile, "Bot string" );

BEGIN_LN
LN  "0 O"
LN  "0 g"
LN  "0 To  % begin text"
END_LN

  fprintf( psfile, "%s %1d %1d %s\n",  "1 0 0 1", bots.xlo, bots.ylo+DEPTH, "0 Tp    % begin text path" );

BEGIN_LN
LN  "TP                  % end text path"
LN  "0 Tr                % begin render"
LN  "0 Ts                %   set super/subscripting (rise)"
LN  "100 Tz              %   set horizontal scaling"
LN  "0 Tt                %   set user tracking"
LN  "0 TA                %   set pairwise kerning"
LN  "0 0 5 TC            %   set character spacing"
LN  "100 100 200 TW      %   set word spacing"
LN  "0 0 0 Ti            %   set indentation"
LN  "0 Ta                %   set alignment, 0=flushleft, 1=center, 2=flushright"
LN  "0 Tq                %   set hanging quotes"
LN  "0 0 Tl              %   set leading"
LN  "0 Tc                %   set computed char spacing"
LN  "0 Tw                %   set computed word spacing"
LN  "/_Times-Bold 24 Tf  %   set font name and size"
END_LN

  fprintf( psfile, "%s%s%s\n", "(", opt.botstring, "\\r) Tx   % text" );
  fprintf( psfile, "%s\n", "TO   % end text" );

  endobject( psfile );

  fprintf( psfile, "%s\n",  "Q   % Illustrator equiv of: grestore  (This matches the gsave just before the botstring clip)" );
}

/***************************************************************************/

void beginobject( FILE *psfile, char *objname )
{
  fprintf( psfile, "%s%s%s\n",  "%AI3_Note:     (", objname, ")" );
  fprintf( psfile, "%s%s%s\n",  "%%BeginObject: (", objname, ")" );
}


/***************************************************************************/

void endobject( FILE *psfile )
{
  fprintf( psfile, "%s\n",  "%%EndObject" );
}


/***************************************************************************
 * ps_coords:   Incorporates the requested domain clipping and             *
 *              plot domain ratio as specified in struct 'opt'             *
 *              (fields: xmin, xmax, ymin, ymax, ydivx) by modifying:      *
 *                in.x and in.y                                            *
 *              as well as the GLOBALS:                                    *
 *                struct Box plot, tops, bots;    (bounding boxes)         *
 ***************************************************************************/

/*
 * The maximum area available in default PostScript coordinates:
 */

#define XMIN    72     /*  1 inch */
#define XMAX    540    /*  7.5 inch */
#define YMIN    72
#define YMAX    720    /* 10 inch */
#define CAPTION 36     /* 0.5 inch */
#define MARGIN  3      /* distance of plot bounding box to selected domain. 
                          Needed because lines will have non-zero thickness */

/*
    ............................
    :         Box tops         :  <--- present only if opt.caption == TRUE
    :..........................:
    : ------------------------ :
    : |                      | :
    : |                      | :
    : |       Box plot       | :  <--- MARGIN for plot domain BB
    : |                      | :
    : |                      | :
    : ------------------------ :
    :..........................:
    :         Box bots         :  <--- present only if opt.caption == TRUE
    :..........................:
 */


void ps_coords( struct Input in, struct Options opt )
{ 
  int32   i, dimx, dimy;
  float32 ratio, xlo, xhi, ylo, yhi, tmp;

/*
 * First set 'xlo, xhi, ylo, yhi, ratio' to the values of the max PS domain avail for the plot:
 */

  xlo = XMIN + MARGIN;
  xhi = XMAX - MARGIN;

  if ( opt.caption == TRUE )  /* substract space for caption strings */
  {
    ylo = YMIN + CAPTION + MARGIN;
    yhi = YMAX - CAPTION - MARGIN;
  }
  else                        /* all PS space can be used for the plot */
  {
    ylo = YMIN + MARGIN;
    yhi = YMAX - MARGIN;
  }

  ratio = (yhi-ylo)/(xhi-xlo);

/*
 * Now fit in the max PS subdomain with the correct ratio (opt.ydivx) of the domain sides:
 */

  if ( opt.ydivx > ratio )        /* reduce x-size */
  {
    tmp = (xlo + xhi - (yhi-ylo)/opt.ydivx) / 2;
    xhi = (xlo + xhi + (yhi-ylo)/opt.ydivx) / 2;
    xlo = tmp; 
  }
  else                            /* reduce y-size */
  {
    tmp = (ylo + yhi - (xhi-xlo)*opt.ydivx) / 2;
    yhi = (ylo + yhi + (xhi-xlo)*opt.ydivx) / 2;
    ylo = tmp;
  }

/*
 * Scale the vertex coordinates so that the selected domain (opt.xmin, ...) maps to the
 * box with PostScript box 'xlo, xhi, ylo, yhi' (in default PS coord system):
 */

  if (in.ftyp == RECT) 
  { dimx = in.dim0;
    dimy = in.dim1; }
  else if (in.ftyp == CRV)
    dimx = dimy = in.dim0*in.dim1;
  else if (in.ftyp == VSET)
    dimx = dimy = in.nv;
  else
    quit("ps_coords: Unknown ftyp.");

  for ( i=0; i<dimx; i++ )
  {
    in.x[i] = xlo + (in.x[i]-opt.xmin)/(opt.xmax-opt.xmin)*(xhi-xlo);
  }

  for ( i=0; i<dimy; i++ )
  {
    in.y[i] = ylo + (in.y[i]-opt.ymin)/(opt.ymax-opt.ymin)*(yhi-ylo);
  }

/*
 * Set coords of the bounding boxes for captions and plot:
 */

  plot.xlo = ROUNDDOWN(xlo) - MARGIN;
  plot.xhi = ROUNDUP(xhi)   + MARGIN;
  plot.ylo = ROUNDDOWN(ylo) - MARGIN;
  plot.yhi = ROUNDUP(yhi)   + MARGIN;

  tops.xlo = bots.xlo = plot.xlo;
  tops.xhi = bots.xhi = plot.xhi;

  tops.ylo = plot.yhi;
  tops.yhi = plot.yhi + ((opt.caption==TRUE)?CAPTION:0);

  bots.ylo = plot.ylo - ((opt.caption==TRUE)?CAPTION:0);
  bots.yhi = plot.ylo;

/*
 * The PostScript BoundingBox:
 */

  bound.xlo = plot.xlo;
  bound.xhi = plot.xhi;
  bound.ylo = bots.ylo;
  bound.yhi = tops.yhi;
}

/***********************************************************************
 * adobe:  Writes the Adobe Illustrator 3.0 resources.                 *
 ***********************************************************************/


#if ( defined(MAC) && defined(MPW) )
#pragma segment Adobe
#endif



void adobe( FILE *psfile )
{
#ifdef RESOURCES_FROM_FILE

/*  Since the resources are rather voluminous, one may favor 
 *  to store them in a separate file. The file contents should match 
 *  the header comments (%%DocumentSuppliedResources:...) as written
 *  in pshead().                        
 */

  FILE *resfile;
  int c;

 /*
  * The resources are given in the file 'Adobe_resources':
  */

  if ( (resfile = fopen( "Adobe_resources", "r")) == NULL )
    quit("adobe: Cannot read file: Adobe_resources.");

  while ((c = getc(resfile)) != EOF)
    putc(c, psfile);

#else
  adobe_part1( psfile );
  adobe_part2( psfile );
#endif
}


#ifndef RESOURCES_FROM_FILE

 /*
  * A large number of fprintf's is needed because few compilers allow
  * string literals of arbitrary size to be built as folows (this is ANSI C):
  *
  *      fprintf( psfile, "%s",  
  *        "first line\n" 
  *        "second line\n"
  *        "etc...\n" );
  *     (No comma's between "...\n" "...\n" since we're building a single string.)
  * Mac MPW C requires that the combined length of such string literals is less than 512.
  *   Also, Mac/MPW currently doesn't allow large string pools contained in a single
  *   function, therefore the resources are split over two functions.
  * The old DEC Ultrix C does not allow string literal construction as given above at all.
  * Convex C works fine though.
  */


void adobe_part1( FILE *psfile )
{
BEGIN_LN
LN  "%%BeginResource: procset Adobe_packedarray 2.0 0"
LN  "%%Title: (Packed Array Operators)"
LN  "%%Version: 2.0"
LN  "%%CreationDate: (8/2/90) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "userdict /Adobe_packedarray 5 dict dup begin put"
LN  "/initialize			% - initialize -"
LN  "{"
LN  "/packedarray where"
LN  "	{"
LN  "	pop"
LN  "	}"
LN  "	{"
LN  "	Adobe_packedarray begin"
LN  "	Adobe_packedarray"
LN  "		{"
LN  "		dup xcheck"
LN  "			{"
LN  "			bind"
LN  "			} if"
LN  "		userdict 3 1 roll put"
LN  "		} forall"
LN  "	end"
LN  "	} ifelse"
LN  "} def"
LN  "/terminate			% - terminate -"
LN  "{"
LN  "} def"
LN  "/packedarray		% arguments count packedarray array"
LN  "{"
LN  "array astore readonly"
LN  "} def"
LN  "/setpacking			% boolean setpacking -"
LN  "{"
LN  "pop"
LN  "} def"
LN  "/currentpacking		% - setpacking boolean"
LN  "{"
LN  "false"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "%%EndResource"
LN  "Adobe_packedarray /initialize get exec"
LN  "%%BeginResource: procset Adobe_cmykcolor 1.1 0"
LN  "%%Title: (CMYK Color Operators)"
LN  "%%Version: 1.1"
LN  "%%CreationDate: (1/23/89) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "currentpacking true setpacking"
LN  "userdict /Adobe_cmykcolor 4 dict dup begin put"
LN  "/initialize			% - initialize -"
LN  "{"
LN  "/setcmykcolor where"
LN  "	{"
LN  "	pop"
LN  "	}"
LN  "	{"
LN  "	userdict /Adobe_cmykcolor_vars 2 dict dup begin put"
LN  "	/_setrgbcolor"
LN  "		/setrgbcolor load def"
LN  "	/_currentrgbcolor"
LN  "		/currentrgbcolor load def"
LN  "	Adobe_cmykcolor begin"
LN  "	Adobe_cmykcolor"
LN  "		{"
LN  "		dup xcheck"
LN  "			{"
LN  "			bind"
LN  "			} if"
LN  "		pop pop"
LN  "		} forall"
LN  "	end"
LN  "	end"
LN  "	Adobe_cmykcolor begin"
LN  "	} ifelse"
LN  "} def"
LN  "/terminate			% - terminate -"
LN  "{"
LN  "currentdict Adobe_cmykcolor eq"
LN  "	{"
LN  "	end"
LN  "	} if"
LN  "} def"
LN  "/setcmykcolor		% cyan magenta yellow black setcmykcolor -"
LN  "{"
LN  "1 sub 4 1 roll"
LN  "3"
LN  "	{"
LN  "	3 index add neg dup 0 lt"
LN  "		{"
LN  "		pop 0"
LN  "		} if"
LN  "	3 1 roll"
LN  "	} repeat"
LN  "Adobe_cmykcolor_vars /_setrgbcolor get exec"
LN  "pop"
LN  "} def"
LN  "/currentcmykcolor	% - currentcmykcolor cyan magenta yellow black"
LN  "{"
LN  "Adobe_cmykcolor_vars /_currentrgbcolor get exec"
LN  "3"
LN  "	{"
LN  "	1 sub neg 3 1 roll"
LN  "	} repeat"
LN  "0"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "setpacking"
LN  "%%EndResource"
LN  "%%BeginResource: procset Adobe_cshow 1.1 0"
LN  "%%Title: (cshow Operator)"
LN  "%%Version: 1.1"
LN  "%%CreationDate: (1/23/89) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "currentpacking true setpacking"
LN  "userdict /Adobe_cshow 3 dict dup begin put"
LN  "/initialize			% - initialize -"
LN  "{"
LN  "/cshow where"
LN  "	{"
LN  "	pop"
LN  "	}"
LN  "	{"
LN  "	userdict /Adobe_cshow_vars 1 dict dup begin put"
LN  "	/_cshow		% - _cshow proc"
LN  "		{} def"
LN  "	Adobe_cshow begin"
LN  "	Adobe_cshow"
LN  "		{"
LN  "		dup xcheck"
LN  "			{"
LN  "			bind"
LN  "			} if"
LN  "		userdict 3 1 roll put"
LN  "		} forall"
LN  "	end"
LN  "	end"
LN  "	} ifelse"
LN  "} def"
LN  "/terminate			% - terminate -"
LN  "{"
LN  "} def"
LN  "/cshow				% proc string cshow -"
LN  "{"
LN  "exch"
LN  "Adobe_cshow_vars"
LN  "	exch /_cshow"
LN  "	exch put"
LN  "	{"
LN  "	0 0 Adobe_cshow_vars /_cshow get exec"
LN  "	} forall"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "setpacking"
LN  "%%EndResource"
LN  "%%BeginResource: procset Adobe_customcolor 1.0 0"
LN  "%%Title: (Custom Color Operators)"
LN  "%%Version: 1.0"
LN  "%%CreationDate: (5/9/88) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "currentpacking true setpacking"
LN  "userdict /Adobe_customcolor 5 dict dup begin put"
LN  "/initialize			% - initialize -"
LN  "{"
LN  "/setcustomcolor where"
LN  "	{"
LN  "	pop"
LN  "	}"
LN  "	{"
LN  "	Adobe_customcolor begin"
LN  "	Adobe_customcolor"
LN  "		{"
LN  "		dup xcheck"
LN  "			{"
LN  "			bind"
LN  "			} if"
LN  "		pop pop"
LN  "		} forall"
LN  "	end"
LN  "	Adobe_customcolor begin"
LN  "	} ifelse"
LN  "} def"
LN  "/terminate			% - terminate -"
LN  "{"
LN  "currentdict Adobe_customcolor eq"
LN  "	{"
LN  "	end"
LN  "	} if"
LN  "} def"
LN  "/findcmykcustomcolor	% cyan magenta yellow black name findcmykcustomcolor object"
LN  "{"
LN  "5 packedarray"
LN  "}  def"
LN  "/setcustomcolor		% object tint setcustomcolor -"
LN  "{"
LN  "exch"
LN  "aload pop pop"
LN  "4"
LN  "	{"
LN  "	4 index mul 4 1 roll"
LN  "	} repeat"
LN  "5 -1 roll pop"
LN  "setcmykcolor"
LN  "} def"
LN  "/setoverprint		% boolean setoverprint -"
LN  "{"
LN  "pop"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "setpacking"
LN  "%%EndResource"
LN  "%%BeginResource: procset Adobe_typography_AI3 1.0 0"
LN  "%%Title: (Typography Operators)"
LN  "%%Version: 1.0"
LN  "%%CreationDate:(5/31/90) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "currentpacking true setpacking"
LN  "userdict /Adobe_typography_AI3 46 dict dup begin put"
LN  "/initialize			% - initialize -"
LN  "{"
LN  "/TZ"
LN  " where"
LN  "	{"
LN  "	pop"
LN  "	}"
LN  "	{"
LN  "	Adobe_typography_AI3 begin"
LN  "	Adobe_typography_AI3"
LN  "		{"
LN  "		dup xcheck"
LN  "			{"
LN  "			bind"
LN  "			} if"
LN  "		pop pop"
LN  "		} forall"
LN  "	end"
LN  "	Adobe_typography_AI3 begin"
LN  "	} ifelse"
LN  "} def"
LN  "/terminate			% - terminate -"
LN  "{"
LN  "currentdict Adobe_typography_AI3 eq"
LN  "	{"
LN  "	end"
LN  "	} if"
LN  "} def"
LN  "% [ number value stream [ array for encoding modification ] modifyEncoding ==> [ modified array ]"
LN  "/modifyEncoding"
LN  "{"
LN  "	/_tempEncode exch ddef"
LN  ""
LN  "	% pointer for sequential encodings"
LN  "	/_pntr 0 ddef"
LN  ""
LN  "	{"
LN  "		% get bottom object"
LN  "		counttomark -1 roll"
LN  "		% is it a mark ?"
LN  "		dup type dup /marktype eq"
LN  "		{"
LN  "			% exit"
LN  "			pop pop exit"
LN  "		}"
LN  "		{"
LN  "			% ... object ... type ...."
LN  "			% insert if a nametype"
LN  "			/nametype eq"
LN  "			{"
LN  "				% insert the name at _pntr and increment pointer"
LN  "				_tempEncode /_pntr dup load dup 3 1 roll 1 add ddef 3 -1 roll"
LN  "				put"
LN  "			}"
LN  "			{"
LN  "				% reset _pntr if it's a number"
LN  "				/_pntr exch ddef"
LN  "			}"
LN  "			ifelse"
LN  "		}"
LN  "		ifelse"
LN  "	}"
LN  "	loop"
LN  ""
LN  "	% return the modified encoding"
LN  "	_tempEncode"
LN  "}"
LN  "def"
LN  "/TE	% Set std platform encoding 	% (encoding pairs) TE -"
LN  "{"
LN  "	StandardEncoding 256 array copy modifyEncoding"
LN  "	/_nativeEncoding exch def"
LN  "} def"
LN  "% re-define font"
LN  "% expected arguments"
LN  "% for 'normal fonts :"
LN  "%	[ /_Helvetica-Bold/Helvetica-Bold direction fontScript defaultEncoding TZ"
LN  "%"
LN  "% for cartographic, pictographic, and expert fonts :"
LN  "% 	[ ... number value stream ... /_Helvetica-Bold/Helvetica-Bold"
LN  "%	direction fontScript defaultEncoding TZ"
LN  "/TZ"
LN  "{"
LN  "	% platform dependent coding flag"
LN  "	/_useNativeEncoding exch def"
LN  "	% pop fontScript & direction"
LN  "	pop pop"
LN  ""
LN  "	% create a new dictionary with length"
LN  "	% equal to original dictionary length + 2"
LN  "	% copy all the key/value pairs except FID"
LN  "	findfont dup length 2 add dict"
LN  ""
LN  "	begin"
LN  ""
LN  "		% copy all the values but the FID"
LN  "		% into the new dictionary"
LN  "		mark exch"
LN  "		{"
LN  "			1 index /FID ne { def } if cleartomark mark"
LN  "		}"
LN  "		forall"
LN  "		% discard last mark"
LN  "		pop"
LN  ""
LN  "		% define FontName"
LN  "		/FontName exch def"
LN  ""
LN  "		% if no re-encoding stream is present"
LN  "		% then if the base encoding vector of the font"
LN  "		% is the same as StandardEncoding"
LN  "		% and the use platform encoding flag is true"
LN  "		% then install AI platform encoding"
LN  "		% else leave the base encoding in effect"
LN  "		counttomark 0 eq"
LN  "		{"
LN  "			Encoding StandardEncoding eq 1 _useNativeEncoding eq and"
LN  "			{"
LN  "				/Encoding _nativeEncoding def"
LN  "			}"
LN  "			if"
LN  "			% clean up"
LN  "			cleartomark"
LN  "		}"
LN  "		{"
LN  "			% custom encoding to be done"
LN  "			% start off with a copy of the font's standard encoding"
LN  "			/Encoding load 256 array copy"
LN  "			modifyEncoding /Encoding exch def"
LN  "		}"
LN  "		ifelse"
LN  "		FontName currentdict"
LN  "	end"
LN  ""
LN  "	% register the new font"
LN  "	definefont pop"
LN  "}"
LN  "def"
LN  "% text painting operators"
LN  "/tr					% string tr ax ay string"
LN  "{"
LN  "_ax _ay 3 2 roll"
LN  "} def"
LN  "/trj				% string trj cx cy fillchar ax ay string"
LN  "{"
LN  "_cx _cy _sp _ax _ay 6 5 roll"
LN  "} def"
LN  "/a0"
LN  "{"
LN  "/Tx	% text							% textString Tx -"
LN  "	{"
LN  "	dup"
LN  "	currentpoint 3 2 roll"
LN  "	tr _psf"
LN  "	newpath moveto"
LN  "	tr _ctm _pss"
LN  "	} ddef"
LN  "/Tj	% justified text				% textString Tj -"
LN  "	{"
LN  "	dup"
LN  "	currentpoint 3 2 roll"
LN  "	trj _pjsf"
LN  "	newpath moveto"
LN  "	trj _ctm _pjss"
LN  "	} ddef"
LN  ""
LN  "} def"
LN  "/a1"
LN  "{"
LN  "W B"
LN  "} def"
LN  "/e0"
LN  "{"
LN  "/Tx	% text							% textString Tx -"
LN  "	{"
LN  "	tr _psf"
LN  "	} ddef"
LN  "/Tj	% justified text				% textString Tj -"
LN  "	{"
LN  "	trj _pjsf"
LN  "	} ddef"
LN  "} def"
LN  "/e1"
LN  "{"
LN  "W F"
LN  "} def"
LN  "/i0"
LN  "{"
LN  "/Tx	% text							% textString Tx -"
LN  "	{"
LN  "	tr sp"
LN  "	} ddef"
LN  "/Tj	% justified text				% textString Tj -"
LN  "	{"
LN  "	trj jsp"
LN  "	} ddef"
LN  "} def"
LN  "/o0"
LN  "{"
LN  "/Tx	% text							% textString Tx -"
LN  "	{"
LN  "	tr sw rmoveto"
LN  "	} ddef"
LN  "/Tj	% justified text				% textString Tj -"
LN  "	{"
LN  "	trj swj rmoveto"
LN  "	} ddef"
LN  "} def"
LN  "/r0"
LN  "{"
LN  "/Tx	% text							% textString Tx -"
LN  "	{"
LN  "	tr _ctm _pss"
LN  "	} ddef"
LN  "/Tj	% justified text				% textString Tj -"
LN  "	{"
LN  "	trj _ctm _pjss"
LN  "	} ddef"
LN  "} def"
LN  "/r1"
LN  "{"
LN  "W S"
LN  "} def"
LN  "% font operators"
LN  "% Binding"
LN  "/To	% begin text 					% bindType To -"
LN  "{"
LN  "	pop _ctm currentmatrix pop"
LN  "} def"
LN  "/TO	% end text					% TO -"
LN  "{"
LN  "	Te _ctm setmatrix newpath"
LN  "} def"
LN  "% Text paths"
LN  "/Tp	% begin text path				% a b c d tx ty startPt Tp -"
LN  "{"
LN  "	pop _tm astore pop _ctm setmatrix"
LN  "	2 dict begin /W {} def /h {} def"
LN  "} def"
LN  "/TP	% end text path					% TP -"
LN  "{"
LN  "	end"
LN  "	iTm 0 0 moveto"
LN  "} def"
LN  "% Render mode & matrix operators"
LN  "/Tr	% begin render					% render Tr -"
LN  "{"
LN  "	Te currentpoint newpath moveto"
LN  "	dup 8 eq {pop 0} {dup 9 eq {pop 1} if} ifelse"
LN  "	dup /_render exch ddef"
LN  "	_renderStart exch get load exec"
LN  "} def"
LN  "/iTm % internal set text matrix		% - iTm -	(uses _tm as implicit argument)"
LN  "{"
LN  "_ctm setmatrix _tm concat 0 _rise translate _hs 1 scale"
LN  "} def"
LN  "/Tm % set text matrix				% a b c d tx ty Tm -"
LN  "{"
LN  "_tm astore pop iTm 0 0 moveto"
LN  "} def"
LN  "/Td % translate text matrix 		% tx ty Td -"
LN  "{"
LN  "_mtx translate _tm _tm concatmatrix pop iTm 0 0 moveto"
LN  "} def"
LN  "/Te	% end render					% - Te -"
LN  "{"
LN  "	_render -1 eq {} {_renderEnd _render get dup null ne {load exec} {pop} ifelse} ifelse"
LN  "	/_render -1 ddef"
LN  "} def"
LN  "% Attributes"
LN  "/Ta	% set alignment					% alignment Ta -"
LN  "{"
LN  "pop"
LN  "} def"
LN  "/Tf	% set font name and size		% fontname size Tf -"
LN  "{"
LN  "dup 1000 div /_fScl exch ddef"
LN  "exch findfont exch scalefont setfont"
LN  "} def"
LN  "/Tl	% set leading					% leading paragraphLeading Tl -"
LN  "{"
LN  "pop"
LN  "0 exch _leading astore pop"
LN  "} def"
LN  "/Tt	% set user tracking				% userTracking Tt -"
LN  "{"
LN  "pop"
LN  "} def"
LN  "/TW % set word spacing				% minSpace optSpace maxSpace TW -"
LN  "{"
LN  "3 npop"
LN  "} def"
LN  "/Tw	% set computed word spacing		% wordSpace Tw"
LN  "{"
LN  "/_cx exch ddef"
LN  "} def"
LN  "/TC % set character spacing			% minSpace optSpace maxSpace TC -"
LN  "{"
LN  "3 npop"
LN  "} def"
LN  "/Tc	% set computed char spacing 	% charSpace Tc -"
LN  "{"
LN  "/_ax exch ddef"
LN  "} def"
LN  "/Ts % set super/subscripting (rise)	% rise Ts -"
LN  "{"
LN  "/_rise exch ddef"
LN  "currentpoint"
LN  "iTm"
LN  "moveto"
LN  "} def"
LN  "/Ti	% set indentation				% firstStartIndent otherStartIndent stopIndent Ti -"
LN  "{"
LN  "3 npop"
LN  "} def"
LN  "/Tz % set horizontal scaling		% scalePercent Tz -"
LN  "{"
LN  "100 div /_hs exch ddef"
LN  "iTm"
LN  "} def"
LN  "/TA % set pairwise kerning			% autoKern TA -"
LN  "									%	autoKern = 0 -> no pair kerning"
LN  "									%			 = 1 -> automatic pair kerning"
LN  "{"
LN  "pop"
LN  "} def"
LN  "/Tq % set hanging quotes			% hangingQuotes Tq -"
LN  "									%	hangingQuotes 	= 0 -> no hanging quotes"
LN  "									%			 		= 1 -> hanging quotes"
LN  "{"
LN  "pop"
LN  "} def"
LN  "% Text Bodies"
LN  "/TX {pop} def"
LN  "%/Tx	% non-justified text			% textString Tx -"
LN  "%/Tj	% justified text				% textString Tj -"
LN  "/Tk	% kern							% autoKern kernValue Tk -"
LN  "									%  	autoKern = 0 -> manual kern, = 1 -> auto kern"
LN  "									%	kernValue = kern value in em/1000 space"
LN  "{"
LN  "exch pop _fScl mul neg 0 rmoveto"
LN  "} def"
LN  "/TK	% non-printing kern				% autoKern kernValue TK -"
LN  "{"
LN  "2 npop"
LN  "} def"
LN  "/T* % carriage return & line feed	% - T* -"
LN  "{"
LN  "_leading aload pop neg Td"
LN  "} def"
LN  "/T*- % carriage return & negative line feed	% - T*- -"
LN  "{"
LN  "_leading aload pop Td"
LN  "} def"
LN  "/T-	% print a discretionary hyphen	% - T- -"
LN  "{"
LN  "_hyphen Tx"
LN  "} def"
LN  "/T+	% discretionary hyphen hyphen	% - T+ -"
LN  "{} def"
LN  "/TR	% reset pattern matrix 			% a b c d tx ty TR -"
LN  "{"
LN  "_ctm currentmatrix pop"
LN  "_tm astore pop"
LN  "iTm 0 0 moveto"
LN  "} def"
LN  "/TS	% special chars					% textString justified TS -"
LN  "{"
LN  "0 eq {Tx} {Tj} ifelse"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "setpacking"
LN  "%%EndResource"
END_LN
}

void adobe_part2( FILE *psfile )
{
BEGIN_LN
LN  "%%BeginResource: procset Adobe_IllustratorA_AI3 1.0 0"
LN  "%%Title: (Adobe Illustrator (R) Version 3.0 Abbreviated Prolog)"
LN  "%%Version: 1.0"
LN  "%%CreationDate: (7/22/89) ()"
LN  "%%Copyright: ((C) 1987-1990 Adobe Systems Incorporated All Rights Reserved)"
LN  "currentpacking true setpacking"
LN  "userdict /Adobe_IllustratorA_AI3 61 dict dup begin put"
LN  "% initialization"
LN  "/initialize				% - initialize -"
LN  "{"
LN  "userdict /Adobe_IllustratorA_AI3_vars 46 dict dup begin put"
LN  "% paint operands"
LN  "/_lp /none def"
LN  "/_pf {} def"
LN  "/_ps {} def"
LN  "/_psf {} def"
LN  "/_pss {} def"
LN  "/_pjsf {} def"
LN  "/_pjss {} def"
LN  "/_pola 0 def"
LN  "/_doClip 0 def"
LN  "% paint operators"
LN  "/cf	currentflat def	% - cf flatness"
LN  "% typography operands"
LN  "/_tm matrix def"
LN  "/_renderStart [/e0 /r0 /a0 /o0 /i0 /i0 /i0 /i0] def"
LN  "/_renderEnd [null null null null /e1 /r1 /a1 /clip] def"
LN  "/_render -1 def"
LN  "/_rise 0 def"
LN  "/_ax 0 def			% x character spacing	(_ax, _ay, _cx, _cy follows awidthshow naming convention)"
LN  "/_ay 0 def			% y character spacing"
LN  "/_cx 0 def			% x word spacing"
LN  "/_cy 0 def			% y word spacing"
LN  "/_leading [0 0] def"
LN  "/_ctm matrix def"
LN  "/_mtx matrix def"
LN  "/_sp 16#020 def"
LN  "/_hyphen (-) def"
LN  "/_fScl 0 def"
LN  "/_cnt 0 def"
LN  "/_hs 1 def"
LN  "/_nativeEncoding 0 def"
LN  "/_useNativeEncoding 0 def"
LN  "/_tempEncode 0 def"
LN  "/_pntr 0 def"
LN  "% typography operators"
LN  "/Tx {} def"
LN  "/Tj {} def"
LN  "% compound path operators"
LN  "/CRender {} def"
LN  "% printing"
LN  "/_AI3_savepage {} def"
LN  "% color operands"
LN  "/_gf null def"
LN  "/_cf 4 array def"
LN  "/_if null def"
LN  "/_of false def"
LN  "/_fc {} def"
LN  "/_gs null def"
LN  "/_cs 4 array def"
LN  "/_is null def"
LN  "/_os false def"
LN  "/_sc {} def"
LN  "/_i null def"
LN  "Adobe_IllustratorA_AI3 begin"
LN  "Adobe_IllustratorA_AI3"
LN  "	{"
LN  "	dup xcheck"
LN  "		{"
LN  "		bind"
LN  "		} if"
LN  "	pop pop"
LN  "	} forall"
LN  "end"
LN  "end"
LN  "Adobe_IllustratorA_AI3 begin"
LN  "Adobe_IllustratorA_AI3_vars begin"
LN  "newpath"
LN  "} def"
LN  "/terminate				% - terminate -"
LN  "{"
LN  "end"
LN  "end"
LN  "} def"
LN  "% definition operators"
LN  "/_					% - _ null"
LN  "null def"
LN  "/ddef				% key value ddef -"
LN  "{"
LN  "Adobe_IllustratorA_AI3_vars 3 1 roll put"
LN  "} def"
LN  "/xput				% key value literal xput -"
LN  "{"
LN  "dup load dup length exch maxlength eq"
LN  "	{"
LN  "	dup dup load dup"
LN  "	length 2 mul dict copy def"
LN  "	} if"
LN  "load begin def end"
LN  "} def"
LN  "/npop				% integer npop -"
LN  "{"
LN  "	{"
LN  "	pop"
LN  "	} repeat"
LN  "} def"
LN  "% marking operators"
LN  "/sw					% ax ay string sw x y"
LN  "{"
LN  "dup length exch stringwidth"
LN  "exch 5 -1 roll 3 index 1 sub mul add"
LN  "4 1 roll 3 1 roll 1 sub mul add"
LN  "} def"
LN  "/swj				% cx cy fillchar ax ay string swj x y"
LN  "{"
LN  "dup 4 1 roll"
LN  "dup length exch stringwidth"
LN  "exch 5 -1 roll 3 index 1 sub mul add"
LN  "4 1 roll 3 1 roll 1 sub mul add"
LN  "6 2 roll /_cnt 0 ddef"
LN  "{1 index eq {/_cnt _cnt 1 add ddef} if} forall pop"
LN  "exch _cnt mul exch _cnt mul 2 index add 4 1 roll 2 index add 4 1 roll pop pop"
LN  "} def"
LN  "/ss					% ax ay string matrix ss -"
LN  "{"
LN  "4 1 roll"
LN  "	{				% matrix ax ay char 0 0 {proc} -"
LN  "	2 npop"
LN  "	(0) exch 2 copy 0 exch put pop"
LN  "	gsave"
LN  "	false charpath currentpoint"
LN  "	4 index setmatrix"
LN  "	stroke"
LN  "	grestore"
LN  "	moveto"
LN  "	2 copy rmoveto"
LN  "	} exch cshow"
LN  "3 npop"
LN  "} def"
LN  "/jss				% cx cy fillchar ax ay string matrix jss -"
LN  "{"
LN  "4 1 roll"
LN  "	{				% cx cy fillchar matrix ax ay char 0 0 {proc} -"
LN  "	2 npop"
LN  "	(0) exch 2 copy 0 exch put"
LN  "	gsave"
LN  "	_sp eq"
LN  "		{"
LN  "		exch 6 index 6 index 6 index 5 -1 roll widthshow"
LN  "		currentpoint"
LN  "		}"
LN  "		{"
LN  "		false charpath currentpoint"
LN  "		4 index setmatrix stroke"
LN  "		}ifelse"
LN  "	grestore"
LN  "	moveto"
LN  "	2 copy rmoveto"
LN  "	} exch cshow"
LN  "6 npop"
LN  "} def"
LN  "% path operators"
LN  "/sp					% ax ay string sp -"
LN  "{"
LN  "	{"
LN  "	2 npop (0) exch"
LN  "	2 copy 0 exch put pop"
LN  "	false charpath"
LN  "	2 copy rmoveto"
LN  "	} exch cshow"
LN  "2 npop"
LN  "} def"
LN  "/jsp					% cx cy fillchar ax ay string jsp -"
LN  "{"
LN  "	{					% cx cy fillchar ax ay char 0 0 {proc} -"
LN  "	2 npop"
LN  "	(0) exch 2 copy 0 exch put"
LN  "	_sp eq"
LN  "		{"
LN  "		exch 5 index 5 index 5 index 5 -1 roll widthshow"
LN  "		}"
LN  "		{"
LN  "		false charpath"
LN  "		}ifelse"
LN  "	2 copy rmoveto"
LN  "	} exch cshow"
LN  "5 npop"
LN  "} def"
LN  "% path construction operators"
LN  "/pl				% x y pl x y"
LN  "{"
LN  "transform"
LN  "0.25 sub round 0.25 add exch"
LN  "0.25 sub round 0.25 add exch"
LN  "itransform"
LN  "} def"
LN  "/setstrokeadjust where"
LN  "	{"
LN  "	pop true setstrokeadjust"
LN  "	/c				% x1 y1 x2 y2 x3 y3 c -"
LN  "	{"
LN  "	curveto"
LN  "	} def"
LN  "	/C"
LN  "	/c load def"
LN  "	/v				% x2 y2 x3 y3 v -"
LN  "	{"
LN  "	currentpoint 6 2 roll curveto"
LN  "	} def"
LN  "	/V"
LN  "	/v load def"
LN  "	/y				% x1 y1 x2 y2 y -"
LN  "	{"
LN  "	2 copy curveto"
LN  "	} def"
LN  "	/Y"
LN  "	/y load def"
LN  "	/l				% x y l -"
LN  "	{"
LN  "	lineto"
LN  "	} def"
LN  "	/L"
LN  "	/l load def"
LN  "	/m				% x y m -"
LN  "	{"
LN  "	moveto"
LN  "	} def"
LN  "	}"
LN  "	{%else"
LN  "	/c"
LN  "	{"
LN  "	pl curveto"
LN  "	} def"
LN  "	/C"
LN  "	/c load def"
LN  "	/v"
LN  "	{"
LN  "	currentpoint 6 2 roll pl curveto"
LN  "	} def"
LN  "	/V"
LN  "	/v load def"
LN  "	/y"
LN  "	{"
LN  "	pl 2 copy curveto"
LN  "	} def"
LN  "	/Y"
LN  "	/y load def"
LN  "	/l"
LN  "	{"
LN  "	pl lineto"
LN  "	} def"
LN  "	/L"
LN  "	/l load def"
LN  "	/m"
LN  "	{"
LN  "	pl moveto"
LN  "	} def"
LN  "	}ifelse"
LN  "% graphic state operators"
LN  "/d					% array phase d -"
LN  "{"
LN  "setdash"
LN  "} def"
LN  "/cf	{} def			% - cf flatness"
LN  "/i					% flatness i -"
LN  "{"
LN  "dup 0 eq"
LN  "	{"
LN  "	pop cf"
LN  "	} if"
LN  "setflat"
LN  "} def"
LN  "/j					% linejoin j -"
LN  "{"
LN  "setlinejoin"
LN  "} def"
LN  "/J					% linecap J -"
LN  "{"
LN  "setlinecap"
LN  "} def"
LN  "/M					% miterlimit M -"
LN  "{"
LN  "setmiterlimit"
LN  "} def"
LN  "/w					% linewidth w -"
LN  "{"
LN  "setlinewidth"
LN  "} def"
LN  "% path painting operators"
LN  "/H					% - H -"
LN  "{} def"
LN  "/h					% - h -"
LN  "{"
LN  "closepath"
LN  "} def"
LN  "/N					% - N -"
LN  "{"
LN  "_pola 0 eq"
LN  "	{"
LN  "	_doClip 1 eq {clip /_doClip 0 ddef} if"
LN  "	newpath"
LN  "	}"
LN  "	{"
LN  "	/CRender {N} ddef"
LN  "	}ifelse"
LN  "} def"
LN  "/n					% - n -"
LN  "{N} def"
LN  "/F					% - F -"
LN  "{"
LN  "_pola 0 eq"
LN  "	{"
LN  "	_doClip 1 eq"
LN  "		{"
LN  "		gsave _pf grestore clip newpath /_lp /none ddef _fc"
LN  "		/_doClip 0 ddef"
LN  "		}"
LN  "		{"
LN  "		_pf"
LN  "		}ifelse"
LN  "	}"
LN  "	{"
LN  "	/CRender {F} ddef"
LN  "	}ifelse"
LN  "} def"
LN  "/f					% - f -"
LN  "{"
LN  "closepath"
LN  "F"
LN  "} def"
LN  "/S					% - S -"
LN  "{"
LN  "_pola 0 eq"
LN  "	{"
LN  "	_doClip 1 eq"
LN  "		{"
LN  "		gsave _ps grestore clip newpath /_lp /none ddef _sc"
LN  "		/_doClip 0 ddef"
LN  "		}"
LN  "		{"
LN  "		_ps"
LN  "		}ifelse"
LN  "	}"
LN  "	{"
LN  "	/CRender {S} ddef"
LN  "	}ifelse"
LN  "} def"
LN  "/s					% - s -"
LN  "{"
LN  "closepath"
LN  "S"
LN  "} def"
LN  "/B					% - B -"
LN  "{"
LN  "_pola 0 eq"
LN  "	{"
LN  "	_doClip 1 eq 	% F clears _doClip"
LN  "	gsave F grestore"
LN  "		{"
LN  "		gsave S grestore clip newpath /_lp /none ddef _sc"
LN  "		/_doClip 0 ddef"
LN  "		}"
LN  "		{"
LN  "		S"
LN  "		}ifelse"
LN  "	}"
LN  "	{"
LN  "	/CRender {B} ddef"
LN  "	}ifelse"
LN  "} def"
LN  "/b					% - b -"
LN  "{"
LN  "closepath"
LN  "B"
LN  "} def"
LN  "/W					% - W -"
LN  "{"
LN  "/_doClip 1 ddef"
LN  "} def"
LN  "/*					% - [string] * -"
LN  "{"
LN  "count 0 ne"
LN  "	{"
LN  "	dup type (stringtype) eq {pop} if"
LN  "	} if"
LN  "_pola 0 eq {newpath} if"
LN  "} def"
LN  "% group operators"
LN  "/u					% - u -"
LN  "{} def"
LN  "/U					% - U -"
LN  "{} def"
LN  "/q					% - q -"
LN  "{"
LN  "_pola 0 eq {gsave} if"
LN  "} def"
LN  "/Q					% - Q -"
LN  "{"
LN  "_pola 0 eq {grestore} if"
LN  "} def"
LN  "/*u					% - *u -"
LN  "{"
LN  "_pola 1 add /_pola exch ddef"
LN  "} def"
LN  "/*U					% - *U -"
LN  "{"
LN  "_pola 1 sub /_pola exch ddef"
LN  "_pola 0 eq {CRender} if"
LN  "} def"
LN  "/D					% polarized D -"
LN  "{pop} def"
LN  "/*w					% - *w -"
LN  "{} def"
LN  "/*W					% - *W -"
LN  "{} def"
LN  "% place operators"
LN  "/`					% matrix llx lly urx ury string ` -"
LN  "{"
LN  "/_i save ddef"
LN  "6 1 roll 4 npop"
LN  "concat"
LN  "userdict begin"
LN  "/showpage {} def"
LN  "false setoverprint"
LN  "pop"
LN  "} def"
LN  "/~					% - ~ -"
LN  "{"
LN  "end"
LN  "_i restore"
LN  "} def"
LN  "% color operators"
LN  "/O					% flag O -"
LN  "{"
LN  "0 ne"
LN  "/_of exch ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/R					% flag R -"
LN  "{"
LN  "0 ne"
LN  "/_os exch ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/g					% gray g -"
LN  "{"
LN  "/_gf exch ddef"
LN  "/_fc"
LN  "{"
LN  "_lp /fill ne"
LN  "	{"
LN  "	_of setoverprint"
LN  "	_gf setgray"
LN  "	/_lp /fill ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_pf"
LN  "{"
LN  "_fc"
LN  "fill"
LN  "} ddef"
LN  "/_psf"
LN  "{"
LN  "_fc"
LN  "ashow"
LN  "} ddef"
LN  "/_pjsf"
LN  "{"
LN  "_fc"
LN  "awidthshow"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/G					% gray G -"
LN  "{"
LN  "/_gs exch ddef"
LN  "/_sc"
LN  "{"
LN  "_lp /stroke ne"
LN  "	{"
LN  "	_os setoverprint"
LN  "	_gs setgray"
LN  "	/_lp /stroke ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_ps"
LN  "{"
LN  "_sc"
LN  "stroke"
LN  "} ddef"
LN  "/_pss"
LN  "{"
LN  "_sc"
LN  "ss"
LN  "} ddef"
LN  "/_pjss"
LN  "{"
LN  "_sc"
LN  "jss"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/k					% cyan magenta yellow black k -"
LN  "{"
LN  "_cf astore pop"
LN  "/_fc"
LN  "{"
LN  "_lp /fill ne"
LN  "	{"
LN  "	_of setoverprint"
LN  "	_cf aload pop setcmykcolor"
LN  "	/_lp /fill ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_pf"
LN  "{"
LN  "_fc"
LN  "fill"
LN  "} ddef"
LN  "/_psf"
LN  "{"
LN  "_fc"
LN  "ashow"
LN  "} ddef"
LN  "/_pjsf"
LN  "{"
LN  "_fc"
LN  "awidthshow"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/K					% cyan magenta yellow black K -"
LN  "{"
LN  "_cs astore pop"
LN  "/_sc"
LN  "{"
LN  "_lp /stroke ne"
LN  "	{"
LN  "	_os setoverprint"
LN  "	_cs aload pop setcmykcolor"
LN  "	/_lp /stroke ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_ps"
LN  "{"
LN  "_sc"
LN  "stroke"
LN  "} ddef"
LN  "/_pss"
LN  "{"
LN  "_sc"
LN  "ss"
LN  "} ddef"
LN  "/_pjss"
LN  "{"
LN  "_sc"
LN  "jss"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/x					% cyan magenta yellow black name gray x -"
LN  "{"
LN  "/_gf exch ddef"
LN  "findcmykcustomcolor"
LN  "/_if exch ddef"
LN  "/_fc"
LN  "{"
LN  "_lp /fill ne"
LN  "	{"
LN  "	_of setoverprint"
LN  "	_if _gf 1 exch sub setcustomcolor"
LN  "	/_lp /fill ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_pf"
LN  "{"
LN  "_fc"
LN  "fill"
LN  "} ddef"
LN  "/_psf"
LN  "{"
LN  "_fc"
LN  "ashow"
LN  "} ddef"
LN  "/_pjsf"
LN  "{"
LN  "_fc"
LN  "awidthshow"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "/X					% cyan magenta yellow black name gray X -"
LN  "{"
LN  "/_gs exch ddef"
LN  "findcmykcustomcolor"
LN  "/_is exch ddef"
LN  "/_sc"
LN  "{"
LN  "_lp /stroke ne"
LN  "	{"
LN  "	_os setoverprint"
LN  "	_is _gs 1 exch sub setcustomcolor"
LN  "	/_lp /stroke ddef"
LN  "	} if"
LN  "} ddef"
LN  "/_ps"
LN  "{"
LN  "_sc"
LN  "stroke"
LN  "} ddef"
LN  "/_pss"
LN  "{"
LN  "_sc"
LN  "ss"
LN  "} ddef"
LN  "/_pjss"
LN  "{"
LN  "_sc"
LN  "jss"
LN  "} ddef"
LN  "/_lp /none ddef"
LN  "} def"
LN  "% locked object operator"
LN  "/A					% value A -"
LN  "{"
LN  "pop"
LN  "} def"
LN  "currentdict readonly pop end"
LN  "setpacking"
LN  "% annotate page operator"
LN  "/annotatepage"
LN  "{"
LN  "} def"
LN  "%%EndResource"
LN  "%AI3_BeginRider"
LN  ""
LN  "%----"
LN  "% The following statement can be used to set the default flatness for your print job.  All"
LN  "% objects that have a Paint Style with flatness set to 0 will use the flatness specified"
LN  "% here.  If this statement remains commented out, objects painted with flatness"
LN  "% set to 0 will use the device default."
LN  ""
LN  "%@2 setflat"
LN  "%----"
LN  ""
LN  "%----"
LN  "% The following statements can be used to set the default screen frequency and angle"
LN  "% for your print job.  If these statements remain commented out, then all objects will"
LN  "% use the device default screen frequency and angle.  Note that color printers have"
LN  "% multiple screen frequencies and angles.  See the PostScript Language Reference Manual -"
LN  "% Second Edition for more information on using halftone screen operators for color printers."
LN  ""
LN  "%-- set screen frequency (halftone cells per inch)"
LN  "%@currentscreen 3 -1 roll pop"
LN  "%@30 % <- screen frequency"
LN  "%@3 1 roll setscreen"
LN  ""
LN  "%-- set screen angle (degrees)"
LN  "%@currentscreen 3 -2 roll pop"
LN  "%@30 % <- screen angle"
LN  "%@3 2 roll setscreen"
LN  "%----"
LN  ""
LN  "%----"
LN  "% The following statements can be used to define your own halftone cell spot function"
LN  "% for your print job.  If these statements remain commented out, all objects will"
LN  "% use the device default halftone cell spot function.   Note that a procedure stub"
LN  "% for defining your own spot function is provided, but you need to provide an implementation"
LN  "% if you intend to modify the spot function.  You should not define spot functions unless"
LN  "% you're an experienced PostScript language programmer."
LN  ""
LN  "%-- set  halftone cell spot function"
LN  "%@currentscreen  pop"
LN  "%@{} 	% <- halftone cell spot function must be inserted inside curly braces"
LN  "%@setscreen"
LN  "%----"
LN  ""
LN  "%----"
LN  "% A simple procedure to print a string at the bottom of a page."
LN  "% Can be called by annotatepage (see below) if you wish."
LN  "%"
LN  "%@/PrintX		% string PrintX -"
LN  "%@{"
LN  "%@ count 0 gt"
LN  "%@	{"
LN  "%@  dup type /stringtype eq"
LN  "%@		{"
LN  "%@		/Helvetica findfont 9 scalefont setfont"
LN  "%@		36 36 moveto"
LN  "%@		show"
LN  "%@		} if"
LN  "%@	} if"
LN  "%@} bind def"
LN  ""
LN  "%----"
LN  "% The following procedure is called by the Adobe Illustrator prolog during the print job immediately"
LN  "% before the showpage operator is executed.  You may use this procedure to perform operations like"
LN  "% placing a logo or other information on each page."
LN  ""
LN  "%@/annotatepage"
LN  "%@{"
LN  "%@gsave initgraphics"
LN  "% Insert your PostScript language code between the %===  lines below"
LN  "%====================================="
LN  "%@(From the desk of <your name here?>) PrintX"
LN  "%====================================="
LN  "%@grestore"
LN  "%@} def"
LN  "%----"
LN  ""
LN  ""
LN  "%----"
LN  "% The following statements define an error handler that prints out diagnostics"
LN  "% on the output media if the print job fails due to a PostScript error."
LN  "%"
LN  ""
LN  "% Standard Error Handler"
LN  ""
LN  "/$brkpage 64 dict def $brkpage begin"
LN  "/prnt"
LN  " {dup type/stringtype ne{=string cvs}if dup length 6 mul/tx exch def/ty 10 def"
LN  "  currentpoint/toy exch def/tox exch def 1 setgray newpath"
LN  "  tox toy 2 sub moveto 0 ty rlineto tx 0 rlineto 0 ty neg rlineto"
LN  "  closepath fill tox toy moveto 0 setgray show}bind def"
LN  "/nl{currentpoint exch pop lmargin exch moveto 0 -10 rmoveto}def"
LN  "/=={/cp 0 def typeprint nl}def"
LN  "/typeprint{dup type exec}readonly def"
LN  "/lmargin 72 def"
LN  "/rmargin 72 def"
LN  "/tprint"
LN  "   {dup length cp add rmargin gt{nl/cp 0 def}if"
LN  "    dup length cp add/cp exch def prnt}readonly def"
LN  "/cvsprint{=string cvs tprint( )tprint}readonly def"
LN  "/integertype{cvsprint}readonly def"
LN  "/realtype{cvsprint}readonly def"
LN  "/booleantype{cvsprint}readonly def"
LN  "/operatortype{(--)tprint =string cvs tprint(-- )tprint}readonly def"
LN  "/marktype{pop(-mark- )tprint}readonly def"
LN  "/dicttype{pop(-dictionary- )tprint}readonly def"
LN  "/nulltype{pop(-null- )tprint}readonly def"
LN  "/filetype{pop(-filestream- )tprint}readonly def"
LN  "/savetype{pop(-savelevel- )tprint}readonly def"
LN  "/fonttype{pop(-fontid- )tprint}readonly def"
LN  "/nametype{dup xcheck not{(/)tprint}if cvsprint}readonly def"
LN  "/stringtype"
LN  " {dup rcheck{(\\()tprint tprint(\\))tprint}{pop(-string- )tprint}ifelse"
LN  " }readonly def"
LN  "/arraytype"
LN  " {dup rcheck{dup xcheck"
LN  "  {({)tprint{typeprint}forall(})tprint}"
LN  "  {([)tprint{typeprint}forall(])tprint}ifelse}{pop(-array- )tprint}ifelse"
LN  " }readonly def"
LN  "/packedarraytype"
LN  " {dup rcheck{dup xcheck"
LN  "  {({)tprint{typeprint}forall(})tprint}"
LN  "  {([)tprint{typeprint}forall(])tprint}ifelse}{pop(-packedarray- )tprint}ifelse"
LN  " }readonly def"
LN  "/courier/Courier findfont 10 scalefont def"
LN  "end %$brkpage"
LN  "errordict/handleerror"
LN  " {systemdict begin $error begin $brkpage begin newerror"
LN  "   {/newerror false store"
LN  "    vmstatus pop pop 0 ne{grestoreall}if initgraphics courier setfont"
LN  "    lmargin 720 moveto(ERROR: )prnt errorname prnt"
LN  "    nl(OFFENDING COMMAND: )prnt/command load prnt"
LN  "    $error/ostack"
LN  "    known{nl nl(STACK:)prnt nl nl $error/ostack get aload length{==}repeat}if"
LN  "    systemdict/showpage get exec(%%[ Error: )print"
LN  "    errorname =print(; OffendingCommand: )print/command"
LN  "    load =print( ]%%)= flush}if end end end}"
LN  "dup 0 systemdict put dup 4 $brkpage put bind readonly put"
LN  "%----"
LN  ""
LN  "%AI3_EndRider"
END_LN
}


#endif  /* RESOURCES_FROM_FILE */
