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

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

/*
 * hippoplot.c - Routines for displaying hippo ntuples.
 *
 * Copyright (C)  1991  The Board of Trustees of The Leland Stanford
 * Junior University.  All Rights Reserved.
 *
 * $Id: hplot.c,v 5.0 1993/08/17 21:55:27 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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <float.h>
#include <ctype.h>
#include "hippo.h"
#include "hutil.h"
#include "h3D.h"
#include "hdata.h"

GLOB_QUAL const char hippoplot_c_rcsid[] = 
     "$Id: hplot.c,v 5.0 1993/08/17 21:55:27 rensing Exp $";


#ifdef _NEXT_PLOT_
#include "hplotNxt.h"
/*
 * copied from appkit/View.h; hopefully it does not change
 */
extern short NXDrawingStatus;
#define NX_DRAWING	1	/* we're drawing */
#define NX_PRINTING	2	/* we're printing */
#define NX_COPYING	3	/* we're copying to the scrap */

#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR NEXT
#endif
#endif

#ifdef _UNIXPLOT_PLOT_
#include "hplotUP.h"
#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR UNIXPLOT
#endif
#endif

#ifdef _XIV_PLOT_
#include "hplotXIV.h"
#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR XIVPLOT
#endif
#endif

#ifdef _X11_PLOT_
#include "hplotX11.h"
#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR X11PLOT
#endif
#endif

#ifdef THINK_C
#include "hplotMAC.h"
#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR MAC
#endif
#endif

#include "hplotPS.h"
#ifndef DEF_PLOT_DRVR
#define DEF_PLOT_DRVR PSPLOT
#endif

#define NUM_FUZZ FLT_EPSILON*4

#define MIN(x, y)  (((x) > (y)) ? (y) : (x))
#define MAX(x, y)  (((x) > (y)) ? (x) : (y))

#define XPADDING(disp) ((disp)->drawRect.size.width*0.01)
#define YPADDING(disp) ((disp)->drawRect.size.height*0.01)


#if defined(VMS) || defined(ultrix)
#define Concat3(x,y,z) x/**/y/**/z
#else
#define Concat3(x,y,z) x##y##z
#endif

#ifdef _NEXT_PLOT_
#define NeXTFunc(name,parm)           \
case NEXT:                            \
     rc = Concat3(name,_NeXT,parm);   \
     break;

#else
#define NeXTFunc(name,parm)  
#endif

#ifdef _UNIXPLOT_PLOT_
#define UPFunc(name,parm)           \
case UNIXPLOT:                      \
     rc = Concat3(name,_UP,parm);   \
     break;

#else
#define UPFunc(name,parm)  
#endif

#ifdef _XIV_PLOT_
#define XIVFunc(name,parm)         \
case XIVPLOT:                      \
     Concat3(name,_XIV,parm);      \
     rc = 0;                       \
     break;

#else
#define XIVFunc(name,parm)  
#endif

#ifdef _X11_PLOT_
#define X11Func(name,parm)           \
case X11PLOT:                        \
     Concat3(name,_X11,parm);        \
     rc = 0;                         \
     break;

#else
#define X11Func(name,parm)  
#endif

#ifdef THINK_C
#define MACFunc(name,parm)           \
case MAC:                            \
     Concat3(name,_MAC,parm);        \
     rc = 0;                         \
     break;

#else
#define MACFunc(name,parm)  
#endif

/*
 * always make PS driver available
 */
#define PSFunc(name,parm)           \
case PSPLOT:                        \
case EPSPLOT:                       \
     Concat3(name,_PS,parm);        \
     rc = 0;                        \
     break;

#define PlotSwitch(drvr,name, parm) \
switch (drvr)                       \
{                                   \
     NeXTFunc(name,parm)            \
     UPFunc(name,parm)              \
     XIVFunc(name,parm)             \
     X11Func(name,parm)             \
     MACFunc(name,parm)             \
     PSFunc(name,parm)              \
     default:                       \
	  rc = -1;                  \
}



/* 
 * private functions
 */
static int drawData( display disp );
static int drawData1D( display disp );
static int drawAxis( display disp );
static int drawLabels( display disp );

static int drawLego1D(display disp, float *lego, int *over, int npts,
		      linestyle_t ls);
static int drawPoint1D(display disp, float *xy, int *over, int npts);
static int drawError1D(display disp, float *xy, float *err, 
		       int *over, int npts, int err_type);
static int drawLine1D(display disp, float *xy, int *over, 
		      int npts, int xmin_pt, linestyle_t ls);
static void box_cross(float *p1, float *p2, display disp);
int intcmp(const void *i1, const void *i2);

static int drawColor2D(display disp);
static int drawScatter2D(display disp);
static int drawLego2D(display disp);
static int drawScatter3D(display disp);

static int initPlot(display disp);
static int endPlot(display disp);
static int drawAxisBox(display disp);

static int drawTicks(display disp, binding_t axis);
#define N__TICKS 20
static int drawXTicks(display disp, int nticks, float *ticks, 
		      char labels[N__TICKS][10], float pmag, 
		      float fontSize );
static int drawYTicks(display disp, int nticks, float *ticks, 
		      char labels[N__TICKS][10], float pmag, 
		      float fontSize );
static int drawMag( float x, float y, int mag, float fontSize );

int drawText(char *s, float x, float y, float fontsize, float angle,
	     char xp, char yp );

static int plotFunc(display disp, func_id fun );
static double funcSum(double x, double width, double *parm );

/*
 * global variables.
 */
static int init_done;
static plotdrvr_t plot_drvr = DEF_PLOT_DRVR;

#ifdef _XIV_PLOT_
/* For passing to the X Interviews driver... */
static void* canvas;
static void* painter;
#endif

#ifdef _X11_PLOT_
/* for passing to X11 driver ... */
static Display* dpy;
static Screen* screen;
static Drawable drawable;
static GC gc;
#endif


int h_setPlotDrvr( plotdrvr_t drvr, ...)
{
     int rc;
     va_list argPtr;
     void *parm;
     
     switch (drvr)
     {
#ifdef _NEXT_PLOT_
     case NEXT:
#endif
#ifdef _XIV_PLOT_
     case XIVPLOT:
#endif	  
#ifdef _X11_PLOT_
     case X11PLOT:
#endif	  
#ifdef THINK_C
     case MAC:
#endif
     case LPR:
	  rc = 0;
	  break;
	  	  
     case PSPLOT:
     case EPSPLOT:
	  va_start( argPtr, drvr );
	  parm = va_arg(argPtr, FILE *);
	  rc = initDrvr_PS( parm );
	  va_end(argPtr);
	  break;

#ifdef _UNIXPLOT_PLOT_
     case UNIXPLOT:
	  va_start( argPtr, drvr );
	  parm = va_arg(argPtr, FILE *); /* this shit does not work yet */
	  rc = initDrvr_UP( parm );
	  va_end(argPtr);
	  break;
#endif
	  	  
     default:
	  h_error("h_setPlotDrvr - invalid plot driver. Driver may not be compiled in this version.\n");
	  rc = -1;
     }
     
     if (rc == 0) 
	  plot_drvr = drvr;
     else
	  h_error("h_setPlotDrvr - error in driver init. Driver not changed.\n");
     
     return rc;
}

int h_endPlotDrvr( void )
{
     int rc;
     
     switch (plot_drvr)
     {
     case PSPLOT:
     case EPSPLOT:
	  rc = endDrvr_PS( );
	  break;

     default:
	  rc = 0;
     }
     
     return rc;
}

