This is NXYController.m in view mode; [Download] [Up]
/* Generated by Interface Builder */
#import "NXYController.h"
#import "NXYView.h" /* only for initializeLegendBox */
/* will get warning message from compiler if preceding line is commented out */
#import <appkit/OpenPanel.h>
#import <appkit/Matrix.h>
#import <appkit/Cell.h>
#import <math.h>
#define ALLOCSIZE 2048 /* used in malloc in readData */
@implementation NXYController
- (NXCoord *)xdata { return x;}
- (NXCoord **)ydata { return y;}
- (int)nPoints { return npoints;}
- (int)nCurves { return ncurves;}
- (const char *)provideXtitle {return [xTitle stringValueAt:0];}
- (const char *)provideYtitle {return [yTitle stringValueAt:0];}
- (const char *)provideMaintitle {return [mainTitle stringValueAt:0];}
- (const char *)provideCurveTitle:(int)aCurve
{
return [legendForm stringValueAt:aCurve];
}
- (const char *)provideLegendTitle
{
return [legendTitle stringValueAt:0];
}
- (BOOL) shouldDrawLegend
{
if ( [legendOnOff state] ) return YES;
else return NO;
}
- (BOOL) shouldDrawLegendBox
{
if ( [legendBoxOnOff state] ) return YES;
else return NO;
}
- (BOOL) shouldDrawGrid
{
if ( [gridOnOff state] ) return YES;
else return NO;
}
- (BOOL) shouldDrawBox
{
if ( [borderBoxOnOff state] ) return YES;
else return NO;
}
- (BOOL) shouldMoveLegend
{
if ( [legendMove state] ) return YES;
else return NO;
}
- (BOOL) doZoom
{
if ( [zoomOnOff state] ) return YES;
else return NO;
}
- (BOOL) xaxisLog
{
if ( [xLinLog state] ) return YES;
else return NO;
}
- forceXaxisLinear
{
[xLinLog setState:0];
[xLinLog display];
return self;
}
- (BOOL) yaxisLog
{
if ( [yLinLog state] ) return YES;
else return NO;
}
- forceYaxisLinear
{
[yLinLog setState:0];
[yLinLog display];
return self;
}
- (int)providelinestyle:(int)aCurve
{
int row;
int cellstate;
for (row=0; row<N_LINE_STYLES; row++) {
cellstate = [ [lineMatrix cellAt:row :aCurve] state];
if (cellstate == 1) return row;
}
return 0; /* for safety */
}
- (int)providesymbolstyle:(int)aCurve
{
int row;
int cellstate;
for (row=0; row<N_SYMBOL_STYLES; row++) {
cellstate = [ [symbolMatrix cellAt:row :aCurve] state];
if (cellstate == 1) return row;
}
return 0; /* for safety */
}
- (int)providesymbolsize{ return [symbolSize selectedCol];}
- (int)providelinethickness{ return [lineThickness selectedCol];}
- (float)provideXmin {return [xMin floatValueAt:0];}
- (float)provideXmax {return [xMax floatValueAt:0];}
- (float)provideXinc {return [xInc floatValueAt:0];}
- (float)provideYmin {return [yMin floatValueAt:0];}
- (float)provideYmax {return [yMax floatValueAt:0];}
- (float)provideYinc {return [yInc floatValueAt:0];}
- resetXmin:(float)aNum { [xMin setFloatValue:aNum at:0]; return self; }
- resetXmax:(float)aNum { [xMax setFloatValue:aNum at:0]; return self; }
- resetXinc:(float)aNum { [xInc setFloatValue:aNum at:0]; return self; }
- resetYmin:(float)aNum { [yMin setFloatValue:aNum at:0]; return self; }
- resetYmax:(float)aNum { [yMax setFloatValue:aNum at:0]; return self; }
- resetYinc:(float)aNum { [yInc setFloatValue:aNum at:0]; return self; }
- resetMinMax:sender
{
[xMin setFloatValue:datamin.x at:0];
[xMax setFloatValue:datamax.x at:0];
[xInc setFloatValue:(datamax.x - datamin.x)/5.0 at:0];
[yMin setFloatValue:datamin.y at:0];
[yMax setFloatValue:datamax.y at:0];
[yInc setFloatValue:(datamax.y - datamin.y)/5.0 at:0];
return self;
}
- drawPlotButton:(int)state
{
if (state==0) {
[plotButton highlight:NO]; /* will display normal title */
}
if (state==1) {
[plotButton highlight:YES]; /* will display alternate title */
}
return self;
}
- drawPlot:sender
{
if (!x) return self;
[self drawPlotButton:1]; /* display "Plotting" */
[canvas display];
[self drawPlotButton:0]; /* display "Plot" */
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) readData:(FILE *)aDataStream
{
BOOL inword = NO;
char c;
int j, size = ALLOCSIZE;
int oldncurves = ncurves; /* for linestyle matrix column deleting */
/* set plot button title "Reading" */
[plotButton setAltTitle:"Reading"];
[plotButton highlight:YES];
NXPing(); /* force plotButton redraw */
/* 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) );
}
}
}
/* Adjust the lineMatrix, symbolMatrix, and legendForm */
[self adjustPanels:oldncurves :ncurves];
/* reset plot button */
[plotButton setAltTitle:"Plotting"];
[plotButton highlight:NO];
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 */
}
else {
[xLinLog setState:1]; /* logarithmic axis */
}
}
else {
[xLinLog setState:0]; /* 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 */
}
else {
[yLinLog setState:1]; /* logarithmic axis */
}
}
else {
[yLinLog setState:0]; /* linear */
}
[xLinLog display];
[yLinLog display];
return self;
}
- adjustPanels:(int)oldn :(int)newn
{
/*
* Resize the linestyle matrix, the symbolstyle matrix, and
* the legend form.
* Also arrange to select and highlight only the top button in
* each column of the linestyle and symbolstyle matrices.
* This code could stand to be cleaned up.
*/
char formtitle[80];
int j, k;
if (oldn >= newn) { /* have to delete some columns and revise the remaining */
for (j=oldn-1; j>=newn; j--) {
[lineText removeColAt:j andFree:YES]; /* Titles on Columns */
[symbolText removeColAt:j andFree:YES]; /* Titles on Columns */
[lineMatrix removeColAt:j andFree:YES];
[symbolMatrix removeColAt:j andFree:YES];
[legendForm removeEntryAt:j];
}
for (j=0; j<newn; j++) {
if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [lineMatrix cellAt:0 :j] setState:1];
if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [symbolMatrix cellAt:0 :j] setState:1];
for (k=1; k<N_LINE_STYLES; k++) {
[ [lineMatrix cellAt:k :j] setState:0];
}
for (k=1; k<N_SYMBOL_STYLES; k++) {
[ [symbolMatrix cellAt:k :j] setState:0];
}
sprintf(formtitle, "Curve %d", j+1);
[legendForm setStringValue:formtitle at:j];
[legendForm drawCellAt:j];
sprintf(formtitle, "%d", j+1);
[[lineText cellAt:0 :j] setStringValue:formtitle];
[[symbolText cellAt:0 :j] setStringValue:formtitle];
}
[legendTitle setStringValue:"Legend" at:0];
[lineText sizeToCells]; /* Titles on Columns */
[symbolText sizeToCells]; /* Titles on Columns */
[lineMatrix sizeToCells];
[symbolMatrix sizeToCells];
[legendForm sizeToFit];
[ [lineText window] display]; /* Titles on Columns*/
[ [symbolText window] display]; /* Titles on Columns*/
[ [lineMatrix window] display]; /* redraw the whole window so extra */
[ [symbolMatrix window] display]; /* columns get erased */
[ [legendForm window] display];
}
if ( (oldn == 0) && (newn > 1) ) { /* can only happen first time */
for (j=1; j<newn; j++) {
[lineText addCol];
[symbolText addCol];
[lineMatrix addCol];
if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [lineMatrix cellAt:0 :j] setState:1];
/* highlight 1st row new column -- this occurs automatically */
[symbolMatrix addCol];
if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [symbolMatrix cellAt:0 :j] setState:1];
/* highlighting of 1st row new column occurs automatically */
sprintf(formtitle, "Curve %d:", j+1);
[legendForm addEntry:formtitle];
sprintf(formtitle, "Curve %d", j+1);
[legendForm setStringValue:formtitle at:j];
[legendForm drawCellAt:j];
sprintf(formtitle, "%d", j+1);
[[lineText cellAt:0 :j] setStringValue:formtitle];
[[symbolText cellAt:0 :j] setStringValue:formtitle];
}
[lineText sizeToCells]; /* Titles on Columns */
[symbolText sizeToCells]; /* Titles on Columns */
[lineMatrix sizeToCells];
[symbolMatrix sizeToCells];
[legendForm sizeToFit];
[lineText display];
[symbolText display];
[lineMatrix display]; /* don't need to redraw the window here */
[symbolMatrix display];
[legendForm display];
}
if ( (oldn != 0) && (oldn < newn) ) { /* must add columns */
for (j=0; j<oldn; j++) {
if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [lineMatrix cellAt:0 :j] setState:1];
if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
[ [symbolMatrix cellAt:0 :j] setState:1];
for (k=1; k<N_LINE_STYLES; k++) {
[ [lineMatrix cellAt:k :j] setState:0];
}
for (k=1; k<N_SYMBOL_STYLES; k++) {
[ [symbolMatrix cellAt:k :j] setState:0];
}
sprintf(formtitle, "Curve %d", j+1);
[legendForm setStringValue:formtitle at:j];
[legendForm drawCellAt:j];
sprintf(formtitle, "%d", j+1);
[[lineText cellAt:0 :j] setStringValue:formtitle];
[[symbolText cellAt:0 :j] setStringValue:formtitle];
}
for (j=oldn; j<newn; j++) {
[lineText addCol];
[symbolText addCol];
[lineMatrix addCol];
[ [lineMatrix cellAt:0 :j] setState:1];
/* highlighting of 1st row new column occurs automatically */
[symbolMatrix addCol];
[ [symbolMatrix cellAt:0 :j] setState:1];
/* highlighting of 1st row new column occurs automatically */
sprintf(formtitle, "Curve %d:", j+1);
[legendForm addEntry:formtitle];
sprintf(formtitle, "Curve %d", j+1);
[legendForm setStringValue:formtitle at:j];
[legendForm drawCellAt:j];
sprintf(formtitle, "%d", j+1);
[[lineText cellAt:0 :j] setStringValue:formtitle];
[[symbolText cellAt:0 :j] setStringValue:formtitle];
}
[legendTitle setStringValue:"Legend" at:0];
[lineText sizeToCells]; /* Titles on Columns */
[symbolText sizeToCells]; /* Titles on Columns */
[lineMatrix sizeToCells];
[symbolMatrix sizeToCells];
[legendForm sizeToFit];
[lineText display]; /* Titles on Columns */
[symbolText display]; /* Titles on Columns */
[lineMatrix display]; /* don't need to redraw the window here */
[symbolMatrix display];
[legendForm display];
}
[lineThickness setState:1 at:0:0]; /* reset line thickness to thin */
[symbolSize setState:1 at :0:2]; /* reset symbol size to medium */
[symbolSize display];
[lineThickness 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;
}
// 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 readData:dataStream] == 0) {
NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
NULL, NULL, dataFile);
fclose(dataStream);
return self;
}
fclose(dataStream);
[canvas initializeLegendBox];
[self findMinAndMax];
/* Check for linear or log on x and y axes */
[self checkLinLog];
[self drawPlot:self];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.