ftp.nice.ch/pub/next/graphics/3d/NXplot3d.3.2.NIHS.bs.tar.gz#/NXplot3d.3.2/Source/PControl.m

This is PControl.m in view mode; [Download] [Up]

#import "PControl.h"
#import "Plot3DView.h"
#import "PlotShape.h"
#import <appkit/appkit.h>
#import <dpsclient/event.h>
#import <dpsclient/psops.h>
#import <appkit/color.h>
#import <stdio.h>
#import <math.h>
#import <ctype.h>
#include <libc.h>
#import "Expression.h"
#import "DensView.h"

int comp(float *x,float *y);

@implementation PControl
-init
{
[super init];
return self;
}

/* Called once by the Plot3DView to do initialization and pass pref pointer */
-startup:(SetPref *)Pref
{
	pref=Pref;
/* display initial preferences settings */
	curPref=0;
	bpath=(char *)[[NXBundle mainBundle] directory];
	[symsel selectCellWithTag:pref[0].sym+1];
	equation=[equation docView];
	[equation setText:[pref[0].expr text]];
/* make sure plot agrees with displayed min/max values */
	[gridx setIntValue:pref[curPref].nx];
	[gridy setIntValue:pref[curPref].ny];
	[d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
	[d3View setDelegate:self];
/*[self readFile:self];*/

	I_lim=[I_lim contentView];
	I_dis=[I_dis contentView];
	I_pho=[I_pho contentView];
return self;
}

/* display preferences for a data set */
-disPref:sender
{
int i;
curPref=[[sender selectedCell] tag];
[symsel selectCellWithTag:pref[curPref].sym+1];
[color1 setColor:NXConvertRGBToColor(pref[curPref].mapcol[0][0], 
	pref[curPref].mapcol[0][1],pref[curPref].mapcol[0][2])];
[color2 setColor:NXConvertRGBToColor(pref[curPref].mapcol[1][0], 
	pref[curPref].mapcol[1][1],pref[curPref].mapcol[1][2])];
[color3 setColor:NXConvertRGBToColor(pref[curPref].mapcol[2][0], 
	pref[curPref].mapcol[2][1],pref[curPref].mapcol[2][2])];
[color4 setColor:NXConvertRGBToColor(pref[curPref].mapcol[3][0], 
	pref[curPref].mapcol[3][1],pref[curPref].mapcol[3][2])];
[color5 setColor:NXConvertRGBToColor(pref[curPref].mapcol[4][0], 
	pref[curPref].mapcol[4][1],pref[curPref].mapcol[4][2])];
[alpha setFloatValue:pref[curPref].alpha];

[colortype selectCellWithTag:pref[curPref].mapmode];
if (curPref!=0) [[colortype cellAt:1 :1] setEnabled:NO];
else [[colortype cellAt:1 :1] setEnabled:YES];

for (i=0; i<5; i++) 
	[[colorsel cellAt:0 :i] setIntValue:pref[curPref].mapsel[i]];

if ((pref[curPref].flag&7)==F_EQN) [ffSel selectCellWithTag:0];
else if ((pref[curPref].flag&7)==F_DATA) [ffSel selectCellWithTag:1];
else if ((pref[curPref].flag&7)==F_SDATA) [ffSel selectCellWithTag:2];
else if ((pref[curPref].flag&7)==F_DEM) [ffSel selectCellWithTag:3];

[equation setText:[pref[curPref].expr text]];
[gridx setIntValue:pref[curPref].nx];
[gridy setIntValue:pref[curPref].ny];
[d3View zoom:self];
return self;
}

/* put preferences from screen into SetPref for curPref set */
-stoPref:sender
{
char *tmp;
int i,j,k;
pref[curPref].sym=[[symsel selectedCell] tag]-1;
pref[curPref].nx=[gridx intValue];
if (pref[curPref].nx>MAXGRID||pref[curPref].nx<4) 
	[gridx setIntValue:pref[curPref].nx=10];
pref[curPref].ny=[gridy intValue];
if (pref[curPref].ny>MAXGRID||pref[curPref].ny<4) 
	[gridy setIntValue:pref[curPref].ny=10];
tmp=malloc([equation textLength]+50);
[equation getSubstring:tmp start:0 length:[equation textLength]+1];
/* Fix equation for parsing */
k=strlen(tmp);
for (i=0; i<k; i++) {
	if (tmp[i]=='[') tmp[i]='(';
	if (tmp[i]==']') tmp[i]=')';
	if (isupper(tmp[i])) tmp[i]=tolower(tmp[i]);
	if ((tmp[i]=='+'||tmp[i]=='-')&&(isdigit(tmp[i+1])||tmp[i+1]=='.')) {
		for (j=k; j>i; j--) tmp[j+1]=tmp[j];
		tmp[i+1]=' ';
		k++;
	}
}

/* if the equation doesn't parse, set it to 0 */
if (strlen(tmp)==0 ||
	 [pref[curPref].expr parse:tmp]==0) {
	[self error:"Cannot parse equation!"];
	[pref[curPref].expr parse:"0"];
	}

/* Colors */
NXConvertColorToRGB([color1 color],&pref[curPref].mapcol[0][0], 
	&pref[curPref].mapcol[0][1],&pref[curPref].mapcol[0][2]);
NXConvertColorToRGB([color2 color],&pref[curPref].mapcol[1][0], 
	&pref[curPref].mapcol[1][1],&pref[curPref].mapcol[1][2]);
NXConvertColorToRGB([color3 color],&pref[curPref].mapcol[2][0], 
	&pref[curPref].mapcol[2][1],&pref[curPref].mapcol[2][2]);
NXConvertColorToRGB([color4 color],&pref[curPref].mapcol[3][0], 
	&pref[curPref].mapcol[3][1],&pref[curPref].mapcol[3][2]);
NXConvertColorToRGB([color5 color],&pref[curPref].mapcol[4][0], 
	&pref[curPref].mapcol[4][1],&pref[curPref].mapcol[4][2]);
pref[curPref].alpha=[alpha floatValue];

for (i=0; i<5; i++) pref[curPref].mapsel[i]=[[colorsel cellAt:0 :i] intValue];
	
pref[curPref].mapmode=[[colortype selectedCell] tag];

if ([[ffSel selectedCell] tag]==0) pref[curPref].flag=F_EQN;
else if ([[ffSel selectedCell] tag]==1) pref[curPref].flag=F_DATA;
else if ([[ffSel selectedCell] tag]==2) pref[curPref].flag=F_SDATA;
else if ([[ffSel selectedCell] tag]==3) pref[curPref].flag=F_DEM;

/* if spherical plot, set proper limits */
if (pref[curPref].sym==6) {
	[minX setFloatValue:0];
	[maxX setFloatValue:M_PI];
	[minY setFloatValue:0];
	[maxY setFloatValue:M_PI*2.0];
	[self setMinMax:self];
}
[d3View zoom:self];
return self;
}

/* Read 3d data from a file and put it into the current data set */
/* does NOT wait for an "OK" press, like other prefs */
-readFile:sender
{
id panel;
int i,j,k,cc=0,nx,ny;
char s[201],sc[20];
FILE *in;
float x0,y0,z0,z1,f,x[MAXGRID+1],y[MAXGRID+1];
Point *p;

if (pref[curPref].fileData!=NULL) { 
	free(pref[curPref].fileData); 
	pref[curPref].fileData=NULL; 
}

if (sender!=self) {
	/* get a filespec from the user */
	panel=[[OpenPanel new] allowMultipleFiles:NO];
	if (![panel runModalForTypes:NULL]) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		return self;
	}
	
	/* if we can't open the file, go back to equation mode */
	in=fopen([panel filename],"r");
}
else {
	[ffSel selectCellWithTag:1];
	sprintf(s,"%s/title.3d",bpath);
	in=fopen(s,"r");
}

if (fgets(s,200,in)==NULL) {
	[ffSel selectCellWithTag:0];
	pref[curPref].flag=F_EQN;
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	[self error:"Can't open file."];
    return self;
}

/* ignore leading # lines */
while (s[0]=='#') { fgets(s,80,in); cc++; }

/* accept either "x,y,z\n" or "x y z\n" files */
strcpy(sc," %f , %f , %f");
if (sscanf(s,sc,&x[0],&y[0],&z0)!=3) {
	strcpy(sc," %f %f %f");
	if (sscanf(s,sc,&x[0],&y[0],&z0)!=3) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		[self error:"File not in 'x y z' or 'x,y,z' format."];
        return self;
	}
}
z1=z0;

