ftp.nice.ch/pub/next/science/mathematics/hippoplotamus.2.0.s.tar.gz#/hippo2.0/hplotNxt.c

This is hplotNxt.c in view mode; [Download] [Up]

/*
 * hippoplotps.c - Postscript routines for displaying hippo ntuples.
 *
 * Copyright (C)  1991  The Board of Trustees of The Leland Stanford
 * Junior University.  All Rights Reserved.
 *
 * $Header: /nfs/ebnextk/LocalSources/hippoplotamus/hplotNxt.c,v 5.0 1993/08/17 21:55:37 rensing Exp $
 *
 * by jonas karlsson, at SLAC, August 1990
 *  split up by Paul Rensing, Feb 28,1991
 *  modified by M. Gravina, March 28, 1991
 */

#include <string.h>
#include <stdlib.h>
#include <math.h>
#import <appkit/graphics.h>
#include <dpsclient/dpsNeXT.h>
#include "hippo.h"
#include "h3D.h"
#include "hplotNxt.h"
#include "hutil.h"

GLOB_QUAL const char hplotNxt_c_rcsid[] = 
     "$Id: hplotNxt.c,v 5.0 1993/08/17 21:55:37 rensing Exp $";
GLOB_QUAL const char hplotNxt_h_rcsid[] = _HPLOTNXT_H_RCSID_;

/* find offset to matrix element */
#define INDEX(row, column, totcols) ((row) * (totcols) + (column))

#define MIN(x, y)  (((x) > (y)) ? (y) : (x))
#define YPADDING(disp) ((disp)->drawRect.size.height*0.01)

/* following copied from <appkit/View.h> */
/* look out of changes in future releases */
extern short NXDrawingStatus;

/* NXDrawingStatus values */

#define NX_DRAWING	1	/* we're drawing */
#define NX_PRINTING	2	/* we're printing */
#define NX_COPYING	3	/* we're copying to the scrap */

/*
 * maximum number of point to use in userpaths
 */
#define MAXNPTS_SCREEN 1000
#define MAXNPTS_PRINT 500

/*
 * ditto for xyshow
 */
#define MAXXYSHOW_SCREEN 3000
#define MAXXYSHOW_PRINT 1500

/*
 * other constants
 */
#define PI 3.1415926536
#define TWOPI (2.0*PI)
#define HALFPI (PI/2.0)

/* various globals */

static rectangle outerRect;	/* Outer rectangle in device units */
static rectangle innerRect;	/* inner (ie plotting area) rectangle in device units */
static rectangle userRect;	/* ditto in user units */ 
static float bbox[4];		/* bounding box required by DPSDoUserPath */
static float xScale, yScale;	/* used for getting into user coords from device coords */

/*
 * External C Functions 
 */
int drawText(char *s, float x, float y, float fontsize, float angle,
		    char xp, char yp );
/*
 * Internal C Functions 
 */
static void drawSides_NeXT(display disp, int inside);
static void drawWireFrame_NeXT(display disp);
static void drawMesh_NeXT(display disp);


int initPlot_NeXT(rectangle *rect1, rectangle *rect2, rectangle *rect3)
{
	/* Save rectangle pointers for global use */
	memcpy((char *) &outerRect, (const char *)rect1, sizeof(rectangle) );
	memcpy((char *) &innerRect, (const char *)rect2, sizeof(rectangle) );
	memcpy((char *) &userRect, (const char *)rect3, sizeof(rectangle) );
	
	xScale = innerRect.size.width / userRect.size.width;
	yScale = innerRect.size.height / userRect.size.height;

	/* specify bounding box  (add a small margin for safety) */	
	bbox[0] = userRect.origin.x -
	     (innerRect.origin.x - outerRect.origin.x)/xScale;
	bbox[1] = userRect.origin.y - 
	     (innerRect.origin.y - outerRect.origin.y)/yScale;
	bbox[2] = userRect.origin.x + userRect.size.width +
	     (outerRect.size.width+outerRect.origin.x - 
	      (innerRect.origin.x+innerRect.size.width))/xScale;
	bbox[3] = userRect.origin.y + userRect.size.height +
	     (outerRect.size.height+outerRect.origin.y - 
	      (innerRect.origin.y+innerRect.size.height))/yScale;

	return 0;
}

int drawLine_NeXT(float *coords, int nCoords, linestyle_t ls)
{
     char ops[3] = { dps_moveto, 0, dps_lineto };
     int repeatCount;		/* this number + 32 must be < 255 */
     				/* to display and < 127 for printing */
				/* to fit in 1 char */
     float *co;
     int count;
     float solid[0];
     float dash[]={5,3};
     float dot[]={2,5};
     float dotdash[]={5,3,1,3};
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };

     if ( NXDrawingStatus == NX_DRAWING )
        repeatCount = 220;
     else
        repeatCount = 92;	/* work around apparent bug */
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);
     
     /* set line style */
     switch(ls) {
	case SOLID:   PSsetdash(solid,0,0); break;
        case DOT:     PSsetdash(dot,2,0); break;
        case DASH:    PSsetdash(dash,2,0); break;
        case DOTDASH: PSsetdash(dotdash,4,0); break;
        default:      PSsetdash(solid,0,0); break;
     }           
     /* 
      * the following rather messy code is needed because PostScript
      * uses just 1 character for a repeat counter (specified as
      * 'excess 32').  Using the repeat method means we don't have to
      * malloc some variable length array for the ops, and then free
      * it after use.
      */
     co = coords;
     do
     {
	  if (nCoords > repeatCount) count = repeatCount;
	  else count = nCoords;
	  /*
	   * use DPSDoUserPathWithMatrix to take care of funny effect when
	   * you have unequal scaling.
	   */
	  ops[1] = 32 + count - 1;
	  DPSDoUserPathWithMatrix(co, 2*count, dps_float, ops, 3, 
				  bbox, dps_ustroke, ctm_matrix);
	  co += 2*(count - 1);
	  nCoords -= (count - 1);
     }
     while (nCoords > 1);	/* 1 because we always back up 1 point */
				/*  to connect the segments */

     PSgrestore();
     return 0;
}


