This is PlotView.m in view mode; [Download] [Up]
/* Generated by Interface Builder */ #import "defs.h" #import "PlotView.h" #import "Plot.h" #import <appkit/SavePanel.h> #import <appkit/color.h> #import <appkit/FormCell.h> #import <appkit/PrintPanel.h> #import <appkit/Pasteboard.h> /* The following routines are in auxil.m: */ extern void count_labels(int *, double *, double, double, double); extern void autoformat(double, double, double, int *); extern void handformat(float, char *, int *); @implementation PlotView - (BOOL) acceptsFirstMouse { return YES;} /* grab that mouse down event! */ - provideMainTitleFont { return newMainTitleFont;} - provideXTitleFont { return newXTitleFont;} - provideYTitleFont { return newYTitleFont;} - provideLegendTitleFont { return newLegendTitleFont;} - provideLegendFont { return newLegendFont;} - provideTicLabelFont { return newTicLabelFont;} - (NXPoint) provideLegendBoxOrigin { return legendbox.origin;} - (NXPoint) provideXTitleBoxOrigin { return xtitlebox.origin;} - (NXPoint) provideYTitleBoxOrigin { return ytitlebox.origin;} - (NXPoint) provideMainTitleBoxOrigin { return maintitlebox.origin;} - provideWindowFrame:(NXRect *)windowframe { return [[self window] getFrame:windowframe]; } /* All these method names that start with "force" would more naturally start * with "set", but everything malfunctions then; apparently it's a bad idea * to have methods whose names begin with "set". Is it also a bad idea to * have methods whose names begin with "init"? */ - forceWindowFrame:(NXRect *)windowframe { [[self window] placeWindow:windowframe]; oldbounds = bounds; /* have to do this right here so that the */ [self display]; /* title boxes are not moved around again */ return self; } - forceLegendBoxOrigin: (NXPoint)point { legendbox.origin = point; return self; } - forceXTitleBoxOrigin: (NXPoint)point { xtitlebox.origin = point; return self; } - forceYTitleBoxOrigin: (NXPoint)point { ytitlebox.origin = point; return self; } - forceMainTitleBoxOrigin: (NXPoint)point { maintitlebox.origin = point; return self; } - forceMainTitleFont:(char *)fontname :(float)fontsize { newMainTitleFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - forceXTitleFont:(char *)fontname :(float)fontsize; { newXTitleFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - forceYTitleFont:(char *)fontname :(float)fontsize; { newYTitleFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - forceLegendFont:(char *)fontname :(float)fontsize; { newLegendFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - forceLegendTitleFont:(char *)fontname :(float)fontsize; { newLegendTitleFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - forceTicLabelFont:(char *)fontname :(float)fontsize; { newTicLabelFont = [Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX]; return self; } - initFrame:(const NXRect *)frameRect { [super initFrame:frameRect]; legendbox.origin.x = XOFFSET + 50.0; /* get the legendbox initialized here */ legendbox.origin.y = YOFFSET + 50.0; xtitlebox.origin.x = XOFFSET/4.0 + (bounds.size.width - DEFAULTAXISTITLEWIDTH)/2.0; xtitlebox.origin.y = bounds.origin.y + 10.0; xtitlebox.size.width = DEFAULTAXISTITLEWIDTH; xtitlebox.size.height = DEFAULTFONTSIZE; ytitlebox.origin.x = bounds.origin.x + 6.0; ytitlebox.origin.y = YOFFSET/4.0 + (bounds.size.height - DEFAULTAXISTITLEWIDTH)/2.0; ytitlebox.size.width = DEFAULTFONTSIZE; ytitlebox.size.height = DEFAULTAXISTITLEWIDTH; maintitlebox.origin.x = XOFFSET/4.0 + (bounds.size.width - DEFAULTMAINTITLEWIDTH)/2.0; maintitlebox.origin.y = bounds.size.height - 2.0 - 14.0; maintitlebox.size.width = DEFAULTMAINTITLEWIDTH; maintitlebox.size.height = 14.0; /* * With NS 3.0, we start handling the dragging code here (not in PlotDelegate). * registerForDraggedTypes is a new method in the View class. */ [self registerForDraggedTypes:&NXFilenamePboardType count:1]; oldbounds = bounds; return self; } - (NXCoord *)xdata:(int)n /* we use this in drawing the legend curves */ { xlegend[0] = (legendbox.origin.x + 5.33333)/ppxunit; /* We used to have 5.0 instead of 5.33333, but that can give erroneous-looking * (albeit correct) results when writing to the screen: the pixels that are * turned on when drawing a filled circle, for example, are much different * when the circle's center is precisely at a pixel than when the circle's center * is not precisely at a pixel. The results are much better when the center * of the circle is at a half-pixel point, and so that's what we do. */ xlegend[1] = xlegend[0] + 40.0/ppxunit; /* curve fragment in legend is 40 pixels in length */ if (!drawingLegendLines) xlegend[0] = 0.5*(xlegend[0] + xlegend[1]); return xlegend; } - (NXCoord **)ydata:(int)n /* we use this in drawing the legend curves */ { int ncurves = [plotParam nCurvesTotal]; int j; float yloc, yhgt; const char * curvetitle; if (newLegendFont) { yhgt = [newLegendFont pointSize]; } else { yhgt = DEFAULTFONTSIZE; } yloc = (legendbox.origin.y - 2.0)/ppyunit; ylegend = (NXCoord **)realloc((void *)ylegend, ncurves*sizeof(NXCoord *)); for (j = 0; j < ncurves; j++) { *(ylegend+j) = (NXCoord *)NULL; /* this is necessary */ *(ylegend+j) = (NXCoord *)realloc((void *)*(ylegend+j), 2*sizeof(NXCoord)); /* check for no lines and no symbols */ if ( ([plotParam providelinestyle:j] == NOLINE) && ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue; /* check for empty curvetitle */ curvetitle = [legendForm stringValueAt:j]; if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0') continue; yloc = yloc + (yhgt+0.33333)/ppyunit; /* We used to have just yhgt (instead of yhgt+0.33333); see comments in preceding * routine for the reason to change it. */ } for (j=0; j<ncurves; j++) { *(*(ylegend+j)+0) = yloc; *(*(ylegend+j)+1) = yloc; /* check for no lines and no symbols */ if( ([plotParam providelinestyle:j] == NOLINE) && ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue; /* check for empty curvetitle */ curvetitle = [legendForm stringValueAt:j]; if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0') continue; yloc -= yhgt/ppyunit; } return ylegend; } - (BOOL)has_ebars:(int)n { return NO; } - (int)nPoints:(int)n { if (drawingLegendLines) return 2; else return 1; } - (int)nCurves:(int)n { return [plotParam nCurvesTotal]; } - (int)nFiles { return 1; } - startPlot { const char * xtitle = [xTitle stringValueAt:0]; const char * ytitle = [yTitle stringValueAt:0]; const char * maintitle = [mainTitle stringValueAt:0]; float yhgt; id mainTitleFont,axisTitleFont; NXSetColor([plotParam provideBackgroundColor]); [self clear:self]; PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); NXSetColor([plotParam provideTextColor]); /* get new font from fontmanager */ if ([plotParam shouldChangeXTitleFont]) { newXTitleFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newXTitleFont) { axisTitleFont = [Font newFont:[newXTitleFont name] size:[newXTitleFont pointSize] style:[newXTitleFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newXTitleFont pointSize]; } else { axisTitleFont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [axisTitleFont set]; xtitlebox.size.width = [axisTitleFont getWidthOf:xtitle]; xtitlebox.size.height = yhgt; if (oldbounds.size.width != bounds.size.width || oldbounds.size.height != bounds.size.height) { /* Couldn't figure out how to do this with windowDidResize or superviewSizeChanged, * so this grubby idea (with the global oldbounds variable) is used instead. */ xtitlebox.origin.x = (xtitlebox.origin.x + 0.5*xtitlebox.size.width) *(bounds.size.width/oldbounds.size.width) - 0.5*xtitlebox.size.width; xtitlebox.origin.y = (xtitlebox.origin.y + 0.5*xtitlebox.size.height) *(bounds.size.height/oldbounds.size.height) - 0.5*xtitlebox.size.height; legendbox.origin.x = (legendbox.origin.x + 0.5*legendbox.size.width) *(bounds.size.width/oldbounds.size.width) - 0.5*legendbox.size.width; legendbox.origin.y = (legendbox.origin.y + 0.5*legendbox.size.height) *(bounds.size.height/oldbounds.size.height) - 0.5*legendbox.size.height; } PSmoveto(xtitlebox.origin.x, xtitlebox.origin.y); PSshow((char *)xtitle); /* get new font from fontmanager */ if ([plotParam shouldChangeYTitleFont]) { newYTitleFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newYTitleFont) { axisTitleFont = [Font newFont:[newYTitleFont name] size:[newYTitleFont pointSize] style:[newYTitleFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newYTitleFont pointSize]; } else { axisTitleFont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [axisTitleFont set]; ytitlebox.size.width = yhgt; ytitlebox.size.height = [axisTitleFont getWidthOf:ytitle]; if (oldbounds.size.width != bounds.size.width || oldbounds.size.height != bounds.size.height) { ytitlebox.origin.x = (ytitlebox.origin.x + 0.5*ytitlebox.size.height) *(bounds.size.width/oldbounds.size.width) - 0.5*ytitlebox.size.height; ytitlebox.origin.y = (ytitlebox.origin.y + 0.5*ytitlebox.size.width) *(bounds.size.height/oldbounds.size.height) - 0.5*ytitlebox.size.width; } PSmoveto(ytitlebox.origin.x + yhgt, ytitlebox.origin.y); PSgsave(); PSrotate(90.0); PSshow((char *)ytitle); PSgrestore(); if ([plotParam shouldChangeMainTitleFont]) { newMainTitleFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newMainTitleFont) { mainTitleFont = [Font newFont:[newMainTitleFont name] size:[newMainTitleFont pointSize] style:[newMainTitleFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newMainTitleFont pointSize]; } else { mainTitleFont = [Font newFont:"Helvetica" size:14.0 style:0 matrix:NX_IDENTITYMATRIX]; yhgt = 14.0; } [mainTitleFont set]; maintitlebox.size.width = [mainTitleFont getWidthOf:maintitle]; maintitlebox.size.height = yhgt; if (oldbounds.size.width != bounds.size.width || oldbounds.size.height != bounds.size.height) { maintitlebox.origin.x = (maintitlebox.origin.x + 0.5*maintitlebox.size.width) *(bounds.size.width/oldbounds.size.width) - 0.5*maintitlebox.size.width; maintitlebox.origin.y = (maintitlebox.origin.y + 0.5*maintitlebox.size.height) *(bounds.size.height/oldbounds.size.height) - 0.5*maintitlebox.size.height; oldbounds = bounds; } PSmoveto(maintitlebox.origin.x, maintitlebox.origin.y); PSshow((char *)maintitle); // box around the plot: if ([borderBoxOnOff state]) { PSsetlinewidth([borderBoxThicknessText floatValue]); PSmoveto(0.0, 0.0); PSlineto(bounds.size.width, 0.0); PSlineto(bounds.size.width, bounds.size.height); PSlineto(0.0, bounds.size.height); PSlineto(0.0, 0.0); PSstroke(); PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); } // The preceding doesn't work quite right: for some reason the vertical line // on the right hand side of the plot doesn't show up on the display. The // line is there if you print or preview the file, however. return self; } - setDrawColor:(float) color { PSsetgray(color); return self; } - drawLines:sender :(BOOL)xaxislog :(BOOL)yaxislog /* * This is coded so that when drawLines is called with plotParam as argument, * it draws the data curves; when drawLines is called with self (plotView) as * argument, it draws the short line segments in the legend box. */ { int i, j, jrun, n; NXCoord *x; NXCoord **y; int npoints, ncurves; int curveindex = 0; /* cumulative index */ int linestyle; float thick = [lineThicknessText floatValue]; float pattern0[] = {}; /* solid */ float pattern1[] = {4.0, 4.0}; /* dash */ float pattern2[] = {1.0, 3.0}; /* dot */ float pattern3[] = {7.0, 3.0, 3.0, 3.0}; /* chain dash */ float pattern4[] = {7.0, 4.0, 1.0, 4.0}; /* chain dot */ const char * curvetitle; for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */ x = [sender xdata:n]; y = [sender ydata:n]; ncurves = [sender nCurves:n]; npoints = [sender nPoints:n]; PSnewpath(); PSsetlinewidth(NXDrawingStatus==NX_DRAWING? thick : [accPrintColorButton state]==0? MAX(thick, LINE_WIDTH_IF_PRINTING_BW) : MAX(thick, LINE_WIDTH_IF_PRINTING_COLOR)); for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) { linestyle = [plotParam providelinestyle:jrun]; NXSetColor([plotParam provideCurveColor:jrun]); switch(linestyle) { case SOLID: PSsetdash(pattern0, 0, 0.0); break; case DASH: PSsetdash(pattern1, 2, 0.0); break; case DOT: PSsetdash(pattern2, 2, 0.0); break; case CHAINDASH: PSsetdash(pattern3, 4, 0.0); break; case CHAINDOT: PSsetdash(pattern4, 4, 0.0); break; case NOLINE: /* no lines */ continue; } if (linestyle == NOLINE) continue; /* no lines, go to next curve */ /* If we're drawing the legend and there is no title, don't * bother to draw the line: */ curvetitle = [legendForm stringValueAt:jrun]; if (drawingLegendLines && curvetitle[0]=='\0') continue; j = jrun - curveindex; /* for indexing into the y array */ if (!xaxislog && !yaxislog) { PSmoveto(x[0]*ppxunit, *(*(y + j)) * ppyunit ); for (i=1; i<npoints; i++) { if ( (i % 512) == 0 ) { PSstroke(); PSnewpath(); PSmoveto(x[i-1]*ppxunit, *(*(y+j)+i-1) * ppyunit); } PSlineto(x[i]*ppxunit, *(*(y+j)+i) * ppyunit); } PSstroke(); } else if (!xaxislog && yaxislog) { PSmoveto(x[0]*ppxunit, (float)log10((double)*(*(y + j))) * ppyunit ); for (i=1; i<npoints; i++) { if ( (i % 512) == 0 ) { PSstroke(); PSnewpath(); PSmoveto(x[i-1]*ppxunit, (float)log10((double)*(*(y+j)+i-1)) * ppyunit); } PSlineto(x[i]*ppxunit, (float)log10((double)*(*(y+j)+i)) * ppyunit); } PSstroke(); } else if (xaxislog && !yaxislog) { PSmoveto((float)log10((double)x[0])*ppxunit, *(*(y + j)) * ppyunit ); for (i=1; i<npoints; i++) { if ( (i % 512) == 0 ) { PSstroke(); PSnewpath(); PSmoveto((float)log10((double)x[i-1])*ppxunit, *(*(y+j)+i-1) * ppyunit); } PSlineto((float)log10((double)x[i])*ppxunit, *(*(y+j)+i) * ppyunit); } PSstroke(); } else if (xaxislog && yaxislog) { PSmoveto((float)log10((double)x[0])*ppxunit, (float)log10((double)*(*(y + j))) * ppyunit ); for (i=1; i<npoints; i++) { if ( (i % 512) == 0 ) { PSstroke(); PSnewpath(); PSmoveto((float)log10((double)x[i-1])*ppxunit, (float)log10((double)*(*(y+j)+i-1)) * ppyunit); } PSlineto((float)log10((double)x[i])*ppxunit, (float)log10((double)*(*(y+j)+i)) * ppyunit); } PSstroke(); } } curveindex += ncurves; } PSsetdash(pattern0, 0, 0.0); /* back to solid lines */ return self; } /* * Our first idea for drawing the symbols was: draw each symbol just once * in an off-screen window, then composite them in where needed. This worked * fine for screen drawing (modulo a little difficulty in compositing to just * the right spot), but was a miserable failure when it came to printing: the * print machinery scaled the composite bitmap. We finally decided to abandon * compositing, in the interest of keeping our screen drawing code the same as * our printing code. Should check this again under version 2.0. */ - drawSymbols:sender :(BOOL)xaxislog :(BOOL)yaxislog /* * This is coded so that when drawSymbols is called with plotParam as argument, * it draws the data curves; when drawSymbols is called with self (plotView) as * argument, it draws the symbols in the legend box. */ { /* Our current code in drawSymbols could perhaps be sped up by using wraps * or DPSuserpath(). See if it's too slow as it stands before trying to * speed it up. */ int i, j, jrun, n; float size = 4.0; NXCoord *x; NXCoord **y; int npoints, ncurves; int curveindex = 0; int symbolstyle; register float xtmp, ytmp; const char * curvetitle; size = [symbolSizeText floatValue]; PSgsave(); for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */ x = [sender xdata:n]; y = [sender ydata:n]; ncurves = [sender nCurves:n]; npoints = [sender nPoints:n]; PSnewpath(); PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) { symbolstyle = [plotParam providesymbolstyle:jrun]; NXSetColor([plotParam provideCurveColor:jrun]); /* If we're drawing the legend and there is no title, don't * bother to draw the symbol: */ curvetitle = [legendForm stringValueAt:jrun]; if (drawingLegendSymbols && curvetitle[0]=='\0') continue; j = jrun - curveindex; /* for indexing into the y array */ switch(symbolstyle) { case NOSYMBOL: continue; break; case CIRCLE: for (i=0; i<npoints; i++) { /* circle_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSarc(xtmp * ppxunit, ytmp * ppyunit, size, 0.0, 360.0); PSfill(); } break; case XMARK: for (i=0; i<npoints; i++) { /* x_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit - size); PSrlineto(2.0*size, 2.0*size); PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit + size); PSrlineto(2.0*size, -2.0*size); PSstroke(); } break; case UPTRIANGLE: for (i=0; i<npoints; i++) { /* uptriangle_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit - size/SQRT3); PSrlineto(2.0*size, 0.0); PSrlineto(-size, size*SQRT3); PSclosepath(); PSfill(); } break; case DOWNTRIANGLE: for (i=0; i<npoints; i++) { /* downtriangle_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit + size/SQRT3); PSrlineto(2.0*size, 0.0); PSrlineto(-size, -size*SQRT3); PSclosepath(); PSfill(); } break; case DIAMOND: for (i=0; i<npoints; i++) { /* diamond_at(x[i], *(*(y+j)+i)); */ /* 3 pixels horizontal, 4 pixels vertical */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSmoveto(xtmp * ppxunit, ytmp * ppyunit - size); PSrlineto(3.0/4.0*size, size); PSrlineto(-3.0/4.0*size, size); /* gives a pleasing diamond shape */ PSrlineto(-3.0/4.0*size, -size); PSclosepath(); PSfill(); } break; case SQUARE: for (i=0; i<npoints; i++) { /* square_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSrectfill(xtmp * ppxunit - size, ytmp * ppyunit - size, 2.0*size, 2.0*size); } break; case PLUS: for (i=0; i<npoints; i++) { /* plus_at(x[i], *(*(y+j)+i)); */ xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); PSmoveto(xtmp * ppxunit, ytmp * ppyunit - size); PSrlineto(0.0, 2.0*size); PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit); PSrlineto(2.0*size, 0.0); PSstroke(); } break; } } curveindex += ncurves; } PSgrestore(); return self; } - drawErrorBars :(BOOL)xaxislog :(BOOL)yaxislog { int n, j, npoints, ncurves, jrun, i; NXCoord *x, *ex = NULL; /* ex and ey initialized to avoid */ NXCoord **y, **ey = NULL; /* compiler warning */ int curveindex = 0; register float xtmp, ytmp, tmp1, tmp2; float width = [errorBarBaseWidth floatValue]; BOOL exbars, eybars; PSgsave(); for (n=0; n<[plotParam nFiles]; n++) { /* loop over all active files */ exbars = [plotParam has_exbars:n]; eybars = [plotParam has_eybars:n]; ncurves = [plotParam nCurves:n]; if (!exbars && !eybars ) { /* skip if no error bars */ curveindex += ncurves; /* but have to bump this index, */ continue; /* to get the colors correct */ } x = [plotParam xdata:n]; /* we have to draw error bars */ if (exbars) { ex = [plotParam exdata:n]; } y = [plotParam ydata:n]; if (eybars) { ey = [plotParam eydata:n]; } npoints = [plotParam nPoints:n]; PSnewpath(); PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) { NXSetColor([plotParam provideCurveColor:jrun]); j = jrun - curveindex; /* for indexing into the y and ey arrays */ /* check if error bars are desired */ if (exbars && [[errorBarMatrix cellAt :n :0] state] == 1) { for (i=0; i<npoints; i++) { ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i)); if (xaxislog) { if (x[i] - ex[i] <= 0.0) { /* avoid log of negative */ tmp1 = (float)log10( (double) x[i] ); /* what to do? */ } else { tmp1 = (float)log10( (double) (x[i] - ex[i]) ); } if (x[i] + ex[i] <= 0.0) { /* again avoid log of negative */ tmp2 = (float)log10( (double) x[i] ); } else { tmp2 = (float)log10( (double) (x[i] + ex[i]) ); } } else { tmp1 = x[i] - ex[i]; tmp2 = x[i] + ex[i]; } PSmoveto(tmp1 * ppxunit, ytmp * ppyunit); PSrlineto((tmp2-tmp1)*ppxunit, 0.0); if (width > 0.0) { PSmoveto(tmp1 * ppxunit, ytmp*ppyunit - width/2.0); PSrlineto(0.0, width); PSmoveto(tmp2 * ppxunit, ytmp*ppyunit - width/2.0); PSrlineto(0.0, width); } PSstroke(); } } if (eybars && [[errorBarMatrix cellAt :n :j+1] state] == 1) { for (i=0; i<npoints; i++) { xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]); if (yaxislog) { if ((*(*(y+j)+i) - *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */ tmp1 = (float)log10( (double) (*(*(y+j)+i)) ); } else { tmp1 = (float)log10( (double) (*(*(y+j)+i) - *(*(ey+j)+i)) ); } if ((*(*(y+j)+i) + *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */ tmp2 = (float)log10( (double) (*(*(y+j)+i)) ); } else { tmp2 = (float)log10( (double) (*(*(y+j)+i) + *(*(ey+j)+i)) ); } } else { tmp1 = *(*(y+j)+i) - *(*(ey+j)+i); tmp2 = *(*(y+j)+i) + *(*(ey+j)+i); } PSmoveto(xtmp * ppxunit, tmp1 * ppyunit); PSrlineto(0.0, (tmp2-tmp1)*ppyunit); if (width > 0.0) { PSmoveto(xtmp*ppxunit - width/2.0, tmp1 * ppyunit); PSrlineto(width, 0.0); PSmoveto(xtmp*ppxunit - width/2.0, tmp2 * ppyunit); PSrlineto(width, 0.0); } PSstroke(); } } } curveindex += ncurves; } PSgrestore(); return self; } - startLegend { id legendtextfont,legendTitleFont; const char * curvetitle; const char * legendtitle = [legendTitle stringValueAt:0]; int j; int ncurves = [plotParam nCurvesTotal]; float maxcurvetitlewid = 0; float yhgt; if ([plotParam shouldChangeLegendFont]) { newLegendFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newLegendFont) { legendtextfont = [Font newFont:[newLegendFont name] size:[newLegendFont pointSize] style:[newLegendFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newLegendFont pointSize]; /* height of text */ } else { legendtextfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [legendtextfont set]; legendbox.size.height = 10.0; for (j=0; j<ncurves; j++) { /* check for no lines and no symbols */ if( ([plotParam providelinestyle:j] == NOLINE) && ( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue; curvetitle = [legendForm stringValueAt:j]; /* skip this curve if there is no title: */ if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue; maxcurvetitlewid = MAX(maxcurvetitlewid, [legendtextfont getWidthOf:curvetitle]); legendbox.size.height += yhgt; /* increment by yhgt for every curve */ } /* check on legend title width also */ if ([plotParam shouldChangeLegendTitleFont]) { newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newLegendTitleFont) { legendTitleFont = [Font newFont:[newLegendTitleFont name] size:[newLegendTitleFont pointSize] style:[newLegendTitleFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newLegendTitleFont pointSize]; /* height of text */ } else { legendTitleFont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [legendTitleFont set]; maxcurvetitlewid = MAX(maxcurvetitlewid, [legendTitleFont getWidthOf:legendtitle]); legendbox.size.width = 5.0 + 40.0 + 5.0 + maxcurvetitlewid + 5.0; /* legendboxwidth = L. margin + 40 + space + curve title + R. margin */ if([legendTitleFont getWidthOf:legendtitle] != 0) legendbox.size.height = legendbox.size.height + 2.0*yhgt - 4.0; return self; } - drawLegend:sender { id legendtextfont,legendTitleFont; const char * curvetitle; const char * legendtitle = [legendTitle stringValueAt:0]; int j; int ncurves = [plotParam nCurvesTotal]; float yhgt; if ([plotParam shouldChangeLegendFont]) { newLegendFont = [theFontManager convertFont:[theFontManager selFont]]; } /* get legend text font initialized */ if (newLegendFont) { legendtextfont = [Font newFont:[newLegendFont name] size:[newLegendFont pointSize] style:[newLegendFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newLegendFont pointSize]; } else { legendtextfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [legendtextfont set]; drawingLegendLines = YES; [self xdata:0]; [self ydata:0]; NXSetColor([plotParam provideBackgroundColor]); if ([legendOpaque state] == 0) { NXRectFill(&legendbox); } [sender drawLines:sender :NO :NO]; /* legend lines, linear axes always */ NXSetColor([plotParam provideTextColor]); for (j=0; j<ncurves; j++) { /* check for no lines and no symbols */ if ( ([plotParam providelinestyle:j] == NOLINE) && ([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue; curvetitle = [legendForm stringValueAt:j]; /* skip this curve if there is no title: */ if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue; PSmoveto(xlegend[1]*ppxunit + 5.0, *(*(ylegend+j)+0) * ppyunit - 0.33*yhgt); PSshow((char *)curvetitle); } if ([plotParam shouldChangeLegendTitleFont]) { newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newLegendTitleFont) { legendTitleFont = [Font newFont:[newLegendTitleFont name] size:[newLegendTitleFont pointSize] style:[newLegendTitleFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newLegendTitleFont pointSize]; /* height of text */ } else { legendTitleFont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } [legendTitleFont set]; PSmoveto(xlegend[0]*ppxunit - 5.0 + legendbox.size.width/2.0 - 0.5*[legendTitleFont getWidthOf:legendtitle], *(*(ylegend+0)+0) * ppyunit + yhgt); PSshow((char *)legendtitle); if([legendBoxOnOff state]) { PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); PSrectstroke(legendbox.origin.x, legendbox.origin.y, legendbox.size.width, legendbox.size.height); } drawingLegendLines = NO; drawingLegendSymbols = YES; [sender drawSymbols:sender :NO :NO]; /* legend symbols, linear axes always */ drawingLegendSymbols = NO; return self; } - clear:sender { /* * Derek Lisoski (dlisoski@cco.caltech.edu) suggests not drawing the * opaque background rectangle when you print or save a file. Then * when you read the saved plots into a separate drawing program you * can overlay multiple plots or get the plots arbitrarily close to * each other. There may be some problems with background colors * when importing into various applications (Create or Draw, e.g.) */ if (NXDrawingStatus == NX_DRAWING || [opaqueBackgroundButton state] == 0) { NXRectFill(&bounds); /* for color; had NXEraseRect, but that always fills with white */ } return self; } /* * This routine assumes it is called with xmin != xmax and ymin != ymax. * Bad things may happen if this is not the case. * The input parameters are assumed to be in pixel coordinates. */ - drawTicMarks:(float)xmin :(float)xmax :(float)ymin :(float)ymax { float pattern0[] = {}; /* solid */ float pattern2[] = {1.0, 3.0}; /* dot */ double xinc_unscaled = [plotParam provideXinc]; double yinc_unscaled = [plotParam provideYinc]; double xmin_unscaled = [plotParam provideXmin]; double ymin_unscaled = [plotParam provideYmin]; double xmax_unscaled = [plotParam provideXmax]; double ymax_unscaled = [plotParam provideYmax]; /* It is useful in some fairly extreme cases to have the increments * not in pixel coordinates (otherwise can get "bad" labels). */ char ticlabel[32]; id ticfont, ticfont1; float x, y, ticloc_rat, xticloc, yticloc; BOOL drawGrid = [gridOnOff state]; BOOL xaxislog = [plotParam xaxisLog]; BOOL yaxislog = [plotParam yaxisLog]; BOOL drawMinorTics = [minorTicMarksOnOff state]; int ticLocation; /* 0=axes, 1=2 sides, 2=4 sides */ float ticmarklen = [ticMarkLengthText floatValue]; int nmin, ninc, nmax, j, i; float ticloc, xwid, yhgt, yhgt1; double first; int nlabels; int axformat[3]; if (strncmp([ticMarkLocation title], "Axes", 4) == 0) ticLocation = 0; else if (strncmp([ticMarkLocation title], "Frame (2 sides)", 15) == 0) ticLocation = 1; else ticLocation = 2; if ([plotParam shouldChangeTicLabelFont]) { newTicLabelFont = [theFontManager convertFont:[theFontManager selFont]]; } if (newTicLabelFont) { ticfont = [Font newFont:[newTicLabelFont name] size:[newTicLabelFont pointSize] style:[newTicLabelFont style] matrix:NX_IDENTITYMATRIX]; yhgt = [newTicLabelFont pointSize]; yhgt1 = (yhgt >= 10.0? yhgt - 2.0 : 8.0); ticfont1 = [Font newFont:[newTicLabelFont name] size:yhgt1 style:[newTicLabelFont style] matrix:NX_IDENTITYMATRIX]; } else { ticfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE style:0 matrix:NX_IDENTITYMATRIX]; ticfont1 = [Font newFont:"Helvetica" size:10.0 style:0 matrix:NX_IDENTITYMATRIX]; yhgt = DEFAULTFONTSIZE; } /* get tic font initialized */ [ticfont set]; PSnewpath(); PSsetlinewidth([ticMarkThicknessText floatValue]); if (xaxislog) { /* x-axis is logarithmic */ nmin = (int)floor((double)(xmin/ppxunit)); nmax = (int)ceil((double)(xmax/ppxunit)); ninc = (int)rint(log10(xinc_unscaled)); if (ninc == 0) ninc = 1; /* avoid infinite loop */ if ([handFormatXaxis state] == 1) { axformat[0] = [xFormatLeft intValue]; axformat[1] = [xFormatRight intValue]; axformat[2] = [xFormatExponent intValue]; } for (j=nmin; j<=nmax; j+=ninc) { if ( (float)j * ppxunit >= xmin && (float)j * ppxunit <= xmax ) { if (drawGrid) { PSsetlinewidth([gridThicknessText floatValue]); PSmoveto((float)j * ppxunit, ymin); if ([gridDotted state]) PSsetdash(pattern2, 2, 0.0); else PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ABS(ymax-ymin)); PSstroke(); PSsetlinewidth([ticMarkThicknessText floatValue]); } PSmoveto((float)j * ppxunit, ymin - ticmarklen*6.0); /* big tic mark */ PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ticmarklen*6.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto((float)j * ppxunit, ymax); /* big tic mark */ PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ticmarklen*6.0); PSstroke(); } if ([handFormatXaxis state] == 0) { ticloc_rat = yhgt/DEFAULTFONTSIZE; PSmoveto((float)j * ppxunit - ticloc_rat*8.0, ymin - MAX(24.0*ticloc_rat, 24.0*ticloc_rat*(ticmarklen+10.0)/10.0)); PSshow("10"); [ticfont1 set]; PSmoveto((float)j * ppxunit + ticloc_rat*4.0, ymin - MAX(16.0*ticloc_rat, 8.0*ticloc_rat*(2.0 + 0.3*ticmarklen))); sprintf(ticlabel, "%-5d", j); PSshow(ticlabel); [ticfont set]; } else { x = (float) pow((double)10.0, (double)j); handformat(x, ticlabel, axformat); xwid = [ticfont getWidthOf:ticlabel]; PSmoveto((float)j * ppxunit - xwid/2.0, ymin - yhgt - MAX(5.0, 5.0*ticmarklen)); PSshow(ticlabel); } } if (drawMinorTics) { for (i=2; i<=9; i++) { ticloc = (float)j * ppxunit + ppxunit*(float)log10((double)i); if ( ticloc>xmin && ticloc<xmax ) { PSmoveto(ticloc, ymin - ticmarklen*3.0); /* small tic mark */ PSrlineto(0.0, ticmarklen*3.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(ticloc, ymax); /* small tic mark */ PSrlineto(0.0, ticmarklen*3.0); PSstroke(); } } } } } } else { /* x-axis is linear */ yticloc = (ticLocation > 0 ? ymin : 2.0*ticmarklen) ; /* If inc is big, skip tic marks entirely */ if (fabs(xinc_unscaled) < fabs(xmax_unscaled - xmin_unscaled)) { count_labels(&nlabels, &first, xmin_unscaled, xinc_unscaled, xmax_unscaled); if ([handFormatXaxis state] == 1) { axformat[0] = [xFormatLeft intValue]; axformat[1] = [xFormatRight intValue]; axformat[2] = [xFormatExponent intValue]; } else { autoformat(xmin_unscaled, xinc_unscaled, xmax_unscaled, axformat); [xFormatLeft setIntValue:axformat[0]]; [xFormatRight setIntValue:axformat[1]]; [xFormatExponent setIntValue:axformat[2]]; } /* * next loop starts at -1 because there may be room for minor tic * marks to the left of the first major tic mark (after a zoom, e.g.) */ for (i = -1; i < nlabels; i++) { /* Special test here for what should be exact 0 (but isn't sometimes * due to floating-point arithmetic. */ if (fabs(first/xinc_unscaled + (float)i) < 4.0e-7) { /* ugly */ x = 0.0; } else { x = (first + (xinc_unscaled)*(float)i) * ppxunit; } if (x >= xmin) { /* ensure major tic mark won't be off edge */ if (drawGrid) { PSsetlinewidth([gridThicknessText floatValue]); PSmoveto(x, ymin); if ([gridDotted state]) PSsetdash(pattern2, 2, 0.0); else PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ABS(ymax-ymin)); PSstroke(); PSsetlinewidth([ticMarkThicknessText floatValue]); } /* Nothing at 0 if we're putting tic marks on axes: */ if (ticLocation > 0 || x != 0.0) { PSmoveto(x, yticloc - ticmarklen*4.0); PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ticmarklen*4.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(x, ymax); PSsetdash(pattern0, 0, 0.0); PSrlineto(0.0, ticmarklen*4.0); PSstroke(); } handformat(x/ppxunit, ticlabel, axformat); xwid = [ticfont getWidthOf:ticlabel]; PSmoveto(x - xwid/2.0, yticloc - yhgt - MAX(5.0, 5.0*ticmarklen)); PSshow(ticlabel); } } if (drawMinorTics) { if (ticLocation > 0) { /* tic marks on frame */ for (j=1; j<=9; j++) { ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit; if (ticloc>xmin && ticloc<xmax) { PSmoveto(ticloc, yticloc - ticmarklen*2.0); PSrlineto(0.0, ticmarklen*2.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(ticloc, ymax); PSrlineto(0.0, ticmarklen*2.0); PSstroke(); } } } } else { for (j=1; j<=9; j++) { ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit; if (ticloc>xmin && ticloc<xmax) { PSmoveto(ticloc, -ticmarklen); PSrlineto(0.0, 2.0*ticmarklen); PSstroke(); } } } } } } } if (yaxislog) { /* y-axis is logarithmic */ nmin = (int)floor((double)(ymin/ppyunit)); nmax = (int)ceil((double)(ymax/ppyunit)); ninc = (int)rint(log10(yinc_unscaled)); if (ninc == 0) ninc = 1; /* avoid infinite loop */ if ([handFormatYaxis state] == 1) { axformat[0] = [yFormatLeft intValue]; axformat[1] = [yFormatRight intValue]; axformat[2] = [yFormatExponent intValue]; } for (j=nmin; j<=nmax; j+=ninc) { if ( (float)j * ppyunit >= ymin && (float)j * ppyunit <= ymax ) { if (drawGrid) { PSsetlinewidth([gridThicknessText floatValue]); PSmoveto(xmin, (float)j * ppyunit); if ([gridDotted state]) PSsetdash(pattern2, 2, 0.0); else PSsetdash(pattern0, 0, 0.0); PSrlineto(ABS(xmax-xmin), 0.0); PSstroke(); PSsetlinewidth([ticMarkThicknessText floatValue]); } PSmoveto(xmin - ticmarklen*6.0, (float)j * ppyunit); /* big tic mark */ PSsetdash(pattern0, 0, 0.0); PSrlineto(ticmarklen*6.0, 0.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(xmax, (float)j * ppyunit); /* big tic mark */ PSsetdash(pattern0, 0, 0.0); PSrlineto(ticmarklen*6.0, 0.0); PSstroke(); } if ([handFormatYaxis state] == 0) { ticloc_rat = yhgt/DEFAULTFONTSIZE; PSmoveto(xmin - MAX(40.0*ticloc_rat, 40.0*ticloc_rat*(ticmarklen+10.0)/10.0), (float)j * ppyunit - ticloc_rat*7.0); PSshow("10"); [ticfont1 set]; PSmoveto(xmin - MAX(24.0*ticloc_rat, 4.0*ticloc_rat*(ticmarklen + 6.0)), (float)j * ppyunit - ticloc_rat*1.0); sprintf(ticlabel, "%-5d", j); PSshow(ticlabel); [ticfont set]; } else { y = (float) pow((double)10.0, (double)j); handformat(y, ticlabel, axformat); xwid = [ticfont getWidthOf:ticlabel]; PSmoveto(xmin - xwid - MAX(10.0, 5.0*ticmarklen), (float)j * ppyunit + 2.0 - yhgt/2.0); PSshow(ticlabel); } } if (drawMinorTics) { for (i=2; i<=9; i++) { ticloc = (float)j * ppyunit + ppyunit*(float)log10((double)i); if (ticloc>ymin && ticloc<ymax) { PSmoveto(xmin - ticmarklen*3.0, ticloc); /* small tic mark */ PSrlineto(ticmarklen*3.0, 0.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(xmax, ticloc); /* small tic mark */ PSrlineto(ticmarklen*3.0, 0.0); PSstroke(); } } } } } } else { /* y-axis is linear */ xticloc = (ticLocation > 0 ? xmin : 2.0*ticmarklen) ; /* If inc is big, skip tic marks entirely */ if (fabs(yinc_unscaled) < fabs(ymax_unscaled - ymin_unscaled)) { count_labels(&nlabels, &first, ymin_unscaled, yinc_unscaled, ymax_unscaled); if ([handFormatYaxis state] == 1) { axformat[0] = [yFormatLeft intValue]; axformat[1] = [yFormatRight intValue]; axformat[2] = [yFormatExponent intValue]; } else { autoformat(ymin_unscaled, yinc_unscaled, ymax_unscaled, axformat); [yFormatLeft setIntValue:axformat[0]]; [yFormatRight setIntValue:axformat[1]]; [yFormatExponent setIntValue:axformat[2]]; } /* * next loop starts at -1 because there may be room for minor tic * marks to the left of the first major tic mark (after a zoom, e.g.) */ for (i = -1; i < nlabels; i++) { /* Special test here for what should be exact 0 (but isn't sometimes * due to floating-point arithmetic. */ if (fabs(first/yinc_unscaled + (float)i) < 4.0e-7) { /* ugly */ y = 0.0; } else { y = (first + (yinc_unscaled)*(float)i) * ppyunit; } if (y >= ymin) { /* ensure major tic mark won't be off edge */ if (drawGrid) { PSsetlinewidth([gridThicknessText floatValue]); PSmoveto(xmin, y); if ([gridDotted state]) PSsetdash(pattern2, 2, 0.0); else PSsetdash(pattern0, 0, 0.0); PSrlineto(ABS(xmax-xmin), 0.0); PSstroke(); PSsetlinewidth([ticMarkThicknessText floatValue]); } /* Nothing at 0 if we're putting tic marks on axes: */ if (ticLocation > 0 || y != 0.0) { PSmoveto(xticloc - ticmarklen*4.0, y); PSsetdash(pattern0, 0, 0.0); PSrlineto(ticmarklen*4.0, 0.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(xmax, y); PSsetdash(pattern0, 0, 0.0); PSrlineto(ticmarklen*4.0, 0.0); PSstroke(); } handformat(y/ppyunit, ticlabel, axformat); xwid = [ticfont getWidthOf:ticlabel]; PSmoveto(xticloc - xwid - MAX(10.0, 5.0*ticmarklen), y + 2.0 - yhgt/2.0); PSshow(ticlabel); } } if (drawMinorTics) { if (ticLocation > 0) { /* tics on frame */ for (j=1; j<=9; j++) { ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit; if (ticloc>ymin && ticloc<ymax) { PSmoveto(xticloc - ticmarklen*2.0, ticloc); PSrlineto(ticmarklen*2.0, 0.0); PSstroke(); if (ticLocation == 2) { /* tics on right and top */ PSmoveto(xmax, ticloc); PSrlineto(ticmarklen*2.0, 0.0); PSstroke(); } } } } else { for (j=1; j<=9; j++) { ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit; if (ticloc>ymin && ticloc<ymax) { PSmoveto(-ticmarklen, ticloc); PSrlineto(2.0*ticmarklen, 0.0); PSstroke(); } } } } } } } return self; } - drawSelf: (const NXRect *)rects :(int)rectCount { float xmin = (float)[plotParam provideXmin]; float xmax = (float)[plotParam provideXmax]; float ymin = (float)[plotParam provideYmin]; float ymax = (float)[plotParam provideYmax]; [self startPlot]; if ([plotParam nFiles] == 0) return self; /* no data */ if (xmin==xmax || ymin==ymax) return self; /* avoid division by zero */ PSgsave(); PStranslate(XOFFSET, YOFFSET); if ([plotParam xaxisLog]) { /* x-axis is logarithmic */ ppxunit = 0.9*(bounds.size.width-XOFFSET) /(float)log10((double)(xmax/xmin)); xmin = (float)log10((double)xmin) * ppxunit; xmax = (float)log10((double)xmax) * ppxunit; } else { /* x-axis is linear */ ppxunit = 0.9*(bounds.size.width-XOFFSET)/(xmax-xmin); xmin = xmin*ppxunit; /* drawing is all in pixel coordinates */ xmax = xmax*ppxunit; } if ([plotParam yaxisLog]) { /* y-axis is logarithmic */ ppyunit = 0.9*(bounds.size.height-YOFFSET) /(float)log10((double)(ymax/ymin)); ymin = (float)log10((double)ymin) * ppyunit; ymax = (float)log10((double)ymax) * ppyunit; } else { /* y-axis is linear */ ppyunit = 0.9*(bounds.size.height-YOFFSET)/(ymax-ymin); ymin = ymin*ppyunit; ymax = ymax*ppyunit; } PStranslate(-xmin, -ymin); // inner "frame" box if ([frameBoxOnOff state]) { PSsetlinewidth([frameBoxThicknessText floatValue]); PSnewpath(); PSmoveto(xmin, ymin); PSlineto(xmax, ymin); PSlineto(xmax, ymax); PSlineto(xmin, ymax); PSclosepath(); PSstroke(); /* reset line width */ PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 : [accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW : LINE_WIDTH_IF_PRINTING_COLOR); } if ([axesOnOff state]) { PSnewpath(); PSsetlinewidth([axisThicknessText floatValue]); PSmoveto(0.0, ymin); PSlineto(0.0, ymax); PSstroke(); PSmoveto(xmin, 0.0); PSlineto(xmax, 0.0); PSstroke(); } if ([majorTicMarksOnOff state]) [self drawTicMarks:xmin :xmax :ymin :ymax]; /* do this before clipping */ /* MIN and MAX below in case xmin > xmax and/or ymin > ymax. */ PSrectclip(MIN(xmin,xmax), MIN(ymin,ymax), ABS(xmax-xmin), ABS(ymax-ymin)); [self drawLines:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]]; [self drawSymbols:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]]; [self drawErrorBars :[plotParam xaxisLog] :[plotParam yaxisLog]]; PSgrestore(); if ([legendOnOff state]) { [self startLegend]; [self drawLegend:self]; } return self; } - doPrinting:sender { id myPrintPanel = [PrintPanel new]; [ [NXApp printInfo] setMarginLeft:72.0 right:72.0 top:72.0 bottom:72.0 ]; if (bounds.size.height > bounds.size.width) { /* portrait mode */ if (bounds.size.height/bounds.size.width > 9.0/6.5) { [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.height]; } else { [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.width]; } [ [NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES ]; } else { /* landscape mode */ if (bounds.size.width/bounds.size.height > 9.0/6.5) { [ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.width]; } else { [ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.height]; } [ [NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES ]; } [myPrintPanel setAccessoryView:printPanelAccessory]; [self printPSCode:sender]; return self; } - mouseDown:(NXEvent *)e /* * This code taken from the Mandelbrot example in /NextDeveloper (and modified). * We implement the mouseDown method so the user can sweep out a section of * the view and select that as the current window in which to view the curve(s). */ { int looping = YES; int oldMask; NXRect bbox; NXPoint startPoint, currPoint; float xmin, xmax, ymin, ymax; register float t1, t2, t3, t4; int zooming; BOOL xaxislog = [plotParam xaxisLog]; BOOL yaxislog = [plotParam yaxisLog]; zooming = ZOOM; if (strncmp([zoomChoice title], "Zoom/Move", 9) == 0) zooming = NOZOOM; else if (strncmp([zoomChoice title], "Move legend", 11) == 0) zooming = MOVELEGEND; else if (strncmp([zoomChoice title], "Move x title", 12) == 0) zooming = MOVEXTITLE; else if (strncmp([zoomChoice title], "Move y title", 12) == 0) zooming = MOVEYTITLE; else if (strncmp([zoomChoice title], "Move main title", 15) == 0) zooming = MOVEMAINTITLE; if (zooming == ZOOM) { /* really zooming */ if (xaxislog) { xmin = (float)log10([plotParam provideXmin]) * ppxunit; xmax = (float)log10([plotParam provideXmax]) * ppxunit; } else { xmin = [plotParam provideXmin] * ppxunit; xmax = [plotParam provideXmax] * ppxunit; } if (yaxislog) { ymin = (float)log10([plotParam provideYmin]) * ppyunit; ymax = (float)log10([plotParam provideYmax]) * ppyunit; } else { ymin = [plotParam provideYmin] * ppyunit; ymax = [plotParam provideYmax] * ppyunit; } oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; startPoint = e->location; [self convertPoint:&startPoint fromView:nil]; NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0); [self lockFocus]; while (looping) { e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK]; currPoint = e->location; [self convertPoint:&currPoint fromView:nil]; bbox.size.width = (currPoint.x - startPoint.x); bbox.size.height = (currPoint.y - startPoint.y); /* Normalize bbox to always have positive width and height */ if (bbox.size.width < 0) { bbox.size.width = -bbox.size.width; bbox.origin.x = startPoint.x - bbox.size.width; } if (bbox.size.height < 0) { bbox.size.height = -bbox.size.height; bbox.origin.y = startPoint.y - bbox.size.height; } PSnewinstance(); if (looping = (e->type == NX_MOUSEDRAGGED)) { PSsetinstance(YES); NXSetColor([plotParam provideTextColor]); NXFrameRect(&bbox); PSsetinstance(NO); } } [self unlockFocus]; [window setEventMask:oldMask]; if ((bbox.size.width > 0) && (bbox.size.height > 0)) { /* At this point, bbox is in window coordinates. Convert to curve * coordinates based on this view's coordinates and the bounding box. */ xmin = xmin + (bbox.origin.x - XOFFSET); xmax = xmin + bbox.size.width; ymin = ymin + (bbox.origin.y - YOFFSET); ymax = ymin + bbox.size.height; /* save old min/max -- must adjust if log axis */ t1 = xmin/ppxunit; t2 = xmax/ppxunit; t3 = ymin/ppyunit; t4 = ymax/ppyunit; if (xaxislog) { t1 = (float) pow((double)10.0, (double)t1); t2 = (float) pow((double)10.0, (double)t2); } if (yaxislog) { t3 = (float) pow((double)10.0, (double)t3); t4 = (float) pow((double)10.0, (double)t4); } [plotParam stackOldMinMax:t1 :t2 :t3 :t4]; if (xaxislog) { [plotParam resetXmin:pow((double)10.0, (double)(xmin/ppxunit))]; [plotParam resetXmax:pow((double)10.0, (double)(xmax/ppxunit))]; } else { [plotParam resetXmin:(double)(xmin/ppxunit)]; [plotParam resetXmax:(double)(xmax/ppxunit)]; } if (yaxislog) { [plotParam resetYmin:pow((double)10.0, (double)(ymin/ppyunit))]; [plotParam resetYmax:pow((double)10.0, (double)(ymax/ppyunit))]; } else { [plotParam resetYmin:(double)(ymin/ppyunit)]; [plotParam resetYmax:(double)(ymax/ppyunit)]; } /* Call [self display] to force current values of xmin/xmax/ymin/ymax * to take effect (otherwise xmin, etc., get incremented too many times). */ [plotParam drawPlotButton:1]; [self display]; [plotParam drawPlotButton:0]; } } else if (zooming >= MOVELEGEND) { oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; startPoint = e->location; [self convertPoint:&startPoint fromView:nil]; if (zooming == MOVELEGEND) { NXSetRect(&bbox,startPoint.x,startPoint.y, legendbox.size.width,legendbox.size.height); } else if (zooming == MOVEXTITLE) { NXSetRect(&bbox,startPoint.x,startPoint.y, xtitlebox.size.width,xtitlebox.size.height); } else if (zooming == MOVEYTITLE) { NXSetRect(&bbox,startPoint.x,startPoint.y, ytitlebox.size.width,ytitlebox.size.height); } else if (zooming == MOVEMAINTITLE) { NXSetRect(&bbox,startPoint.x,startPoint.y, maintitlebox.size.width,maintitlebox.size.height); } [self lockFocus]; while (looping) { e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK]; currPoint = e->location; [self convertPoint:&currPoint fromView:nil]; bbox.origin.x = currPoint.x; bbox.origin.y = currPoint.y; PSnewinstance(); if (looping = (e->type == NX_MOUSEDRAGGED)) { PSsetinstance(YES); NXSetColor([plotParam provideTextColor]); NXFrameRect(&bbox); PSsetinstance(NO); } } [self unlockFocus]; [window setEventMask:oldMask]; /* * At this point, bbox is in window coordinates. */ if (zooming == MOVELEGEND) { legendbox.origin.x = bbox.origin.x; legendbox.origin.y = bbox.origin.y; } else if (zooming == MOVEXTITLE) { xtitlebox.origin.x = bbox.origin.x; xtitlebox.origin.y = bbox.origin.y; } else if (zooming == MOVEYTITLE) { ytitlebox.origin.x = bbox.origin.x; ytitlebox.origin.y = bbox.origin.y; } else if (zooming == MOVEMAINTITLE) { maintitlebox.origin.x = bbox.origin.x; maintitlebox.origin.y = bbox.origin.y; } /* Call [self display] to give immediate feedback */ [plotParam drawPlotButton:1]; [self display]; [plotParam drawPlotButton:0]; } return self; } - saveEPS:sender { id savePanel = [[SavePanel new] setRequiredFileType:"eps"]; char *eps_fileName; // Name of the EPS file for output [savePanel setTitle:"Save EPS"]; /* make sure title is OK */ if ([savePanel runModal]) { eps_fileName = (char *) calloc(strlen([savePanel filename])+1, sizeof(char)); strcpy(eps_fileName, [savePanel filename]); [self savePSCode:eps_fileName]; } return self; } - savePSCode:(char *)aFile { NXStream *psStream; psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if (!psStream) { return self; } [self getBounds:&bounds]; [self copyPSCodeInside:&bounds to:psStream]; NXFlush(psStream); NXSaveToFile(psStream, aFile); NXCloseMemory(psStream, NX_FREEBUFFER); return self; } /* * Following code taken from the Graph example in NextDeveloper/Examples. * * Copies the current view into the Pasteboard as PostScript. */ - copyPScode:sender { NXStream *psStream; id pb; char *data; int dataLen, maxDataLen; /* Open a stream on memory where we will collect the PostScript */ psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if (!psStream) return self; /* Tell the Pasteboard we're going to copy PostScript */ pb = [Pasteboard new]; [pb declareTypes:&NXPostScriptPboardType num:1 owner:self]; /* writes the PostScript for the whole plot as EPS into the stream */ [self getBounds:&bounds]; [self copyPSCodeInside:&bounds to:psStream]; /* get the buffered up PostScript out of the stream */ NXGetMemoryBuffer(psStream, &data, &dataLen, &maxDataLen); /* put the buffer in the Pasteboard, free the stream (and the buffer) */ [pb writeType:NXPostScriptPboardType data:data length:dataLen]; NXCloseMemory(psStream, NX_FREEBUFFER); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.