/* count the number of points in the file */
nx=ny=1;
while (fscanf(in,sc,&x[nx],&y[ny],&f)==3) {
	if (f<z0) z0=f;
	for (i=0; i<nx; i++) if (x[nx]==x[i]) break;
	if (i==nx) nx++;
	for (i=0; i<ny; i++) if (y[ny]==y[i]) break;
	if (i==ny) ny++;
	if (nx==MAXGRID+1||ny==MAXGRID+1) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		[self error:"More than MAXGRID points in x or y."];
		fclose(in);
 		return self;
	}
}
qsort(x,nx,sizeof(float),comp);	
qsort(y,ny,sizeof(float),comp);	
rewind(in);

/* 2nd pass, allocate memory,initialize and read data into array */
p=pref[curPref].fileData=malloc(sizeof(Point)*nx*ny);
pref[curPref].nfdata=nx*ny;
for (i=0; i<nx; i++) {
	for (j=0; j<ny; j++) {
		p[i+nx*j].x=x[i];
		p[i+nx*j].y=y[j];
		p[i+nx*j].z=z0;
	}
}

for (i=0; i<cc; i++) fgets(s,240,in);
while (fscanf(in,sc,&x0,&y0,&f)==3) {
	for (j=0; j<nx; j++) if (x0==x[j]) break;
	for (k=0; k<ny; k++) if (y0==y[k]) break;
	if (k==ny||j==nx) [self error:"Unusual error, notify author."];
	p[j+nx*k].z=f;
}
if (pref[curPref].data!=NULL) free(pref[curPref].data);
pref[curPref].data=malloc(sizeof(Point)*pref[curPref].nfdata);
pref[curPref].color=malloc(sizeof(RtColor)*pref[curPref].nfdata);
[minX setFloatValue:x[0]];
[minY setFloatValue:y[0]];
[maxX setFloatValue:x[nx-1]];
[maxY setFloatValue:y[ny-1]];