int h_endPage( void )
{
     int rc;
     
     switch (plot_drvr)
     {
     case PSPLOT:
     case EPSPLOT:
	  rc = endPage_PS( );
	  break;

     default:
	  rc = 0;
     }
     
     return rc;
}

int h_plot(display disp,...)
{ 
     int rc = 0;
     int xLogFlag, yLogFlag;
#if defined(_XIV_PLOT_) || defined(_X11_PLOT_)
     va_list argPtr;
#endif

     if (disp == NULL || disp->tuple == NULL) return -1;

     /* 
      * be nice. If driver is lineprinter, call h_print.
      */
     if (plot_drvr == LPR)
     {
	  h_print( disp );
	  return 0;
     }
     
#ifdef _XIV_PLOT_
     if (plot_drvr == XIVPLOT)
     {
	  va_start( argPtr, disp );
	  painter = va_arg(argPtr, void *);
	  canvas  = va_arg(argPtr, void *);
	  va_end(argPtr);
     }
#endif

#ifdef _X11_PLOT_
     if (plot_drvr == X11PLOT)
     {
	  va_start( argPtr, disp );
	  dpy       = va_arg(argPtr, Display *);
	  screen    = va_arg(argPtr, Screen *);
	  drawable  = va_arg(argPtr, Drawable);
	  gc        = va_arg(argPtr, GC);
	  va_end(argPtr);
     }
#endif

     /*
      * make sure min/max of ntuple columns is up-to-date
      */
     if (disp->tuple->extremeBad) h_findExtreme(disp->tuple);
     
     /*
      * bin the data. Function will decide if it is needed.
      */
     if (h_bin(disp) != 0) return -1;
     
     init_done = 0;
     
     /* 
      * We want to ignore the log flags if not appropriate (binned axes)  
      * Save the flag value and reset afterwards. (bit of a hack)
      */
     xLogFlag = disp->xAxis.flags.log;
     yLogFlag = disp->yAxis.flags.log;
     if (disp->graphtype == HISTOGRAM || disp->graphtype == LEGOPLOT
         || disp->graphtype == COLORPLOT)
	  disp->xAxis.flags.log = 0;
     if (disp->graphtype == LEGOPLOT || disp->graphtype == COLORPLOT)
	  disp->yAxis.flags.log = 0;
	  
     drawData(disp);
     
     /*
      * Plot the functions
      */
     if (disp->flags.plotFuncs)
     {
	func_id fun;
	for (fun = disp->plot_func; fun != NULL && rc == 0; fun = fun->next ) {
	       if ( fun->funcPtr != NULL ) {
        	       rc = plotFunc( disp, fun );
	       } else {
	               rc = 0;
	       }
	}
     }
     if (disp->flags.plotFuncSum)
     {
          func_id fun;
	  fun = disp->plot_func;
	  if (fun == NULL) {	/* cannot have a sum without a function */
	      disp->flags.plotFuncSum = 0;
	  } else { 
	      /* build a "fake" function block and call plotFunc */
	      func_id_t fun;
	      fun.funcPtr = (int (*)())funcSum;
	      fun.paramBlk = (double *)disp;
	      fun.lineStyle = disp->fSumLineStyle;
	      plotFunc( disp, &fun );
	  }
     }

     if (disp->graphtype != LEGOPLOT && disp->graphtype != THREEDSCATTER) {
	  if (disp->flags.drawAxes) drawAxis(disp);	
	  if (disp->flags.drawTitles) drawLabels( disp );
     }

     endPlot( disp );
     
     disp->xAxis.flags.log = xLogFlag;
     disp->yAxis.flags.log = yLogFlag;
     
     return 0;
}


static int drawAxis(display disp)
{
     if (disp == NULL) return -1;
     
     if (!init_done)
     {
	  initPlot(disp);
	  init_done = 1;
     }
     
     drawAxisBox( disp );
     if (drawTicks(disp,YAXIS))
	  h_error("drawAxis: error drawing y ticks");
     if (drawTicks(disp,XAXIS)) 
	  h_error("drawAxis: error drawing x ticks");

     return 0;
}


static int drawData(display disp)
{
     float	fontHeight;

     if (disp->tuple == NULL) return 0;

     if (disp->tuple->ndata == 0)
     { 
	  fontHeight= MIN(disp->drawRect.size.width*0.05,24.0);
	  drawText("No Data in Tuple",
		   disp->marginRect.origin.x+disp->marginRect.size.width/2.0,
		   disp->marginRect.origin.y+disp->marginRect.size.height/2.0,
		   fontHeight, 0, 'c', 'c');
	  return 0;
     }
     
     switch (disp->graphtype) 
     {
     case HISTOGRAM:
     case XYPLOT:
     case STRIPCHART:
	  drawData1D( disp );
	  break;

     case COLORPLOT:		/* 2D COLOR */
	  drawColor2D(disp);
	  break;
	  
     case SCATTERPLOT:		/* 2D SCATTER */
	  drawScatter2D(disp);
	  break;
	  
     case LEGOPLOT:		/* 3D LEGO */
	  drawLego2D(disp);
	  break;

     case THREEDSCATTER:	/* 3D SCATTER */
	  drawScatter3D(disp);
	  break;
     }
     
     
     return 0;
}