int drawPoints_NeXT(float *coords, int nCoords, plotsymbol_t symbol,
		    float symbolSize)
{

     int ci, oi, i;
     int filled=0;
     int count;
     int maxnpts;
     float *cArray;
     char *oArray;
     float xPointSide, yPointSide;
     float xHalfPointSide, yHalfPointSide;
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };

     if ( NXDrawingStatus == NX_DRAWING )
	  maxnpts = MAXNPTS_SCREEN;
     else
	  maxnpts = MAXNPTS_PRINT;
     
     if (symbol == SOLIDSQUARE)
	  filled = 1;
     
     if (filled) maxnpts /= 5;

     xPointSide = symbolSize / xScale;
     yPointSide = symbolSize / yScale;
     xHalfPointSide = xPointSide / 2.0;
     yHalfPointSide = yPointSide / 2.0;
     
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);

     if (nCoords > maxnpts) i = maxnpts;
     else i = nCoords;
     if ((cArray = (float *) malloc( 8*i * sizeof(float) )) == NULL)
     {
	  h_error("drawPoints_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((oArray = (char *) malloc( 5*i * sizeof(char) )) == NULL)
     {
	  h_error("drawPoints_NeXT - memory exceeded ");
	  return -1;
     }
     
     do
     {
	  if (nCoords > maxnpts) count = maxnpts;
	  else count = nCoords;
	  
	  ci = oi = 0;
	  
	  switch (symbol)
	  {
	  case SQUARE:
	  case SOLIDSQUARE:
	       for (i = 0; i < count; i++)
	       {
		    cArray[ci++] = *coords++ - xHalfPointSide;     
		    cArray[ci++] = *coords++ - yHalfPointSide;
		    oArray[oi++] = dps_moveto;
		    
		    cArray[ci++] = +xPointSide;
		    cArray[ci++] = +0.0;
		    oArray[oi++] = dps_rlineto;
		    
		    cArray[ci++] = +0.0;
		    cArray[ci++] = +yPointSide;
		    oArray[oi++] = dps_rlineto;
		    
		    cArray[ci++] = -xPointSide;
		    cArray[ci++] = +0.0;
		    oArray[oi++] = dps_rlineto;
		    oArray[oi++] = dps_closepath;
	       }
	       break;
	       
	  case PLUS:
	       for (i = 0; i < count; i++)
	       {
		    cArray[ci++] = *coords++ - xHalfPointSide;     
		    cArray[ci++] = *coords++;
		    oArray[oi++] = dps_moveto;
		    
		    cArray[ci++] = +xPointSide;
		    cArray[ci++] = +0.0;
		    oArray[oi++] = dps_rlineto;
		    
		    cArray[ci++] = -xHalfPointSide;
		    cArray[ci++] = +yHalfPointSide;
		    oArray[oi++] = dps_rmoveto;
		    
		    cArray[ci++] = 0.0;
		    cArray[ci++] = -yPointSide;
		    oArray[oi++] = dps_rlineto;
	       }
	       break;
	       
	  case TIMES:
	       for (i = 0; i < count; i++)
	       {
		    cArray[ci++] = *coords++ - xHalfPointSide;     
		    cArray[ci++] = *coords++ - yHalfPointSide;
		    oArray[oi++] = dps_moveto;
		    
		    cArray[ci++] = +xPointSide;
		    cArray[ci++] = +yPointSide;
		    oArray[oi++] = dps_rlineto;
		    
		    cArray[ci++] = 0.0;
		    cArray[ci++] = -yPointSide;
		    oArray[oi++] = dps_rmoveto;
		    
		    cArray[ci++] = -xPointSide;
		    cArray[ci++] = +yPointSide;
		    oArray[oi++] = dps_rlineto;
	       }
	       break;
	       
	  default:
	       break;
	  }
	  
	  /*
	   * use DPSDoUserPathWithMatrix to take care of funny effect when
	   * you have unequal scaling. Applies to line widths and patterns,
	   * so not useful for filled symbols.
	   */
	  if (filled)
	       DPSDoUserPath(cArray, ci, dps_float, oArray, oi, bbox, 
			     dps_ufill);
	  else
	       DPSDoUserPathWithMatrix(cArray, ci, dps_float, oArray, oi, bbox,
				       dps_ustroke,ctm_matrix);

	  nCoords -= count;
     }
     while (nCoords > 0);
     
     free (cArray);
     free (oArray);
     PSgrestore();

     return 0;
}

int drawPointsScat_NeXT(float *coords, int nCoords, plotsymbol_t symbol,
			float symbolSize)
{
     int maxXYShow, count, count2;
     float *floatBuf, *to;
     char *charBuf, plotChar[]="+";
     
     /*
      * Set plot character
      */
     switch (symbol) {
     case SQUARE:
	  plotChar[0] = 'O';
	  break;
     case SOLIDSQUARE:
	  plotChar[0] = 'M';	/* tried a period but it ends up off-center */
	  break;
     case PLUS:
	  plotChar[0] = '+';
	  break;
     case TIMES:
	  plotChar[0] = 'X';
	  break;
     }
     
     /*
      * Pick a suitable blocking size so as not to overflow the printer etc
      */
     if ( NXDrawingStatus == NX_DRAWING )
	  maxXYShow = MAXXYSHOW_SCREEN;
     else
	  maxXYShow = MAXXYSHOW_PRINT;
     if (nCoords < maxXYShow) maxXYShow = nCoords;
     
     /*
      * Allocate space for scatter points. First pair of floats is the
      * starting (X,Y) position, remainder are (dX,dY) increments. It is
      * terminated by a (0.0, 0.0)
      */
     if ((floatBuf = (float *)malloc((2*maxXYShow + 2) * sizeof(float)))
	 == NULL) {
	  h_error("drawPointsScat_NeXT - memory exceeded");
	  return -1;
     }
     
     /*
      * Allocate space for plot char buffer, and fill it. This is just a 
      * long string of all the same character, terminated by a null.
      */
     if ((charBuf = (char *)malloc((maxXYShow + 1) * sizeof(char))) 
	 == NULL) {
	  h_error("drawPointsScat_NeXT - memory exceeded");
	  return -1;
     }
     memset(charBuf,plotChar[0],maxXYShow);
     charBuf[maxXYShow] = 0;
     
     /*
      * Reformat coords into form required by xyshow. We have to use screen
      * coordinates because if you do scaling to user coordinates, the letters
      * in xyshow get scaled as well. Seems no way around this.
      */
     do 
     {
	  if (nCoords > maxXYShow) count = maxXYShow;
	  else count = nCoords;
	  
	  count2 = count;		/* copy */
	  *(charBuf+count) = 0;	/* null terminate the char buffer */
	  
	  to = floatBuf;
	  
	  /* save first XY pair */
	  *to++ = (*coords - userRect.origin.x) * xScale;
	  *to++ = (*(coords+1) - userRect.origin.y) * yScale;
	  count--;
	  coords += 2;
	  
	  /* now save the increments from previous coord */
	  while (count--) {
	       *to++ = (*coords - *(coords-2)) * xScale;
	       *to++ = (*(coords+1) - *(coords-1)) * yScale;
	       coords += 2;
	  }
	  
	  /* finish up with a pair of zeros*/
	  *to++ = 0.0;
	  *to++ = 0.0;
	  
	  /*
	   * Draw scatter points, using xyshow 
	   */
	  PSgsave();
	  PStranslate (innerRect.origin.x, innerRect.origin.y);
	  
	  PSWXYShow(*floatBuf, *(floatBuf+1), plotChar, charBuf, floatBuf+2,
		    count2*2, symbolSize);
	  PSgrestore();
	  
	  /*
	   * check for more
	   */
	  nCoords -= count2;
	  
     }
     while (nCoords > 0);
     
     /*
      * Free the work buffers
      */
     free (floatBuf);
     free (charBuf);
     
     return 0;
}



int drawYError_NeXT(float *coords, float *errorYs, int nCoords)
{
#define ERRORCAPSIZE 2.0

     int ci, oi, i;
     int count;
     int maxnpts;
     float *cArray;
     char *oArray;
     float halfErrorCap = ERRORCAPSIZE/xScale * 0.5;
     float x, ylo, yhi; 
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };

     if ( NXDrawingStatus == NX_DRAWING )
	  maxnpts = MAXNPTS_SCREEN;
     else
	  maxnpts = MAXNPTS_PRINT;

     if (nCoords > maxnpts) i = maxnpts;
     else i = nCoords;
     if ((cArray = (float *) malloc (12 * i * sizeof(float))) == NULL)
     {
	  h_error("drawYError_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((oArray = (char *) malloc (6 * i * sizeof(char))) == NULL)
     {
	  h_error("drawYError_NeXT - memory exceeded ");
	  return -1;
     }
     
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);
     
     do 
     {
	  if (nCoords > maxnpts) count = maxnpts;
	  else count = nCoords;
	  
	  ci = oi = 0;
	  
	  for (i = 0; i < count; i++)
	  {
	       x = *coords++;	/* x coord */
	       coords++;	/* skip over this y coord - not needed */
	       yhi = *errorYs++; /* y of high end of error bar */
	       ylo = *errorYs++; /* y of low end of error bar */
	       
	       cArray[ci++] = x;     
	       cArray[ci++] = ylo;
	       oArray[oi++] = dps_moveto; /* move to mid bottom */
	  
	       cArray[ci++] = x;
	       cArray[ci++] = yhi;
	       oArray[oi++] = dps_lineto;		/* line to mid top */
	       
	       cArray[ci++] = x - halfErrorCap;
	       cArray[ci++] = yhi;
	       oArray[oi++] = dps_moveto; /* move to top left */
	       
	       cArray[ci++] = x + halfErrorCap;
	       cArray[ci++] = yhi;
	       oArray[oi++] = dps_lineto; /* line to top right */
	       
	       cArray[ci++] = x - halfErrorCap;
	       cArray[ci++] = ylo;
	       oArray[oi++] = dps_moveto; /* move to bottom left */
	       
	       cArray[ci++] = x + halfErrorCap;
	       cArray[ci++] = ylo;
	       oArray[oi++] = dps_lineto; /* line to bottom right */
	  }
	  
	  DPSDoUserPathWithMatrix(cArray, ci, dps_float, oArray, oi, bbox, 
				  dps_ustroke, ctm_matrix);
	  nCoords -= count;
     }
     while (nCoords > 0);

     PSgrestore();
     
     free (cArray);
     free (oArray);
     return 0;
}


int drawXError_NeXT(float *coords, float *errorXs, int nCoords)
{
     int ci, oi, i;
     int count;
     int maxnpts;
     float *cArray;
     char *oArray;
     float halfErrorCap = ERRORCAPSIZE/yScale * 0.5;
     float y, xlo, xhi; 
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };

     if ( NXDrawingStatus == NX_DRAWING )
	  maxnpts = MAXNPTS_SCREEN;
     else
	  maxnpts = MAXNPTS_PRINT;

     if (nCoords > maxnpts) i = maxnpts;
     else i = nCoords;
     if ((cArray = (float *) malloc (12 * i * sizeof(float))) == NULL)
     {
	  h_error("drawXError_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((oArray = (char *) malloc (6 * i * sizeof(char))) == NULL)
     {
	  h_error("drawXError_NeXT - memory exceeded ");
	  return -1;
     }
     
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);
     
     do 
     {
	  if (nCoords > maxnpts) count = maxnpts;
	  else count = nCoords;
	  
	  ci = oi = 0;
	  
	  for (i = 0; i < count; i++)
	  {
	       coords++;	/* skip over this x coord - not needed */
	       y = *coords++;	/* y coord */
	       xhi = *errorXs++; /* x of high end of error bar */
	       xlo = *errorXs++; /* x of low end of error bar */
	       
	       cArray[ci++] = xlo;     
	       cArray[ci++] = y;
	       oArray[oi++] = dps_moveto; /* move to mid bottom */
	  
	       cArray[ci++] = xhi;
	       cArray[ci++] = y;
	       oArray[oi++] = dps_lineto;		/* line to mid top */
	       
	       cArray[ci++] = xlo;
	       cArray[ci++] = y - halfErrorCap;
	       oArray[oi++] = dps_moveto; /* move to top left */
	       
	       cArray[ci++] = xlo;
	       cArray[ci++] = y + halfErrorCap;
	       oArray[oi++] = dps_lineto; /* line to top right */
	       
	       cArray[ci++] = xhi;
	       cArray[ci++] = y - halfErrorCap;
	       oArray[oi++] = dps_moveto; /* move to bottom left */
	       
	       cArray[ci++] = xhi;
	       cArray[ci++] = y + halfErrorCap;
	       oArray[oi++] = dps_lineto; /* line to bottom right */
	  }
	  
	  DPSDoUserPathWithMatrix(cArray, ci, dps_float, oArray, oi, bbox, 
				  dps_ustroke, ctm_matrix);
	  nCoords -= count;
     }
     while (nCoords > 0);

     PSgrestore();
     
     free (cArray);
     free (oArray);
     return 0;
}

int drawYTicks_NeXT( float *y, int nt, float tickwidth, int side )
{
     int ci, oi, i;
     float *cArray;
     char *oArray;
     float start;
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };
     
     tickwidth /= xScale;
     
     if (side == 0) 
	  start = userRect.origin.x;
     else
	  start = userRect.origin.x + userRect.size.width - tickwidth;

     if ((cArray = (float *) malloc (4 * nt * sizeof(float))) == NULL)
     {
	  h_error("drawYTicks_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((oArray = (char *) malloc (2 * nt * sizeof(char))) == NULL)
     {
	  h_error("drawYTicks_NeXT - memory exceeded ");
	  return -1;
     }
     
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);
     
     ci = oi = 0;
     
     for (i = 0; i < nt; i++)
     {
	  cArray[ci++] = start;
	  cArray[ci++] = y[i];
	  oArray[oi++] = dps_moveto;
	  
	  cArray[ci++] = tickwidth;
	  cArray[ci++] = 0.0;
	  oArray[oi++] = dps_rlineto;
     }
     DPSDoUserPathWithMatrix(cArray, ci, dps_float, oArray, oi, bbox, 
			     dps_ustroke, ctm_matrix);
     PSgrestore();

     free(cArray);
     free(oArray);
     
     return 0;
}

int drawXTicks_NeXT( float *y, int nt, float tickwidth, int side )
{
     int ci, oi, i;
     float *cArray;
     float start;
     char *oArray;
     float ctm_matrix[6] = {1.0/xScale, 0.0, 0.0, 1.0/yScale, 0.0, 0.0 };
     
     tickwidth /= yScale;

     if (side == 0) 
	  start = userRect.origin.y;
     else
	  start = userRect.origin.y + userRect.size.height - tickwidth;
     
     if ((cArray = (float *) malloc (4 * nt * sizeof(float))) == NULL)
     {
	  h_error("drawYTicks_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((oArray = (char *) malloc (2 * nt * sizeof(char))) == NULL)
     {
	  h_error("drawYTicks_NeXT - memory exceeded ");
	  return -1;
     }
     
     PSgsave();
     
     /* get into user coords */
     PStranslate (innerRect.origin.x, innerRect.origin.y);
     PSscale (xScale, yScale);
     PStranslate (-userRect.origin.x, -userRect.origin.y);
     
     ci = oi = 0;
     
     for (i = 0; i < nt; i++)
     {
	  cArray[ci++] = y[i];
	  cArray[ci++] = start; 
	  oArray[oi++] = dps_moveto;
	  
	  cArray[ci++] = 0.0;
	  cArray[ci++] = tickwidth;
	  oArray[oi++] = dps_rlineto;
     }
     DPSDoUserPathWithMatrix(cArray, ci, dps_float, oArray, oi, bbox, 
			     dps_ustroke, ctm_matrix);
     PSgrestore();

     free(cArray);
     free(oArray);
     
     return 0;
}

int drawMag_NeXT(float x, float y, int mag, float fontSize)
{
     char str[10];
     
     PSselectfont("Helvetica",fontSize);
     PSmoveto( x, y );
     PSshow("x10");
     PSrmoveto( 0.0, 0.5*fontSize );
     sprintf(str,"%d",mag);
     PSshow(str);
     
     return 0;
}


int drawText_NeXT(char *string, float x, float y, float fontSize, 
		  float rotation, char xAlign, char yAlign)
{
     float yStep = 0.0;
     
     switch (yAlign)
     {
     case 'C':
     case 'c':
	  yStep = 0.4;
	  break;
	 
     case 'T':
     case 't':
	  yStep = 0.8;
	  break;
     }
     
     switch (xAlign)
     {
     case 'C':
     case 'c':
	  drawTextPS( string, x, y, fontSize, rotation, 0.5, yStep,
		     "Helvetica" );
	  break;
	  
     case 'R':
     case 'r':
	  drawTextPS( string, x, y, fontSize, rotation, 1.0, yStep,
		     "Helvetica" );
	  break;
	  
     case 'L':
     case 'l':
     default:
	  drawTextPS( string, x, y, fontSize, rotation, 0.0, yStep,
		     "Helvetica" );
	  break;
     }
     
     return 0;
}



int drawColor2D_NeXT(int nXBins, int nYBins, float binMin, float binMax, 
		     float *data, int useColor )
{
     int  iBin, iYBin;
     int totBins = nXBins * nYBins;
     float xlo = innerRect.origin.x;
     float xhi = xlo + innerRect.size.width;
     float ylo = innerRect.origin.y;
     float yhi = ylo + innerRect.size.height;
     float deltaX = (xhi - xlo) / nXBins;
     float deltaY = (yhi - ylo) / nYBins;
     NXRect *rects;
     int iRect, nRects;
     int rectChunk = 30;	/* >40 rects at a time seems to overflow PS */
     float *grays;
     float colorScale, blueHue, magHue, saturation, brightness;
     float grayScale, nearWhite = 0.9;
     float  xCoord, yCoord;
     int i;
     NXColor myColor;
     float myHue;
     
     /*
      * alloc space for every rect, 
      * even though we will only draw the non-zero ones
      */
     if ((rects = (NXRect *) malloc (totBins * sizeof (NXRect))) == NULL)
     {
	  h_error("drawColor2D_NeXT - memory exceeded ");
	  return -1;
     }
     
     if ((grays = (float *) malloc (totBins * sizeof (float))) == NULL)
     {
	  h_error("drawColor2D_NeXT - memory exceeded ");
	  free(rects);
	  return -1;
     }
     
     /* 
      * this routine leaves color in unpredictable state, 
      * so we need a gsave/grestore
      */ 
     PSgsave();    
     
     if (binMin == binMax)
	  grayScale = 1.0;
     else 
	  grayScale = nearWhite / (binMax - binMin);
     xCoord = xlo;
     yCoord = ylo;
     iYBin = 0;
     iRect = 0;
     for (iBin = 0; iBin < totBins; iBin++)
     {
	  /* bins are arranged as bins[x][y] with y changing fastest */
	  if ((data[iBin] != 0) 
	  && (data[iBin] > binMin) 
	  && (data[iBin] < binMax)) {
	       NXSetRect (&rects[iRect], xCoord, yCoord, deltaX, deltaY);
	       /* 
		* we set the grey value to the range: 
		*  black (most) to nearWhite (least)
		*/
	       grays[iRect] = (binMax - data[iBin]) * grayScale;
	       iRect ++;
	  }
	  yCoord += deltaY;
	  iYBin++;
	  if ( iYBin == nYBins) 
	  {
	       iYBin = 0;
	       yCoord = ylo;
	       xCoord += deltaX;
	  }
     }
     
     if (! useColor)
     {
	  /* do the greyscale version */
	  for (i = 0; i < iRect; i += rectChunk)
	  {
	       nRects = rectChunk;
	       if  ((iRect - i) < rectChunk)  nRects = (iRect - i);
	       NXRectFillListWithGrays (&rects[i], &grays[i], nRects);
	  }
     }
     else
     {
	  /* do the color version */
	  
	  /* set background to black */
	  NXSetColor (NX_COLORBLACK);
	  NXRectFill ((NXRect *) &innerRect);
	  
	  /* establish the color scale */
	  NXConvertColorToHSB (NX_COLORBLUE, &blueHue, 
			       &saturation, &brightness);
	  NXConvertColorToHSB (NX_COLORMAGENTA, &magHue, 
			       &saturation, &brightness);
	  
	  /* 
	   * start at magenta (hot), increase to 1.0, 
	   * then wraparound to 0.0 and up to blue (cold)
	   */
	  colorScale = (1.0 - magHue + blueHue) / nearWhite;
	  
	  for (i=0; i < iRect; i++)
	  {
	       myHue = magHue + (grays[i]  * colorScale);
	       if (myHue > 1.0) myHue = myHue - 1.0;
	       myColor = NXConvertHSBToColor (myHue, 1.0, 1.0);
	       NXSetColor (myColor);
	       NXRectFill (&rects[i]);
	  }
     }
     free (rects);
     free (grays);
     
     /*
      * color/gray is left unpredictable, so we need a gsave/grestore 
      */
     PSgrestore();
     return 0;
}


int drawScatter3D_NeXT(display disp, float *coords, int nCoords)
{
    threeD_t	       *threeD;    	  
    static int          inside = 1, outside = 0;
    char	        charBuf0[2], plotChar='+';
    float 		x, y;
    float 		fontSize;
    char 		string[80];
     

    if (disp->threeDWorkArea == NULL) h3D_init3D(disp);

    threeD = disp->threeDWorkArea;	/* set local pointer */

    PSsetlinewidth(0.0);
    
   /* Check that correct 'user space' is set */
    if (disp->doScatter) {
	h3D_checkPointScale(disp, nCoords);
    }     

 /*
  * Get into user plotting space, ie (-1,-1) to (+1,+1) 
  * This is the user 3D space after going through the transformation to 2D 
  */
    PSgsave();
    PStranslate (disp->drawRect.origin.x, disp->drawRect.origin.y); 
    PSscale (disp->drawRect.size.width/2.0, disp->drawRect.size.height/2.0);
    PStranslate (1.0, 1.0);

 /*
  * Now do transformations, then the xyshow
  */
    if (disp->doCube && disp->flags.drawAxes) {
	h3D_transformCube(disp);
	drawSides_NeXT(disp, inside);
    }
    if (disp->doScatter) {
	if (threeD->reCalculateScatterPoints) {
	    h3D_checkAllocScatterResults(disp, nCoords);
	    h3D_renderXYShow(disp, coords);
	    threeD->reCalculateScatterPoints = 0;
	}

    /* did the plot character change? */
    switch (disp->plotSymbol) {
    case SQUARE:
	plotChar = 'O';
	break;
    case SOLIDSQUARE:
	plotChar = 'M';	
	break;
    case PLUS:
	plotChar = '+';
	break;
    case TIMES:
	plotChar = 'X';
	break;
    }
    if (plotChar != threeD->plotCharBuffer[0]) {
    	memset(threeD->plotCharBuffer,plotChar,threeD->nScatterPoints);
    	threeD->plotCharBuffer[threeD->nScatterPoints] = 0;
    }

    	charBuf0[0] = threeD->plotCharBuffer[0];
    	charBuf0[1] = 0;        
    	PSWXYShow(threeD->scatterResults[0], threeD->scatterResults[1],
	    charBuf0, threeD->plotCharBuffer, &threeD->scatterResults[2], 	
	    threeD->nScatterPoints*2, 
	    disp->symbolSize*(2.0/disp->drawRect.size.width));
    }

    if (disp->doCube && disp->flags.drawAxes) {
	drawSides_NeXT(disp, outside);
    }

    PSgrestore();

    if (disp->flags.drawTitles) {
     	fontSize = disp->drawRect.size.width*0.05;
     	fontSize = MIN(fontSize, 
	    (disp->drawRect.size.height - 
	    disp->marginRect.size.height - 2*YPADDING(disp) -
	    (disp->marginRect.origin.y-disp->drawRect.origin.y)));
     	x = disp->marginRect.origin.x + 0.5 * disp->marginRect.size.width;
     	y = disp->drawRect.origin.y + disp->drawRect.size.height 
            - YPADDING(disp);
     	h_expandLabel(string,disp->title,80,disp);
     	if (fontSize > disp->marginRect.size.width/strlen(string)*2)
     	{
	    fontSize = disp->drawRect.size.width/strlen(string)*2;
	    x = disp->drawRect.origin.x + 0.5 * disp->drawRect.size.width;
     	}
     
     	drawText(string, x, y, fontSize, 0.0, 'c', 't');
    }

    return 0;
}

static void drawSides_NeXT(display disp, int inside)
{
    threeD_t	       *threeD;    	  
    int                 side, p, q, r, s;
    int	                insideVisible, showFace;
    static float        pattern[2] = {0.005, 0.01};
    int                 i;
    static int          corners[4][4] = {0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, 3, 
    			0, 4, 7};
    static int          leftmostChoices[4][2] = {3, 2, 0, 3, 1, 0, 2, 1};
    int                 firstChoice, secondChoice;
    int                 doFirstChoice;
    float              *tickU, *tickV, *tickU2, *tickV2;
    float               tickLength = 0.03;
    float		tickFontSize=0.05, strShift, labelFontSize=0.08;
    char	       *font = "Times-Roman";
    float		labelX, labelY, labelShift = 0.14, labelAngle;
    float		labelRotation;
    char 		string[80];
    
    threeD = disp->threeDWorkArea;	/* set local pointer */

 /* figure out whose left edge to put the tick marks on */
    firstChoice = leftmostChoices[threeD->preferredFace][0];
    secondChoice = leftmostChoices[threeD->preferredFace][1];
    doFirstChoice = 0;

    for (side = 0; side < 4; side++) {
	p = corners[side][0];
	q = corners[side][1];
	r = corners[side][2];
	s = corners[side][3];

	insideVisible = 0;
	if (threeD->cubeV[q] > threeD->cubeV[p]) {
	    if (threeD->cubeU[s] > (threeD->cubeU[p] + (threeD->cubeV[s] 			
		- threeD->cubeV[p]) * (threeD->cubeU[r]
		- threeD->cubeU[p]) / (threeD->cubeV[r] -
		threeD->cubeV[p]))) {
		insideVisible = 1;
	    }
	} else {
	    if (threeD->cubeU[p] > (threeD->cubeU[q] +
		(threeD->cubeV[p] - threeD->cubeV[q]) * (threeD->cubeU[s]
		- threeD->cubeU[q]) / (threeD->cubeV[s] -
		threeD->cubeV[q]))) {
		insideVisible = 1;
	    }
	}

	showFace = (inside) ? insideVisible : !insideVisible;

	if (showFace) {
	    PSsetgray(0.0);
	    PSmoveto(threeD->cubeU[p], threeD->cubeV[p]);
	    PSlineto(threeD->cubeU[q], threeD->cubeV[q]);
	    PSlineto(threeD->cubeU[r], threeD->cubeV[r]);
	    PSlineto(threeD->cubeU[s], threeD->cubeV[s]);
	    PSclosepath();
	    PSstroke();

	    /* now do the inside and outside tick marks */
	    if (!disp->doSpeedy) {
	    	if (inside) {
		    if (threeD->nZTicks > 0) {
		    	if (side == firstChoice)
			    doFirstChoice = 1;
		    	PSgsave();
			PSsetdash(pattern, 2, 0); 

		    	/* point to correct ticks for these 2 verticals */
		    	tickU = &threeD->zTickU[p * threeD->nZTicks];
		    	tickV = &threeD->zTickV[p * threeD->nZTicks];
		    	tickU2 = &threeD->zTickU[q * threeD->nZTicks];
		    	tickV2 = &threeD->zTickV[q * threeD->nZTicks];

		    	for (i = 0; i < threeD->nZTicks; i++) {
			    PSmoveto(tickU[i], tickV[i]);
			    PSlineto(tickU2[i], tickV2[i]);
		    	}
		    	PSstroke();
		    	PSgrestore();	/* remove setdash */
		    }
	    	} else {
	            /* do base ticks for outside face */
		    p = corners[side][0];
		    if (p == 0 || p == 2) {
		    	if (threeD->nXTicks > 0) {	/* point to correct set
							 * of x ticks for this
							 * base line */
			    tickU = &threeD->xTickU[(p / 2) * threeD->nXTicks];
			    tickV = &threeD->xTickV[(p / 2) * threeD->nXTicks];
			    for (i = 0; i < threeD->nXTicks; i++) {
			    	PSmoveto(tickU[i], tickV[i]);
			    	PSrlineto(0.0, -tickLength);
			    	if (disp->theta < -HALFPI || 
			    	    disp->theta > HALFPI) {
			    	    strShift = 0.5 * (1.0 + sin(disp->theta));
			    	} else {
			    	    strShift = 0.5 * (1.0 - sin(disp->theta));
				}
	  	    		drawTextPS(threeD->xLabels[i], tickU[i],
				    (tickV[i]-tickLength-0.04), tickFontSize, 
				    0.0, strShift, 0.0, font);
			    }
			    PSstroke();
		    	    if (disp->flags.drawTitles) {
			    labelAngle = 	
				atan2((tickV[threeD->nXTicks-1]-tickV[0]),
		    	    	(tickU[threeD->nXTicks-1]-tickU[0]));
			    if (disp->theta < -HALFPI || 
			    	disp->theta > HALFPI) labelAngle-=PI;
			    if (labelAngle < TWOPI) labelAngle+=TWOPI;
		    	    labelX = tickU[0] +
			    	(tickU[threeD->nXTicks-1]-tickU[0])/2.0
				+ labelShift*sin(labelAngle);
		    	    labelY = tickV[0] +
			    	(tickV[threeD->nXTicks-1]-tickV[0])/2.0
				 - labelShift*cos(labelAngle);
			    labelRotation = labelAngle *180.0/PI; 
			    h_expandLabel(string,disp->xAxis.label,80,disp);
    		    	    drawText(string, labelX, labelY, 
			    	labelFontSize, labelRotation, 'C', 'T');
			    }
		    	}
		    } else {
		    	if (threeD->nYTicks > 0) {  
		            /* point to correct y ticks for this base line */
			    tickU = &threeD->yTickU[(p / 2) * threeD->nYTicks];
			    tickV = &threeD->yTickV[(p / 2) * threeD->nYTicks];
			    for (i = 0; i < threeD->nYTicks; i++) {
			    	PSmoveto(tickU[i], tickV[i]);
			    	PSrlineto(0.0, -tickLength);
			    	if (disp->theta < 0.0) {
			    	    strShift = 0.5 * (1.0 - cos(disp->theta));
			        } else {
			    	    strShift = 0.5 * (1.0 + cos(disp->theta));
			    	}
	  	    		drawTextPS(threeD->yLabels[i], tickU[i],
				    (tickV[i]-tickLength-0.04), tickFontSize, 
				    0.0, strShift, 0.0, font);
			    }
			    PSstroke();
		    	    if (disp->flags.drawTitles) {
		    	    labelAngle = 	
				atan2((tickV[threeD->nYTicks-1]-tickV[0]),
		    	    	(tickU[threeD->nYTicks-1]-tickU[0]));
			    if (disp->theta > 0.0) labelAngle-=PI; 
			    if (labelAngle < TWOPI) labelAngle+=TWOPI;
		    	    labelX = tickU[0] +
			    	(tickU[threeD->nYTicks-1]-tickU[0])/2.0
				+ labelShift*sin(labelAngle);
		    	    labelY = tickV[0] +
			    	(tickV[threeD->nYTicks-1]-tickV[0])/2.0
				 - labelShift*cos(labelAngle);
			    labelRotation = labelAngle *180.0/PI; 
			    h_expandLabel(string,disp->yAxis.label,80,disp);
    		    	    drawText(string, labelX, labelY, 
			    	labelFontSize, labelRotation, 'C', 'T');
			    }
		    	}
		    }
	    	}
	    }
	}
    }

 /* now the tick marks on the leftmost vertical */
    if (!disp->doSpeedy) {
    	if (threeD->nZTicks > 0) {
	    if (inside) {
	    	if (doFirstChoice) {
		    side = firstChoice;
	    	} else {
		    side = secondChoice;
	    	}
	    	q = corners[side][1];

		/* point to correct set of ticks for this vertical */
	    	tickU = &threeD->zTickU[q * threeD->nZTicks];
	    	tickV = &threeD->zTickV[q * threeD->nZTicks];
	    	for (i = 0; i < threeD->nZTicks; i++) {
		    PSmoveto(tickU[i], tickV[i]);
		    PSrlineto(-tickLength, 0.0);
	  	    drawTextPS(threeD->zLabels[i], (tickU[i]-tickLength-0.01),
			tickV[i], tickFontSize, 0.0, 1.0, 0.0, font);
	    	}
	        PSstroke();
		if ((disp->graphtype == THREEDSCATTER) && 	
		    disp->flags.drawTitles) {
		    labelX = tickU[0] + 
		    	(tickU[threeD->nZTicks-1]-tickU[0])/2.0 - labelShift;
		    labelY = tickV[0] + 	
		        (tickV[threeD->nZTicks-1]-tickV[0])/2.0;
		    labelAngle = atan2((tickV[0]-tickV[threeD->nZTicks-1]),
		        (tickU[0]-tickU[threeD->nZTicks-1]))*180.0/PI;
		    h_expandLabel(string,disp->zAxis.label,80,disp);
    		    drawText(string, labelX, labelY, 
		        labelFontSize, labelAngle, 'C', 'T');
		}
	    }
	}
    }

    return;
}

int shade_NeXT(float xlow, float xhigh, float ylow, float yhigh )
{
     NXRect myRect;
     
     PSgsave();
     PSsetgray(0.6);	
     NXSetRect(&myRect, xlow, ylow, (xhigh-xlow),(yhigh-ylow));
     NXRectFill(&myRect);
     PSgrestore();
     
     return 0;
}


/*
 * Draw a 'wire-frame' lego plot in the given drawing space 
 */
static void drawWireFrame_NeXT(display disp)
{
    threeD_t	       *threeD;    	  
    float               *p, *q, *u, *v;
    int                 i, ci, oi, nLines;

    threeD = disp->threeDWorkArea;	/* set local pointer */

    ci = oi = nLines = 0;
    u = threeD->legoResults[0];
    v = threeD->legoResults[1];
    for (i = 0; i < threeD->nLegoPoints; i += 8) {
	p = &u[i];
	q = &v[i];
	threeD->cArray[ci++] = p[0];
	threeD->cArray[ci++] = q[0];
	threeD->oArray[oi++] = dps_moveto;
	threeD->cArray[ci++] = p[1];
	threeD->cArray[ci++] = q[1];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[2];
	threeD->cArray[ci++] = q[2];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[3];
	threeD->cArray[ci++] = q[3];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[0];
	threeD->cArray[ci++] = q[0];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[4];
	threeD->cArray[ci++] = q[4];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[5];
	threeD->cArray[ci++] = q[5];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[6];
	threeD->cArray[ci++] = q[6];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[7];
	threeD->cArray[ci++] = q[7];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[4];
	threeD->cArray[ci++] = q[4];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[1];
	threeD->cArray[ci++] = q[1];
	threeD->oArray[oi++] = dps_moveto;
	threeD->cArray[ci++] = p[5];
	threeD->cArray[ci++] = q[5];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[2];
	threeD->cArray[ci++] = q[2];
	threeD->oArray[oi++] = dps_moveto;
	threeD->cArray[ci++] = p[6];
	threeD->cArray[ci++] = q[6];
	threeD->oArray[oi++] = dps_lineto;
	threeD->cArray[ci++] = p[3];
	threeD->cArray[ci++] = q[3];
	threeD->oArray[oi++] = dps_moveto;
	threeD->cArray[ci++] = p[7];
	threeD->cArray[ci++] = q[7];
	threeD->oArray[oi++] = dps_lineto;
	nLines += 16;
	if (nLines > (MAXPSLINES-16)) {	/* printer limit 1500 lines??? */
	    DPSDoUserPath(threeD->cArray, ci, dps_float,
			  threeD->oArray, oi, threeD->boundingBox,
			  dps_ustroke);
	    ci = oi = nLines = 0;
	}
    }
    if (nLines > 0)		/* flush last buffer */
	DPSDoUserPath(threeD->cArray, ci, dps_float,
		      threeD->oArray, oi, threeD->boundingBox,
		      dps_ustroke);

    return;
}

/*
 * Draw a mesh surface through the to centers of each 'lego' 
 */
static void drawMesh_NeXT(display disp)
{
    threeD_t	       *threeD;  	  
    float              *p, *q, *u, *v;
    int                 i, j, k, ci, oi, nLines;

    threeD = disp->threeDWorkArea;	/* set local pointer */

    ci = oi = nLines = 0;
    u = threeD->legoResults[0];
    v = threeD->legoResults[1];
    for (i = 0; i < disp->bins.xAxis.nBins; i++) {
	k = i * 8 * disp->bins.yAxis.nBins;
	p = &u[k];
	q = &v[k];
	threeD->cArray[ci++] = (p[4] + p[6]) / 2.0;
	threeD->cArray[ci++] = (q[4] + q[6]) / 2.0;
	threeD->oArray[oi++] = dps_moveto;
	for (j = 1; j < disp->bins.yAxis.nBins; j++) {
	    p = &u[k + j * 8];
	    q = &v[k + j * 8];
	    threeD->cArray[ci++] = (p[4] + p[6]) / 2.0;
	    threeD->cArray[ci++] = (q[4] + q[6]) / 2.0;
	    threeD->oArray[oi++] = dps_lineto;
	}
	nLines += (disp->bins.yAxis.nBins - 1);
	if (nLines > (MAXPSLINES-disp->bins.yAxis.nBins)) {
	    DPSDoUserPath(threeD->cArray, ci, dps_float,
		threeD->oArray, oi, threeD->boundingBox,
		dps_ustroke);
	    ci = oi = nLines = 0;
	}
    }

    for (j = 0; j < disp->bins.yAxis.nBins; j++) {
	k = j * 8;
	p = &u[k];
	q = &v[k];
	threeD->cArray[ci++] = (p[4] + p[6]) / 2.0;
	threeD->cArray[ci++] = (q[4] + q[6]) / 2.0;
	threeD->oArray[oi++] = dps_moveto;
	for (i = 1; i < disp->bins.xAxis.nBins; i++) {
	    p = &u[k + i * 8 * disp->bins.yAxis.nBins];
	    q = &v[k + i * 8 * disp->bins.yAxis.nBins];
	    threeD->cArray[ci++] = (p[4] + p[6]) / 2.0;
	    threeD->cArray[ci++] = (q[4] + q[6]) / 2.0;
	    threeD->oArray[oi++] = dps_lineto;
	}
	nLines += (disp->bins.yAxis.nBins - 1);
	if (nLines > (MAXPSLINES-disp->bins.yAxis.nBins)) {
	    DPSDoUserPath(threeD->cArray, ci, dps_float,
			  threeD->oArray, oi, threeD->boundingBox,
			  dps_ustroke);
	    ci = oi = nLines = 0;
	}
    }
    if (nLines > 0)		/* flush last buffer */
	DPSDoUserPath(threeD->cArray, ci, dps_float,
		      threeD->oArray, oi, threeD->boundingBox,
		      dps_ustroke);

    return;
}


int drawLego2D_NeXT(display disp)
{
    threeD_t	       *threeD;    	  
    int                 i;
    static int          corners0[4][8] = {3, 0, 4, 7, 0, 1, 5, 4,
					  0, 1, 5, 4, 1, 2, 6, 5,
					  1, 2, 6, 5, 2, 3, 7, 6,
					  2, 3, 7, 6, 3, 0, 4, 7};
    static int          corners1[4][8] = {0, 1, 5, 4, 3, 0, 4, 7,
					  1, 2, 6, 5, 0, 1, 5, 4,
					  2, 3, 7, 6, 1, 2, 6, 5,
					  3, 0, 4, 7, 2, 3, 7, 6};
    int                *cornerIndex;
    int                	thisCorner, nearTopCorner, farTopCorner;
    static int         	inside = 1, outside = 0;
    float              *x, *y;
    float 		titleX, titleY;
    float 		fontSize;
    char 		string[80];

    if (disp->threeDWorkArea == NULL) h3D_init3D(disp);

    threeD = disp->threeDWorkArea;	/* set local pointer */

    PSsetlinewidth(0.0);
    
   /* Check that correct 'user space' is set */
    if (disp->doFill || disp->doWireFrame || disp->doMesh){
	h3D_checkBinScale(disp);
    } 

 /*
  * Get into user plotting space, ie (-1,-1) to (+1,+1) 
  * This is the user 3D space after going through the transformation to 2D 
  */
    PSgsave();
    PStranslate (disp->drawRect.origin.x, disp->drawRect.origin.y); 
    PSscale (disp->drawRect.size.width/2.0, disp->drawRect.size.height/2.0);
    PStranslate (1.0, 1.0);

 /*
  * Now do transformations and drawing
  */
    if (disp->doCube && disp->flags.drawAxes) {
	h3D_transformCube(disp);
	drawSides_NeXT(disp, inside);
    }

    if (disp->doFill && !disp->doSpeedy) { 
    	h3D_transformLego(disp);

	/* draw the blocks, starting from farthest */
	for (i = threeD->numBlocks - 1; i >= 0; i--) {
	    x = threeD->blockPointer[i]->xData;
	    y = threeD->blockPointer[i]->yData;
	    thisCorner = threeD->blockPointer[i]->nearCorner;
	    if (y[4] > y[0]) {	/* might as well skip drawing the zero bins */
		if (thisCorner == threeD->preferredFace) {
		    cornerIndex = &corners0[thisCorner][0];
		} else {
		    cornerIndex = &corners1[thisCorner][0];
		}
		PSmoveto(x[cornerIndex[0]],
			 y[cornerIndex[0]]);
		PSlineto(x[cornerIndex[1]],
			 y[cornerIndex[1]]);
		PSlineto(x[cornerIndex[2]],
			 y[cornerIndex[2]]);
		PSlineto(x[cornerIndex[3]],
			 y[cornerIndex[3]]);
		PSclosepath();
		PSsetgray(disp->color[cornerIndex[0]]);
		if (disp->doWireFrame) {
		    PSgsave();
		    PSfill();
		    PSgrestore();
		    PSsetgray(0.0);
		    PSstroke();
		} else {
		    PSfill();
		}
		PSmoveto(x[cornerIndex[4]],
			 y[cornerIndex[4]]);
		PSlineto(x[cornerIndex[5]],
			 y[cornerIndex[5]]);
		PSlineto(x[cornerIndex[6]],
			 y[cornerIndex[6]]);
		PSlineto(x[cornerIndex[7]],
			 y[cornerIndex[7]]);
		PSclosepath();
		PSsetgray(disp->color[cornerIndex[4]]);
		if (disp->doWireFrame) {
		    PSgsave();
		    PSfill();
		    PSgrestore();
		    PSsetgray(0.0);
		    PSstroke();
		} else {
		    PSfill();
		}
	    }
	    /* determine if top is visible */
	    nearTopCorner = thisCorner + 4;
	    farTopCorner = nearTopCorner + 2;
	    if (farTopCorner > 7)
		farTopCorner -= 4;
	    if (y[farTopCorner] > y[nearTopCorner]) {
		PSmoveto(x[4], y[4]);
		PSlineto(x[5], y[5]);
		PSlineto(x[6], y[6]);
		PSlineto(x[7], y[7]);
		PSclosepath();
		PSsetgray(disp->color[4]);
		if (disp->doWireFrame) {
		    PSgsave();
		    PSfill();
		    PSgrestore();
		    PSsetgray(0.0);
		    PSstroke();
		} else {
		    PSfill();
		}
	    }
	}
    }
    /* end of doFill */

    else if (disp->doWireFrame || (disp->doFill && !disp->doSpeedy)) {
	h3D_transformWireFrame(disp);
	drawWireFrame_NeXT(disp);
    }

    if (disp->doMesh) {
	h3D_transformMesh(disp);
	drawMesh_NeXT(disp);
    }

    if (disp->doCube && disp->flags.drawAxes) {
	drawSides_NeXT(disp, outside);
    }

    PSgrestore();

    if (disp->flags.drawTitles) {
     	fontSize = disp->drawRect.size.width*0.05;
     	fontSize = MIN(fontSize, 
	    (disp->drawRect.size.height - 
	    disp->marginRect.size.height - 2*YPADDING(disp) -
	    (disp->marginRect.origin.y-disp->drawRect.origin.y)));
     	titleX = disp->marginRect.origin.x + 0.5 * disp->marginRect.size.width;
     	titleY = disp->drawRect.origin.y + disp->drawRect.size.height 
            - YPADDING(disp);
     	h_expandLabel(string,disp->title,80,disp);
     	if (fontSize > disp->marginRect.size.width/strlen(string)*2)
     	{
	    fontSize = disp->drawRect.size.width/strlen(string)*2;
	    titleX = disp->drawRect.origin.x + 0.5 * disp->drawRect.size.width;
     	}
     
     	drawText(string, titleX, titleY, fontSize, 0.0, 'c', 't');
    }

    return 0;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.