/* equation is now used to modify data Z values, set initial formula for */
/* z(x,y,z)=z. ie - for log plots you would enter "log(z)" */
[equation setText:"z"];
[pref[curPref].expr parse:"z"];
pref[curPref].flag=F_DATA;

/* update the display */
[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* Read processed DEM data from a file and put it into the DEM data set */
/* does NOT wait for an "OK" press, like other prefs */
-readDEM:sender
{
id panel;
int nx,ny;
FILE *in;

if (pref[curPref].fileData!=NULL) { 
	free(pref[curPref].fileData); 
	pref[curPref].fileData=NULL; 
}
if (pref[curPref].demData!=NULL) { 
	free(pref[curPref].demData); 
	pref[curPref].demData=NULL; 
}

/* get a filespec from the user */
panel=[[OpenPanel new] allowMultipleFiles:NO];

/* if we can't open the file, go back to equation mode */
if (![panel runModalForTypes:NULL]) {
	[ffSel selectCellWithTag:0];
	pref[curPref].flag=F_EQN;
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	return self;
}	
in=fopen([panel filename],"r");

/* read the processed DEM header */
fread(&pref[curPref].demnx,sizeof(int),1,in);
fread(&pref[curPref].demny,sizeof(int),1,in);
fread(pref[curPref].demx,sizeof(float),2,in);
fread(pref[curPref].demy,sizeof(float),2,in);
if (fread(pref[curPref].demz,sizeof(float),2,in)!=2|| 
		pref[curPref].demnx<10||pref[curPref].demnx>5000||pref[curPref].demny<10||pref[curPref].demny>5000) {
	[ffSel selectCellWithTag:0];
	pref[curPref].flag=F_EQN;
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	[self error:"Can't open file."];
    return self;
}
pref[curPref].demx[2]=(pref[curPref].demx[1]-pref[curPref].demx[0])/(float)(pref[curPref].demnx-1);
pref[curPref].demy[2]=(pref[curPref].demy[1]-pref[curPref].demy[0])/(float)(pref[curPref].demny-1);

/* allocate a big chunk of memory */
nx=pref[curPref].demnx;
ny=pref[curPref].demny;
pref[curPref].demData=malloc(sizeof(short)*nx*ny);

/* read the data */
if (fread(pref[curPref].demData,nx*sizeof(short),ny,in)!=ny) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		[self error:"More than MAXGRID points in x or y."];
		fclose(in);
 		return self;
}
[minX setFloatValue:pref[curPref].demx[0]];
[minY setFloatValue:pref[curPref].demy[0]];
[maxX setFloatValue:pref[curPref].demx[1]];
[maxY setFloatValue:pref[curPref].demy[1]];

/* equation is now used to modify data Z values, set initial formula for */
/* z(x,y,z)=z. ie - for log plots you would enter "log(z)" */
[equation setText:"z"];
[pref[curPref].expr parse:"z"];
pref[curPref].flag=F_DEM;

/* update the display */
[self setMinMax:self];
[d3View zoom:self];
return self;
}