static int drawData1D(display disp)
{
     float *xy = NULL, *lego = NULL, *xerr = NULL, *yerr = NULL;
     int *over = NULL, xmin_pt = 0, npts;
     struct maxs limits;
     
     /*
      * first get the appropriate data, depending on the graphtype.
      */
     switch (disp->graphtype) 
     {
     case HISTOGRAM:		/* Histogra */
	  npts = getHistoData(disp,&xy,&lego,&xerr,&yerr,&over,&limits);
	  if (npts == 0) return 0;
	  if (npts < 0) 
	  {
	       h_error("h_plot - error getting bin data");
	       return -1;
	  }

	  if (disp->yAxis.flags.autoScale)
	  {
	       if (limits.yh < limits.yl)
	       {
		    /* must be no points in range. */
		    return 0;
	       }			 
	       else if (limits.yh == limits.yl)
	       {
		    /* must be only on point in range. */
		    disp->yAxis.low = limits.yh - 0.5;
		    disp->yAxis.high = limits.yh + 0.5;
	       }
	       else
	       {
		    if (limits.yh > 0.0)
			 disp->yAxis.high = limits.yh;
		    else
		    {
			 if (disp->yAxis.flags.log)
			 {
			      h_error("All data < 0 on log axis");
			      return -1;
			 }
			 disp->yAxis.high = 0.0;
		    }
		    
		    if (limits.yl < 0.0 || disp->yAxis.flags.log)
			 disp->yAxis.low = limits.yl;
		    else
			 disp->yAxis.low = 0.0;
	       }
	  }
	  else if (disp->yAxis.flags.log && disp->yAxis.low <= 0.0)
	  {
	       if (limits.yl > 0.0)
		    disp->yAxis.low = limits.yl;
	       else
		    disp->yAxis.low = pow(10.0, FLT_MIN);
	       if (disp->yAxis.high <= disp->yAxis.low)
		    disp->yAxis.high = 
		           disp->yAxis.low*(1.0+NUM_FUZZ) + FLT_MIN;
	  }

	  break;
	  
     case XYPLOT:
     case STRIPCHART:
	  npts = getXYData(disp,&xy,&xerr,&yerr,&over,&limits,&xmin_pt);
	  if (npts == 0) return 0;
	  if (npts < 0) 
	  {
	       h_error("h_plot - error getting data for x-y plot");
	       return -1;
	  }

	  if (disp->graphtype == XYPLOT) xmin_pt = 0;
	  
	  /* 
	   * autoscale axes 
	   */
	  if (disp->xAxis.flags.autoScale)
	  {
	       if (limits.xh > limits.xl)
	       {
		    disp->xAxis.low = limits.xl;
		    disp->xAxis.high = limits.xh;
	       }
	       else if (limits.xh == limits.xl)
	       {
		    /* must be only on point in range. */
		    disp->xAxis.low = limits.xl - 0.5;
		    disp->xAxis.high = limits.xh + 0.5;
	       }
	       else
	       {
		    /* must be no points in range. */
		    return 0;
	       }			 
	       h_adjustAxis(&(disp->xAxis.low), &(disp->xAxis.high),
			    0, disp->xAxis.flags.log);
	  }
	  else if (disp->xAxis.flags.log && disp->xAxis.low <= 0.0)
	  {
	       if (limits.xl > 0.0)
		    disp->xAxis.low = limits.xl;
	       else
		    disp->xAxis.low = pow(10.0, FLT_MIN);
	       if (disp->xAxis.high <= disp->xAxis.low)
		    disp->xAxis.high = 
		        disp->xAxis.low*(1.0+NUM_FUZZ) + FLT_MIN;
	  }

	  if (disp->yAxis.flags.autoScale)
	  {
	       if (limits.yh > limits.yl)
	       {
		    disp->yAxis.low = limits.yl;
		    disp->yAxis.high = limits.yh;
	       }
	       else if (limits.yh == limits.yl)
	       {
		    /* must be only on point in range. */
		    disp->yAxis.low = limits.yl - 0.5;
		    disp->yAxis.high = limits.yh + 0.5;
	       }
	       else
	       {
		    /* must be no points in range. */
		    return 0;
	       }			 
	  }
	  else if (disp->yAxis.flags.log && disp->yAxis.low <= 0.0)
	  {
	       if (limits.yl > 0.0)
		    disp->yAxis.low = limits.yl;
	       else
		    disp->yAxis.low = pow(10.0, FLT_MIN);
	       if (disp->yAxis.high <= disp->yAxis.low)
		    disp->yAxis.high = 
		        disp->yAxis.low*(1.0+NUM_FUZZ) + FLT_MIN;
	  }
	  
	  break;
	  
     default:
	  h_error("hippo error: invalid disp->graphtype\n");
	  return -1;
	  break;
     }

     if (disp->yAxis.flags.autoScale)
	  h_adjustAxis(&(disp->yAxis.low), &(disp->yAxis.high),
		       0, disp->yAxis.flags.log );
     
     /*
      * init the plot device
      */
     if (!init_done)
     {
	  initPlot( disp );
	  init_done = 1;
     }

     /*
      * draw the points, lines etc.
      */

     /* draw points */
     if ( disp->drawtype & POINT )
	  drawPoint1D(disp, xy, over, npts);
     
     /* draw error bars */
     if ( disp->drawtype & ERRBAR )
     {
	  if (yerr != NULL) drawError1D(disp,xy,yerr,over,npts,YERROR);
	  if (xerr != NULL) drawError1D(disp,xy,xerr,over,npts,XERROR);
     }
     
     
     /* join points */
     if ( disp->drawtype & LINE )
	  drawLine1D(disp,xy,over,npts,xmin_pt,disp->lineStyle);
     
     /* draw bars */
     if ( (disp->drawtype & BOX) && disp->graphtype==HISTOGRAM )
	  drawLego1D(disp, lego, over, npts,disp->lineStyle);
     
     free(lego);
     free(xy);
     free(xerr);
     free(yerr);
     free(over);
     
     return 0;
}


static int initPlot(display disp)
{
     int rc;
     rectangle userRect;

     if (disp->xAxis.flags.log)
     {
	  userRect.origin.x = log10(disp->xAxis.low);
	  userRect.size.width = log10(disp->xAxis.high / disp->xAxis.low);
     }
     else
     {
	  userRect.origin.x = disp->xAxis.low;
	  userRect.size.width = disp->xAxis.high - disp->xAxis.low;
     }
     if (disp->yAxis.flags.log)
     {
	  userRect.origin.y = log10(disp->yAxis.low);
	  userRect.size.height = log10(disp->yAxis.high / disp->yAxis.low);
     }
     else
     {
	  userRect.origin.y = disp->yAxis.low;
	  userRect.size.height = disp->yAxis.high - disp->yAxis.low;
     }
     
     switch (plot_drvr)
     {
	  NeXTFunc(initPlot,(&disp->drawRect, &disp->marginRect, &userRect));
	  UPFunc(initPlot,(&disp->drawRect, &disp->marginRect, &userRect));
	  MACFunc(initPlot,(&disp->drawRect, &disp->marginRect, &userRect));
	  PSFunc(initPlot,(&disp->drawRect, &disp->marginRect, &userRect));
	  
#ifdef _XIV_PLOT_
     case XIVPLOT:
	  initPlot_XIV(painter,canvas);
	  setHistoCoords_XIV(&disp->marginRect, &userRect);
	  rc = 0;
	  break;
#endif

#ifdef _X11_PLOT_
     case X11PLOT:
	  initPlot_X11(dpy,screen,drawable,gc);
	  setHistoCoords_X11(&disp->drawRect, &disp->marginRect, &userRect);
	  rc = 0;
	  break;
#endif

     default:
	  rc = -1;
     }
     
     return rc;
}



static int endPlot(display disp)
{
     int rc;
     
     switch (plot_drvr)
     {
	  UPFunc(endPlot,());
	  	  
     default:
	  rc = 0;
     }
     
     return rc;
}



static int drawLabels( display disp )
{
     float x, y;
     float fontSize;
     char string[80];
     
     /*
      * title
      */
     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;
     }
     
     if (drawText(string, x, y, fontSize, 0.0, 'c', 't') != 0)
     {
	  h_error("Error drawing plot title");
	  return -1;
     }

     /*
      * x axis label
      */
     y -= fontSize;
     x = disp->marginRect.origin.x + 0.5 * disp->marginRect.size.width;
     fontSize = MIN(disp->drawRect.size.width*0.04,14.0);
     fontSize = MIN(disp->drawRect.size.height*0.04,14.0);
     fontSize = MIN(fontSize, 
		    disp->drawRect.size.width/strlen(disp->xAxis.label)*2);
     h_expandLabel(string,disp->xAxis.label,80,disp);
     if (disp->xAxis.flags.labelLocation & PLOTBOTTOM)
     {
 	   y = disp->drawRect.origin.y + YPADDING(disp);
     	   if (drawText(string, x, y, fontSize,0.0,'c','b') != 0)
	   {
	       h_error("Error drawing x axis label");
	       return -1;
	   }
     }
     else if (disp->xAxis.flags.labelLocation & PLOTTOP)
     {
 	   y -= YPADDING(disp);
     	   if (drawText(string, x, y, fontSize,0.0,'c','t') != 0)
	   {
	       h_error("Error drawing x axis label");
	       return -1;
	   }
     }
     
     /*
      * y axis label
      */
     fontSize = MIN(disp->drawRect.size.width*0.04,14.0);
     fontSize = MIN(fontSize, 
		    disp->drawRect.size.height/strlen(disp->yAxis.label)*2);
     y = disp->marginRect.origin.y + disp->marginRect.size.height/2.0;
     h_expandLabel(string,disp->yAxis.label,80,disp);
     if (disp->yAxis.flags.labelLocation & PLOTLEFT)
     {
         x = disp->drawRect.origin.x + XPADDING(disp);
         if (drawText(string, x, y, fontSize,90.0,'c','t') != 0)
         {
	      h_error("Error drawing y axis label");
	      return -1;
         }
     }
     else if (disp->yAxis.flags.labelLocation & PLOTRIGHT)
     {
         x = disp->drawRect.origin.x + disp->drawRect.size.width 
	     - XPADDING(disp);
         if (drawText(string, x, y, fontSize,-90.0,'c','t') != 0)
         {
	      h_error("Error drawing y axis label");
	      return -1;
         }
     }

     
     return 0;
}

     

