This is ContourPlotApp.m in view mode; [Download] [Up]
/* Test application for ContourView.m class */ /* Izumi Ohzawa 92-5-22 */ #import <appkit/appkit.h> #import "ContourPlotApp.h" #import "ContourView.h" #import "CurveView.h" #import "version.h" #import <stdio.h> #import <stdlib.h> #import <math.h> #define PI 3.141592654 @implementation ContourPlotApp -appDidInit:sender { static NXDefaultsVector CPDefaults = { {"SaveDir", "~"}, {NULL} }; NXRegisterDefaults(APPNAME, CPDefaults); zdata = NULL; // #### Some default settings which may be overridden later. #### nxInterp = 60; nyInterp = 60; xgstep = ygstep = 1.0; manual_scaling = 0; // auto zrange_in_file = 0; min_zval = 0.0; max_zval = 1.0; xt = yt = NULL; xcurve = ycurve = NULL; [NXApp setDelegate:self]; openPanel = [OpenPanel new]; // just caching [brightCWell setColor:NX_COLORRED]; [darkCWell setColor:NX_COLORBLUE]; [bgCWell setColor:NX_COLORWHITE]; [lineCWell setColor:NX_COLORBLACK]; [self colorWellsAction:self]; // reflect color well settings [versionField setStringValue: VERSION]; [xposTextField setFloatingPointFormat:YES left:3 right:2]; [yposTextField setFloatingPointFormat:YES left:3 right:2]; [maxZplotField setFloatingPointFormat:YES left:3 right:2]; [minZplotField setFloatingPointFormat:YES left:3 right:2]; [maxZdataField setFloatingPointFormat:YES left:3 right:2]; [minZdataField setFloatingPointFormat:YES left:3 right:2]; // [contourView setDebugEnable:YES]; // only for debugging [contourView setDelegate:self]; // set ContourView's delegete to myself [contourView enableContinuousNotify:YES]; [contourView setBipolar:YES]; [contourView setEnabled: NO]; // start up disabled [contourView setFillEnable:YES]; // if color fill should be used [contourView setMinNumberOfPointsPerContour: 4]; // (optional) eliminates tiny noise [[contourView window] makeKeyAndOrderFront:self]; return self; } - actionPrintCrossHair:sender { if([sender state]) [contourView enablePrintCrossHair:YES]; else [contourView enablePrintCrossHair:NO]; return self; } - markerDidMove:(int)viewtag :(int)marker :(float)x :(float)y :(int)mup { // Marker 2 is generated by drag of cross point. if(marker == 0 || marker == 2) { [yposTextField setFloatValue: y]; // Y coord of Horiz line [contourView horizCrossSectionAtY:y :nxInterp forXs:xt :xcurve]; [horizCSView setNewTrace: xcurve]; [horizCSView drawIt:self]; } if(marker == 1 || marker == 2) { [xposTextField setFloatValue: x]; // X coord of Vertical line [contourView vertCrossSectionAtX:x :nyInterp forYs:yt :ycurve]; [vertCSView setNewTrace: ycurve]; [vertCSView drawIt:self]; } return self; } // Loads the sample data file from app wrapper - openSample:sender { char buf[MAXPATHLEN + 1]; NXBundle *bundle; bundle = [NXBundle mainBundle]; if( [bundle getPath:buf forResource:"SpaceTimeRF" ofType:"2d"] ) { [self openFile: buf]; } return self; } - viewSampleFile:sender { char buf[MAXPATHLEN + 1]; NXBundle *bundle; NXStream *stream; bundle = [NXBundle mainBundle]; if( [bundle getPath:buf forResource:"SpaceTimeRF" ofType:"2d"] ) { if ((stream = NXMapFile(buf, NX_READONLY)) == NULL) { NXRunAlertPanel("File Error", "Cannot open file %s","OK",NULL,NULL, buf); return nil; } [commentText readText: stream]; NXClose(stream); [[[commentText window] setTitleAsFilename: buf] orderFront:self]; } return self; } /* * appOpenFile:type: is invoked by Workspace via command-dragging of file to app * icon. ContourPlot does not have any special doc type, so double-clicking * does not apply. */ - (int)appOpenFile:(const char *)path type:(const char *)type { if ([self openFile:path] == nil) return NO; else return YES; } /* * The following method indicates that Yap is ready to open multiple * files at one time. Really NO, but without this, appicon does not * accept command-dragged data file. */ - (BOOL)appAcceptsAnotherFile:sender { return YES; } // connect to Menu's File -> Open button. // - open:sender { static int FirstFile = 1; const char *fileName; const char *const types[2] = {NULL, NULL}; if(FirstFile) { if ([openPanel runModalForDirectory: NXHomeDirectory() file: NULL types: types] && (fileName =[openPanel filename])) { [self openFile:fileName]; FirstFile = 0; } } else { /* Use last directory again */ if ([openPanel runModalForTypes:types] && (fileName =[openPanel filename])) { [self openFile:fileName]; } } return self; } static char *keyword[] = { "NUMPOINTS", "XRANGE", "YRANGE", "ZDATA", "GRIDSTEP", "NINTERPOL", "ZRANGE", "" }; #define NKEYWORD 7 /* # of names in *keyword[] */ - openFile:(const char *)fileName { FILE *fpin; char linebuf[256], wdbuf[256]; int i, n2read; int Done = 0; // raised when ZDATA keyword is detected int BadData = 0; // raised if bad data detected int prmnumber = 0; int nzRead = 0; // # of Z values read from file NXStream *scStream; char *scBuffer, *mbuf, *fptr; int scLen, dummy; float zmaxtmp = -1.0e30, zmintmp = 1.0e30; float xstep = 0.0, ystep = 0.0; BOOL bipolar = YES; if((fpin = fopen(fileName, "r")) == NULL) { NXRunAlertPanel("File Error", "Cannot open file %s","OK",NULL,NULL, fileName); return nil; } fptr = rindex(fileName, '/'); fptr++; if(fptr) strncpy(datafile, fptr, 254); // for later use in EPS save. [[contourView window] setTitleAsFilename: fileName]; // Invalidate data until new file loaded successfully. [contourView setEnabled:NO]; // disable plot until OK [contourView setComment:"% None" :4]; nx = ny = -1; zrange_in_file = 0; scStream = NXOpenMemory(NULL, 0, NX_WRITEONLY); // create memory stream NXPrintf(scStream, "%% Contour Plot by: %s\n", VERSION); NXPrintf(scStream, "%% From Grid File: %s\n", fileName); // Read line by line until we see ZDATA keyword, upon which // we switch to reading nx*ny float numbers regardless of line breaks. // while(!Done && !BadData) { if(fgets(linebuf, 250, fpin) == NULL) break; linebuf[250] = '\0'; /* for safety */ wdbuf[0] = '\0'; sscanf(linebuf, "%s", wdbuf); /* first word */ if(wdbuf[0] == '%' || wdbuf[0] == '#' || wdbuf[0] == ';' || strlen(wdbuf) == 0) { if(wdbuf[0] == '%') NXPrintf(scStream, "%s", linebuf); continue; /* skip comment or blank lines */ } prmnumber = -1; for(i=0; i<NKEYWORD; i++) { if( strcasecmp(wdbuf, keyword[i]) == 0 ) { prmnumber = i; break; } } /* --- lookup in *parname[] done ----- */ if( prmnumber == -1 ) { fprintf(stderr, "...Unknown parameter: %s\n", wdbuf); } else { /* valid prmnumber .. param name found */ switch(prmnumber) { case 0: /* NUMPOINTS */ sscanf(linebuf, "%*s %d %d", &nx, &ny); if(nx >= 5 && ny >= 5) { if(zdata) free(zdata); zdata = (float *)malloc((nx*ny)*sizeof(float)); } else { Done = BadData = 1; NXRunAlertPanel("File Error", "Bad number of points: (%d %d)","OK",NULL,NULL, nx, ny); } break; case 1: /* XRANGE */ sscanf(linebuf, "%*s %f %f", &xmin, &xmax); break; case 2: /* YRANGE */ sscanf(linebuf, "%*s %f %f", &ymin, &ymax); break; case 3: /* ZDATA */ Done = 1; break; case 4: /* GRIDSTEP -- Optional */ sscanf(linebuf, "%*s %f %f", &xgstep, &ygstep); break; case 5: /* NINTERPOL -- Optional */ sscanf(linebuf, "%*s %d %d", &nxInterp, &nyInterp); break; case 6: /* ZRANGE -- Optional */ sscanf(linebuf, "%*s %f %f", &min_zval, &max_zval); zrange_in_file = 1; if(min_zval < 0.0 && max_zval > 0.0) bipolar = YES; else bipolar = NO; break; } /* end switch(prmnumber) */ } /* end if( prmnumber == ..){} else {} */ } /* end while( ... ) */ if(BadData) { fclose(fpin); return nil; } // Now, start reading zdata[] values one by one nzRead = 0; n2read = nx*ny; for(i=0; i<n2read; i++) { if(fscanf(fpin, "%f", &zdata[i]) == EOF) { BadData = 1; NXRunAlertPanel("File Error", "Premature end of file (%d read, %d expected)", "OK",NULL,NULL, nzRead, n2read); break; } nzRead++; if(zmaxtmp < zdata[i]) zmaxtmp = zdata[i]; if(zmintmp > zdata[i]) zmintmp = zdata[i]; } /* end of for(i=0...) */ fclose(fpin); if(BadData) return nil; // Data successfully read // Copy comments in the data file to ContourView's comment to // keep track of data source. NXPrintf(scStream, "%% ---\n"); NXFlush(scStream); NXGetMemoryBuffer(scStream, &scBuffer, &scLen, &dummy); // This may not be needed, but I don't know if scBuffer above is null terminated. // If it is, malloc() stuff is not needed. mbuf = (char *)malloc(scLen+1); strncpy(mbuf, scBuffer, scLen); mbuf[scLen] = '\0'; [contourView setComment: mbuf : scLen]; [horizCSView setComment: mbuf : scLen]; [vertCSView setComment: mbuf : scLen]; [[commentText window] setTitle: "Comments from Data File"]; [commentText setText: mbuf]; free(mbuf); NXCloseMemory(scStream, NX_FREEBUFFER); // Set info fields from file [nxField setIntValue: nx]; [nyField setIntValue: ny]; [xminField setFloatValue: xmin]; [xmaxField setFloatValue: xmax]; [yminField setFloatValue: ymin]; [ymaxField setFloatValue: ymax]; // These may be optionally specified in file. [xgStepField setFloatValue: xgstep]; [ygStepField setFloatValue: ygstep]; [nxInterpField setIntValue: nxInterp]; [nyInterpField setIntValue: nyInterp]; // #### Check logic here #### [self setMinMaxZdata:zmintmp :zmaxtmp]; if(!manual_scaling) [self loadPlotRangeFromData:self]; if(zmintmp < 0.0 && zmaxtmp > 0.0) bipolar = YES; else bipolar = NO; if(zrange_in_file && !manual_scaling) [self setMinMaxZplot:min_zval :max_zval]; else [self getMinMaxZplot]; [contourView setBipolar: bipolar]; // Set real data portion here. [contourView setCartesianGridData: zdata : xmin : xmax : ymin : ymax ofSize: nx : ny withInterpolationTo: nxInterp : nyInterp]; [contourView setGridLineWidth:0.2 spacing: xgstep : ygstep]; // if grid is used // Horizontal Cross Section display initialization [horizCSView freeTraceCache]; [horizCSView allocTraceCache: 1 : nxInterp]; [horizCSView setScaleX: xmin : xmax]; [horizCSView setScaleY: min_zval - (max_zval - min_zval)*0.1 : max_zval + (max_zval - min_zval)*0.1]; xstep = (xmax - xmin)/(nxInterp-1); if(xt) free(xt); if(xcurve) free(xcurve); xt = (float *) malloc(sizeof(float)*nxInterp); xcurve = (float *) malloc(sizeof(float)*nxInterp); for(i=0; i<nxInterp; i++) { xt[i] = xmin + xstep * i; xcurve[i] = 0.0; } [horizCSView setXvalues: xt]; [horizCSView setDataPoints: NULL : NULL : 0]; [horizCSView cacheDataPointsAndBackGround]; [horizCSView setNewTrace: xcurve]; [vertCSView freeTraceCache]; [vertCSView allocTraceCache: 1 : nyInterp]; [vertCSView setScaleX: ymin : ymax]; [vertCSView setScaleY: min_zval - (max_zval - min_zval)*0.1 : max_zval + (max_zval - min_zval)*0.1]; ystep = (ymax - ymin)/(nyInterp-1); if(yt) free(yt); if(ycurve) free(ycurve); yt = (float *) malloc(sizeof(float)*nyInterp); ycurve = (float *) malloc(sizeof(float)*nyInterp); for(i=0; i<nyInterp; i++) { yt[i] = ymin + ystep * i; ycurve[i] = 0.0; } [vertCSView setXvalues: yt]; [vertCSView setDataPoints: NULL : NULL : 0]; [vertCSView cacheDataPointsAndBackGround]; [vertCSView setNewTrace: ycurve]; [self redisplayView]; return self; } // For saving ContourView to an EPS file. // This way, you don't have to go through a 2-step process of using BBFig or Yap // to save as EPS. // - saveEPSRequest:sender { char pathbuf[1024]; char msgbuf[256]; char filename[256]; char *cptr, *sptr; id savePanel; sptr = (char *)NXGetDefaultValue(APPNAME, "SaveDir"); if(sptr[0] == '~') strcpy(pathbuf, NXHomeDirectory()); else strcpy(pathbuf, sptr); strcpy(filename, datafile); strcat(filename, ".eps"); /* make it an EPS file */ savePanel = [SavePanel new]; if ([savePanel runModalForDirectory:pathbuf file:filename] && (saveFile = [savePanel filename])) { // Assume filename is OK. if([contourView saveEPStoFile: saveFile] == -1) { // Couldn't save to EPS file sprintf(msgbuf, "Cannot open %s for saving EPS file.", saveFile); NXRunAlertPanel("Save Error",msgbuf,"OK",NULL,NULL); } else { // Save was successful. strcpy(pathbuf, saveFile); cptr = rindex(pathbuf, '/'); cptr = '\0'; NXWriteDefault(APPNAME, "SaveDir", pathbuf); } } return self; } - redisplayView { if(manual_scaling || zrange_in_file) { [self getMinMaxZplot]; [contourView setMinMaxOfContourLevels:min_zval :max_zval]; } [contourView setEnabled: YES]; [contourView updateImageCache]; [contourView display]; [horizCSView setScaleY: min_zval - (max_zval - min_zval)*0.1 : max_zval + (max_zval - min_zval)*0.1]; [vertCSView setScaleY: min_zval - (max_zval - min_zval)*0.1 : max_zval + (max_zval - min_zval)*0.1]; [horizCSView drawIt:self]; [vertCSView drawIt:self]; return self; } /* from button to set plot range */ - loadPlotRangeFromData:sender { [self getMinMaxZdata]; [self setMinMaxZplot:min_zval :max_zval]; [self redisplayView]; return self; } - getMinMaxZplot { max_zval = [maxZplotField floatValue]; min_zval = [minZplotField floatValue]; return self; } - getMinMaxZdata { max_zval = [maxZdataField floatValue]; min_zval = [minZdataField floatValue]; return self; } - setMinMaxZplot:(float)min :(float)max { [maxZplotField setFloatValue: max]; [minZplotField setFloatValue: min]; return self; } - setMinMaxZdata:(float)min :(float)max { [maxZdataField setFloatValue: max]; [minZdataField setFloatValue: min]; return self; } /* from Z-scaling Min Max fields */ - setZscaleMinMax:sender { zrange_in_file = 0; // once manually set, ignore file's [self getMinMaxZplot]; [self redisplayView]; return self; } - setAutoManualScaling:sender { switch([scaleSelectRadio selectedRow]) { case 0: /* Auto selected */ manual_scaling = 0; [contourView setCartesianGridData: zdata : xmin : xmax : ymin : ymax ofSize: nx : ny withInterpolationTo: nxInterp : nyInterp]; break; case 1: /* manual selected */ manual_scaling = 1; [self getMinMaxZplot]; break; default: break; } [self redisplayView]; return self; } - setNcontourLevels:sender { static int new, old=16; // new = [sender intValue]/2 *2; // force to even number new = [sender intValue]; // force to even number if(new == old) return self; old = new; [nlevelField setIntValue:new]; [contourView setNumberOfContourLevels:new]; [contourView updateImageCache]; [contourView display]; return self; } - setLineWidth:sender { float newwidth; newwidth = [sender floatValue]; if(newwidth < 0.0) newwidth = 0.0; if(newwidth > 20.0) newwidth = 20.0; [contourView setContourLineWidthMinor:newwidth andMajor: newwidth]; [contourView updateImageCache]; [contourView display]; return self; } - setGridSpacing:sender { float gx, gy; gx = [xgStepField floatValue]; xgstep = gx; gy = [ygStepField floatValue]; ygstep = gy; [contourView setGridLineWidth:0.2 spacing: xgstep : ygstep]; // if grid is used if([tickSwitch state] || [gridSwitch state]) { [contourView updateImageCache]; [contourView display]; } return self; } - setNInterpolation:sender { int ix, iy; int i; float xstep, ystep; ix = [nxInterpField intValue]; if(ix < 5) [nxInterpField setIntValue: nxInterp]; else nxInterp = ix; iy = [nyInterpField intValue]; if(iy < 5) [nyInterpField setIntValue: nyInterp]; else nyInterp = iy; [contourView setCartesianGridData: zdata : xmin : xmax : ymin : ymax ofSize: nx : ny withInterpolationTo: nxInterp : nyInterp]; [contourView updateImageCache]; [contourView display]; [horizCSView freeTraceCache]; [horizCSView allocTraceCache: 1 : nxInterp]; // realloc trace cache xstep = (xmax - xmin)/(nxInterp-1); if(xt) free(xt); if(xcurve) free(xcurve); xt = (float *) malloc(sizeof(float)*nxInterp); xcurve = (float *) malloc(sizeof(float)*nxInterp); for(i=0; i<nxInterp; i++) { xt[i] = xmin + xstep * i; } [horizCSView setXvalues: xt]; [horizCSView cacheDataPointsAndBackGround]; [contourView fakeMakerMovedFor:0]; // have fake delegate message [vertCSView freeTraceCache]; [vertCSView allocTraceCache: 1 : nyInterp]; // realloc trace cache ystep = (ymax - ymin)/(nyInterp-1); if(yt) free(yt); if(ycurve) free(ycurve); yt = (float *) malloc(sizeof(float)*nyInterp); ycurve = (float *) malloc(sizeof(float)*nyInterp); for(i=0; i<nyInterp; i++) { yt[i] = ymin + ystep * i; } [vertCSView setXvalues: yt]; [vertCSView cacheDataPointsAndBackGround]; [contourView fakeMakerMovedFor:1]; return self; } - clearAction:sender { [contourView clear:self]; return self; } - drawAction:sender { [contourView updateImageCache]; [contourView display]; return self; } - colorWellsAction:sender { NXColor cB, cD, cBG, cLine; cB =[brightCWell color]; cD =[darkCWell color]; cBG = [bgCWell color]; cLine = [lineCWell color]; [contourView setFillColors:cB :cD]; [contourView setBackgroundColor:cBG]; [contourView setContourLineColor:cLine]; [contourView updateImageCache]; [contourView display]; return self; } - useColorAction:sender { int state; state = [sender state]; [contourView setFillEnable:(BOOL)state]; [contourView updateImageCache]; [contourView display]; return self; } - setGridMesh:sender { if([sender state]) [contourView setGridEnable:YES]; else [contourView setGridEnable:NO]; [contourView updateImageCache]; [contourView display]; return self; } - setTickMark:sender { if([sender state]) [contourView setTicksEnable:YES]; else [contourView setTicksEnable:NO]; [contourView updateImageCache]; [contourView display]; return self; } - setAxis:sender { if([sender state]) [contourView setAxisEnable:YES]; else [contourView setAxisEnable:NO]; [contourView updateImageCache]; [contourView display]; return self; } - flipYAction:sender { int state; state = [sender state]; [contourView setInvertY:(BOOL)state]; [contourView updateImageCache]; [contourView display]; return self; } - setMinNumberContour:sender { [contourView setMinNumberOfPointsPerContour:[sender intValue]]; [contourView updateImageCache]; [contourView display]; return self; } - setDash:sender /* set Dash pattern of contour */ { float cdash[2]; cdash[0] = [dashMarkField floatValue]; cdash[1] = [dashSpaceField floatValue]; [contourView setContourDash: cdash : 2]; [contourView updateImageCache]; [contourView display]; return self; } - setStrokeContour:sender { int state; state = [sender state]; [contourView setContourStrokeEnable:(BOOL)state]; [contourView updateImageCache]; [contourView display]; return self; } // This fixes the problem of messed up text when you resize the // document window. - windowDidResize:sender { [commentText calcLine]; [commentText sizeToFit]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.