/* Read 3d scatter data from a file and put it into the current data set */
/* does NOT wait for an "OK" press, like other prefs */
-readSFile:sender
{
id panel;
int i,j,k,cc=0,n;
char s[201],sc[20];
FILE *in;
float x0,y0,x1,y1,z0,z1,f;
Point *p;

if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].
fileData=NULL; }

if (sender!=self) {
	/* get a filespec from the user */
	panel=[[OpenPanel new] allowMultipleFiles:NO];
	if (![panel runModalForTypes:NULL]) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		return self;
	}
	
	/* if we can't open the file, go back to equation mode */
	in=fopen([panel filename],"r");
}
else {
	[ffSel selectCellWithTag:1];
	sprintf(s,"%s/title.3d",bpath);
	in=fopen(s,"r");
}

if (fgets(s,200,in)==NULL) {
	[ffSel selectCellWithTag:0];
	pref[curPref].flag=F_EQN;
	[equation setText:"0"];
	[pref[curPref].expr parse:"0"];
	[self error:"Can't open file."];
    return self;
}

/* ignore leading # lines */
while (s[0]=='#') { fgets(s,80,in); cc++; }

/* accept either "x,y,z\n" or "x y z\n" files */
strcpy(sc," %f , %f , %f");
if (sscanf(s,sc,&x0,&y0,&z0)!=3) {
	strcpy(sc," %f %f %f");
	if (sscanf(s,sc,&x0,&y0,&z0)!=3) {
		[ffSel selectCellWithTag:0];
		pref[curPref].flag=F_EQN;
		[equation setText:"0"];
		[pref[curPref].expr parse:"0"];
		[self error:"File not in 'x y z' or 'x,y,z' format."];
        return self;
	}
	x1=x0;
	y1=y0;
}

/* count the number of points in the file */
n=1;
while (fscanf(in,sc,&f,&f,&f)==3) n++;

rewind(in);

/* 2nd pass, allocate memory,initialize and read data into array */
p=pref[curPref].fileData=malloc(sizeof(Point)*n);
pref[curPref].nfdata=n;

for (i=0; i<cc; i++) fgets(s,240,in);
n=0;
while (fscanf(in,sc,&p[n].x,&p[n].y,&p[n].z)==3) {
	if (p[n].x<x0) x0=p[n].x;
	if (p[n].x>x1) x1=p[n].x;
	if (p[n].y<y0) y0=p[n].y;
	if (p[n].y>y1) y1=p[n].y;
	n++;
}

if (pref[curPref].data!=NULL) free(pref[curPref].data);
pref[curPref].data=malloc(sizeof(Point)*pref[curPref].nfdata);
pref[curPref].color=malloc(sizeof(RtColor)*pref[curPref].nfdata);
[minX setFloatValue:x0];
[minY setFloatValue:y0];
[maxX setFloatValue:x1];
[maxY setFloatValue:y1];

/* equation is now used to modify data Z values, set initial formula for */
/* z(x,y,z)=z. ie - for log plots you would enter "log(z)" */
[equation setText:"z"];
[pref[curPref].expr parse:"z"];
pref[curPref].flag=F_SDATA;

/* update the display */
[self setMinMax:self];
[d3View zoom:self];
return self;
}