int drawText(char *s, float x, float y, float fontsize, float angle,
	     char xp, char yp )
{
     int rc;
     
     PlotSwitch(plot_drvr,drawText,(s, x, y, fontsize, angle, xp, yp));
     
     return rc;
}


static int drawAxisBox( display disp )
{
     int rc = 0;
     float xy[10];

     if (disp->xAxis.flags.log)
     {
	  xy[0] = xy[2] = xy[8] = log10(disp->xAxis.low);
	  xy[4] = xy[6] = log10(disp->xAxis.high);
     }
     else
     {
	  xy[0] = xy[2] = xy[8] = disp->xAxis.low;
	  xy[4] = xy[6] = disp->xAxis.high;
     }
     if (disp->yAxis.flags.log)
     {
	  xy[1] = xy[7] = xy[9] = log10(disp->yAxis.low);
	  xy[3] = xy[5] = log10(disp->yAxis.high);
     }
     else
     {
	  xy[1] = xy[7] = xy[9] = disp->yAxis.low;
	  xy[3] = xy[5] = disp->yAxis.high;
     }
     
     PlotSwitch(plot_drvr,drawLine,(xy,5, SOLID));

     return rc;
}

static int drawTicks(display disp, binding_t axis)
{
    int nticks = 0;
   
    float ticks[N__TICKS];
    char labels[N__TICKS][10];
    float fontSize;
    float pmag;
    
    axis_t *thisAxis;
     
    if (axis == XAXIS)
	 thisAxis = &disp->xAxis;
    else if (axis == YAXIS)
	 thisAxis = &disp->yAxis;
    else
	 return -1;
    
    genTicks(thisAxis, N__TICKS, &nticks, ticks, labels, &pmag); 

    /*
     * determine the font size. We want the x and y axes to be labeled in
     *  the same font size, so make the same calculation, independent of
     *  which axis we are dealing with.
     */
    if (thisAxis->scaleFontSize > 0.0)
    	fontSize = thisAxis->scaleFontSize;
    else
    {
	 fontSize = MIN(disp->drawRect.size.width*0.04,14.0);
	 if (disp->xAxis.flags.scaleLocation & PLOTBOTTOM)
	 {
	      fontSize = MIN(fontSize, (disp->marginRect.origin.y -
					disp->drawRect.origin.y)/2.0);
	 }
	 else if (disp->xAxis.flags.scaleLocation & PLOTTOP)
	 {
	      fontSize = MIN(fontSize, (disp->drawRect.size.height -
					disp->marginRect.size.height - 
					(disp->marginRect.origin.y -
					 disp->drawRect.origin.y))/2.0
			     );
	 }
	 
	 if (disp->yAxis.flags.scaleLocation & PLOTLEFT)
	 {
	      /* estimate the space needed for the y axis label */
	      float yr;
	      if (disp->flags.drawTitles && 
		  disp->yAxis.flags.labelLocation & PLOTLEFT)
		   yr = 14;
	      else
		   yr = 0;
	      fontSize = MIN(fontSize, 
			     (disp->marginRect.origin.x -
			      disp->drawRect.origin.x - yr)
			     /strlen(labels[nticks-1]));
	 }
	 else if (disp->yAxis.flags.scaleLocation & PLOTRIGHT)
	 {
	      /* estimate the space needed for the y axis label */
	      float yr;
	      if (disp->flags.drawTitles && 
		  disp->yAxis.flags.labelLocation & PLOTRIGHT)
		   yr = 14;
	      else
		   yr = 0;
	      fontSize = MIN(fontSize, 
			     (disp->drawRect.size.width - 
			      disp->marginRect.size.width - 
			      (disp->marginRect.origin.x - 
			       disp->drawRect.origin.x) - yr)
			     /strlen(labels[nticks-1]));
	 }
    }
    
    /*
     * Plot the tick marks and labels
     */
    switch (axis)
    {
    case XAXIS:
	 return drawXTicks(disp, nticks, ticks, labels, pmag, fontSize );
	 break;

    case YAXIS:
	 return drawYTicks(disp, nticks, ticks, labels, pmag, fontSize );
	 break;

    default:
	 return -1;
    }
	 
    return 0;
}

static int drawXTicks(display disp, int nticks, float *ticks, 
		       char labels[N__TICKS][10], float pmag, 
		       float fontSize )
{
     int rc = 0;
     int i;
     float pOrigin, aOrigin, scale;
     char yalign;
     float x, y;
    
     if (disp->xAxis.flags.tickLocation & PLOTBOTTOM)
     {
	  PlotSwitch(plot_drvr,drawXTicks,
		     (ticks,nticks,disp->xAxis.tickLength,0));
     }
     if (disp->xAxis.flags.tickLocation & PLOTTOP)
     {
	  PlotSwitch(plot_drvr,drawXTicks,
		     (ticks,nticks,disp->xAxis.tickLength,1));
     }
     
     if (rc != 0 || disp->xAxis.flags.scaleLocation == 0) return rc;
     
     pOrigin = disp->marginRect.origin.x;
     if (! disp->xAxis.flags.log)
     {
	  scale = disp->marginRect.size.width /
	       (disp->xAxis.high - disp->xAxis.low);
	  aOrigin = disp->xAxis.low;
     }
     else
     {
	  scale = disp->marginRect.size.width /
	       log10(disp->xAxis.high / disp->xAxis.low);
	  aOrigin = log10(disp->xAxis.low);
     }
     
     if (disp->xAxis.flags.scaleLocation & PLOTBOTTOM)
     {
	  y = disp->marginRect.origin.y - YPADDING(disp);
	  yalign = 't';
     }
     else
     {
	  y = disp->marginRect.origin.y + disp->marginRect.size.height +
	       YPADDING(disp);
	  yalign = 'b';
     }

     for (i=0; i<nticks; i++ )
     {
	  x = pOrigin + (ticks[i] - aOrigin) * scale;
	  if (drawText(labels[i], x, y, fontSize, 0.0, 'c', yalign ) != 0)
	  {
	       h_error("Error draw label on axis.");
	       return -1;
	  }
     }	  
     
     if (pmag != 0.0)
     {
	  x = pOrigin + (ticks[nticks-1] - aOrigin) * scale + 
	       strlen(labels[nticks-1]) * fontSize / 2.0;
	  x = MIN( x, disp->marginRect.origin.x + 
		  disp->marginRect.size.width + XPADDING(disp));

	  if (yalign == 't') {
	  	y -= fontSize*0.8;
	  } else {
	  	y += fontSize*0.2;
	  }
	  drawMag(x, y, (int)pmag, fontSize*0.75 );
     }
     
     return rc;
}


