This is Plot.m in view mode; [Download] [Up]
/* Generated by Interface Builder */ #import "Plot.h" #import "NXYView.h" /* only for initializeLegendBox */ #import <appkit/Matrix.h> #import <appkit/Cell.h> #import <math.h> #import <appkit/OpenPanel.h> #import <appkit/publicWraps.h> // For NXBeep (from 1.7) #define ALLOCSIZE 2048 /* used in malloc in readData */ //Added for 1.7 functions int beepError=0; // Added from nxyplot 1.7 /* The following routines are in auxil.m: */ extern void computeNiceLinInc(float *, float *, float *); extern void computeNiceLogInc(float *, float *, float *); @implementation Plot - (NXCoord *)xdata { return x;} - (NXCoord **)ydata { return y;} - (int)nPoints { return npoints;} - (int)nCurves { return ncurves;} //- (const char *)provideXtitle // {return [xTitle stringValueAt:0];} // {return [canvas xaxisLabel];} //- (const char *)provideYtitle // {return [yTitle stringValueAt:0];} // {return [canvas yaxisLabel];} //- (const char *)provideMaintitle // {return [mainTitle stringValueAt:0];} //{return [canvas mainTitle];} - (BOOL) shouldDrawGrid { // if ( [gridOnOff state] ) return YES; if ( [canvas gridState] ) return YES; else return NO; } - (BOOL) shouldDrawBox { // if ( [borderBoxOnOff state] ) return YES; if ( [canvas borderState] ) return YES; else return NO; } - (BOOL) xaxisLog { // if ( [xLinLog state] ) return YES; if([canvas xLinLogState]) return YES; else return NO; } - forceXaxisLinear { // [xLinLog setState:0]; // [xLinLog display]; [canvas setXLinLogState:LINEAR]; return self; } - (BOOL) yaxisLog { // if ( [yLinLog state] ) return YES; if([canvas yLinLogState]) return YES; else return NO; } - forceYaxisLinear { // [yLinLog setState:0]; // [yLinLog display]; [canvas setYLinLogState:LINEAR]; return self; } - (int)providelinestyle:(int)aCurve { return 0; /* for safety */ } - (int)providesymbolstyle:(int)aCurve { return 0; /* for safety */ } - (int)providesymbolsize{ return 0;} - (int)providelinethickness{ return 0;} //- (float)provideXmin {return [xMin floatValueAt:0];} - (float)provideXmin {return [canvas xMinValue];} //- (float)provideXmax {return [xMax floatValueAt:0];} - (float)provideXmax {return [canvas xMaxValue];} //- (float)provideXinc {return [xInc floatValueAt:0];} - (float)provideXinc {return [canvas xIncValue];} //- (float)provideYmin {return [yMin floatValueAt:0];} - (float)provideYmin {return [canvas yMinValue];} //- (float)provideYmax {return [yMax floatValueAt:0];} - (float)provideYmax {return [canvas yMaxValue];} //- (float)provideYinc {return [yInc floatValueAt:0];} - (float)provideYinc {return [canvas yIncValue];} //- resetXmin:(float)aNum { [xMin setFloatValue:aNum at:0]; return self; } - resetXmin:(float)aNum { [canvas setXminValue:aNum]; return self; } //- resetXmax:(float)aNum { [xMax setFloatValue:aNum at:0]; return self; } - resetXmax:(float)aNum { [canvas setXmaxValue:aNum]; return self; } //- resetXinc:(float)aNum { [xInc setFloatValue:aNum at:0]; return self; } - resetXinc:(float)aNum { [canvas setXincValue:aNum]; return self; } //- resetYmin:(float)aNum { [yMin setFloatValue:aNum at:0]; return self; } - resetYmin:(float)aNum { [canvas setYminValue:aNum]; return self; } //- resetYmax:(float)aNum { [yMax setFloatValue:aNum at:0]; return self; } - resetYmax:(float)aNum { [canvas setYmaxValue:aNum]; return self; } //- resetYinc:(float)aNum { [yInc setFloatValue:aNum at:0]; return self; } - resetYinc:(float)aNum { [canvas setYincValue:aNum]; return self; } - drawPlot:sender { // if (!x) return self; [self sanityCheck]; /* disallow various bad parameters */ [canvas display]; return self; } // Allocate enough memory and read the data points /* * WARNING: This code will go into an infinite loop if there is illegal * crud in the data file (such as a comma, for example). The culprit is * the "fscanf" below; we will put up with this for now, but we should * make this more robust. */ - (int) readDataFromFile:(FILE *)aDataStream { BOOL inword = NO; char c; int j, size = ALLOCSIZE; // int oldncurves = ncurves; /* for linestyle matrix column deleting */ NXPing(); /* Probably not needed now--was for Plot Button */ /* First, free x and y here if there's already data in them */ if (x) { free( (void *)x ); for (j = 0; j < ncurves; j++) { free( (void *)(*(y+j)) ); } free( (void *)y ); } /* Figure out the number of curves in the file by reading characters */ /* until a newline is encountered; the number of curves is one less */ /* than the number of words found. */ /* For now we assume the input file is an ascii file. */ ncurves = -1; while (1) { c = getc(aDataStream); if (c == '\n') { break; /* breaks out of while loop */ } if ((inword==NO) && !(c==' ' || c=='\t')) { ncurves++; inword = YES; } if ((inword==YES) && (c==' ' || c=='\t')) { inword = NO; } } if (ncurves == -1) { /* couldn't find "\n", give up */ return 0; } /* Now read the data into memory */ rewind(aDataStream); y = (NXCoord **)malloc( ncurves * sizeof(NXCoord *) ); x = (NXCoord *)malloc( size * sizeof(NXCoord) ); for (j = 0; j < ncurves; j++) { *(y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) ); } npoints = 0; while(1) { if( (fscanf(aDataStream, "%f", x+npoints)) == EOF ) { break; /* breaks out of the while loop */ } for (j = 0; j < ncurves; j++) { fscanf(aDataStream, "%f", *(y+j)+npoints); } npoints++; if (npoints == size) { /* get more memory */ size += ALLOCSIZE; x = (NXCoord *)realloc(x, size * sizeof(NXCoord)); for (j = 0; j < ncurves; j++) { *(y+j) = (NXCoord *)realloc( *(y+j), size * sizeof(NXCoord) ); } } } return npoints; } // Following was added by cwf. It is just the above method modified for a NXStream // This should be replaced by the equivalent code from nxyplot1.7 /* * WARNING: This code will go into an infinite loop if there is illegal * crud in the data file (such as a comma, for example). The culprit is * the "fscanf" below; we will put up with this for now, but we should * make this more robust. */ - (int) readDataFromStream:(NXStream *)aDataStream; { BOOL inword = NO; char c; int j, size = ALLOCSIZE; // int oldncurves = ncurves; /* for linestyle matrix column deleting */ NXPing(); /* Probably not needed now--was for Plot Button */ /* First, free x and y here if there's already data in them */ if (x) { free( (void *)x ); for (j = 0; j < ncurves; j++) { free( (void *)(*(y+j)) ); } free( (void *)y ); } /* Figure out the number of curves in the file by reading characters */ /* until a newline is encountered; the number of curves is one less */ /* than the number of words found. */ /* For now we assume the input file is an ascii file. */ ncurves = -1; while (1) { c = NXGetc(aDataStream); if (c == '\n') { break; /* breaks out of while loop */ } if ((inword==NO) && !(c==' ' || c=='\t')) { ncurves++; inword = YES; } if ((inword==YES) && (c==' ' || c=='\t')) { inword = NO; } } if (ncurves == -1) { /* couldn't find "\n", give up */ return 0; } /* Now read the data into memory */ NXSeek(aDataStream,0L,NX_FROMSTART); y = (NXCoord **)malloc( ncurves * sizeof(NXCoord *) ); x = (NXCoord *)malloc( size * sizeof(NXCoord) ); for (j = 0; j < ncurves; j++) { *(y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) ); } npoints = 0; while(1) { if( (NXScanf(aDataStream, "%f", x+npoints)) == EOF ) { break; /* breaks out of the while loop */ } for (j = 0; j < ncurves; j++) { NXScanf(aDataStream, "%f", *(y+j)+npoints); } npoints++; if (npoints == size) { /* get more memory */ size += ALLOCSIZE; x = (NXCoord *)realloc(x, size * sizeof(NXCoord)); for (j = 0; j < ncurves; j++) { *(y+j) = (NXCoord *)realloc( *(y+j), size * sizeof(NXCoord) ); } } } return npoints; } /* Might want to make sure INLINE_MATH is defined when math.h is included */ - checkLinLog { /* Check x and y axes -- do a heuristic test for log/lin. * The test employed comes from M. Merriam and xyplot. */ double scale, linsum, logsum; register double tmp; int i, j; /* First test x axis. */ if (datamax.x > 0.0 && datamin.x > 0.0 && datamax.x != datamin.x) { scale = fabs( (double)datamax.x - (double)datamin.x ); linsum = 0.0; for (j=1; j<npoints; j++) { tmp = ( (double)x[j]-(double)x[j-1] )/(double)scale; /* avoid overflow */ linsum += tmp*tmp; } scale = log10( (double)datamax.x/(double)datamin.x ); /* what if datamax.x<datamin.x? */ logsum = 0.0; for (i=1; i<npoints; i++) { tmp = log10( (double)x[i]/(double)x[i-1] ) / scale; logsum += tmp*tmp; } if (linsum < logsum) { // [xLinLog setState:0]; /* linear axis */ [canvas setXLinLogState:LINEAR]; } else { // [xLinLog setState:1]; /* logarithmic axis */ [canvas setXLinLogState:LOG]; } } else { // [xLinLog setState:0]; /* linear */ [canvas setXLinLogState:LINEAR]; } /* Now test y axis */ if (datamax.y > 0.0 && datamin.y > 0.0 && datamax.y != datamin.y) { scale = fabs( (double)datamax.y - (double)datamin.y ); linsum = 0.0; for (j=0; j<ncurves; j++) { for (i=1; i<npoints; i++) { tmp = ( (double)*(*(y+j)+i) - (double)*(*(y+j)+i-1) ) / scale; /* avoid overflow */ linsum += tmp*tmp; } } scale = log10((double)datamax.y/(double)datamin.y); /* what if datamax.y<datamin.y? */ logsum = 0.0; for (j=0; j<ncurves; j++) { for (i=1; i<npoints;i++) { tmp = log10( (double)*(*(y+j)+i)/(double)*(*(y+j)+i-1) ) / scale; logsum += tmp*tmp; } } if (linsum < logsum) { // [yLinLog setState:0]; /* linear axis */ [canvas setYLinLogState:LINEAR]; } else { // [yLinLog setState:1]; /* logarithmic axis */ [canvas setYLinLogState:LOG]; } } else { // [yLinLog setState:0]; /* linear */ [canvas setYLinLogState:LINEAR]; } // [xLinLog display]; // [yLinLog display]; return self; } // Go through the data set and find values for datamin.x, // datamax.x, datamin.y, datamax.y - findMinAndMax { if (x) { int i, j; datamin.x = datamax.x = x[0]; for (i = 1; i < npoints; i++) { if (x[i] < datamin.x) datamin.x = x[i]; else if (x[i] > datamax.x) datamax.x = x[i]; } datamin.y = datamax.y = *(*(y+0)+0); for (j = 0; j < ncurves; j++) { for (i = 0; i < npoints; i++) { if (*(*(y+j)+i) < datamin.y) datamin.y = *(*(y+j)+i); else if (*(*(y+j)+i) > datamax.y) datamax.y = *(*(y+j)+i); } } } // reset min and max here (may want to change the placement of this) // [self resetXmin:datamin.x]; // [self resetXmax:datamax.x]; // [self resetXinc:(datamax.x - datamin.x)/5.0]; // [self resetYmin:datamin.y]; // [self resetYmax:datamax.y]; // [self resetYinc:(datamax.y - datamin.y)/5.0]; return self; } // Added from nxyplot 1.7 // Get pleasing values for the min, max, and increments - niceMinMaxInc { float fmin, fmax, finc; fmin = datamin.x; fmax = datamax.x; if ([self xaxisLog] ) { computeNiceLogInc(&fmin, &fmax, &finc); } else { computeNiceLinInc(&fmin, &fmax, &finc); } [self resetXmin:fmin]; [self resetXmax:fmax]; [self resetXinc:finc]; fmin = datamin.y; fmax = datamax.y; if ([self yaxisLog] ) { computeNiceLogInc(&fmin, &fmax, &finc); } else { computeNiceLinInc(&fmin, &fmax, &finc); } [self resetYmin:fmin]; [self resetYmax:fmax]; [self resetYinc:finc]; return self; } //Added from nxyplot 1.7 (Beep code "removed" for now) // We make the plotParam object responsible for checking parameters // before the PlotView object is called. Thus the PlotView object can // assume the parameters are OK, and it doesn't have to do any checking. // Things to check: xinc has the same sign as xmax-xmin (same for y); // no negative data if log plot requested on x or y axis; there won't be // too many tic marks requested. - sanityCheck { float xinc = [self provideXinc]; float xmax = [self provideXmax], xmin = [self provideXmin]; float yinc = [self provideYinc]; float ymax = [self provideYmax], ymin = [self provideYmin]; int nticmarks; /* First check: no nonpositive data if logarithmic axis */ if ( [self xaxisLog] ) { if (datamin.x <= 0.0 || xmin <= 0.0 || xmax <= 0.0) { // [xLinLog setState:0]; /* back to linear */ [canvas setXLinLogState:0]; //Changes above line since there is no control panel NXBeep(); /* audible alert */ beepError = 1; } } if ( [self yaxisLog] ) { if (datamin.y <= 0.0 || ymin <= 0.0 || ymax <= 0.0) { // [yLinLog setState:0]; /* back to linear */ [canvas setYLinLogState:0]; //Changes above line since there is no control panel NXBeep(); /* audible alert */ beepError = 2; } } /* Second check: xinc has same sign as xmax and xmin */ if (xinc*(xmax-xmin) <= 0.0) { /* the bad case - avoid infinite loop */ if (xinc < 0.0) { /* in PlotView:drawSelf */ xinc = -xinc; [self resetXinc:xinc]; NXBeep(); beepError = 3; } if (xmax <= xmin) { [self niceMinMaxInc]; NXBeep(); /* alert */ beepError = 4; } } /* And similarly for yinc */ if (yinc*(ymax-ymin) <= 0.0) { /* the bad case - avoid infinite loop */ if (yinc < 0.0) { yinc = -yinc; [self resetYinc:yinc]; NXBeep(); /* alert */ beepError = 5; } if (ymax <= ymin) { [self niceMinMaxInc]; NXBeep(); /* alert */ beepError = 6; } } /* Third check: no more than 100 (say) tic marks on either axis */ if ( ![self xaxisLog] ) { /* linear axis */ nticmarks = (int) ((xmax - xmin) / xinc) ; if (nticmarks > 100) { computeNiceLinInc(&xmin, &xmax, &xinc); [self resetXmin:xmin]; [self resetXmax:xmax]; [self resetXinc:xinc]; NXBeep(); /* alert */ // beepError = 7; } } if ( ![self yaxisLog] ) { /* linear axis */ nticmarks = (int) ((ymax - ymin) / yinc) ; if (nticmarks > 100) { computeNiceLinInc(&ymin, &ymax, &yinc); [self resetYmin:ymin]; [self resetYmax:ymax]; [self resetYinc:yinc]; NXBeep(); /* alert */ beepError = 8; } } // This check not implemented in palette /* Another check: the axes should be in the frame box. */ // if ([self shouldDrawAxes] && (ymin>0.0 || ymax<0.0 || xmin>0.0 || xmax<0.0)) { // NXBeep(); // beepError = 14; /* this is only a warning message */ // } return self; } // The next method (whyTheBeep) was added from 1.7, but must be implemented by the user // Look for the key words "NXBeep()" and "beepError", some have been commented out - whyTheBeep:sender { switch(beepError) { case 0: NXRunAlertPanel("The Beep Panel", "Press this button if the program beeps\n" "and you want to know why", "OK", NULL, NULL); break; case 1: NXRunAlertPanel("The Beep Happened Because:", "x-axis changed from log to linear", "OK", NULL, NULL); break; case 2: NXRunAlertPanel("The Beep Happened Because:", "y-axis changed from log to linear", "OK", NULL, NULL); break; case 3: NXRunAlertPanel("The Beep Happened Because:", "x-increment was negative\n (I changed it)", "OK", NULL, NULL); break; case 4: NXRunAlertPanel("The Beep Happened Because:", "xmax was less than xmin\n (I changed them)", "OK", NULL, NULL); break; case 5: NXRunAlertPanel("The Beep Happened Because:", "y-increment was negative\n (I changed it)", "OK", NULL, NULL); break; case 6: NXRunAlertPanel("The Beep Happened Because:", "ymax was less than ymin\n (I changed them)", "OK", NULL, NULL); break; case 7: NXRunAlertPanel("The Beep Happened Because:", "Too many tic marks would have been on the x-axis\n" " (I changed the min, max, and/or increment)", "OK", NULL, NULL); break; case 8: NXRunAlertPanel("The Beep Happened Because:", "Too many tic marks would have been on the y-axis\n" " (I changed the min, max, and/or increment)", "OK", NULL, NULL); break; case 9: NXRunAlertPanel("The Beep Happened Because:", "x-axis was logarithmic but some datum was negative\n" " (I changed x-axis to linear)", "OK", NULL, NULL); break; case 10: NXRunAlertPanel("The Beep Happened Because:", "y-axis was logarithmic but some datum was negative\n" " (I changed y-axis to linear)", "OK", NULL, NULL); break; case 11: NXRunAlertPanel("The Beep Happened Because:", "tried to set color of a non-existent curve", "OK", NULL, NULL); break; case 12: NXRunAlertPanel("The Beep Happened Because:", "box thickness was negative\n" " (I changed it to a positive number)", "OK", NULL, NULL); break; case 13: NXRunAlertPanel("The Beep Happened Because:", "axis thickness was negative\n" " (I changed it to a positive number)", "OK", NULL, NULL); break; case 14: NXRunAlertPanel("The Beep Happened Because:", "axis drawing was requested and at least\n" "one axis is outside the framing box", "OK", NULL, NULL); break; } return self; } // Use the OpenPanel object to get a filename - open:sender { char const *fileTypes[2] = {0,0}; // this type is all ASCII files (?) // char const *fileTypes[2] = {"xyp",0}; // this would be only files with an xyp extension char fname[1024]; id openPanel = [OpenPanel new]; if ([openPanel runModalForTypes:fileTypes]) { strncpy(fname, [openPanel filename], 1024); [self openFile:fname]; } return self; } - openFile:(char *)dataFile { FILE *dataStream; if ((dataStream = fopen(dataFile, "r")) == NULL) { NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, dataFile); return self; } if ([self readDataFromFile:dataStream] == 0) { NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK", NULL, NULL, dataFile); fclose(dataStream); return self; } fclose(dataStream); // [canvas initializeLegendBox]; if([canvas autoMaxMinState] == YES) { // Get Max and Min from data [self findMinAndMax]; [self niceMinMaxInc]; } //else // Use values set in Inspector // [canvas setMaxMinValues]; /* Check for linear or log on x and y axes */ // [self checkLinLog]; if([canvas autoPaperState]) { [self checkLinLog]; [self niceMinMaxInc]; } [self drawPlot:self]; return self; } // The following was added to use a stream for data to plot which has been sent from // some controling App--this will(should) be replaced by later nxyplot (1.7) code since // it uses streams for all data plots. -useDataStreamAndPlot:(NXStream *)dataStream { [self readDataFromStream:dataStream]; if([canvas autoMaxMinState] == YES) { [self findMinAndMax]; [self niceMinMaxInc]; } /* Check for linear or log on x and y axes */ if([canvas autoPaperState]) { [self checkLinLog]; [self niceMinMaxInc]; } [self drawPlot:self]; return self; } //This archiving is unnecessary for the most part (canvas and ?? need to be archived, // the other instance variables could be removed.) Just here on the off chance I figure // out how to add a control panel. - read:(NXTypedStream*)stream /* * Unarchives the PlotView. Initializes four of the PlotView's instance variables * to the values stored in the stream. */ { [super read:stream]; NXReadTypes(stream, "@", &canvas); return self; } - write:(NXTypedStream*)stream /* * Archives the PlotView by writing its important instance variables to the stream. */ { [super write:stream]; NXWriteTypes(stream, "@", &canvas); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.