/* return a data set to equation mode, free memory used by the file */
-clearFile:sender
{
if (pref[curPref].fileData!=NULL) { free(pref[curPref].fileData); pref[curPref].fileData=NULL; }
pref[curPref].nfdata=0;

[equation setText:"0"];
[pref[curPref].expr parse:"0"];
[d3View zoom:self];
return(self);
}

/* minz and maxz can be modified before returning, but currently aren't */
-minmaxZ:(float *)minz :(float *)maxz
{
[dminZ setFloatValue:*minz];
[dmaxZ setFloatValue:*maxz];
if ([dzSwitch intValue]) {
	[minZ setFloatValue:*minz];
	[maxZ setFloatValue:*maxz];
}
*minz=[minZ floatValue];
*maxz=[maxZ floatValue];
return self;
}

/* display new min/max values (does not inform the views of the change) */
-setMM:(float)minx :(float)maxx :(float)miny :(float)maxy
{
[minX setFloatValue:minx];
[minY setFloatValue:miny];
[maxX setFloatValue:maxx];
[maxY setFloatValue:maxy];
return self;
}

/* Tell the Plot3DView and (indirectly) the DensView about new max/min vals */
-setMinMax:sender
{
int aspect;

aspect=[demAsp intValue];
if (aspect) 
	[maxY setFloatValue:[maxX floatValue]-[minX floatValue]+[minY floatValue]];
[d3View zoomTo:[minX floatValue] :[minY floatValue] :[maxX floatValue] :[maxY floatValue]];
if (aspect)
	[maxZ setFloatValue:[minZ floatValue]+([maxX floatValue]-[minX floatValue])*83000.0];
[d3View zoom:self];
return self;
}

-stoMM:sender
{
sMM[0]=[minX floatValue];
sMM[1]=[minY floatValue];
sMM[2]=[maxX floatValue];
sMM[3]=[maxY floatValue];
return self;
}