static int drawYTicks(display disp, int nticks, float *ticks, 
		       char labels[N__TICKS][10], float pmag, 
		       float fontSize )
{
     int rc = 0;
     int i;
     float pOrigin, aOrigin, scale;
     char xalign;
     float x, y;
    
     if (disp->yAxis.flags.tickLocation & PLOTLEFT)
     {
	  PlotSwitch(plot_drvr,drawYTicks,
		     (ticks,nticks,disp->yAxis.tickLength,0));
     }
     if (disp->yAxis.flags.tickLocation & PLOTRIGHT)
     {
	  PlotSwitch(plot_drvr,drawYTicks,
		     (ticks,nticks,disp->yAxis.tickLength,1));
     }

     if (rc != 0 || disp->yAxis.flags.scaleLocation == 0) return rc;

     pOrigin = disp->marginRect.origin.y;
     if (! disp->yAxis.flags.log)
     {
	  scale = disp->marginRect.size.height / 
	       (disp->yAxis.high - disp->yAxis.low);
	  aOrigin = disp->yAxis.low;
     }
     else
     {
	  scale = disp->marginRect.size.height / 
	       log10(disp->yAxis.high / disp->yAxis.low);
	  aOrigin = log10(disp->yAxis.low);
     }
     
     if (disp->yAxis.flags.scaleLocation & PLOTLEFT)
     {
	  x = disp->marginRect.origin.x - XPADDING(disp);
	  xalign = 'r';
     }
     else 
     {
	  x = disp->marginRect.origin.x + disp->marginRect.size.width
	       + XPADDING(disp);
	  xalign = 'l';
     }
     for (i=0; i<nticks; i++ )
     {
	  y = pOrigin + (ticks[i] - aOrigin) * scale;
	  if (drawText(labels[i], x, y, fontSize, 0.0, xalign, 'c' ) != 0)
	  {
	       h_error("Error draw label on axis.");
	       return -1;
	  }
     }	  
     
     if (pmag != 0.0)
     {
	  if (disp->yAxis.flags.scaleLocation & PLOTLEFT)
	       x = disp->marginRect.origin.x - XPADDING(disp) - 
		    fontSize*(strlen(labels[nticks-1])+2);
	  else
	       x = disp->marginRect.origin.x + disp->marginRect.size.width
		    + XPADDING(disp) +
		    fontSize*(strlen(labels[nticks-1]));
	  
	  y = pOrigin + (ticks[nticks-1] - aOrigin)*scale - fontSize*0.4;

/*****	  y = disp->marginRect.origin.y + disp->marginRect.size.height
	       + fontSize/2.0 + YPADDING(disp); *****/

	  drawMag(x, y, (int)pmag, fontSize*0.75 );
     }
     
     return rc;
}


static int drawMag( float x, float y, int mag, float fontSize )
{
     int rc;

     PlotSwitch(plot_drvr,drawMag,( x, y, mag, fontSize ));
     
     return rc;
}
     


static int drawLego1D(display disp, float *xylego, int *over, 
		      int npts, linestyle_t ls)
{
     int rc = 0;
     int end;
     int i_over;
     float ylow, yhigh;
     
     if (disp->yAxis.flags.log)
     {
	  yhigh = log10(disp->yAxis.high);
	  ylow = log10(disp->yAxis.low);
     }
     else
     {
	  yhigh = disp->yAxis.high;
	  ylow = disp->yAxis.low;
     }
     
     /*
      * first, check the first group of points, since
      * it is not associated with a bin. (last group is check
      * by last element in over)
      */
     if (xylego[1] > yhigh) xylego[1] = yhigh;
     if (xylego[1] < ylow) xylego[1] = ylow;
     if (xylego[3] > yhigh) xylego[3] = yhigh;
     if (xylego[3] < ylow) xylego[3] = ylow;
          
     i_over = 0;
     do
     {
	  end = over[i_over++];
	  if (xylego[4*(end+1)+1] > yhigh) xylego[4*(end+1)+1] = yhigh;
	  if (xylego[4*(end+1)+1] < ylow) xylego[4*(end+1)+1] = ylow;
	  if (xylego[4*(end+1)+3] > yhigh) xylego[4*(end+1)+3] = yhigh;
	  if (xylego[4*(end+1)+3] < ylow) xylego[4*(end+1)+3] = ylow;
     }
     while (end < npts);


     PlotSwitch(plot_drvr,drawLine,( xylego, 2*(npts+2), ls ));

     return rc;
}


static int drawPoint1D(display disp, float *xy, int *over, int npts)
{
     int rc=0;
     int start = -1, end;
     int i_over=0;
          
     float xlow, ylow;
     float xhigh, yhigh;
     
     if (disp->xAxis.flags.log)
     {
	  xhigh = log10(disp->xAxis.high);
	  xlow = log10(disp->xAxis.low);
     }
     else
     {
	  xhigh = disp->xAxis.high;
	  xlow = disp->xAxis.low;
     }
     if (disp->yAxis.flags.log)
     {
	  yhigh = log10(disp->yAxis.high);
	  ylow = log10(disp->yAxis.low);
     }
     else
     {
	  yhigh = disp->yAxis.high;
	  ylow = disp->yAxis.low;
     }
     
     do
     {
	  /*
	   * Check that points marked as over are really over (it may be
	   * that error bar is over).
	   */
	  do 
	  {
	       if (start == -1)
		    start = 0;
	       else
		    start = over[i_over++]+1;
	       end = over[i_over];

	       while (end<npts)
	       {
		    /* include some fuzz in comparisons */
		    if (xy[2*end+1]<(ylow*(1.0-NUM_FUZZ)) || 
			xy[2*end+1]>(yhigh*(1.0+NUM_FUZZ))) break;
		    if (xy[2*end]<(xlow*(1.0-NUM_FUZZ)) || 
			xy[2*end]>(xhigh*(1.0+NUM_FUZZ))) break;
		    end = over[++i_over];
	       }
	  }
	  while ( (end-start) <= 0 && start<npts );
	  	  
	  if (start >= npts) continue;
	  
	  PlotSwitch(plot_drvr,drawPoints,(&(xy[2*start]),end-start,
					   disp->plotSymbol,disp->symbolSize));
     }
     while (rc == 0 && end < npts && start < npts);
     
     return rc;
}


