This is D3View.m in view mode; [Download] [Up]
/* 3DView.m Copyright 1992 Steve Ludtke */
/* This view allows the display of molecules with real time rotation */
/* The quick mode is handled in drawPS:: */
#import "Mtypes.h"
#import "D3View.h"
#import "MolObj.h"
#import "MolShape.h"
#import "SelectView.h"
#import <appkit/appkit.h>
#import <dpsclient/event.h>
#import <dpsclient/psops.h>
#import <stdio.h>
#import <math.h>
#include <libc.h>
#include <sys/file.h>
void PSsethel(); /* makes Helvetica current font */
int comp(struct DSORT *a,struct DSORT *b);
#define LRAD 10.0
extern id NXApp;
@implementation D3View
-initFrame:(NXRect *)myrect
{
char s[120];
RtPoint lFromP = {5.0,10.0,5.0},lToP = {0,0,0};
int i;
id aShader;
fromP[0]=fromP[1]=0; fromP[2]=60.0;
toP[0]=toP[1]=toP[2]=0.0;
bpath=(char *)[[NXBundle mainBundle] directory];
[super initFrame:(NXRect *)myrect];
/* set up light sources, etc... */
[self setBackgroundColor:NX_COLORWHITE];
[self setDrawBackgroundColor:YES];
fromP[2]=60.0;
[self setEyeAt:fromP toward:toP roll:0.0];
aShader=[[N3DShader alloc] init];
[(N3DShader *)aShader setShader:"matte"];
shape=[[MolShape alloc] init];
[(N3DShape *) shape setShader:aShader];
[shape scale:-1.0 :1.0 :1.0];
[shape setBSSR:2.0 :0.1];
[[self setWorldShape:shape] free];
ambLight=[[N3DLight alloc] init];
[ambLight makeAmbientWithIntensity:0.4];
[self addLight:ambLight];
ltheta=.785;
lchi=-.785;
aLight=[[N3DLight alloc] init];
lFromP[0]= LRAD*sin(lchi)*cos(ltheta);
lFromP[2]= LRAD*cos(lchi)*cos(ltheta);
lFromP[1]= LRAD*sin(ltheta);
[aLight makeDistantFrom:lFromP to:lToP intensity:1.0];
[self addLight:aLight];
Mmode=0;
Rmode=4; /* start in "quick" mode */
useColor=0;
/* start spinning, all angles in radians */
chi=phi=0;
theta=0;
/*dchi=0.025;*/
dchi=dtheta=0.0;
initflag=1;
Rflags=0;
/* printer setup */
[[[NXApp printInfo] setVertCentered:YES] setOrientation:NX_PORTRAIT andAdjust:YES];
[[NXApp printInfo] setMarginLeft:0.0 right:0.0 top:0.0 bottom:0.0];
[[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
[[NXApp printInfo] setVertPagination:NX_FITPAGINATION];
busy=0;
/* images for spinning icon */
for (i=0; i<8; i++) {
sprintf(s,"%s/icon%d.tiff",bpath,i);
AIMGS[i]=[[NXImage alloc] initFromFile:s];
}
timer=(void *)
DPSAddTimedEntry(TIMESTEP,(void *)itstime,self,NX_RUNMODALTHRESHOLD);
minshape=0;
return self;
}
- appDidInit: sender
{
AICV=[[NXApp appIcon] contentView];
AIMG=[NXImage findImageNamed: "NXAppTile"];
Ior1.x=Ior1.y=0.0;
Ior2.x=Ior2.y=8.0;
animicon=0;
[self setSurfaceTypeForAll:N3D_WireFrame chooseHider:YES];
[self zoom:self];
initflag=0;
return self;
}
-setData:(Molecule *)Mol :(int)Nmol :(struct ELINFO *)Elinfo
{
mol=Mol;
nmol=Nmol;
elinfo=Elinfo;
[self zoom:self];
return self;
}
/* fix the scaling after being resized */
-superviewSizeChanged:(const NXSize *)oldsize
{
[super superviewSizeChanged:oldsize];
[[winSize cellAt:0 :0] setFloatValue:frame.size.width];
[[winSize cellAt:0 :1] setFloatValue:frame.size.height];
[self zoom:self];
return self;
}
-free
{
DPSRemoveTimedEntry(timer);
[super free];
return self;
}
-(BOOL)canPrintRIB
{
if (Rmode==4) return NO;
return YES;
}
-createQuick:(int)n
{
FILE *out;
char s[180];
out=fopen("/tmp/el.rib","w");
fprintf(out, "Display \"%s/Library/MolViewer/el%02d.tiff\" \"file\" \"rgba\"\n", (char *)getenv("HOME"),n);
fprintf(out,"##RenderMan RIB-Structure 1.0\n##System NeXT Release 3\nFrameBegin 1\n#\nFormat 100 100 1.000000\n\nScreenWindow -1 1 -1 1\n");
fprintf(out,"Projection \"perspective\" \"fov\" [ 40 ]\nTransform [ -1 0 0 0\n 0 1 0 0 0 0 -1 0 0 0 3.269231 1 ]\nWorldBegin\nTransformBegin\nConcatTransform [ 1 0 0 0\n 0 1 0 0 0 0 1 0 0 0 0 1 ]\n");
fprintf(out,"LightSource \"Light0x15178c\" \"distantlight\" \"intensity\" [ .95 ] \"lightcolor\" [ 1 1 1 ] \"from\" [ -4.999999 7.068252 5.003982 ] \"to\" [ 0 0 0 ]\n");
fprintf(out,"TransformEnd\nTransformBegin\nConcatTransform [ 1 0 0 0\n 0 1 0 0 0 0 1 0 0 0 0 1 ]\nLightSource \"Light0x14dbdc\" \"ambientlight\" \"intensity\" [ .3 ] \"lightcolor\" [ 1 1 1 ]\n");
fprintf(out,"TransformEnd\nShadingRate 1\nAttributeBegin\nColor [ 1 1 1 ]\nDeclare \"Ka\" \"float\"\nDeclare \"Kd\" \"float\"\n#\nSurface \"matte\" \"Ka\" 1 \"Kd\" 1\nShadingInterpolation \"smooth\"\n");
fprintf(out,"GeometricRepresentation \"primitive\"\nTransformBegin\nConcatTransform [ -1 0 0 0\n 0 1 0 0 0 0 1 0 0 0 0 1 ]\n");
fprintf(out,"Surface \"plastic\"\nColor [ %f %f %f ]\nSphere %f %f %f 360\n",elinfo[n].color[0],elinfo[n].color[1],elinfo[n].color[2],elinfo[n].arad,-elinfo[n].arad,elinfo[n].arad);
fprintf(out,"TransformEnd\nAttributeEnd\nWorldEnd\nFrameEnd\n");
fclose(out);
sprintf(s,"Rendering %c%c. Please be patient.", elinfo[n].name[0],elinfo[n].name[1]);
[controller error:s :NO];
system("/usr/prman/prman /tmp/el.rib");
unlink("/tmp/el.rib");
return self;
}
/* This is called after QRM has finished rendering. In quick mode */
/* nothing is rendered. It's all done here */
-drawPS:(NXRect *)rects :(int)rectCount
{
char s[80];
float mx[9];
int i,j,k,t;
float tx,ty,tz,sca,xc,yc,xw,yw,mp[3];
static struct DSORT *dsort=NULL;
static int dsmax=1000000;
NXPoint point;
NXSize size;
if (mol==NULL) return self;
PSsethel();
if (Rmode==4) {
/* ok, since the QRman interface has a bug, here we'll do some quick stuff */
/* with roughly the same effect (this is also much faster for space-filling) */
if (mol[0].numa>dsmax) { free(dsort); dsort=NULL; }
/* dsort structure holds drawing information. For space filling mode it */
/* is actually sorted. In stick mode it just holds the transformed */
/* molecule coordinates */
if (dsort==NULL) {
dsort=malloc(sizeof(struct DSORT)*(mol[0].numa+1));
dsmax=mol[0].numa;
}
PSsetlinewidth(0.0);
PSsetgray(1.0);
NXRectFill(&bounds);
PSsetgray(NX_BLACK);
/* a transform that almost matches the QRM transformation */
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
mx[0]=cos(chi)*cos(phi)-cos(theta)*sin(phi)*sin(chi);
mx[1]=cos(chi)*sin(phi)+cos(theta)*cos(phi)*sin(chi);
mx[2]=sin(chi)*sin(theta);
mx[3]= -sin(chi)*cos(phi)-cos(theta)*sin(phi)*cos(chi);
mx[4]= -sin(chi)*sin(phi)+cos(theta)*cos(phi)*cos(chi);
mx[5]=cos(chi)*sin(theta);
mx[6]=sin(theta)*sin(phi);
mx[7]= -sin(theta)*cos(phi);
mx[8]=cos(theta);
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
/* scaling info */
xw=bounds.size.width;
yw=bounds.size.height;
xc=bounds.size.width/2.0;
yc=bounds.size.height/2.0;
sca=bounds.size.height*1.8;
/* transform the coordinates -> DSORT struct */
for (i=0; i<mol[0].numa; i++) {
tx=mol[0].coord[i][0];
ty= -mol[0].coord[i][2];
tz=mol[0].coord[i][1];
dsort[i].c[1]=tx*mx[3]+ty*mx[4]+tz*mx[5]+fromP[2];
if (dsort[i].c[1]<=0.0) continue;
dsort[i].c[0]=(tx*mx[0]+ty*mx[1]+tz*mx[2]+fromP[0])*sca/dsort[i].c[1]+xc;
dsort[i].c[2]=(tx*mx[6]+ty*mx[7]+tz*mx[8]-fromP[1])*sca/dsort[i].c[1]+yc;
dsort[i].anum=mol[0].atom[i].anum;
dsort[i].atom=i;
dsort[i].sel=mol[0].atom[i].sel;
dsort[i].lab=mol[0].atom[i].lab;
if (Rflags==0&&(dsort[i].c[0]<0 ||dsort[i].c[0]>xw||dsort[i].c[2]<0|| dsort[i].c[2]>yw)) dsort[i].c[0]=-1.0;
}
/* stick mode. Use user paths for speed */
if (Rflags==0) {
pathc=0;
for (i=0; i<mol[0].numlb; i++) {
j=mol[0].lb[i].n1;
k=mol[0].lb[i].n2;
if (dsort[j].c[0]==-1.0||dsort[k].c[0]==-1.0) continue;
path[pathc*2]=dsort[j].c[0];
path[pathc*2+1]=dsort[j].c[2];
path[pathc*2+2]=dsort[k].c[0];
path[pathc*2+3]=dsort[k].c[2];
ops[pathc]=dps_moveto;
ops[pathc+1]=dps_lineto;
pathc+=2;
if (pathc>=1400) {
DPSDoUserPath(path, pathc * 2, dps_float, ops, pathc, &bounds,
dps_ustroke);
pathc=0;
}
}
if (pathc!=0) DPSDoUserPath(path, pathc * 2, dps_float, ops, pathc,
&bounds, dps_ustroke);
sca*=1.05;
sca/=fromP[2];
/* circle selected atoms */
for (i=0; i<mol[0].numa; i++) {
if (dsort[i].lab) {
PSsetgray(0.0);
PSmoveto(dsort[i].c[0]+2.0,dsort[i].c[2]-3.0);
sprintf(s,"%c%c%1d",elinfo[dsort[i].anum].name[0], elinfo[dsort[i].anum].name[1],dsort[i].atom);
PSshow(s);
PSstroke();
}
if (dsort[i].sel) {
PSsetgray(0.0);
PSmoveto(dsort[i].c[0]+sca/3.0,dsort[i].c[2]);
PSarc(dsort[i].c[0],dsort[i].c[2],sca/3.0,0.0,360.0);
PSstroke();
}
}
}
/* ball & stick is stick mode with colors & double bonds */
else if (Rflags==1) {
for (i=0; i<mol[0].numlb; i++) {
j=mol[0].lb[i].n1;
k=mol[0].lb[i].n2;
t=mol[0].lb[i].type;
if (dsort[j].c[0]==-1.0||dsort[k].c[0]==-1.0) continue;
mp[0]=(dsort[j].c[0]+dsort[k].c[0])/2.0;
mp[2]=(dsort[j].c[2]+dsort[k].c[2])/2.0;
if (t=='D') PSsetlinewidth(4.0);
else PSsetlinewidth(1.0);
PSsetrgbcolor(elinfo[dsort[j].anum].color[0], elinfo[dsort[j].anum].color[1],elinfo[dsort[j].anum].color[2]);
PSmoveto(dsort[j].c[0],dsort[j].c[2]);
PSlineto(mp[0],mp[2]);
PSstroke();
PSsetrgbcolor(elinfo[dsort[k].anum].color[0], elinfo[dsort[k].anum].color[1],elinfo[dsort[k].anum].color[2]);
PSmoveto(dsort[k].c[0],dsort[k].c[2]);
PSlineto(mp[0],mp[2]);
PSstroke();
}
sca*=1.05;
sca/=fromP[2];
/* circle selected atoms */
for (i=0; i<mol[0].numa; i++) {
if (dsort[i].lab) {
PSsetgray(0.0);
PSmoveto(dsort[i].c[0]+2.0,dsort[i].c[2]);
sprintf(s,"%c%c%1d",elinfo[dsort[i].anum].name[0], elinfo[dsort[i].anum].name[1],dsort[i].atom);
PSshow(s);
PSstroke();
}
if (dsort[i].sel) {
PSsetgray(0.0);
PSmoveto(dsort[i].c[0]+sca/3.0,dsort[i].c[2]);
PSarc(dsort[i].c[0],dsort[i].c[2],sca/3.0,0.0,360.0);
PSstroke();
}
}
}
/* Space filling mode */
else {
sca*=1.05;
sca*=(elinfo[0].rad/elinfo[0].arad);
sca/=fromP[2];
size.height=size.width=floor(sca)*2.0;
/* scale the pre-rendered images. I tried using RIB images */
/* here, but it gave all sorts of errors. NeXT said that it */
/* should work. Maybe next release ... */
for (i=0; i<16; i++) {
if (elinfo[i].image==nil) continue;
[elinfo[i].image setSize:&size];
}
/* Sort for painters algorithm */
qsort(dsort,mol[0].numa,sizeof(struct DSORT),(void *)comp);
/* composite images */
for (i=0; i<mol[0].numa; i++) {
if (dsort[i].c[1]<0.0) continue;
j=dsort[i].anum;
point.x=dsort[i].c[0]-sca;
point.y=dsort[i].c[2]-sca;
[elinfo[j].image composite:NX_SOVER toPoint:&point];
/* label atoms with label flag */
if (dsort[i].lab) {
PSsetgray(0.5);
PSmoveto(dsort[i].c[0]+2.0,dsort[i].c[2]);
sprintf(s,"%c%c%1d",elinfo[dsort[i].anum].name[0], elinfo[dsort[i].anum].name[1],dsort[i].atom+1);
PSshow(s);
PSstroke();
}
/* put a dot on selected atoms (or circle if H) */
if (dsort[i].sel) {
PSmoveto(dsort[i].c[0]+sca/3.0,dsort[i].c[2]);
PSarc(dsort[i].c[0],dsort[i].c[2],sca/3.0,0.0,360.0);
if (j==0) {
PSsetgray(0.0);
PSstroke();
} else {
PSsetgray(1.0);
PSfill();
}
}
}
}
}
if (bounds.size.width<150) { busy=0; return self; }
/* display alt and az in upper right corner */
PSsetgray(0.0);
sprintf(s,"Alt:%6.2f Az:%6.2f",theta*57.295787,chi*57.295787);
PSmoveto(bounds.size.width-100.0,bounds.size.height-15.0);
PSshow(s);
sprintf(s,"Psi:%6.2f",phi/DRC);
PSmoveto(bounds.size.width-60.0,bounds.size.height-28.0);
PSshow(s);
PSstroke();
busy=0;
return self;
}
/* recalculates and redisplays data */
-zoom:sender
{
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[shape setAng:theta :chi :phi];
[self display];
return self;
}
/* obvious */
-setAng:(float)az :(float)alt :(float)Phi
{
theta=alt;
chi=az;
phi=Phi;
return self;
}
-setMmode:sender
{
Mmode=[sender selectedCol];
return self;
}
-zeroRot:sender
{
dtheta=dchi=theta=chi=phi=0.0;
[[mangS cellAt:0 :0] setFloatValue:theta/DRC];
[[mangS cellAt:1 :0] setFloatValue:chi/DRC];
[[mangS cellAt:2 :0] setFloatValue:phi/DRC];
[[mangT cellAt:0 :0] setFloatValue:theta/DRC];
[[mangT cellAt:1 :0] setFloatValue:chi/DRC];
[[mangT cellAt:2 :0] setFloatValue:phi/DRC];
[self display];
return self;
}
-zeroXlate:sender
{
fromP[0]=fromP[1]=0.0;
toP[0]=toP[1]=0.0;
[self setEyeAt:fromP toward:toP roll:0.0];
[self display];
return self;
}
-deSel:sender
{
[selView deselectAll];
[selView notifyO];
return self;
}
-selectAt:(float)x :(float)y :(int) flag
{
float mx[9];
int i,s= -1;
float tx,ty,tz,sca,xc,yc,xw,yw,x2,y2,z2,d=1000.0,d2;
/* a transform that almost matches the QRM transformation */
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
mx[0]=cos(chi)*cos(phi)-cos(theta)*sin(phi)*sin(chi);
mx[1]=cos(chi)*sin(phi)+cos(theta)*cos(phi)*sin(chi);
mx[2]=sin(chi)*sin(theta);
mx[3]= -sin(chi)*cos(phi)-cos(theta)*sin(phi)*cos(chi);
mx[4]= -sin(chi)*sin(phi)+cos(theta)*cos(phi)*cos(chi);
mx[5]=cos(chi)*sin(theta);
mx[6]=sin(theta)*sin(phi);
mx[7]= -sin(theta)*cos(phi);
mx[8]=cos(theta);
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
/* scaling info */
xw=bounds.size.width;
yw=bounds.size.height;
xc=bounds.size.width/2.0;
yc=bounds.size.height/2.0;
sca=bounds.size.height*1.8;
/* transform the coordinates */
for (i=0; i<mol[0].numa; i++) {
tx=mol[0].coord[i][0];
ty= -mol[0].coord[i][2];
tz=mol[0].coord[i][1];
y2=tx*mx[3]+ty*mx[4]+tz*mx[5]+fromP[2];
if (y2<=0.0) continue;
x2=(tx*mx[0]+ty*mx[1]+tz*mx[2]+fromP[0])*sca/y2+xc;
z2=(tx*mx[6]+ty*mx[7]+tz*mx[8]-fromP[1])*sca/y2+yc;
d2=sqrt(SQR(x2-x)+SQR(z2-y));
if (d2<10.0 && d2<d) { s=i; d=d2; }
/* printf("%d %f %f %f %f %f\n",i,x,x2,y,y2,d2);*/
}
if (s==-1) return self;
if (!flag) [selView deselectAll];
[selView select:s];
return self;
}
/* allows spinning and zooming with the mouse */
-mouseDown:(NXEvent *)oevent
{
int oldMask,loop=1;
float ix=0,iy=0,ix1=0,iy1=0,ix2=0,iy2=0,ix3=0,iy3=0;
NXEvent *event,evs;
long tm,tm2;
evs=*oevent;
oevent=&evs;
[self convertPoint:&oevent->location fromView:nil];
ix2=ix=oevent->location.x;
iy2=iy=oevent->location.y;
tm2=tm=oevent->time;
oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
while (loop) {
event = [NXApp getNextEvent:(NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK)];
[self convertPoint:&event->location fromView:nil];
/* [shape setDrawAsBox:YES];*/
switch (event->type) {
case NX_LMOUSEUP:
loop = 0;
if (Mmode==0) {
if (event->time-tm2==0) tm2=event->time-1;
dchi=(event->location.x-ix2)/(float)(event->time-tm2)/10.0;
dtheta=-(event->location.y-iy2)/(float)(event->time-tm2)/10.0;
if (fabs(dchi)<.004) dchi=0.0;
[shape setAng:theta :chi :phi];
[self display];
[[mangS cellAt:0 :0] setFloatValue:theta/DRC];
[[mangS cellAt:1 :0] setFloatValue:chi/DRC];
[[mangT cellAt:0 :0] setFloatValue:theta/DRC];
[[mangT cellAt:1 :0] setFloatValue:chi/DRC];
}
else if (Mmode==1) {
}
else if (Mmode==2) {
if (event->flags&NX_SHIFTMASK)
[self selectAt:event->location.x :event->location.y :1];
else [self selectAt:event->location.x :event->location.y :0];
}
break;
case NX_LMOUSEDRAGGED:
if (Mmode==0) {
theta-=(event->location.y-iy)/30.0;
chi+=(event->location.x-ix)/30.0;
if (theta>M_PI) theta-=2.0*M_PI;
if (theta<-M_PI) theta+=2.0*M_PI;
while (chi>2.0*M_PI) chi-=2.0*M_PI;
while (chi<0.0) chi+=2.0*M_PI;
ix2=ix;
iy2=iy;
tm2=tm;
ix=event->location.x;
iy=event->location.y;
tm=event->time;
[shape setAng:theta :chi :phi];
[self display];
}
else if (Mmode==1) {
toP[0]=fromP[0]+=(event->location.x-ix)/25.0;
toP[1]=fromP[1]-=(event->location.y-iy)/25.0;
ix=event->location.x;
iy=event->location.y;
[self setEyeAt:fromP toward:toP roll:0.0];
[self display];
}
break;
default:
break;
}
}
[window setEventMask:oldMask];
return self;
}
/* function called by timer */
void itstime(DPSTimedEntry entry,double now,id call)
{
[call step];
return;
}
/* do one time step */
-step
{
if (initflag) {
/* first time, do initialization stuff */
}
/* This animates the icon */
if (animicon>=0 && (dchi!=0 || dtheta!=0)) {
[AICV lockFocus];
[AIMG composite:NX_SOVER toPoint:&Ior1];
[AIMGS[animicon] composite:NX_SOVER toPoint:&Ior2];
[AICV unlockFocus];
[AICV display];
animicon++;
if (animicon>7) animicon=0;
}
if (minshape) {
[controller minStep:self];
if (dchi==0.0) {
[self display];
return self;
}
}
if (dchi==0.0&&dtheta==0.0) return self;
chi+=dchi;
theta+=dtheta;
if (theta>M_PI) theta-=2.0*M_PI;
if (theta<-M_PI) theta+=2.0*M_PI;
while (chi>2.0*M_PI) chi-=2.0*M_PI;
while (chi<0.0) chi+=2.0*M_PI;
if (busy) {
return self;
}
[shape setAng:theta :chi :phi];
[[mangS cellAt:0 :0] setFloatValue:theta/DRC];
[[mangS cellAt:1 :0] setFloatValue:chi/DRC];
[[mangS cellAt:2 :0] setFloatValue:phi/DRC];
[[mangS cellAt:3 :0] setFloatValue:fromP[2]];
[[mangT cellAt:0 :0] setFloatValue:theta/DRC];
[[mangT cellAt:1 :0] setFloatValue:chi/DRC];
[[mangT cellAt:2 :0] setFloatValue:phi/DRC];
[[mangT cellAt:3 :0] setFloatValue:fromP[2]];
[self display];
return self;
}
-(int)acceptsFirstMouse { return (YES); }
-setcontroller:con
{
controller=con;
return self;
}
/* allow user to pause spinning (not used)*/
-togFreeze:sender
{
static float Tdchi;
if ([sender intValue]) {
Tdchi=dchi;
dchi=0.0;
return self;
}
dchi=Tdchi;
return self;
}
/* just set a few things before MolShape takes over */
- renderSelf:(RtToken)context
{
RtFloat par[2]= {8.0,8.0};
/*RiErrorIgnore();*/
RiShadingRate(2.0);
RiGeometricApproximation(RI_TESSELATION,RI_PARAMETRIC,par,RI_NULL);
busy=1;
return self;
}
/* dump the current screen image as a RIB file */
- dumpRib:sender
{
static id savePanel=nil;
NXStream *ts;
char buf[MAXPATHLEN+1];
int omode;
if (!savePanel) {
savePanel=[SavePanel new];
[savePanel setRequiredFileType:"rib"];
}
if([savePanel runModal]){
ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
strcpy(buf, [savePanel filename]);
strrchr(buf,'.')[0]='\0';
NXPrintf(ts, "Display \"%s.tiff\" \"file\" \"rgb\"\n", buf);
omode=Rmode;
Rmode=3;
/* the Rmode+8 causes a white rectangle to appear behind the molecule */
[shape setData:mol :nmol :elinfo :Rmode+8 :Rflags];
[self copyRIBCode:ts];
Rmode=omode;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
NXSaveToFile(ts, [savePanel filename]);
NXCloseMemory(ts,NX_FREEBUFFER);
}
return self;
}
- setAmbLight:sender
{
[ambLight setIntensity:[sender floatValue]];
[self display];
return self;
}
- setLight:sender
{
[aLight setIntensity:[sender floatValue]];
[self display];
return self;
}
-setLightX:sender
{
RtPoint from;
lchi= -[sender floatValue];
from[0]= LRAD*sin(lchi)*cos(ltheta);
from[2]= LRAD*cos(lchi)*cos(ltheta);
from[1]= LRAD*sin(ltheta);
[aLight setFrom:from];
[self display];
return self;
}
-setLightY:sender
{
RtPoint from;
ltheta=[sender floatValue];
from[0]= LRAD*sin(lchi)*cos(ltheta);
from[2]= LRAD*cos(lchi)*cos(ltheta);
from[1]= LRAD*sin(ltheta);
[aLight setFrom:from];
[self display];
return self;
}
/* rendering mode - ie. point,line,surface,smooth,quick ... */
- setMode:sender
{
int i;
Rmode=i=[sender selectedRow];
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
switch(i) {
case 0:
[self setSurfaceTypeForAll:N3D_PointCloud chooseHider:YES];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
case 1:
[self setSurfaceTypeForAll:N3D_WireFrame chooseHider:YES];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
case 2:
[self setSurfaceTypeForAll:N3D_FacetedSolids chooseHider:YES];
break;
case 3:
[self setSurfaceTypeForAll:N3D_SmoothSolids chooseHider:YES];
break;
case 4:
[self setSurfaceTypeForAll:N3D_WireFrame chooseHider:NO];
[self setHider:N3D_NoRendering];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
}
[self display];
return self;
}
-printPS:sender
{
id image;
int fd;
NXStream *out;
[shape setData:mol :nmol :elinfo :Rmode+8 :Rflags];
if (Rflags==2&&Rmode==4) {
[self lockFocus];
image=[[NXBitmapImageRep alloc] initData:NULL fromRect:&bounds];
[self unlockFocus];
out=NXOpenFile(fd=open("/tmp/MVprint.tiff",O_WRONLY|O_CREAT,0777),NX_WRITEONLY);
[image writeTIFF:out];
NXClose(out);
close(fd);
[image free];
system("open /tmp/MVprint.tiff");
}
else [super printPSCode:sender];
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
return self;
}
/* mode, ie. stick, ball&stick or spacefill */
-setFlags:sender
{
int i;
Rflags=0;
if ([[flagSel findCellWithTag:1] intValue]) Rflags+=1;
if ([[flagSel findCellWithTag:2] intValue]) Rflags+=2;
if (Rflags==2&&Rmode==4) {
for (i=0; i<40; i++) if (elinfo[i].image!=nil) break;
if (i==40) [controller error:"Warning: you must render some atoms with 'make quick' on the preferences panel and restart before this mode will work" :NO];
}
if ([chgCol intValue]) Rflags+=4;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self display];
return self;
}
/* dist form observer to object */
-setEDist:sender
{
fromP[2]= -[sender floatValue];
[self setEyeAt:fromP toward:toP roll:0.0];
[self display];
return self;
}
/* generate a sequence of RIB's as an animation */
- ribRot:sender
{
static id savePanel=nil;
NXStream *ts;
float ophi,step,start;
char buf[MAXPATHLEN+1],s[20];
int i,omode,j;
if (savePanel==nil) savePanel=[SavePanel new];
[savePanel setRequiredFileType:"rib"];
j=[[animSpin cellAt:1 :0] intValue];
step=2.0*M_PI/(float)j;
start=[[animSpin cellAt:0 :0] floatValue];
ophi=phi;
omode=Rmode;
if([savePanel runModal]){
/* if you want to change the number of frames in the animation, change */
/* the next 2 lines */
for (i=0; i<j; i++) {
phi=(float)i*step+start;
[shape setAng:theta :chi :phi];
strcpy(buf, [savePanel filename]);
strrchr(buf,'.')[0]='\0';
ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
NXPrintf(ts, "Display \"%s.%d.tiff\" \"file\" \"rgb\"\n", buf,i);
Rmode=3;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self copyRIBCode:ts];
Rmode=omode;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self display];
sprintf(s,".%d.rib",i);
strcat(buf,s);
NXSaveToFile(ts, buf);
NXCloseMemory(ts,NX_FREEBUFFER);
}
}
phi=ophi;
[shape setAng:theta :chi :phi];
[self display];
return self;
}
/* generate a sequence of RIB's as an animation */
- ribConform:sender
{
static id savePanel=nil;
NXStream *ts;
float ophi;
char buf[MAXPATHLEN+1],s[20];
int i,omode;
float x,y;
if (savePanel==nil) savePanel=[SavePanel new];
[savePanel setRequiredFileType:"rib"];
ophi=phi;
omode=Rmode;
if([savePanel runModal]){
/* if you want to change the number of frames in the animation, change */
/* the next 2 lines */
for (i=1; i<=60; i+=1) {
x=cos((float)i*.1047+.785)*180.0*(float)(65-i)/65.0;
y=sin((float)i*.1047+.785)*180.0*(float)(65-i)/65.0;
[controller setPhi:x];
[controller setPsi:y];
strcpy(buf, [savePanel filename]);
strrchr(buf,'.')[0]='\0';
ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
NXPrintf(ts, "Display \"%s.%d.tiff\" \"file\" \"rgb\"\n", buf,i);
Rmode=3;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self copyRIBCode:ts];
Rmode=omode;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self display];
sprintf(s,".%d.rib",i);
strcat(buf,s);
NXSaveToFile(ts, buf);
NXCloseMemory(ts,NX_FREEBUFFER);
}
}
phi=ophi;
[shape setAng:theta :chi :phi];
[self display];
return self;
}
/* generate a sequence of RIB's as an animation */
- ribConfTo:sender
{
static id savePanel=nil;
NXStream *ts;
float ophi;
char buf[MAXPATHLEN+1],s[20];
int i,steps,omode;
float x,y,x2,y2;
if (savePanel==nil) savePanel=[SavePanel new];
[savePanel setRequiredFileType:"rib"];
ophi=phi;
omode=Rmode;
if([savePanel runModal]){
/* if you want to change the number of frames in the animation, change */
/* the next 2 lines */
x=mol[0].am[1].phi/DRC;
y=mol[0].am[1].psi/DRC;
x2=[[animConf cellAt:0 :0] floatValue];
y2=[[animConf cellAt:1 :0] floatValue];
steps=[[animConf cellAt:2 :0] intValue];
for (i=1; i<=steps; i++) {
[controller setPhi:(x2-x)*(float)i/(float)steps+x];
[controller setPsi:(y2-y)*(float)i/(float)steps+y];
phi=(float)i*.15708;
[shape setAng:theta :chi :phi];
strcpy(buf, [savePanel filename]);
strrchr(buf,'.')[0]='\0';
ts=NXOpenMemory(NULL, 0, NX_WRITEONLY);
NXPrintf(ts, "Display \"%s.%d.tiff\" \"file\" \"rgb\"\n", buf,i);
Rmode=3;
[shape setData:mol :nmol :elinfo :Rmode :2];
[self copyRIBCode:ts];
Rmode=omode;
[shape setData:mol :nmol :elinfo :Rmode :Rflags];
[self display];
sprintf(s,".%d.rib",i);
strcat(buf,s);
NXSaveToFile(ts, buf);
NXCloseMemory(ts,NX_FREEBUFFER);
}
}
phi=ophi;
[shape setAng:theta :chi :phi];
[self display];
return self;
}
/* This used to dump a DKB raytracer format file. Now it dumps a POV */
/* (based on DKB) file. You can get POV from wuarchive.wustl.edu */
/* in /graphics/graphics/ray. POV is certainly slower than renderman, */
/* but it is much more flexible. */
-dkbDump:sender
{
static id savePanel=nil;
FILE *out;
char s[80];
int i,n;
float x,y,z,mx[9];
if (savePanel==nil) savePanel=[SavePanel new];
[savePanel setRequiredFileType:"pov"];
if([savePanel runModal]){
sprintf(s,"cp %s/header.dat %s",bpath,[savePanel filename]);
system(s);
out=fopen([savePanel filename],"a");
fprintf(out,"camera {\n location <0 0 %f>\n",fromP[2]);
fprintf(out," up <0 1 0>\n right <1.33333 0 0>\n");
fprintf(out," look_at <0 0 0>\n sky <0 1 0>\n}\n");
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
mx[0]=cos(chi)*cos(phi)-cos(theta)*sin(phi)*sin(chi);
mx[1]=cos(chi)*sin(phi)+cos(theta)*cos(phi)*sin(chi);
mx[2]=sin(chi)*sin(theta);
mx[3]= -sin(chi)*cos(phi)-cos(theta)*sin(phi)*cos(chi);
mx[4]= -sin(chi)*sin(phi)+cos(theta)*cos(phi)*cos(chi);
mx[5]=cos(chi)*sin(theta);
mx[6]=sin(theta)*sin(phi);
mx[7]= -sin(theta)*cos(phi);
mx[8]=cos(theta);
chi*=-1.0;
phi*=-1.0;
theta*=-1.0;
for (i=0; i<mol[0].numa; i++) {
x=mol[0].coord[i][0];
y=mol[0].coord[i][1];
z=mol[0].coord[i][2];
n=mol[0].atom[i].anum;
fprintf(out,"object {\n sphere { <%f %f %f> %f }\n",
-(x*mx[0]+y*mx[1]+z*mx[2]),x*mx[3]+y*mx[4]+z*mx[5],
x*mx[6]+y*mx[7]+z*mx[8],elinfo[n].rad);
fprintf(out," texture {\n\tcolor red %5.3f green %5.3f blue %5.3f\n\tphong 1.0\n }\n}\n\n",elinfo[n].color[0], elinfo[n].color[1],elinfo[n].color[2]);
}
fclose(out);
}
return self;
}
-togMin:sender
{
if ([sender intValue]) minshape=1;
else minshape=0;
return self;
}
/* save the current scene as EPS */
- dumpEPS:sender
{
static id savePanel=nil;
NXStream *out;
if (!savePanel) {
savePanel=[SavePanel new];
[savePanel setRequiredFileType:"eps"];
}
if([savePanel runModal]){
out=NXOpenMemory(NULL,0, NX_WRITEONLY);
[self copyPSCodeInside:&bounds to:out];
NXSaveToFile(out,[savePanel filename]);
NXCloseMemory(out,NX_FREEBUFFER);
}
return self;
}
-setColor:sender
{
int i;
useColor=[sender intValue];
i=Rmode;
[self setSurfaceTypeForAll:N3D_PointCloud chooseHider:YES];
switch(i) {
case 0:
[self setSurfaceTypeForAll:N3D_PointCloud chooseHider:YES];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
case 1:
[self setSurfaceTypeForAll:N3D_WireFrame chooseHider:YES];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
case 2:
[self setSurfaceTypeForAll:N3D_FacetedSolids chooseHider:YES];
break;
case 3:
[self setSurfaceTypeForAll:N3D_SmoothSolids chooseHider:YES];
break;
case 4:
[self setSurfaceTypeForAll:N3D_WireFrame chooseHider:NO];
[self setHider:N3D_NoRendering];
if (!useColor) [window setDepthLimit:NX_TwoBitGrayDepth];
break;
}
[self display];
return self;
}
-setAngT:sender
{
int i;
dchi=dtheta=0;
i=[sender selectedRow];
switch (i)
{
case 0:
theta=[[mangT cellAt:0 :0] floatValue]*DRC;
[[mangS cellAt:0 :0] setFloatValue:theta/DRC];
break;
case 1:
chi=[[mangT cellAt:1 :0] floatValue]*DRC;
[[mangS cellAt:1 :0] setFloatValue:chi/DRC];
break;
case 2:
phi=[[mangT cellAt:2 :0] floatValue]*DRC;
[[mangS cellAt:2 :0] setFloatValue:phi/DRC];
break;
case 3:
fromP[2]=[[mangT cellAt:3 :0] floatValue];
[[mangS cellAt:3 :0] setFloatValue:fromP[2]];
[self setEyeAt:fromP toward:toP roll:0.0];
break;
}
[shape setAng:theta :chi :phi];
[self display];
return self;
}
-setAngS:sender
{
int i;
dchi=dtheta=0;
i=[sender selectedRow];
switch (i)
{
case 0:
theta=[[mangS cellAt:0 :0] floatValue]*DRC;
[[mangT cellAt:0 :0] setFloatValue:theta/DRC];
break;
case 1:
chi=[[mangS cellAt:1 :0] floatValue]*DRC;
[[mangT cellAt:1 :0] setFloatValue:chi/DRC];
break;
case 2:
phi=[[mangS cellAt:2 :0] floatValue]*DRC;
[[mangT cellAt:2 :0] setFloatValue:phi/DRC];
break;
case 3:
fromP[2]=[[mangS cellAt:3 :0] floatValue];
[[mangT cellAt:3 :0] setFloatValue:fromP[2]];
[self setEyeAt:fromP toward:toP roll:0.0];
break;
}
[shape setAng:theta :chi :phi];
[self display];
return self;
}
/* NeXT said that this should clean up most of the QRM problems. */
/* As far as I can tell it doesn't have any effect */
- flushRIB
{
RiSynchronize( RI_FLUSH );
RiSynchronize( RI_WAIT );
return self;
}
-newBSSR:sender
{
[shape setBSSR:[[sender cellAt:0 :0] floatValue] :[[sender cellAt:1 :0] floatValue]];
[self display];
return self;
}
int comp(struct DSORT *a,struct DSORT *b)
{
if (a->c[1]>b->c[1]) return(-1);
if (a->c[1]==b->c[1]) return(0);
else return(1);
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.