-rclMM:sender
{
[minX setFloatValue:sMM[0]];
[minY setFloatValue:sMM[1]];
[maxX setFloatValue:sMM[2]];
[maxY setFloatValue:sMM[3]];

[self setMinMax:sender];
return self;
}
/* zoom in by a fixed amount */
-zoomIn:sender
{
float x,y,x1,y1;
x1=[maxX floatValue];
x=[minX floatValue];
y1=[maxY floatValue];
y=[minY floatValue];

[maxX setFloatValue:x1-(x1-x)/4.0];
[minX setFloatValue:x+(x1-x)/4.0];
[maxY setFloatValue:y1-(y1-y)/4.0];
[minY setFloatValue:y+(y1-y)/4.0];

[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* zoom out by a fixed amount */
-zoomOut:sender
{
float x,y,x1,y1;
x1=[maxX floatValue];
x=[minX floatValue];
y1=[maxY floatValue];
y=[minY floatValue];

[maxX setFloatValue:x1+(x1-x)/4.0];
[minX setFloatValue:x-(x1-x)/4.0];
[maxY setFloatValue:y1+(y1-y)/4.0];
[minY setFloatValue:y-(y1-y)/4.0];

[self setMinMax:self];
[d3View zoom:self];
return self;
}

/* sent by DensView, min/max are from 0.0 to 1.0, not real units */
-zoomTo:(float)minx :(float)miny :(float)maxx :(float)maxy
{
float x0,x1,y0,y1;

x0=[minX floatValue];
x1=[maxX floatValue];
y0=[minY floatValue];
y1=[maxY floatValue];

[minX setFloatValue:x0+(x1-x0)*minx];
[minY setFloatValue:y0+(y1-y0)*miny];
[maxX setFloatValue:x0+(x1-x0)*maxx];
[maxY setFloatValue:y0+(y1-y0)*maxy];
[self setMinMax:self];
[d3View zoom:self];
return self;
}

-dumpContour
{
[dView saveTiff:self];
return self;
}

/* update the density plot */
-updDen:(RtPoint)TickO :(RtPoint)TickS
{
static float *densData=NULL;
static int ndd=0;
int k,i,nx,ny;
NXRect rec,rec2,tick;
float x0=0,x1=0,y0=0,y1=0,zlim[4] = { -1.0,1.0,0,0};
char s[80];
float lev0,lev1;

zlim[2]=[minZ floatValue];
zlim[3]=[maxZ floatValue];

if ([autoCont intValue]) {
	i=[[levels cellAt:2 :0] intValue];
	lev1=2.0/(float)i;
	lev0= -1.0+lev1/2.0;
	[[levels cellAt:0 :0] setFloatValue:(lev0+1.0)/2.0* 
		(zlim[3]-zlim[2])+zlim[2]];
	[[levels cellAt:1 :0] setFloatValue:lev1/2.0* 
		(zlim[3]-zlim[2])];
}
else {
	lev0=[[levels cellAt:0 :0] floatValue];
	lev1=[[levels cellAt:1 :0] floatValue];
	lev0=(lev0-zlim[2])/(zlim[3]-zlim[2])*2.0-1.0;
	lev1=lev1/(zlim[3]-zlim[2])*2.0;
}

if (lev1<=0) lev1=.2;
[[d3View worldShape] setContour:lev0/2.0+.5 :lev1/2.0];

tick.origin.x=(TickO[0]+1.0)/2.0;
tick.origin.y=(TickO[1]+1.0)/2.0;
tick.size.width=TickS[0]/2.0;
tick.size.height=TickS[1]/2.0;

rec2.origin.x=[minX floatValue];
rec2.origin.y=[minY floatValue];
rec2.size.width=[maxX floatValue]-[minX floatValue];
rec2.size.height=[maxY floatValue]-[minY floatValue];
rec=rec2;
if (pref[curPref].ndata==0||(pref[curPref].flag&3)==F_SDATA) {
	[dView setData:0 :0 :NULL :zlim :tick :rec :rec2 :lev0 :lev1 :NULL];
	return self;
}

nx=pref[curPref].nx; 
ny=pref[curPref].ny;
if (ndd<(nx*ny)) {
	if (densData!=NULL) free(densData);
	densData=malloc(nx*ny*sizeof(float));
	ndd=nx*ny;
}

if (pref[curPref].fileData==NULL||nx*ny==pref[curPref].ndata) {
	for (k=0; k<pref[curPref].ndata; k++) densData[k]=pref[curPref].data[k].z;
}
else {
	sprintf(s,"Error nx*ny=%d  ndata=%d\n",nx*ny,pref[curPref].ndata);
	[self error:s];
/*	for (k=0; k<(nx*ny); k++) densData[k]= -.5;
	for (k=0; k<pref[curPref].ndata; k++) {
		i=floor((pref[curPref].data[k].x+.5)*(float)nx)+ 
			floor((pref[curPref].data[k].y+.5)*(float)ny)*nx;
		if (i>=0 && i<ndd) densData[i]=pref[curPref].data[k].z;
	}*/
}

if (pref[curPref].fileData!=NULL) {
	for (i=0; i<pref[curPref].nfdata; i++) {
		x0=pref[curPref].fileData[i].x;
		y0=pref[curPref].fileData[i].y;
		if (x0>=rec2.origin.x && y0>=rec2.origin.y) break;
	}
	for (i=pref[curPref].nfdata-1; i>=0; i--) {
		x1=pref[curPref].fileData[i].x;
		y1=pref[curPref].fileData[i].y;
		if (x1<=rec2.origin.x+rec2.size.width && 
			y1<=rec2.origin.y+rec2.size.height) break;
	}

	rec.size.width=x1-x0;
	rec.origin.x=x0;
	rec.size.height=y1-y0;
	rec.origin.y=y0;
}

if (pref[curPref].sym!= -1) {
	if (pref[curPref].mapmode!=0) [dView setData:nx :ny :densData :zlim :tick :rec :rec2 :lev0 :lev1 :pref[curPref].color];
	else [dView setData:nx :ny :densData :zlim :tick :rec :rec2 :lev0 :lev1 :NULL];
}
else [dView setData:0 :0 :NULL :zlim :tick :rec :rec2 :lev0 :lev1 :NULL];
return self;
}

int comp(float *x,float *y)
{
if (x<y) return(-1);
if (y>x) return(1);
return (0);
}

-newInsp:sender
{
int i;

i=[[sender selectedCell] tag];
switch(i) {
case 0: [I_BOX setContentView:I_lim];
		break;
case 1:	[I_BOX setContentView:I_dis];
		break;
case 2:	[I_BOX setContentView:I_pho];
		break;
}
[I_BOX display];
return self;
}

/* variable slider changed */
-setVarS:sender
{
int i;

for (i=0; i<5; i++) 
	[[varText cellAt:i :0] setFloatValue:[[varSli cellAt:i :0] floatValue]];
[d3View zoom:self];
return self;
}

/* variable text changed */
-setVarT:sender
{
int i;
for (i=0; i<5; i++) 
	[[varSli cellAt:i :0] setFloatValue:[[varText cellAt:i :0] floatValue]];
[d3View zoom:self];
return self;
}

/* new var min/max */
-setVarMinMax:sender
{
int i;

for (i=0; i<5; i++)
	[[[varSli cellAt:i :0] setMaxValue:[[varMax cellAt:i :0] floatValue]] setMinValue:[[varMin cellAt:i :0] floatValue]];
return self;
}

-saveData:sender
{
id pan;
FILE *out;
int i;
float x0,x1,y0,y1,z0,z1;
x0=[minX floatValue];
x1=[maxX floatValue]-x0;
y0=[minY floatValue];
y1=[maxY floatValue]-y0;
z0=[minZ floatValue];
z1=[maxZ floatValue]-z0;

pan=[SavePanel new];
if ([pan runModal]) {
	out=fopen([pan filename],"w");
	fprintf(out,"# Created by Plot3D\n");
	for (i=0; i<pref[curPref].ndata; i++) 
		fprintf(out,"%f\t%f\t%f\n",(pref[curPref].data[i].x/2.0+.5)*x1+x0,
			(pref[curPref].data[i].y/2.0+.5)*y1+y0,
			(pref[curPref].data[i].z/2.0+.5)*z1+z0);
	fclose(out);
}
return self;
}

/* Start rendering an animation */
-makeAnim:sender
{
int afl=0,n=16,cf;
static id savePanel=nil;
float chi,chistp,theta,t,tstp;
char animFile[100];		/* animation directory */
char animName[30];			/* animation name */
char fsp[MAXPATHLEN];
int i,j=0;

if (!savePanel) {
    savePanel=[SavePanel new];
    [savePanel setRequiredFileType:"anim"];
}

if(![savePanel runModal]) return self;
if (strlen([savePanel filename])>78) {
	[self error:"Filespec too long. Please notify author."];
	return self;
}
strcpy(animFile, [savePanel filename]);
mkdir(animFile,0777);
for (i=strlen(animFile)-1; i>=0; i--) if (animFile[i]=='/') break;
i++;
while (i<strlen(animFile)-5) animName[j++]=animFile[i++];
animName[j]=0;
afl=[[animFlag cellAt:0 :0] intValue]*ANIM_spin+
	[[animFlag cellAt:1 :0] intValue]*ANIM_t;
if (afl==0) {
	[self error:"Animation doesn't do anything!"];
	return self;
}
n=[animFrames intValue];
chistp=M_PI*2.0/(float)n;
chi=[d3View getChi];
theta=[d3View getTheta];
t=[[varMin cellAt:0 :0] floatValue];
tstp=([[varMax cellAt:0 :0] floatValue]-t)/(float)(n-1);

for (cf=0; cf<n; cf++) {
	[[varText cellAt:0 :0] setFloatValue:t];
	[[varSli cellAt:0 :0] setFloatValue:t];
	[d3View setAng:theta :chi];
	[d3View zoom:self];
	sprintf(fsp,"%s/%s.%d.tiff",animFile,animName,cf+1);
	[self message:"Rendering animation. This will take a while ..." :fsp];
	[d3View renderTIFF:fsp];
	if (afl&ANIM_t) t+=tstp;
	if (afl&ANIM_spin) chi+=chistp;
}
[self message:"Rendering complete !!!" :"(use Movie.app or a similar program to view)"];
return self;
}

-error:(char *)msg
{
[errTitle setStringValue:"ERROR"];
[errMsg setStringValue:msg];
[errPan makeKeyAndOrderFront:self];
return self;
}

-message:(char *)s1 :(char *)s2
{
[errTitle setStringValue:s1];
[errMsg setStringValue:s2];
[errPan makeKeyAndOrderFront:self];
return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.