static int drawLine1D(display disp, float *xy, int *over, 
		      int npts, int xmin_pt, linestyle_t ls)
{
     int rc = 0;
     int start, end;
     int s_saved, e_saved;
     int i_over = 0;
     float *xy_alt;
     int *over_alt;
     float xlow, ylow;
     float xhigh, yhigh;
     
     float start_save[2],end_save[2];

     if (npts < 2) return 0;
     if (ls == LS_INVISIBLE) return 0;
     
     if (xmin_pt > 0)
     {
	  if ( (xy_alt = (float *)malloc( 2*npts*sizeof(float) ))
	      == NULL)
	  {
	       h_error("drawLine1D - ran out of memory");
	       return -1;
	  }
	  if ( (over_alt = (int *)malloc( (npts+1)*sizeof(int) ))
	      == NULL)
	  {
	       h_error("drawLine1D - ran out of memory");
	       return -1;
	  }
	  memcpy((char *)xy_alt, (char *)&xy[2*xmin_pt],
		 2*(npts-xmin_pt)*sizeof(float) );
	  memcpy((char *)&xy_alt[2*(npts-xmin_pt)], (char *)xy,
		 2*xmin_pt*sizeof(float) );
	  for (i_over=0; over[i_over]<npts; i_over++ )
	  {
	       if (over[i_over] >= xmin_pt)
		    over_alt[i_over] = over[i_over] - xmin_pt;
	       else
		    over_alt[i_over] = over[i_over] + npts - xmin_pt;
	  }
	  over_alt[i_over++] = npts;
	  if (i_over > 1)
	       qsort( over_alt, (size_t)i_over, sizeof(int), intcmp );
     }
     else
     {
	  over_alt = over;
	  xy_alt = xy;
     }
          
     if (disp->xAxis.flags.log)
     {
	  xhigh = log10(disp->xAxis.high);
	  xlow = log10(disp->xAxis.low);
     }
     else
     {
	  xhigh = disp->xAxis.high;
	  xlow = disp->xAxis.low;
     }
     if (disp->yAxis.flags.log)
     {
	  yhigh = log10(disp->yAxis.high);
	  ylow = log10(disp->yAxis.low);
     }
     else
     {
	  yhigh = disp->yAxis.high;
	  ylow = disp->yAxis.low;
     }
     
     i_over = 0;
     start = -1;
     do
     {
	  /*
	   * Check that points marked as over are really over (it may be
	   * that error bar is over). Interpolate to find point where
	   * line crosses axes.
	   */
	  s_saved = 0;
	  e_saved = 0;

	  do 
	  {
	       if (start == -1)
		    start = 0;
	       else
		    start = over_alt[i_over++]+1;
	       end = over_alt[i_over];

	       while (end<npts)
	       {
		    /* include some fuzz in comparisons */
		    if (xy_alt[2*end+1]<(ylow*(1.0-NUM_FUZZ)) || 
			xy_alt[2*end+1]>(yhigh*(1.0+NUM_FUZZ))) break;
		    if (xy_alt[2*end]<(xlow*(1.0-NUM_FUZZ)) ||
			xy_alt[2*end]>(xhigh*(1.0+NUM_FUZZ))) break;
		    end = over_alt[++i_over];
	       }
	  }
	  while ( (end-start) <= 0 && start<npts );
	  	  
	  if (start >= npts) continue;

	  if (start > 0)
	  {
	       memcpy(start_save,&(xy_alt[2*(start-1)]),2*sizeof(float) );
	       s_saved = 1;
	       box_cross( &xy_alt[2*(start-1)], &xy_alt[2*start], disp);
	       start--;
	  }
	  if (end < npts)
	  {
	       memcpy(end_save,&(xy_alt[2*end]),2*sizeof(float) );
	       e_saved = 1;
	       box_cross( &xy_alt[2*end], &xy_alt[2*(end-1)], disp);
	       end++;
	  }

	  PlotSwitch(plot_drvr,drawLine,
		     (&(xy_alt[2*start]),end-start,ls));

	  if (s_saved)
	       memcpy(&(xy_alt[2*start]),start_save,2*sizeof(float) );
	  if (e_saved)
	       memcpy(&(xy_alt[2*(end-1)]),end_save,2*sizeof(float) );


     }
     while (rc == 0 && end < npts && start < npts);

     if (xmin_pt != 0)
     {
	  free(xy_alt);
	  free(over_alt);
     }
     
     return rc;
}

/*
 * integer comparison for qsort.
 */
int intcmp(const void *i1, const void *i2)
{
     if ((int *)i1 > (int *)i2)
	  return 1;
     else if ((int *)i1 == (int *)i2)
	  return 0;
     else 
	  return -1;
}

/*
 * find point where interpolation between p1 and p2 cross the 
 *  axes box.
 */
static void box_cross( float *p1, float *p2, display disp )
{
     float xlow,ylow, xhigh, yhigh;
     float x_int1,x_int2,y_int1,y_int2;
     
     if (disp->xAxis.flags.log)
     {
	  xlow = log10(disp->xAxis.low);
	  xhigh = log10(disp->xAxis.high);
     }
     else
     {
	  xlow = disp->xAxis.low;
	  xhigh = disp->xAxis.high;
     }
     if (disp->yAxis.flags.log)
     {
	  ylow = log10(disp->yAxis.low);
	  yhigh = log10(disp->yAxis.high);
     }
     else
     {
	  ylow = disp->yAxis.low;
	  yhigh = disp->yAxis.high;
     }
     
     x_int1 = p1[0] + (ylow-p1[1])*(p2[0]-p1[0])/(p2[1]-p1[1]);
     if (x_int1 >= xlow && x_int1 <= xhigh &&
	 ylow >= p1[1] && ylow <= p2[1])
     {
	  p1[0] = x_int1;
	  p1[1] = ylow;
	  return;
     }

     x_int2 = p1[0] + (yhigh-p1[1])*(p2[0]-p1[0])/(p2[1]-p1[1]);
     if (x_int2 >= xlow && x_int2 <= xhigh &&
	 yhigh <= p1[1] && yhigh >= p2[1])
     {
	  p1[0] = x_int2;
	  p1[1] = yhigh;
	  return;
     }

     y_int1 = p1[1] + (xlow-p1[0])*(p2[1]-p1[1])/(p2[0]-p1[0]);
     if (y_int1 >= ylow && y_int1 <= yhigh &&
	 xlow >= p1[0] && xlow <= p2[0])
     {
	  p1[0] = xlow;
	  p1[1] = y_int1;
	  return;
     }

     y_int2 = p1[1] + (xhigh-p1[0])*(p2[1]-p1[1])/(p2[0]-p1[0]);
     if (y_int2 >= ylow && y_int1 <= yhigh &&
	 xhigh <= p1[0] && xhigh >= p2[0])
     {
	  p1[0] = xhigh;
	  p1[1] = y_int2;
	  return;
     }
}


/*
 * draw errors - err_type specifies whether they are x or y errors.
 */
static int drawError1D(display disp, float *xy, float *err, 
		       int *over, int npts, int err_type)
{
     int rc = 0;
     int start = -1;
     int end;
     int i_over = 0;
     
     float xlow, ylow;
     float xhigh, yhigh;
     
     if (disp->xAxis.flags.log)
     {
	  xhigh = log10(disp->xAxis.high);
	  xlow = log10(disp->xAxis.low);
     }
     else
     {
	  xhigh = disp->xAxis.high;
	  xlow = disp->xAxis.low;
     }
     if (disp->yAxis.flags.log)
     {
	  yhigh = log10(disp->yAxis.high);
	  ylow = log10(disp->yAxis.low);
     }
     else
     {
	  yhigh = disp->yAxis.high;
	  ylow = disp->yAxis.low;
     }
     
     do
     {
	  /*
	   * Plot any x/y error bar where the y/x coordinate is in range
	   *  because of bars which reach into plot, even when point
	   *  is outside.
	   */
	  do 
	  {
	       if (start == -1)
		    start = 0;
	       else
		    start = over[i_over++]+1;
	       end = over[i_over];

	       while (end<npts)
	       {
		    if (err_type==XERROR)
		    {
			 /* include some fuzz in comparisons */
			 if ( xy[2*end+1]<(ylow*(1.0-NUM_FUZZ)) || 
			     xy[2*end+1]>(yhigh*(1.0+NUM_FUZZ))) break;
			 if ( err[2*end]<(xlow*(1.0-NUM_FUZZ)) || 
			     err[2*end+1]>(xhigh*(1.0+NUM_FUZZ))) break;
			 if ( err[2*end] > xhigh)
			      err[2*end] = xhigh;
			 if ( err[2*end+1] < xlow)
			      err[2*end+1] = xlow;
		    }
		    else
		    {
			 if ( xy[2*end]<(xlow*(1.0-NUM_FUZZ)) || 
			     xy[2*end]>(xhigh*(1.0+NUM_FUZZ))) break;
			 if ( err[2*end]<(ylow*(1.0-NUM_FUZZ)) ||
			     err[2*end+1]>(yhigh*(1.0+NUM_FUZZ))) break;
			 if ( err[2*end] > yhigh)
			      err[2*end] = yhigh;
			 if ( err[2*end+1] < ylow)
			      err[2*end+1] = ylow;
		    }
		    end = over[++i_over];
	       }
	  }
	  while ( (end-start) <= 0 && start<npts );
	  	  
	  if (start >= npts) continue;
	  
	  if (err_type == YERROR)
	  {
	       PlotSwitch(plot_drvr,drawYError,(&(xy[2*start]),
						&(err[2*start]),
						end-start));
	  }
	  else
	  {
	       PlotSwitch(plot_drvr,drawXError,(&(xy[2*start]),
						&(err[2*start]),
						end-start));
	  }
     }
     while (rc == 0 && end < npts && start < npts);
     
     return rc;
}


