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.