static int drawColor2D(display disp)
{
     int rc=0;

     if (!init_done)
     {
	  initPlot( disp );
	  init_done = 1;
     }
          
     if (disp->zAxis.flags.autoScale) {
     	disp->zAxis.low = disp->bins.binMin*(1.0-2.0*FLT_EPSILON)-2.0*FLT_MIN;
     	disp->zAxis.high = disp->bins.binMax*(1.0+2.0*FLT_EPSILON)+2.0*FLT_MIN;
     }
     
     PlotSwitch(plot_drvr,drawColor2D,(disp->bins.xAxis.nBins,
				       disp->bins.yAxis.nBins,
				       disp->zAxis.low,
				       disp->zAxis.high,
				       disp->bins.data,
				       disp->drawtype & COLOR));
     
     return rc;
}


static int drawScatter2D(display disp)
{
     int rc;
     int npts;
     float *xylist;
     struct maxs max;
     
     npts = getScatData( disp, &xylist, &max );
     if (npts == 0) return 0;
     if (npts < 0) 
     {
	  h_error("h_plot - error getting scatter plot data");
	  return -1;
     }

     if (disp->xAxis.flags.autoScale)
     {
	  disp->xAxis.low = max.xl;
	  disp->xAxis.high = max.xh;
	  h_adjustAxis(&(disp->xAxis.low),&(disp->xAxis.high),
		       0, disp->xAxis.flags.log);
     }
     if (disp->yAxis.flags.autoScale)
     {
	  disp->yAxis.low = max.yl;
	  disp->yAxis.high = max.yh;
	  h_adjustAxis(&(disp->yAxis.low),&(disp->yAxis.high),
		       0, disp->yAxis.flags.log);
     }
     
     if (!init_done)
     {
	  initPlot( disp );
	  init_done = 1;
     }
          
     switch (plot_drvr)
     {
	  NeXTFunc(drawPointsScat,(xylist,npts,disp->plotSymbol,
				   disp->symbolSize));

	  PSFunc(drawPoints,(xylist,npts,disp->plotSymbol,
			     disp->symbolSize));

	  UPFunc(drawPoints,(xylist,npts,disp->plotSymbol,
			     disp->symbolSize));

	  XIVFunc(drawPoints,(xylist,npts,disp->plotSymbol,
			      disp->symbolSize));

	  X11Func(drawPoints,(xylist,npts,disp->plotSymbol,
			      disp->symbolSize));

     default:
	  rc = -1;
     }
     
     free( xylist );
     return rc;
}


static int drawLego2D(display disp)
{
     int rc;
     
     if (!init_done)
     {
	  initPlot( disp );
	  init_done = 1;
     }
          
     PlotSwitch(plot_drvr,drawLego2D,(disp));
     
     return rc;
}				 

static int drawScatter3D(display disp)
{
     int rc;
     

     int npts;
     float *xyzlist;
     struct maxs max;
     
     npts = getScatData3D( disp, &xyzlist, &max );
     if (npts == 0) return 0;
     if (npts < 0) 
     {
	  h_error("h_plot - error getting scatter plot data");
	  return -1;
     }

     if (disp->xAxis.flags.autoScale)
     {
	  disp->xAxis.low = max.xl;
	  disp->xAxis.high = max.xh;
	  h_adjustAxis(&(disp->xAxis.low),&(disp->xAxis.high),
		       0, disp->xAxis.flags.log);
     }
     if (disp->yAxis.flags.autoScale)
     {
	  disp->yAxis.low = max.yl;
	  disp->yAxis.high = max.yh;
	  h_adjustAxis(&(disp->yAxis.low),&(disp->yAxis.high),
		       0, disp->yAxis.flags.log);
     }
     if (disp->zAxis.flags.autoScale)
     {
	  disp->zAxis.low = max.zl;
	  disp->zAxis.high = max.zh;
	  h_adjustAxis(&(disp->zAxis.low),&(disp->zAxis.high),
		       0, disp->zAxis.flags.log);
     }
     
     if (!init_done)
     {
	  initPlot( disp );
	  init_done = 1;
     }
          
     PlotSwitch(plot_drvr, drawScatter3D,(disp, xyzlist,npts));
     
     free( xyzlist );
     return rc;
}


int h_shade(display disp, float var1, float var2)
/*
 * Shade an area of a plot to indicate area. Make sure not
 * to outside the bounds of the plot.
 */
{
     int rc;
     float low, high;
     
     if (disp == NULL) return -1;

     low = (var1 - disp->xAxis.low)/(disp->xAxis.high - disp->xAxis.low);
     if (low < 0.0) low = 0.0;
     low = low * disp->marginRect.size.width + disp->marginRect.origin.x;

     high = (var2 - disp->xAxis.low)/(disp->xAxis.high - disp->xAxis.low);
     if (high > 1.0) high = 1.0;
     high = high * disp->marginRect.size.width + disp->marginRect.origin.x;
     
     switch (plot_drvr)
     {
	  NeXTFunc(shade,(low,high,disp->marginRect.origin.y,
			  disp->marginRect.origin.y+
			  disp->marginRect.size.height));

	  PSFunc(shade,(low,high,disp->marginRect.origin.y,
			disp->marginRect.origin.y+
			disp->marginRect.size.height));
	  
	  MACFunc(shade,(low,high,disp->marginRect.origin.y,
			  disp->marginRect.origin.y+
			  disp->marginRect.size.height));

	  XIVFunc(shade,(low,high,disp->marginRect.origin.y,
			 disp->marginRect.origin.y+
			 disp->marginRect.size.height));
	  
  	  X11Func(shade,(low,high,disp->marginRect.origin.y,
			 disp->marginRect.origin.y+
			 disp->marginRect.size.height));
	  
     default:
	  rc = -1;
     }
     
     return rc;
}				 


typedef struct fp
{
     float x,f;
     struct fp *n;
} fp;

static fp *pCache(int n );
typedef double (*pfun)( double, double, double *);

static int plotFunc( display disp, func_id func )
{
     int yClip = 0;
     int i, nCacheUsed;
     float ylow, yhigh;
     float maxKink, minDelta;      /* set limits on how many points to plot */
     float delta1;
     float delta2, kink;
     float ydelta1, ydelta2;
     int isover;
     double binWidth;
     float kinkScale;
     float xrange, yrange;
     
     fp *p=NULL, *t;
     int *over, *overp;
     float *xy, *xyp;

#define NSEG_START 10

     if (disp == NULL || func == NULL) return 0;
     if (disp->xAxis.high <= disp->xAxis.low) 
     {
	  h_error("Plotfunc - xAxis.high <= xAxis.low");
	  return 0;
     }
     
     if (func->lineStyle == LS_INVISIBLE) return 0;
     
     if (disp->graphtype == HISTOGRAM)
	  binWidth = h_getBinWidth( disp, XAXIS );
     else
	  binWidth = 1.0;
     
     /*
      * start from the beginning of the cache of point structures.
      */
     nCacheUsed = 0;
  
     /*
      * axis ranges including log
      */
     if (disp->xAxis.flags.log)
	  xrange = log10(disp->xAxis.high) - log10(disp->xAxis.low);
     else
	  xrange = disp->xAxis.high - disp->xAxis.low;
     if (disp->yAxis.flags.log)
	  yrange = log10(disp->yAxis.high) - log10(disp->yAxis.low);
     else
	  yrange = disp->yAxis.high - disp->yAxis.low;

     /* 
      * set the resolution depending on whether printing or drawing to screen
      */
     if (plot_drvr == PSPLOT
#ifdef _NEXT_PLOT_
         || (plot_drvr == NEXT && NXDrawingStatus != NX_DRAWING)
#endif
	 )
     {
         minDelta = xrange / disp->marginRect.size.width;
         maxKink = 3.141593/180.0;
     }
     else
     {
         minDelta = 7.0 * xrange / disp->marginRect.size.width;
         maxKink = 5.0 * 3.141593 / 180.0;
     }

     kinkScale = disp->marginRect.size.height / yrange 
	  * xrange / disp->marginRect.size.width;
	    
     if (init_done || !disp->yAxis.flags.autoScale) yClip = 1;
     ylow = FLT_MAX;
     yhigh = -FLT_MAX;
     
     delta1 = (disp->xAxis.high - disp->xAxis.low)/((float)NSEG_START);
     for (i=0; i<=NSEG_START; i++)
     {
	  if (p == NULL) 
	       p = pCache(nCacheUsed++);
	  else
	  {
	       p->n = pCache(nCacheUsed++);
	       p = p->n;
	  }
	  p->x = disp->xAxis.low + ((float) i) * delta1;
	  p->f = (*(pfun)(func->funcPtr))((double)p->x, binWidth,
					  (double *)func->paramBlk);
	  p->n = NULL;
     }
     
     for (p=pCache(0); (p->n)->n != NULL; p = p->n )
     {
	  /*
	   * This loop is written as an infinite loop with an internal
	   *  break because we always want to calculate delta[12] and kink
	   *  before testing whether to continue, including at the first
	   *  iteration. This seemed like the most compact coding.
	   */
	  while (1)	
	  {
	       if (disp->xAxis.flags.log)
	       {
		    delta2 = log10(((p->n)->n)->x) - log10((p->n)->x);
		    delta1 = log10((p->n)->x) - log10(p->x);
	       }
	       else
	       {
		    delta2 = ((p->n)->n)->x - (p->n)->x;
		    delta1 = (p->n)->x - p->x;
	       }
	       if (disp->yAxis.flags.log)
	       {
		    ydelta2 = log10(((p->n)->n)->f) - log10((p->n)->f);
		    ydelta1 = log10((p->n)->f) - log10(p->f);
	       }
	       else
	       {
		    ydelta2 = ((p->n)->n)->f - (p->n)->f;
		    ydelta1 = (p->n)->f - p->f;
	       }
	       
	       kink = atan(ydelta2 / delta2 * kinkScale) -
		    atan(ydelta1 / delta1 * kinkScale);

	       /*
	        * check the exit condition
		*/
	       if ( fabs(kink) <= maxKink || 
		 (delta1 <= minDelta && delta2 <= minDelta) )
		 	break;
			
	       if (delta1 <= delta2)
	       {
		    t = pCache(nCacheUsed++);
		    t->x = (((p->n)->n)->x + (p->n)->x)/2.0;
		    t->f = (float) 
			 (*(pfun)(func->funcPtr))((double)t->x, binWidth,
					    (double *)func->paramBlk);
		    t->n = (p->n)->n;
		    (p->n)->n = t;
	       }			
	       if (delta1 >= delta2)
	       {
		    t = pCache(nCacheUsed++);
		    t->x = ((p->n)->x + p->x)/2.0;
		    t->f = (float) 
			 (*(pfun)(func->funcPtr))((double)t->x, binWidth,
					    (double *)func->paramBlk);
		    t->n = p->n;
		    p->n = t;
	       }
		
	  }	
     }
     
     /* 
      * We now have the complete list of points to plot. Create the
      *  list of x-y points and overflow flags.
      */
     xy = (float *) malloc( 2 * nCacheUsed * sizeof(float) );
     over = (int *) malloc( (nCacheUsed+1) * sizeof(int) );
     xyp = xy;
     overp = over;
     
     for (p=pCache(0), i=0; p != NULL; p = p->n, i++ )
     {
	  isover = 0;
	  
	  if (disp->xAxis.flags.log)
	       if (p->x > 0.0)
		    *xyp++ = log10(p->x);
	       else
	       {
		    isover = 1;
		    *xyp++ = -FLT_MAX;
	       }
	  else
	       *xyp++ = p->x;

	  if (disp->yAxis.flags.log)
	       if (p->f > 0.0)
		    *xyp++ = log10(p->f);
	       else
	       {
		    isover = 1;
		    *xyp++ = -FLT_MAX;
	       }
	  else
	       *xyp++ = p->f;
	  
	  if (isover || 
	      (yClip && (p->f > disp->yAxis.high || p->f < disp->yAxis.low)))
	       *overp++ = i;
	  else
	  {
	       if (p->f > yhigh) yhigh = p->f;
	       if (p->f < ylow && (!disp->yAxis.flags.log || p->f > 0.0))
		    ylow = p->f;
	  }
     }
     *overp++ = i;
     
     /*
      * Set scales if it has not been done already.
      */
     if (! init_done)
     {
	  if (disp->yAxis.flags.autoScale)
	  {
	       disp->yAxis.low = ylow;
	       disp->yAxis.high = yhigh;
	       h_adjustAxis(&(disp->yAxis.low), &(disp->yAxis.high),
			    0, disp->yAxis.flags.log );
	  }
	  initPlot( disp );
	  init_done = 1;
     }
     
     /* 
      * plot the line, clean up and we're done!
      */
     drawLine1D( disp, xy, over, nCacheUsed, 0, func->lineStyle );
     
     free(xy);
     free(over);
     
     return 0;
}

static fp *pCache(int n )
{
     static nBlks = 0;
#define FP_BLK 100
     static fp **list = NULL;
     
     if (n >= nBlks*FP_BLK)
     {
     	  if ( list )
	      list = (fp **)realloc( list, (nBlks+1) * sizeof(fp *) );
	  else 
	      list = (fp **)malloc( (nBlks+1) * sizeof(fp *) );
	  if ( list == NULL)
	  {
	       h_error("Plotfunc is out of memory");
	       return NULL;
	  }
	  nBlks++;
	  if ( (list[nBlks-1] = (fp *)malloc(FP_BLK * sizeof(fp))) == NULL)
	  {
	       h_error("Plotfunc is out of memory");
	       return NULL;
	  }
     }
     
     return &list[n / FP_BLK][n % FP_BLK];
}

/*
 * This is the externally callable function.
 */
double h_funcSum( display disp, double x )
{
     double sum = 0.0;
     func_id fun;
     double binWidth;
     
     if (disp->graphtype == HISTOGRAM)
	  binWidth = h_getBinWidth( disp, XAXIS );
     else
	  binWidth = 1.0;

     for (fun = disp->plot_func; fun != NULL; fun = fun->next )
	  sum += (*(pfun)(fun->funcPtr))(x, binWidth, 
					  (double *)fun->paramBlk);
     
     return sum;
}

/*
 * This is the internally callable function for the plotFunc routine.
 */
static double funcSum(double x, double width, double *parm )
{
     return h_funcSum( (display)parm, x );
}

     

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