/* * povchem.c; July 12, 1996. */ char *VERSION = "1.00"; /* * Copyright (C) 1995-1996 by Paul Thiessen (paul@ludwig.scs.uiuc.edu) * * This program is shareware and may be used freely for non-profit work. * It is not free for commercial use, however; see the legal info in the * Manual page (below). * * By using this program you are agreeing to abide by all applicable * copyright laws. You also are agreeing that the author cannot be held * liable for any losses incurred by the use of this program. * * For more on legal rights and authorship, and for instructions on how * to use the program, see * * http://ludwig.scs.uiuc.edu/~paul/Manual.html */ /* * To do someday, maybe: * - make clever color assignment manipulator * - make dynamic PDB tag constructor * - put in bond calculator? */ /*------------ THESE ARE THE ONLY USER-CONFIGURABLE OPTIONS -------------*/ /* * The maximum number of elements in the Hydrogen bond donors and acceptors * lists. */ #define MAX_H_BOND_ELEMENTS 10 /* * Basically these just define the floating point type (F_TYPE), the number * of significant figures (roughly) to which that type is accurate, and * the names of various mathematical functions. All of these probably won't * need to be changed, but if memory is extremely tight and you're processing * really big molecules, you can set F_TYPE to 'float' instead of 'double' * and on most machines this will save some space. */ #define F_TYPE double #define SF 15 #define SQRT sqrt #define COS cos #define SIN sin #define ACOS acos #define ASIN asin #define POW pow /*--------------- DO NOT MODIFY ANYTHING BELOW THIS LINE! ----------------*/ #include #include #include #include #include /* Basic models */ enum { CPK=1, BALLnSTICK, CYLINDER }; /* Coloring schemes */ enum { BY_ELEMENT=1, BY_PDB, ALL_THE_SAME, BY_PDB_SEPARATE }; /* BY_PDB options */ enum { PDB_RES=1, PDB_CH, PDB_RES_CH_SEQ, PDB_RES_ELEM }; /* How sticks/cylinders change color */ enum { SPLIT_EQUAL=1, SPLIT_PROPORTIONAL, SPLIT_SHADED }; /* Size of balls in ball and stick model */ enum { CONSTANT=1, PROPORTIONAL }; /* Image orientation */ enum { LANDSCAPE=1, PORTRAIT }; /* Background colors */ enum { BLACK=1, BLUE, WHITE }; /* Views */ enum { BEST_GUESS=1, STANDARD, USER_DEFINED }; #define FALSE 0 #define TRUE 1 #define U_INT unsigned int /* * Configuration file options. Default variables are in all caps, next * to their respective "real" variables. */ char *PERIODIC_TABLE_FILE= NULL; char *POVRAY= NULL; /* text variable assigned later - */ U_INT POV_WIDTH= 0; /* after parsing config file to */ char *POV_OPTIONS= NULL; /* avoid string memory waste. */ U_INT RUN_POVRAY= FALSE; char *VIEWER= NULL; char *FINISH= NULL; char *FINISH_DECLARATION= NULL; U_INT DEFAULT_MODEL= CYLINDER, model; U_INT DEFAULT_COLORMODEL= BY_ELEMENT, cylinderColorModel, sphereColorModel; U_INT DEFAULT_PDB_FIELDS= PDB_RES_CH_SEQ, sphereTagType=0; F_TYPE DEFAULT_CYLINDER_RADIUS= 0.25, cylinderRadius; U_INT DEFAULT_RADIUS_TYPE= PROPORTIONAL, ballSizeModel; F_TYPE DEFAULT_PROPORTIONALITY= 0.20; F_TYPE DEFAULT_BALL_RADIUS= 0.3, ballRadius; U_INT DEFAULT_THICKNESS= CONSTANT, cylinderSizeModel; F_TYPE DEFAULT_STICK_RADIUS= 0.1; F_TYPE DOUBLE_FACTOR= 1.5; F_TYPE TRIPLE_FACTOR= 2.0; F_TYPE HIGH_ORDER_FACTOR= 2.5; U_INT DEFAULT_STICKS_LIKE_BALLS= FALSE; U_INT DEFAULT_STICK_COLORMODEL= BY_PDB; U_INT DEFAULT_STICK_PDB_FIELDS= PDB_RES, stickTagType=0; U_INT DEFAULT_BOND_SPLIT= SPLIT_PROPORTIONAL, stickSplit; F_TYPE POWER= 2.0; F_TYPE DEFAULT_BALL_RED= 0.6; F_TYPE DEFAULT_BALL_GREEN= 0.2; F_TYPE DEFAULT_BALL_BLUE= 0.9; F_TYPE DEFAULT_STICK_RED= 0.2; F_TYPE DEFAULT_STICK_GREEN= 0.8; F_TYPE DEFAULT_STICK_BLUE= 0.5; U_INT DEFAULT_FIND_H_BONDS= FALSE; U_INT DEFAULT_H_BOND_DASHES= 3, HBondDashes; F_TYPE DEFAULT_H_BOND_RADIUS= 0.1, HBondRadius; char *H_BOND_DONORS= NULL; char *H_BOND_ACCEPTORS= NULL; F_TYPE DEFAULT_MINDIST= 1.0, HBondMinDist; F_TYPE DEFAULT_MAXDIST= 3.1, HBondMaxDist; F_TYPE DEFAULT_MIN_DHA_ANGLE= 140.0, HBondMinDHA; F_TYPE DEFAULT_MIN_HAR_ANGLE= 60.0, HBondMinHAR; F_TYPE DEFAULT_H_BOND_RED= 1.0; F_TYPE DEFAULT_H_BOND_GREEN= 1.0; F_TYPE DEFAULT_H_BOND_BLUE= 0.0; F_TYPE DEFAULT_BACKGROUND_RED= 0.0; F_TYPE DEFAULT_BACKGROUND_GREEN= 0.0; F_TYPE DEFAULT_BACKGROUND_BLUE= 0.0; U_INT DEFAULT_ORIENTATION= LANDSCAPE, imageOrientation; U_INT DEFAULT_VIEW= STANDARD, viewType; U_INT DEFAULT_MAKE_STEREO_PAIR= FALSE; F_TYPE STEREO_SEPARATION_FACTOR= 0.10; F_TYPE CAMERA_X= 0.0; F_TYPE CAMERA_Y= 0.0; F_TYPE CAMERA_Z= -20.0; F_TYPE LOOK_AT_X= 0.0; F_TYPE LOOK_AT_Y= 0.0; F_TYPE LOOK_AT_Z= 0.0; F_TYPE SKY_X= 0.0; F_TYPE SKY_Y= 1.0; F_TYPE SKY_Z= 0.0; F_TYPE DIRECTION= 2.0; F_TYPE WINDOW_UP= 1.0; F_TYPE WINDOW_RIGHT= 1.333; U_INT MAX_BONDS_PER_ATOM= 5; /*----------- end configuration file options ---------------*/ /*---------- macros -----------*/ #define VDW(INDEX) ((PeriodicTable[(Atoms[INDEX].Z)]).vdW) #define NAME(INDEX) ((PeriodicTable[(Atoms[INDEX].Z)]).name) #define ZNUM(INDEX) (Atoms[INDEX].Z) #define SERNUM(INDEX) (Atoms[INDEX].serNum) #define TAG(INDEX) (Atoms[INDEX].tag) #define X(INDEX) (Atoms[INDEX].x) #define Y(INDEX) (Atoms[INDEX].y) #define Z(INDEX) (Atoms[INDEX].z) #define RED(Z) ((PeriodicTable[Z]).r) #define GREEN(Z) ((PeriodicTable[Z]).g) #define BLUE(Z) ((PeriodicTable[Z]).b) #define DEGREES(rad) ((180.0/M_PI)*rad) #define RADIANS(deg) ((M_PI/180.0)*deg) unsigned long memAlloced=0; #define STRNCPY0(buf,pos,n) strncpy(buf,pos,n); buf[n]='\0' #define BOMB fclose(pdb); fclose(pov); fclose(inc); exit(EXIT_FAILURE) /*------------------- global variables and type def's ---------------*/ typedef struct { F_TYPE x, y, z; U_INT serNum; unsigned char Z; char *tag; } Atom; Atom *Atoms; U_INT nAtoms; U_INT *BondsTo; unsigned char *BondOrders; #define BoundTo(bond,index) (BondsTo[(bond)*MAX_BONDS_PER_ATOM+(index)]) #define BondOrder(bond,index) (BondOrders[(bond)*MAX_BONDS_PER_ATOM+(index)]) U_INT HBondDonors[MAX_H_BOND_ELEMENTS], nDonors; U_INT HBondAcceptors[MAX_H_BOND_ELEMENTS], nAcceptors; enum { ZERO=0, SINGLE, DOUBLE, TRIPLE, HIGH_ORDER, H_BOND }; /* bond orders */ U_INT nOrders[6]; char *orderTypes[]={"Zero_","Single_","Double_", "Triple_","High_Order_","H_"}; typedef struct { char *name; char symbol[3]; F_TYPE vdW, r, g, b; unsigned char isPresent; } Element; #define NUM_ELEMENTS 111 Element PeriodicTable[NUM_ELEMENTS+1]; FILE *pdb, *inc, *pov; U_INT isInteractive=TRUE; F_TYPE aveX=0.0, aveY=0.0, aveZ=0.0; F_TYPE minX=0.0, maxX=0.0, minY=0.0, maxY=0.0, minZ=0.0, maxZ=0.0; typedef struct { F_TYPE x, y, z; } Vector; Vector unitRight, Light, LeftEye, RightEye; typedef struct { F_TYPE r, g, b; } Color; Color Background, allSphereColor, allCylinderColor, HBondColor; typedef struct { char *name; F_TYPE r, g, b; void *next; } tagType; tagType *firstSphereTag=NULL, *firstCylinderTag=NULL; U_INT nSphereTags=0, nCylinderTags=0; typedef struct { U_INT serial; U_INT Z; char resName[4]; char chainID; U_INT resSeq; F_TYPE x, y, z; } PDBatomInfoType; #define FIND_SERIAL 0x01 #define FIND_Z 0x02 #define FIND_RESNAME 0x04 #define FIND_CHAINID 0x08 #define FIND_RESSEQ 0x10 #define FIND_COORDS 0x20 #define FIND_ALL (FIND_SERIAL | FIND_Z | FIND_RESNAME | FIND_CHAINID | \ FIND_RESSEQ | FIND_COORDS) char **cylinderEndTags; enum { MONO=1, LEFT_EYE, RIGHT_EYE }; char stereoPairCreated=FALSE; char *incName, *povName, *leftName=NULL, *rightName=NULL; /*-------- return a U_INT rounded from an F_TYPE ----------*/ U_INT Round(F_TYPE fVal) { double iVal, frac; frac=modf((double)fVal,&iVal); if (frac>=0.5) iVal+=1.0; return ((U_INT)iVal); } /*---- returns TRUE if a string contains only ' ', '\t', and '\n' ------*/ int isBlank(char *line) { char *pos; int blank=TRUE; if (line) { for (pos=line; *pos!='\0' && blank==TRUE; pos++) { if (*pos!=' ' && *pos!='\t' && *pos!='\n') blank=FALSE; } } else blank=FALSE; return blank; } /*--- converts a string to lower case, with first letter upper case ---*/ char *str_tolower(char *line) { char *pos; if (line && *line) { *line=toupper(*line); for (pos=line+1; *pos!='\0'; pos++) *pos=tolower(*pos); } return line; } /*---- makes a new string, keeping track of memory allocation ----*/ char *New_String(char **new, const char *old) { if ( ((*new)=strdup(old))==NULL ) { puts("FATAL: out of memory during strdup."); exit(EXIT_FAILURE); } else memAlloced+=strlen(*new)+1; return *new; } /*------ Parsing functions - for periodic table and config file -------*/ /*------- parses the hydrogen bond donor/acceptor lists --------*/ U_INT Determine_Z_From_Symbol(const char *symbol) { U_INT Z; for (Z=1; Z1.0) ERROR; *((F_TYPE *)var)=fVal; } else if (var==&DEFAULT_ORIENTATION) { if (strcmp(str_tolower(value),"Portrait")==0) DEFAULT_ORIENTATION=PORTRAIT; else if (strcmp(value,"Landscape")==0) DEFAULT_ORIENTATION=LANDSCAPE; else ERROR; } else if (var==&DEFAULT_VIEW) { if (strcmp(str_tolower(value),"Standard")==0) DEFAULT_VIEW=STANDARD; else if (strcmp(value,"Bestguess")==0) DEFAULT_VIEW=BEST_GUESS; else if (strcmp(value,"Userdefined")==0) DEFAULT_VIEW=USER_DEFINED; else ERROR; } else if (var==&CAMERA_X || var==&CAMERA_Y || var==&CAMERA_Z || var==&LOOK_AT_X || var==&LOOK_AT_Y || var==&LOOK_AT_Z || var==&SKY_X || var==&SKY_Y || var==&SKY_Z) { fVal=atof(value); *((F_TYPE *)var)=fVal; } else printf("Internal error at line %u! Contact the author.\n",lineNum); } parsing_error: if (error) { printf( "Warning: parsing error in line %u of \"%s\". This line will be ignored.\n", lineNum,*fileName); } } fclose(config); printf("Read configuration file \"%s\".\n",*fileName); /* * These are the text-based variables that didn't get initialized earlier * so that space wouldn't be wasted if they're specified in the config file. */ no_config: if (!PERIODIC_TABLE_FILE) New_String(&PERIODIC_TABLE_FILE,"periodic.tab"); if (!FINISH) New_String(&FINISH,"Plastic"); if (!FINISH_DECLARATION) New_String(&FINISH_DECLARATION,"\ finish {\n\ ambient 0.2\n\ diffuse 0.7\n\ reflection 0.0\n\ brilliance 1.0\n\ phong 0.3\n\ phong_size 50\n\ specular 0.0\n\ }\n\n"); if (POVRAY && (!POV_OPTIONS || !POV_WIDTH) ) { puts("Warning: to run POV-Ray from PovChem, you must define *all* the"); puts("associated options in the configuration file!\n"); POVRAY=NULL; RUN_POVRAY=FALSE; } if (!H_BOND_DONORS) New_String(&H_BOND_DONORS,"N,O"); if (!H_BOND_ACCEPTORS) New_String(&H_BOND_ACCEPTORS,"N,O"); return; } /*------------- set up Periodic Table --------------*/ void Parse_Periodic_Table(void) { FILE *table; char line[256], tmpName[80], *tok; U_INT error=FALSE, lineNum=0; int Z; if ((table=fopen(PERIODIC_TABLE_FILE,"r"))==NULL) { printf("Can't read the periodic table from \"%s\"!\n",PERIODIC_TABLE_FILE); perror("Reason"); exit(EXIT_FAILURE); } for (Z=0; Z1.0 || RED(Z)<0.0) ERROR; tok=strtok(NULL," \t,"); if (!tok) ERROR; GREEN(Z)=atof(tok); if (GREEN(Z)>1.0 || GREEN(Z)<0.0) ERROR; tok=strtok(NULL," \t,"); if (!tok) ERROR; BLUE(Z)=atof(tok); if (BLUE(Z)>1.0 || BLUE(Z)<0.0) ERROR; } parsing_error: if (error) { printf("Warning: error parsing line %u of \"%s\".\n",lineNum, PERIODIC_TABLE_FILE); if (Z!=-1) strcpy(PeriodicTable[Z].symbol,"?"); } } fclose(table); printf("Read periodic table \"%s\".\n",PERIODIC_TABLE_FILE); /* for (Z=0;Z=78) { STRNCPY0(symbol,line+76,2); if (*symbol==' ') pos=symbol+1; else pos=symbol; } else try=1; } if (try==1) { /* if not, try to determine from atom name */ pos=strpbrk(line+12, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); if (pos<(line+16)) { STRNCPY0(symbol,pos,2); if (pos==line+15) symbol[1]='\0'; pos=symbol; } else try=2; } if (try<2) { /* try to determine Z from 2 or 1-char string */ *pos=toupper(*pos); if (isalpha(*(pos+1))) { *(pos+1)=tolower(*(pos+1)); } else *(pos+1)='\0'; for (i=1; i<=NUM_ELEMENTS; i++) { if (strncmp(pos,PeriodicTable[i].symbol,2)==0) { Z=i; try=2; break; } } if (Z==0) { for (i=1; i<=NUM_ELEMENTS; i++) { if (strncmp(pos,PeriodicTable[i].symbol,1)==0) { Z=i; try=2; break; } } } } } if (Z==0) { printf("WARNING: Can't determine element of atom serial #%5s\n",line+6); printf("Assigning vdW %.5g, color <%.5g, %.5g, %.5g>.\n", PeriodicTable[0].vdW, PeriodicTable[0].r,PeriodicTable[0].g,PeriodicTable[0].b); } return Z; } F_TYPE Determine_Radius(U_INT Z) { switch (model) { case CPK: return PeriodicTable[Z].vdW; break; case BALLnSTICK: if (ballSizeModel==CONSTANT) return ballRadius; else return ballRadius*PeriodicTable[Z].vdW; break; case CYLINDER: default: return ballRadius; } } void Assign_Colors_To_Tags(U_INT *submodel) { tagType *tag; U_INT i, nTags; F_TYPE deg; if (submodel==&sphereColorModel) { tag=firstSphereTag; nTags=nSphereTags; } else { tag=firstCylinderTag; nTags=nCylinderTags; } printf("Coloring %u different tags...\n\n",nTags); for (i=0;i=0.0 && deg<120.0) { tag->r=1.0-deg/120.0; tag->g=1.0-tag->r; } else if (deg>=120.0 && deg<240.0) { tag->g=1.0-(deg-120.0)/120.0; tag->b=1.0-tag->g; } else { tag->b=1.0-(deg-240.0)/120.0; tag->r=1.0-tag->b; } tag=tag->next; } return; } /*---------- POV material (finish and pigment) statments and declarations ----------*/ void Write_Color_and_Atom_Declarations(void) { U_INT Z, i, nTags; tagType *tag; if (nOrders[H_BOND]) { fputs("// Unfortunately, if the atoms involved in hydrogen-bonding are\n",pov); fputs("// resized, the hydrogen bonds will not be spaced properly. You\n",pov); fputs("// will have to re-run PovChem and change the sizes there.\n\n",pov); } if (model==CPK || (model==BALLnSTICK && ballSizeModel==PROPORTIONAL) ) { if (model==BALLnSTICK) fprintf(pov,"#declare Proportionality_Constant = %.*g\n",SF,ballRadius); for (Z=0;Z\n", PeriodicTable[Z].name,RED(Z),GREEN(Z),BLUE(Z)); } } fputs("\n",pov); } if (sphereColorModel==ALL_THE_SAME) fprintf(pov,"#declare All_Atom_Color = color rgb <%.5g, %.5g, %.5g>\n\n", allSphereColor.r,allSphereColor.g,allSphereColor.b); if (sphereColorModel==BY_PDB) { if (ballSizeModel==CONSTANT) { fputs("#declare Atom_All =\n sphere {\n",pov); fputs(" <0,0,0>, All_Atom_Radius\n",pov); fprintf(pov," finish { %s }\n }\n\n",FINISH); } else { for (Z=0;Z, %s_Radius\n",PeriodicTable[Z].name); fprintf(pov," finish { %s }\n }\n",FINISH); } } fputs("\n",pov); } tag=firstSphereTag; nTags=nSphereTags; for (i=0;i\n", tag->name,tag->r,tag->g,tag->b); tag=tag->next; } } else if (model==CPK || sphereColorModel==BY_ELEMENT || ballSizeModel==PROPORTIONAL) { for (Z=0;Z, All_Atom_Radius\n",pov); else fprintf(pov," <0,0,0>, %s_Radius\n",PeriodicTable[Z].name); fputs( " texture {\n",pov); switch (sphereColorModel) { case BY_ELEMENT: fprintf(pov," pigment { color Atom_%s_Color }\n", PeriodicTable[Z].name); break; case ALL_THE_SAME: fputs( " pigment { color All_Atom_Color }\n",pov); break; } fprintf(pov," finish { %s }\n }\n }\n",FINISH); } } } else if (sphereColorModel==ALL_THE_SAME) { fputs( "#declare Atom_All =\n sphere {\n",pov); fputs( " <0,0,0>, All_Atom_Radius\n",pov); fputs( " texture {\n",pov); fputs( " pigment { color All_Atom_Color }\n",pov); fprintf(pov," finish { %s }\n }\n }\n",FINISH); } fputs("\n",pov); return; } void Write_Bond_Declarations(void) { U_INT i; tagType *tag; F_TYPE factors[5]; factors[0]=0.0; factors[1]=1.0; factors[2]=DOUBLE_FACTOR; factors[3]=TRIPLE_FACTOR; factors[4]=HIGH_ORDER_FACTOR; if (nOrders[H_BOND]) { fprintf(pov,"#declare H_Bond_Radius = %.*g\n",SF,HBondRadius); fprintf(pov,"#declare H_Bond_Color = color rgb <%.5g, %.5g, %.5g>\n", HBondColor.r,HBondColor.g,HBondColor.b); fputs( "#declare H_Bond =\n cylinder {\n",pov); fputs( " <0,0,0>, <1,0,0>, H_Bond_Radius\n",pov); fputs( " texture {\n",pov); fputs( " pigment { H_Bond_Color }\n",pov); fprintf(pov," finish { %s }\n }\n }\n\n",FINISH); } if (model==CYLINDER) fputs("#declare All_Bond_Radius = All_Atom_Radius\n\n",pov); else { if (cylinderSizeModel==CONSTANT) fprintf(pov,"#declare All_Bond_Radius = %.*g\n\n",SF,cylinderRadius); else { fprintf(pov,"#declare Single_Bond_Radius = %.*g\n",SF,cylinderRadius); for (i=DOUBLE;i<=HIGH_ORDER;i++) { if (nOrders[i]) fprintf(pov,"#declare %sBond_Radius = Single_Bond_Radius*%.*g\n", orderTypes[i],SF,factors[i]); } fputs("\n",pov); } } if (cylinderColorModel==ALL_THE_SAME) fprintf(pov,"#declare All_Bond_Color = color rgb <%.5g, %.5g, %.5g>\n\n", allCylinderColor.r,allCylinderColor.g,allCylinderColor.b); fputs( "#declare Bond =\n cylinder {\n",pov); if (cylinderSizeModel==CONSTANT) fputs( " <0,0,0>, <1,0,0>, All_Bond_Radius\n",pov); else fputs( " <0,0,0>, <1,0,0>, 1\t\t// leave as 1 - will be scaled later.\n",pov); if (cylinderColorModel==ALL_THE_SAME) { fputs( " texture {\n",pov); fprintf(pov," pigment { All_Bond_Color }\n"); fprintf(pov," finish { %s }\n }\n",FINISH); } else fprintf(pov," finish { %s }\n",FINISH); fputs( " }\n\n",pov); if (cylinderColorModel==BY_PDB && firstCylinderTag!=firstSphereTag) { tag=firstCylinderTag; for (i=0;i\n", tag->name,tag->r,tag->g,tag->b); tag=tag->next; } } return; } /*--------- math to determine distance between two points, and bond angle between three Atoms -------*/ F_TYPE Distance(F_TYPE x1, F_TYPE y1, F_TYPE z1, F_TYPE x2, F_TYPE y2, F_TYPE z2) { return SQRT((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1)); } F_TYPE Angle(U_INT A, U_INT B, U_INT C) { F_TYPE BAdist, BCdist, BAdotBC; /* ... determine A-B-C angle */ /* from cos(angle)=(BA.BC)/|BA||BC| */ BAdist=Distance(X(A),Y(A),Z(A),X(B),Y(B),Z(B)); BCdist=Distance(X(C),Y(C),Z(C),X(B),Y(B),Z(B)); if (BAdist==0.0 || BCdist==0.0) return 0.0; BAdotBC=(X(A)-X(B))*(X(C)-X(B))+ (Y(A)-Y(B))*(Y(C)-Y(B))+ (Z(A)-Z(B))*(Z(C)-Z(B)); return DEGREES(ACOS(BAdotBC/(BAdist*BCdist))); } /*------------- reads a (possibly null) string from stdin ------------*/ char *Get_User_Input(char *string) { int i=0; do { scanf("%c",&string[i]); } while (string[i++]!='\n'); string[i-1]='\0'; return string; } /*------------ write the POV sphere with appropriate element & radius ----------*/ #define RAND_360 ((360.0/MAXINT)*random()) void Write_Atom(F_TYPE x, F_TYPE y, F_TYPE z, U_INT Z, char *tag) { aveX+=x; /* need these to set up camera location later... */ aveY+=y; aveZ+=z; if (xmaxX) maxX=x; if (ymaxY) maxY=y; if (zmaxZ) maxZ=z; if (sphereColorModel==BY_PDB) { if (model==CPK || (model==BALLnSTICK && ballSizeModel==PROPORTIONAL) ) fprintf(inc,"object { Atom_%s\n",PeriodicTable[Z].name); else fputs( "object { Atom_All\n",inc); fprintf(inc," pigment { color Atom_%s_Color }\n ",tag); } else fprintf(inc,"object { Atom_%s ",tag); /* fprintf(inc,"\n rotate <%.1f, %.1f, %.1f>\n ", RAND_360,RAND_360,RAND_360); */ fprintf(inc,"translate <%.*g, %.*g, %.*g> }\n",SF,x,SF,y,SF,z); if (model==CPK || sphereColorModel==BY_ELEMENT || cylinderColorModel==BY_ELEMENT || ballSizeModel==PROPORTIONAL) PeriodicTable[Z].isPresent=TRUE; return; } void Parse_PDB_Line(char *line, PDBatomInfoType *atom, U_INT searchFlags) { char temp[9]; int i; if (searchFlags & FIND_SERIAL) { STRNCPY0(temp,line+6,5); atom->serial=atoi(temp); } if (searchFlags & FIND_Z) atom->Z=Determine_Z_From_PDB(line); if (searchFlags & FIND_RESNAME) { STRNCPY0(temp,line+17,3); for (i=0;temp[i]==' ';i++) {} strcpy(atom->resName,&temp[i]); for (i=2;(atom->resName[i]==' ' && i>=0);i--) atom->resName[i]='\0'; } if (searchFlags & FIND_CHAINID) { atom->chainID=line[21]; if (atom->chainID==' ') atom->chainID='_'; } if (searchFlags & FIND_RESSEQ) { STRNCPY0(temp,line+22,4); atom->resSeq=atoi(temp); } if (searchFlags & FIND_COORDS) { STRNCPY0(temp,line+30,8); atom->x=(F_TYPE)atof(temp); STRNCPY0(temp,line+38,8); atom->y=(F_TYPE)atof(temp); STRNCPY0(temp,line+46,8); atom->z=(F_TYPE)(-atof(temp)); /* NOTE: POV Z axis is mirror of PDB's */ } return; } char *Make_Tag_From_PDB_Info(PDBatomInfoType info, U_INT *submodel) { static char tag[80], tmp[80]; char defaultFields; U_INT length, *choice; if (submodel==&sphereColorModel) { choice=&sphereTagType; defaultFields=DEFAULT_PDB_FIELDS; } else { choice=&stickTagType; defaultFields=DEFAULT_STICK_PDB_FIELDS; } if (*choice==0) { if (isInteractive) { switch (model) { case CPK: strcpy(tmp, "Atoms"); strcpy(tmp+40," "); break; case CYLINDER: strcpy(tmp, "Cylinders"); strcpy(tmp+40," "); break; case BALLnSTICK: default: if (submodel==&sphereColorModel) { strcpy(tmp, "Balls"); strcpy(tmp+40," "); } else { strcpy(tmp, "Sticks"); strcpy(tmp+40," "); } break; } puts(""); printf("Tag %s by: 1) Residue\n",tmp); printf(" %s 2) Chain\n",tmp+40); printf(" %s 3) Residue + Chain + Sequence number\n",tmp+40); printf(" %s 4) Residue + Element\n",tmp+40); printf("Choice [%u] ? ",defaultFields); *choice=(Get_User_Input(tmp)[0]=='\0' ? defaultFields : (char)atoi(tmp)); if (*choice<1 || *choice>4) *choice=defaultFields; puts(""); } else *choice=defaultFields; } switch (*choice) { case 1: strcpy(tag,info.resName); break; case 2: tag[0]=info.chainID; tag[1]='\0'; break; case 3: strcpy(tag,info.resName); strcat(tag,"_"); length=strlen(tag); tag[length]=info.chainID; tag[length+1]='_'; sprintf(&(tag[length+2]),"%u",info.resSeq); break; case 4: strcpy(tag,PeriodicTable[info.Z].name); strcat(tag,"_"); strcat(tag,info.resName); break; } return tag; } void Make_Tag_List(tagType **firstTag, U_INT *nTags) { tagType *currentTag, *prevTag; char tmp[81], currentName[80]; PDBatomInfoType atom; char isInList; for ( ; (fgets(tmp,81,pdb))!=NULL && strncmp(tmp,"END ",6)!=0 ; ) { if (strncmp(tmp,"ATOM ",6)==0 || strncmp(tmp,"HETATM",6)==0) { Parse_PDB_Line(tmp,&atom, (FIND_Z | FIND_RESNAME | FIND_CHAINID | FIND_RESSEQ)); if (nTags==&nSphereTags) strcpy(currentName,Make_Tag_From_PDB_Info(atom,&sphereColorModel)); else strcpy(currentName,Make_Tag_From_PDB_Info(atom,&cylinderColorModel)); isInList=FALSE; currentTag=*firstTag; prevTag=NULL; do { if (currentTag!=NULL) { if (strcmp(currentName,currentTag->name)==0) { isInList=TRUE; } else { prevTag=currentTag; currentTag=currentTag->next; } } if (currentTag==NULL) { if ((currentTag=(tagType *)malloc(sizeof(tagType)))==NULL) { puts("FATAL: Not enough memory for even one more tag!"); exit(EXIT_FAILURE); } else { memAlloced+=sizeof(tagType); (*nTags)++; New_String(&(currentTag->name),currentName); currentTag->next=NULL; currentTag->r=currentTag->g=currentTag->b=0.0; isInList=TRUE; if (*firstTag==NULL) { printf("\nFound tags: %s",currentTag->name); *firstTag=currentTag; } else { prevTag->next=currentTag; printf(", %s",currentTag->name); } } } } while (isInList==FALSE); } } puts("\n"); rewind(pdb); return; } /*------------- to write out a POV cylinder for the bond -----------------*/ /* Okay, I know this is a roundabout way of doing it, but it has to be this way in order to be able to put any arbitrary object in place of a bond, just in case I wanted to do that someday... :) Also, this allows split-color bonds to be made out of a single cylinder with an appropriate color map. Anyway, this assumes the bond is originally a cylinder from <0,0,0> to <1,0,0> of proper radius. Then 1) color and finish it if necessary, 2) scale it up to the right length, 3) rotate around two angles about the Y and Z axes to align the bond along the proper vector, and 4) translate the thing into place. */ void Write_Cylinder(F_TYPE x1, F_TYPE y1, F_TYPE z1, F_TYPE x2, F_TYPE y2, F_TYPE z2, char *radiusType, char *tag1, char *tag2, F_TYPE vdW1, F_TYPE vdW2) { F_TYPE length, dy, theta, phi, proportion; char type[10], tmp[80]; length=Distance(x1,y1,z1,x2,y2,z2); if (length==0.0) return; if (radiusType==orderTypes[H_BOND]) fputs( "object { H_Bond\n",inc); else { fputs( "object { Bond\n",inc); if (model==BALLnSTICK && cylinderSizeModel==PROPORTIONAL) fprintf(inc," scale <1, %sBond_Radius, %sBond_Radius>\n", radiusType,radiusType); } if (radiusType==orderTypes[H_BOND]) type[0]='\0'; else if (cylinderColorModel==BY_PDB && firstCylinderTag!=firstSphereTag) strcpy(type,"Bond_"); else strcpy(type,"Atom_"); if (radiusType!=orderTypes[H_BOND] && cylinderColorModel!=ALL_THE_SAME) { if (strcmp(tag1,tag2)==0) { fprintf(inc," pigment { color %s%s_Color }\n",type,tag1); } else { fputs( " pigment {\n",inc); fputs( " gradient x\n",inc); fputs( " color_map {\n",inc); switch (stickSplit) { case SPLIT_EQUAL: fprintf(inc," [0.5 color %s%s_Color ]\n",type,tag1); fprintf(inc," [0.5 color %s%s_Color ]\n }\n }\n",type,tag2); break; case SPLIT_PROPORTIONAL: proportion=POW(vdW1,POWER)/ (POW(vdW1,POWER)+POW(vdW2,POWER)); fprintf(inc," [%.5g color %s%s_Color ]\n", proportion,type,tag1); fprintf(inc," [%.5g color %s%s_Color ]\n }\n }\n", proportion,type,tag2); break; case SPLIT_SHADED: fprintf(inc," [0.15 color %s%s_Color ]\n",type,tag1); fprintf(inc," [0.85 color %s%s_Color ]\n }\n }\n",type,tag2); break; } } } fprintf(inc," scale <%.*g, 1, 1>\n",SF,length); phi=ACOS((y2-y1)/length); sprintf(tmp," rotate <0, 0, %.*g>\n",SF,DEGREES(-phi)+90.0); if (strcmp(tmp," rotate <0, 0, 0>\n")!=0) fprintf(inc,"%s",tmp); dy=SQRT((x2-x1)*(x2-x1)+(z2-z1)*(z2-z1)); if (dy!=0.0) { theta=ACOS((x2-x1)/dy); sprintf(tmp," rotate <0, %.*g, 0>\n",SF, (z2-z1)>0.0 ? DEGREES(-theta) : DEGREES(theta)); if (strcmp(tmp," rotate <0, 0, 0>\n")!=0) fprintf(inc,"%s",tmp); } fprintf(inc," translate <%.*g, %.*g, %.*g> }\n",SF,x1,SF,y1,SF,z1); return; } /*---------- find indexes to the "Atoms" array of the atoms given by their serial number ----------------------------*/ #define FIRST 1 #define SECOND 2 char Find_Indexes(U_INT serNumA, U_INT serNumB, U_INT *indexA, U_INT *indexB) { U_INT i, found=0; if (serNumA>0 && Atoms[serNumA-1].serNum==serNumA) { *indexA=serNumA-1; found|=FIRST; /* serial #'s usually in order. */ } if (serNumB>0 && Atoms[serNumB-1].serNum==serNumB) { *indexB=serNumB-1; found|=SECOND; } for ( i=0; (i HIGH_ORDER) *tmpO=HIGH_ORDER; tmpO=&(BondOrder(indexB,b)); if ( ++(*tmpO) > HIGH_ORDER) *tmpO=HIGH_ORDER; } } return; } /*-- function to find the positions of ALL bond ends and write cylinders ---*/ void Write_Bonds(void) { char *name1, *name2, *radiusType; unsigned char order; U_INT index1, index2, i, j, nDashes; F_TYPE proportion, proportion2, atomRadius1, atomRadius2; F_TYPE a1, b1, c1, a2, b2, c2, length, dashSep; nOrders[SINGLE]=nOrders[DOUBLE]=nOrders[TRIPLE]= nOrders[HIGH_ORDER]=nOrders[H_BOND]=0; for (index1=0; index1index1) { order=BondOrder(index1,j); (nOrders[order])++; radiusType=orderTypes[order]; if (order==H_BOND) { atomRadius1=Determine_Radius(Atoms[index1].Z); atomRadius2=Determine_Radius(Atoms[index2].Z); length=Distance(X(index1),Y(index1),Z(index1), X(index2),Y(index2),Z(index2)); proportion=(atomRadius1)/length; proportion2=(atomRadius2)/length; a1=X(index1)+proportion*(X(index2)-X(index1)); b1=Y(index1)+proportion*(Y(index2)-Y(index1)); c1=Z(index1)+proportion*(Z(index2)-Z(index1)); a2=X(index2)+proportion2*(X(index1)-X(index2)); b2=Y(index2)+proportion2*(Y(index1)-Y(index2)); c2=Z(index2)+proportion2*(Z(index1)-Z(index2)); dashSep=1.9/(2.0*HBondDashes+1.0); nDashes=Round((length/dashSep-1.0)/2.0); for (i=0;iZERO && tmp=HBondMinDist && HAdist<=HBondMaxDist) { /* check distance */ DHAangle=Angle(D,indexH,A); if (DHAangle>=HBondMinDHA) { /* check DHA angle */ for (R=0;RZERO && /* if too small, no go */ tmpr; defCol.g=color->g; defCol.b=color->b; do { if (*input=='\0') { color->r=defCol.r; color->g=defCol.g; color->b=defCol.b; return; } okay=TRUE; entry=strtok(input," ,"); if (entry) { color->r=(F_TYPE)strtod(entry,&endptr); if (endptr==(char *)entry || color->r<0.0 || color->r>1.0) { okay=FALSE; goto problem; } } else { okay=FALSE; goto problem; } entry=strtok(NULL," ,"); if (entry) { color->g=(F_TYPE)strtod(entry,&endptr); if (endptr==(char *)entry || color->g<0.0 || color->g>1.0) { okay=FALSE; goto problem; } } else { okay=FALSE; goto problem; } entry=strtok(NULL," ,"); if (entry) { color->b=(F_TYPE)strtod(entry,&endptr); if (endptr==(char *)entry || color->b<0.0 || color->b>1.0) { okay=FALSE; } } else { okay=FALSE; } problem: if (!okay) { puts("You must give me a comma or space-separated list of three"); puts("values between 0.0 and 1.0.\n"); printf("What color [%.5g, %.5g, %.5g] ? ",defCol.r,defCol.g, defCol.b); Get_User_Input(input); } } while (okay==FALSE); return; } /* * This function rotates a point around an arbitrary axis through the origin. */ void Apply_Rotation(F_TYPE axisX, F_TYPE axisY, F_TYPE axisZ, F_TYPE radians, F_TYPE *pointX, F_TYPE *pointY, F_TYPE *pointZ) { F_TYPE lengthP, lengthA, scale, lengthPC; Vector C, H, V; /* * C is a point along A (the axis). The plane perpendicular to A that * intersects axis A at point C also contains P, the point to be rotated. * Unit vector H points from C to P, and defines the "horizontal" axis in the * plane. V, the vertical unit length in the plane, is the cross-product of H * and A. R, the point resulting from rotating P theta radians around A, is * then C + |PC|*V*sin(theta) + |PC|*H*cos(theta). */ lengthA=Distance(0.0,0.0,0.0,axisX,axisY,axisZ); lengthP=Distance(0.0,0.0,0.0,*pointX,*pointY,*pointZ); if (lengthP==0.0) return; scale=((*pointX)*axisX+(*pointY)*axisY+(*pointZ)*axisZ)/(lengthA*lengthA); C.x=axisX*scale; C.y=axisY*scale; C.z=axisZ*scale; H.x=(*pointX)-C.x; H.y=(*pointY)-C.y; H.z=(*pointZ)-C.z; lengthPC=Distance(C.x,C.y,C.z,*pointX,*pointY,*pointZ); if (lengthPC==0.0) { return; } V.x=axisY*H.z-axisZ*H.y; V.y=axisZ*H.x-axisX*H.z; V.z=axisX*H.y-axisY*H.x; H.x/=lengthPC; H.y/=lengthPC; /* make H a unit vector */ H.z/=lengthPC; scale=Distance(0.0,0.0,0.0,V.x,V.y,V.z); V.x/=scale; V.y/=scale; /* make V a unit vector */ V.z/=scale; *pointX=C.x+V.x*lengthPC*SIN(radians)+H.x*lengthPC*COS(radians); *pointY=C.y+V.y*lengthPC*SIN(radians)+H.y*lengthPC*COS(radians); *pointZ=C.z+V.z*lengthPC*SIN(radians)+H.z*lengthPC*COS(radians); return; } /*---------------- camera set up routines --------------------*/ #define MULT (2.0) /* to guess camera distance, defaults to */ /* MULT * maximum spread in X, Y, or Z */ void Set_Up_View(void) { F_TYPE Xspread, Yspread, Zspread, length, theta, lengthS; char user[80], defaultBG, choice; Vector lineOfSight; Xspread=maxX-minX; Yspread=maxY-minY; Zspread=maxZ-minZ; if (isInteractive) { puts("\nDo you want: 1) Best-guess View"); puts( " 2) Standard View along -Z, +Y up, +X right"); puts( " 3) User-defined View"); printf("Choice [%u] ? ",DEFAULT_VIEW); viewType=( (Get_User_Input(user)[0]=='\0') ? DEFAULT_VIEW : atoi(user) ); if (viewType<1 || viewType>3) viewType=DEFAULT_VIEW; puts(""); } else viewType=DEFAULT_VIEW; if (viewType!=USER_DEFINED) { if (isInteractive) { puts( "Make the image orientation: 1) Landscape"); puts( " 2) Portrait"); printf("Choice [%u] ? ",DEFAULT_ORIENTATION); imageOrientation= ((Get_User_Input(user)[0]=='\0') ? DEFAULT_ORIENTATION : atoi(user)); if (imageOrientation<1 || imageOrientation>2) imageOrientation=DEFAULT_ORIENTATION; puts(""); } else imageOrientation=DEFAULT_ORIENTATION; } if (viewType==USER_DEFINED) { if (WINDOW_UP==0.0 || WINDOW_RIGHT==0.0) { puts("Fatal: Window has zero width or height! Aborting.\n"); exit(EXIT_FAILURE); } } else { CAMERA_X=LOOK_AT_X=aveX; /* camera will be moved later */ CAMERA_Y=LOOK_AT_Y=aveY; CAMERA_Z=LOOK_AT_Z=aveZ; DIRECTION=2.0; SKY_X=SKY_Y=SKY_Z=0.0; unitRight.x=unitRight.y=unitRight.z=0.0; switch (imageOrientation) { case LANDSCAPE: WINDOW_UP=1.0; WINDOW_RIGHT=1.333; break; case PORTRAIT: WINDOW_UP=1.333; WINDOW_RIGHT=1.0; break; } } switch (viewType) { case BEST_GUESS: if (Yspread>Xspread) { if (Zspread>Yspread) { /* Z>Y>X */ if (imageOrientation==LANDSCAPE) { CAMERA_X+=Zspread*MULT; SKY_Y=1.0; unitRight.z=1.0; } else { CAMERA_X+=Zspread*MULT; SKY_Z=1.0; unitRight.y=-1.0; } } else { if (Zspread>Xspread) { /* Y>Z>X */ if (imageOrientation==LANDSCAPE) { CAMERA_X+=Yspread*MULT; SKY_Z=1.0; unitRight.y=-1.0; } else { CAMERA_X+=Yspread*MULT; SKY_Y=1.0; unitRight.z=1.0; } } else { /* Y>X>Z */ if (imageOrientation==LANDSCAPE) { CAMERA_Z-=Yspread*MULT; SKY_X=1.0; unitRight.y=-1.0; } else { CAMERA_Z-=Yspread*MULT; SKY_Y=1.0; unitRight.x=1.0; } } } } else { if (Zspread>Xspread) { /* Z>X>Y */ if (imageOrientation==LANDSCAPE) { CAMERA_Y+=Zspread*MULT; SKY_X=1.0; unitRight.z=-1.0; } else { CAMERA_Y+=Zspread*MULT; SKY_Z=1.0; unitRight.x=1.0; } } else { if (Zspread>Yspread) { /* X>Z>Y */ if (imageOrientation==LANDSCAPE) { CAMERA_Y+=Xspread*MULT; SKY_Z=1.0; unitRight.x=1.0; } else { CAMERA_Y+=Xspread*MULT; SKY_X=1.0; unitRight.z=-1.0; } } else { /* X>Y>Z */ if (imageOrientation==LANDSCAPE) { CAMERA_Z-=Xspread*MULT; SKY_Y=1.0; unitRight.x=1.0; } else { CAMERA_Z-=Xspread*MULT; SKY_X=1.0; unitRight.y=-1.0; } } } } break; case STANDARD: if (imageOrientation==LANDSCAPE) CAMERA_Z-=Xspread*MULT; else CAMERA_Z-=Yspread*MULT; SKY_Y=1.0; /* look along Z, sky along y */ unitRight.x=1.0; break; case USER_DEFINED: lineOfSight.x=LOOK_AT_X-CAMERA_X; lineOfSight.y=LOOK_AT_Y-CAMERA_Y; lineOfSight.z=LOOK_AT_Z-CAMERA_Z; unitRight.x=SKY_Y*lineOfSight.z-SKY_Z*lineOfSight.y; unitRight.y=SKY_Z*lineOfSight.x-SKY_X*lineOfSight.z; unitRight.z=SKY_X*lineOfSight.y-SKY_Y*lineOfSight.x; length=Distance(0.0,0.0,0.0,unitRight.x,unitRight.y,unitRight.z); if (length==0.0) { puts("Fatal: error due to contradictory or meaningless camera,"); puts("look at, or sky vectors. Aborting.\n"); exit(EXIT_FAILURE); } unitRight.x/=length; unitRight.y/=length; unitRight.z/=length; length=Distance(0.0,0.0,0.0,lineOfSight.x,lineOfSight.y,lineOfSight.z); lengthS=Distance(0.0,0.0,0.0,SKY_X,SKY_Y,SKY_Z); SKY_X/=lengthS; SKY_Y/=lengthS; /* make Sky unit length */ SKY_Z/=lengthS; theta=ACOS( (SKY_X*lineOfSight.x+SKY_Y*lineOfSight.y+SKY_Z*lineOfSight.z)/ (length) ); if ( DEGREES(theta)>90.0001 || DEGREES(theta)<89.9999 ) { /* puts("\nWarning: sky vector not perpendicular to line of sight!"); puts("Applying correction.\n"); */ Apply_Rotation(unitRight.x,unitRight.y,unitRight.z, theta-M_PI_2, &SKY_X,&SKY_Y,&SKY_Z); } break; } if (isInteractive) { puts( "Do you want the background: 1) Black"); puts( " 2) White"); puts( " 3) Dark blue"); puts( " 4) Other"); if (DEFAULT_BACKGROUND_RED+ DEFAULT_BACKGROUND_GREEN+ DEFAULT_BACKGROUND_BLUE==0.0) defaultBG=1; else defaultBG=4; printf("Choice [%u] ? ",defaultBG); choice= ( (Get_User_Input(user)[0]=='\0') ? defaultBG : atoi(user) ); if (choice<1 || choice>4) choice=defaultBG; puts(""); } else choice=4; switch (choice) { case 1: Background.r=Background.g=Background.b=0.0; break; case 2: Background.r=Background.g=Background.b=1.0; break; case 3: Background.r=0.0; Background.g=0.0; Background.b=0.19608; break; case 4: default: Background.r=DEFAULT_BACKGROUND_RED; Background.g=DEFAULT_BACKGROUND_GREEN; Background.b=DEFAULT_BACKGROUND_BLUE; if (isInteractive) { printf("What color [%.5g, %.5g, %.5g] ? ",DEFAULT_BACKGROUND_RED, DEFAULT_BACKGROUND_GREEN,DEFAULT_BACKGROUND_BLUE); Parse_Colors(Get_User_Input(user),&Background); puts(""); } break; } return; } /*------------- writes the main header .pov file ------------------*/ void Write_POV_Header(U_INT whichEye, char *fileName) { F_TYPE cameraDist, eyeDist, XZproj, rot1_Z, rot2_X, rot3_Y; Vector lineOfSight, eye, eyeOffset, invSky; char tmp[256]; if ( (pov=fopen(fileName,"w"))==0 ) { printf("Can't write pov header file \"%s\"\n",fileName); perror("Reason"); return; } fprintf(pov,"// This file was created by PovChem version %s\n\n",VERSION); /* * If viewType is USER_DEFINED, then the camera will be initially at the * origin, then rotated, then translated into place. Otherwise the camera is * put directly into place. * * This function calculates all appropriate camera positions and angles, * and light positions, including slight camera offset for stereo images. */ cameraDist=Distance(CAMERA_X,CAMERA_Y,CAMERA_Z,LOOK_AT_X,LOOK_AT_Y,LOOK_AT_Z); eye.x=CAMERA_X; eye.y=CAMERA_Y; eye.z=CAMERA_Z; if (whichEye!=MONO) { eyeOffset.x=unitRight.x*cameraDist*(STEREO_SEPARATION_FACTOR/2.0); eyeOffset.y=unitRight.y*cameraDist*(STEREO_SEPARATION_FACTOR/2.0); eyeOffset.z=unitRight.z*cameraDist*(STEREO_SEPARATION_FACTOR/2.0); if (whichEye==LEFT_EYE) { eyeOffset.x=-eyeOffset.x; eyeOffset.y=-eyeOffset.y; eyeOffset.z=-eyeOffset.z; } eye.x+=eyeOffset.x; eye.y+=eyeOffset.y; /* change view position if for stereo image */ eye.z+=eyeOffset.z; } eyeDist=Distance(eye.x,eye.y,eye.z, LOOK_AT_X,LOOK_AT_Y,LOOK_AT_Z); /* * Always set up the basic camera view window first, with camera placed at * the origin, looking along +Z, sky along +Y. */ fprintf(pov,"#declare Camera_Position = < %.6g, %.6g, %.6g >\n\n", eye.x,eye.y,eye.z); fputs( "camera{\n",pov); fputs( " location < 0, 0, 0 >\n",pov); fprintf(pov," direction < 0, 0, %.6g >\n",DIRECTION); fprintf(pov," look_at < 0, 0, %.6g >\n",eyeDist); fputs( " sky < 0, 1, 0 >\n",pov); fprintf(pov," up < 0, %.6g, 0 >\n",WINDOW_UP); fprintf(pov," right < %.6g, 0, 0 >\n",WINDOW_RIGHT); /* * Tricky: rotate the camera into the right direction line of sight vector. * The look_at vector (as above, initially along +Z) and the sky vector * (initially along +Y) must be correctly rotated into their defined values. * Then translate the camera into place. All this is necessary because POV * seems to do some weird things to move the camera instead of rotating it * when a "sky" is defined. */ lineOfSight.x=LOOK_AT_X-eye.x; lineOfSight.y=LOOK_AT_Y-eye.y; lineOfSight.z=LOOK_AT_Z-eye.z; XZproj=SQRT((lineOfSight.x*lineOfSight.x)+(lineOfSight.z*lineOfSight.z)); if (XZproj==0.0) { rot2_X= (lineOfSight.y>0.0) ? -M_PI_2 : M_PI_2; rot3_Y=0.0; } else { rot2_X=-ASIN(lineOfSight.y/eyeDist); rot3_Y= (lineOfSight.x>0.0) ? ACOS(lineOfSight.z/XZproj) : -ACOS(lineOfSight.z/XZproj); } invSky.x=SKY_X; invSky.y=SKY_Y; invSky.z=SKY_Z; Apply_Rotation(0.0,1.0,0.0,-rot3_Y,&(invSky.x),&(invSky.y),&(invSky.z)); Apply_Rotation(1.0,0.0,0.0,-rot2_X,&(invSky.x),&(invSky.y),&(invSky.z)); rot1_Z= (invSky.x>0.0) ? -ACOS(invSky.y) : ACOS(invSky.y); sprintf(tmp," rotate < 0, 0, %.7g >\n",DEGREES(rot1_Z)); if (strcmp(tmp," rotate < 0, 0, 0 >\n")!=0) fprintf(pov,"%s",tmp); sprintf(tmp," rotate < %.7g, 0, 0 >\n",DEGREES(rot2_X)); if (strcmp(tmp," rotate < 0, 0, 0 >\n")!=0) fprintf(pov,"%s",tmp); sprintf(tmp," rotate < 0, %.7g, 0 >\n",DEGREES(rot3_Y)); if (strcmp(tmp," rotate < 0, 0, 0 >\n")!=0) fprintf(pov,"%s",tmp); fputs( " translate Camera_Position\n",pov); fprintf(pov,"}\n\n"); Light.x=CAMERA_X+cameraDist*(SKY_X)*0.65+cameraDist*(unitRight.x)*0.4; Light.y=CAMERA_Y+cameraDist*(SKY_Y)*0.65+cameraDist*(unitRight.y)*0.4; Light.z=CAMERA_Z+cameraDist*(SKY_Z)*0.65+cameraDist*(unitRight.z)*0.4; fprintf(pov,"light_source { < %.5g, %.5g, %.5g > color rgb < 1, 1, 1 > }\n", Light.x,Light.y,Light.z); if (whichEye==MONO) fputs("light_source { Camera_Position",pov); else fprintf(pov,"light_source { < %.5g, %.5g, %.5g >", CAMERA_X,CAMERA_Y,CAMERA_Z); fputs(" color rgb < 0.5, 0.5, 0.5 > }\n\n",pov); fprintf(pov,"background { color rgb < %.5g, %.5g, %.5g > }\n\n", Background.r, Background.g, Background.b); fprintf(pov,"#declare %s =\n",FINISH); fputs(FINISH_DECLARATION,pov); puts("Writing color and atom definitions..."); Write_Color_and_Atom_Declarations(); if (model!=CPK) { puts("Writing bond definitions..."); Write_Bond_Declarations(); } fprintf(pov,"\n#include \"%s\"\n",incName); fclose(pov); printf("Pov header %s created.\n\n",fileName); return; } /*------- adjust view - rotate, translate, zoom, print view params -------*/ void Adjust_View(void) { char choice, tmp[80], quit=9, changed=FALSE; F_TYPE num, distance; Vector translate; do { puts(""); puts( "Options: 1) Rotate in plane"); puts( " 2) Rotate around vertical axis"); puts( " 3) Rotate around horizontal axis"); puts( " 4) Translate horizontal"); puts( " 5) Translate vertical"); puts( " 6) Change distance to camera"); puts( " 7) Change zoom factor"); printf(" %u) Print view parameters\n",quit-1); printf(" %u) Return",quit); printf("\nWhich [%u] ? ",quit); choice= (Get_User_Input(tmp)[0]=='\0') ? quit : atoi(tmp); if (choice<1 || choice>quit) choice=quit; puts(""); if (choice==1) { printf("Positive value rotates clockwise. How many degrees [30] ? "); num= (Get_User_Input(tmp)[0]=='\0') ? 30.0 : atof(tmp); if (num!=0.0) { Apply_Rotation(LOOK_AT_X-CAMERA_X,LOOK_AT_Y-CAMERA_Y, LOOK_AT_Z-CAMERA_Z,RADIANS(num), &SKY_X,&SKY_Y,&SKY_Z); Apply_Rotation(LOOK_AT_X-CAMERA_X,LOOK_AT_Y-CAMERA_Y, LOOK_AT_Z-CAMERA_Z,RADIANS(num), &(unitRight.x),&(unitRight.y),&(unitRight.z)); changed=TRUE; } } else if (choice==2) { printf("Positive value rotates left-to-right. How many degrees [30] ? "); num= (Get_User_Input(tmp)[0]=='\0') ? 30.0 : atof(tmp); if (num!=0.0) { Apply_Rotation(SKY_X,SKY_Y,SKY_Z, RADIANS(num), &(unitRight.x),&(unitRight.y),&(unitRight.z)); translate.x=LOOK_AT_X-CAMERA_X; translate.y=LOOK_AT_Y-CAMERA_Y; translate.z=LOOK_AT_Z-CAMERA_Z; Apply_Rotation(SKY_X,SKY_Y,SKY_Z, RADIANS(num), &(translate.x),&(translate.y),&(translate.z)); CAMERA_X=LOOK_AT_X-translate.x; CAMERA_Y=LOOK_AT_Y-translate.y; CAMERA_Z=LOOK_AT_Z-translate.z; changed=TRUE; } } else if (choice==3) { printf("Positive value rotates top-to-bottom. How many degrees [30] ? "); num= (Get_User_Input(tmp)[0]=='\0') ? 30.0 : atof(tmp); if (num!=0.0) { Apply_Rotation(unitRight.x,unitRight.y,unitRight.z, RADIANS(num), &SKY_X,&SKY_Y,&SKY_Z); translate.x=LOOK_AT_X-CAMERA_X; translate.y=LOOK_AT_Y-CAMERA_Y; translate.z=LOOK_AT_Z-CAMERA_Z; Apply_Rotation(unitRight.x,unitRight.y,unitRight.z, RADIANS(num), &(translate.x),&(translate.y),&(translate.z)); CAMERA_X=LOOK_AT_X-translate.x; CAMERA_Y=LOOK_AT_Y-translate.y; CAMERA_Z=LOOK_AT_Z-translate.z; changed=TRUE; } } else if (choice==4) { printf("Positive value moves right. How many units [10] ? "); num= (Get_User_Input(tmp)[0]=='\0') ? 10.0 : atof(tmp); if (num!=0.0) { CAMERA_X-=num*unitRight.x; CAMERA_Y-=num*unitRight.y; CAMERA_Z-=num*unitRight.z; LOOK_AT_X-=num*unitRight.x; LOOK_AT_Y-=num*unitRight.y; LOOK_AT_Z-=num*unitRight.z; changed=TRUE; } } else if (choice==5) { printf("Positive value moves up. How many units [10] ? "); num= (Get_User_Input(tmp)[0]=='\0') ? 10.0 : atof(tmp); if (num!=0.0) { CAMERA_X-=num*SKY_X; CAMERA_Y-=num*SKY_Y; CAMERA_Y-=num*SKY_Z; LOOK_AT_X-=num*SKY_X; LOOK_AT_Y-=num*SKY_Y; LOOK_AT_Z-=num*SKY_Z; changed=TRUE; } } else if (choice==6) { distance=Distance(CAMERA_X,CAMERA_Y,CAMERA_Z, LOOK_AT_X,LOOK_AT_Y,LOOK_AT_Z); printf("Current distance is %.5g. Change to [%.5g] ? ",distance,distance); num= (Get_User_Input(tmp)[0]=='\0') ? distance : atof(tmp); if (num!=distance) { CAMERA_X=LOOK_AT_X+(num/distance)*(CAMERA_X-LOOK_AT_X); CAMERA_Y=LOOK_AT_Y+(num/distance)*(CAMERA_Y-LOOK_AT_Y); CAMERA_Z=LOOK_AT_Z+(num/distance)*(CAMERA_Z-LOOK_AT_Z); changed=TRUE; } } else if (choice==7) { printf("Current zoom is %.5g. Change to [%.5g] ? ",DIRECTION,DIRECTION); num= (Get_User_Input(tmp)[0]=='\0') ? DIRECTION : atof(tmp); if (num!=DIRECTION) { DIRECTION=num; changed=TRUE; } } else if (choice==(quit-1)) { printf("CameraX = %.5g\n",CAMERA_X); printf("CameraY = %.5g\n",CAMERA_Y); printf("CameraZ = %.5g\n",CAMERA_Z); printf("LookAtX = %.5g\n",LOOK_AT_X); printf("LookAtY = %.5g\n",LOOK_AT_Y); printf("LookAtZ = %.5g\n",LOOK_AT_Z); printf("SkyX = %.6g\n",SKY_X); printf("SkyY = %.6g\n",SKY_Y); printf("SkyZ = %.6g\n",SKY_Z); printf("WindowDistance = %.5g\n",DIRECTION); printf("WindowHeight = %.5g\n",WINDOW_UP); printf("WindowWidth = %.5g\n",WINDOW_RIGHT); } } while (choice!=quit); if (changed) { Write_POV_Header(MONO,povName); if (stereoPairCreated) { Write_POV_Header(LEFT_EYE,leftName); Write_POV_Header(RIGHT_EYE,rightName); } } return; } /*-------------------- the main program body --------------------------*/ void main(int argc, char *argv[]) { char *pdbName, *configName=NULL; char *tgaName=NULL, *leftTga=NULL, *rightTga=NULL; U_INT povWidth, povHeight, nBonds, central, bondedTo, i, j, quit; #define TMPSZ 512 char tmp[TMPSZ], con[6], choice=0, defaultChoice=0; PDBatomInfoType info; FILE *testTga; printf("\nWelcome to PovChem! This is version %s. For general\n",VERSION); puts("instructions see http://ludwig.scs.uiuc.edu/~paul/Manual.html\n"); /*---------------- parse command-line options --------------------*/ for (i=1;i7) { puts("\nUsage:"); puts( "povchem [-default ] [-config ]"); puts( " [-table ]\n"); exit(EXIT_FAILURE); } /*----------- Parse periodic table and config file -------------*/ Assign_Defaults(&configName); /* must be read first in case periodic table is in non-default location. */ Parse_Periodic_Table(); /*------------ Get filenames and open files ---------------*/ if (isInteractive) { printf("\nInput file [.pdb] : "); Get_User_Input(tmp); } if (strstr(tmp,".pdb")==0) strcat(tmp,".pdb"); New_String(&pdbName,tmp); if ( (pdb=fopen(pdbName,"r"))==0 ) { printf("Can't read input file \"%s\".\n",pdbName); perror("Reason"); exit(EXIT_FAILURE); } New_String(&incName,pdbName); *strrchr(incName,'.')='\0'; strcat(incName,".inc"); New_String(&povName,pdbName); *strrchr(povName,'.')='\0'; strcat(povName,".pov"); if (POVRAY) { New_String(&tgaName,pdbName); *strrchr(tgaName,'.')='\0'; strcat(tgaName,".tga"); } if ( (inc=fopen(incName,"w"))==0 ) { printf("Can't write include file \"%s\"\n",incName); perror("Reason"); fclose(pdb); exit(EXIT_FAILURE); } else { fprintf(inc,"// This file was created by PovChem version %s\n\n",VERSION); } if ( (pov=fopen(povName,"w"))==0 ) { printf("Can't write pov header file \"%s\"\n",povName); perror("Reason"); fclose(inc); fclose(pdb); exit(EXIT_FAILURE); } fclose(pov); /*--------------- Decide which model to make ----------------*/ if (isInteractive) { puts("\nWhat type of model? 1) CPK"); puts (" 2) Ball and Stick"); puts (" 3) Cylinder"); printf("Choice [%u] ? ",DEFAULT_MODEL); model=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_MODEL : atoi(tmp) ); if (model<1 || model>3) model=DEFAULT_MODEL; } else model=DEFAULT_MODEL; switch (model) { /*----------------- ball and stick -------------------------*/ case BALLnSTICK: case CYLINDER: /* BALLnSTICK options */ if (model==BALLnSTICK) { if (isInteractive) { puts("\nSet Radius of the Balls: 1) Constant"); puts( " 2) Proportional to vdW radius"); printf("Choice [%u] ? ",DEFAULT_RADIUS_TYPE); ballSizeModel=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_RADIUS_TYPE : atoi(tmp) ); if (ballSizeModel<1 || ballSizeModel>2) ballSizeModel=DEFAULT_RADIUS_TYPE; } else ballSizeModel=DEFAULT_RADIUS_TYPE; if (ballSizeModel==CONSTANT) { if (isInteractive) { printf("\nRadius of the Balls (in Angstroms) [%.2f] ? ", DEFAULT_BALL_RADIUS); ballRadius=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_BALL_RADIUS : atof(tmp) ); if (ballRadius<=0.0) ballRadius=DEFAULT_BALL_RADIUS; } else ballRadius=DEFAULT_BALL_RADIUS; } else { if (isInteractive) { printf("\nProportionality constant? [%.2f] ? ", DEFAULT_PROPORTIONALITY); ballRadius=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_PROPORTIONALITY : atof(tmp) ); if (ballRadius<=0.0) ballRadius=DEFAULT_PROPORTIONALITY; } else ballRadius=DEFAULT_PROPORTIONALITY; } if (isInteractive) { puts("\nColor the Balls by: 1) Element"); puts (" 2) By PDB fields"); puts (" 3) All the same"); printf("Choice [%u] ? ",DEFAULT_COLORMODEL); sphereColorModel=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_COLORMODEL : atoi(tmp) ); if (sphereColorModel<1 || sphereColorModel>3) sphereColorModel=DEFAULT_COLORMODEL; } else sphereColorModel=DEFAULT_COLORMODEL; if (sphereColorModel==ALL_THE_SAME) { allSphereColor.r=DEFAULT_BALL_RED; allSphereColor.g=DEFAULT_BALL_GREEN; allSphereColor.b=DEFAULT_BALL_BLUE; if (isInteractive) { printf("\nWhat color [%.5g, %.5g, %.5g] ? ",DEFAULT_BALL_RED, DEFAULT_BALL_GREEN,DEFAULT_BALL_BLUE); Parse_Colors(Get_User_Input(tmp),&allSphereColor); } } else if (sphereColorModel==BY_PDB) Make_Tag_List(&firstSphereTag,&nSphereTags); if (isInteractive) { printf("\nRadius of the Sticks (for single bonds) [%.2f] ? ", DEFAULT_STICK_RADIUS); cylinderRadius=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_STICK_RADIUS : atof(tmp) ); if (cylinderRadius<=0.0) cylinderRadius=DEFAULT_STICK_RADIUS; } else cylinderRadius=DEFAULT_STICK_RADIUS; if (isInteractive) { puts("\nMake Radius of the Sticks: 1) All the same"); puts( " 2) Thicker for higher order bonds"); printf("Choice [%u] ? ",DEFAULT_THICKNESS); cylinderSizeModel=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_THICKNESS : atoi(tmp) ); if (cylinderSizeModel<1 || cylinderSizeModel>2) cylinderSizeModel=DEFAULT_THICKNESS; } else cylinderSizeModel=DEFAULT_THICKNESS; if (isInteractive) { puts("\nColor the Sticks by: 1) Element"); puts (" 2) By PDB fields"); puts (" 3) All the same"); printf("Choice [%u] ? ",DEFAULT_STICK_COLORMODEL); cylinderColorModel=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_STICK_COLORMODEL : atoi(tmp) ); if (cylinderColorModel<1 || cylinderColorModel>3) cylinderColorModel=DEFAULT_STICK_COLORMODEL; } else cylinderColorModel=DEFAULT_STICK_COLORMODEL; if (cylinderColorModel==BY_PDB) { if (sphereColorModel==BY_PDB) { tmp[0]='\0'; if (isInteractive) { printf("\nUse the same PDB fields as the Balls [%s] ? ", (DEFAULT_STICKS_LIKE_BALLS ? "yes" : "no")); Get_User_Input(tmp); } if (tmp[0]=='\0') tmp[0]=(DEFAULT_STICKS_LIKE_BALLS ? 'y' : 'n'); if (tolower(tmp[0])=='y') { firstCylinderTag=firstSphereTag; nCylinderTags=nSphereTags; stickTagType=sphereTagType; } else Make_Tag_List(&firstCylinderTag,&nCylinderTags); } else Make_Tag_List(&firstCylinderTag,&nCylinderTags); } else if (cylinderColorModel==ALL_THE_SAME) { allCylinderColor.r=DEFAULT_STICK_RED; allCylinderColor.g=DEFAULT_STICK_GREEN; allCylinderColor.b=DEFAULT_STICK_BLUE; if (isInteractive) { printf("\nWhat color [%.5g, %.5g, %.5g] ? ",DEFAULT_STICK_RED, DEFAULT_STICK_GREEN,DEFAULT_STICK_BLUE); Parse_Colors(Get_User_Input(tmp),&allCylinderColor); } } } else { /* CYLINDER options */ if (isInteractive) { printf("\nRadius of the Cylinders (in Angstroms) [%.2f] ? ", DEFAULT_CYLINDER_RADIUS); cylinderRadius=ballRadius=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_CYLINDER_RADIUS : atof(tmp) ); if (cylinderRadius<=0.0) ballRadius=cylinderRadius=DEFAULT_CYLINDER_RADIUS; } else ballRadius=cylinderRadius=DEFAULT_CYLINDER_RADIUS; if (isInteractive) { puts("\nColor the Cylinders by: 1) Element"); puts (" 2) By PDB fields"); puts (" 3) All the same"); printf("Choice [%u] ? ",DEFAULT_COLORMODEL); sphereColorModel=cylinderColorModel=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_COLORMODEL : atoi(tmp) ); if (sphereColorModel<1 || sphereColorModel>3) sphereColorModel=cylinderColorModel=DEFAULT_COLORMODEL; } else sphereColorModel=cylinderColorModel=DEFAULT_COLORMODEL; if (sphereColorModel==ALL_THE_SAME) { allSphereColor.r=DEFAULT_STICK_RED; allSphereColor.g=DEFAULT_STICK_GREEN; allSphereColor.b=DEFAULT_STICK_BLUE; if (isInteractive) { printf("\nWhat color [%.5g, %.5g, %.5g] ? ",DEFAULT_STICK_RED, DEFAULT_STICK_GREEN,DEFAULT_STICK_BLUE); Parse_Colors(Get_User_Input(tmp),&allSphereColor); } allCylinderColor.r=allSphereColor.r; allCylinderColor.g=allSphereColor.g; allCylinderColor.b=allSphereColor.b; } else if (sphereColorModel==BY_PDB) { Make_Tag_List(&firstSphereTag,&nSphereTags); firstCylinderTag=firstSphereTag; nCylinderTags=nSphereTags; } ballSizeModel=cylinderSizeModel=CONSTANT; } /* options for both */ if (cylinderColorModel==BY_ELEMENT || cylinderColorModel==BY_PDB) { if (isInteractive) { puts("\nChange stick colors: 1) In the center"); puts (" 2) At a point propotional to vdW radius"); puts (" 3) Shade smoothly"); printf("Choice [%u] ? ",DEFAULT_BOND_SPLIT); stickSplit=( (Get_User_Input(tmp)[0]=='\0') ? DEFAULT_BOND_SPLIT : atoi(tmp) ); if (stickSplit<1 || stickSplit>3) stickSplit=DEFAULT_BOND_SPLIT; } else stickSplit=DEFAULT_BOND_SPLIT; } puts(""); /*------------------- count atoms, check for CONECT --------------*/ nAtoms=0; nBonds=0; for ( ; fgets(tmp,TMPSZ,pdb)!=0 && strncmp(tmp,"END ",6)!=0 ; ) { if ( strncmp(tmp,"ATOM ",6)==0 || strncmp(tmp,"HETATM",6)==0) nAtoms++; if ( nBonds==0 && strncmp(tmp,"CONECT",6)==0 ) nBonds=1; } rewind(pdb); printf("Found %u atoms...",nAtoms); if ( !nBonds ) { puts("\n... but no CONECT (connectivity) infomation found!"); puts("This is required for anything but CPK models. Aborting."); BOMB; } /* -------------- read atoms into memory, write spheres ---------------*/ if ( (Atoms=(Atom *)malloc(sizeof(Atom)*nAtoms))==NULL ) { printf("\nNot enough memory for atoms: %ux%u bytes.\n", (int)sizeof(Atom),nAtoms); BOMB; } else memAlloced+=nAtoms*sizeof(Atom); if ( model==BALLnSTICK && cylinderColorModel==BY_PDB && (cylinderEndTags= (char **)malloc(sizeof(char *)*nAtoms))==NULL ) { puts("\nNot enough memory for cylinder info."); BOMB; } else memAlloced+=nAtoms*sizeof(char *); for ( i=0 ; i3) sphereColorModel=DEFAULT_COLORMODEL; } else sphereColorModel=DEFAULT_COLORMODEL; if (sphereColorModel==ALL_THE_SAME) { allSphereColor.r=DEFAULT_BALL_RED; allSphereColor.g=DEFAULT_BALL_GREEN; allSphereColor.b=DEFAULT_BALL_BLUE; if (isInteractive) { printf("\nWhat color [%.5g, %.5g, %.5g] ? ",DEFAULT_BALL_RED, DEFAULT_BALL_GREEN,DEFAULT_BALL_BLUE); Parse_Colors(Get_User_Input(tmp),&allSphereColor); } } else if (sphereColorModel==BY_PDB) { Make_Tag_List(&firstSphereTag,&nSphereTags); } puts(""); for (nAtoms=0;fgets(tmp,TMPSZ,pdb)!=0 && strncmp(tmp,"END ",6)!=0;) { if ( strncmp(tmp,"ATOM ",6)==0 || strncmp(tmp,"HETATM",6)==0) { Parse_PDB_Line(tmp,&info,FIND_ALL); switch (sphereColorModel) { case BY_ELEMENT: case ALL_THE_SAME: strcpy(tmp,PeriodicTable[info.Z].name); break; case BY_PDB: strcpy(tmp,Make_Tag_From_PDB_Info(info,&sphereColorModel)); break; } Write_Atom(info.x,info.y,info.z,info.Z,tmp); nAtoms++; } } printf("\nSuccessfully wrote %u spheres.\n",nAtoms); fclose(pdb); fclose(inc); break; } printf("\n%s created.\n",incName); /*---- Now the .inc file is written; we need to set up the actual .pov file with appropriate color definitions, view, etc... -----*/ aveX=aveX/((F_TYPE)nAtoms); if (aveX<0.0001 && aveX>-0.0001) aveX=0.0; aveY=aveY/((F_TYPE)nAtoms); if (aveY<0.0001 && aveY>-0.0001) aveY=0.0; aveZ=aveZ/((F_TYPE)nAtoms); if (aveZ<0.0001 && aveZ>-0.0001) aveZ=0.0; Set_Up_View(); if (sphereColorModel==BY_PDB) Assign_Colors_To_Tags(&sphereColorModel); if (model==BALLnSTICK && cylinderColorModel==BY_PDB && !DEFAULT_STICKS_LIKE_BALLS) Assign_Colors_To_Tags(&cylinderColorModel); Write_POV_Header(MONO,povName); quit=3; if (POVRAY) { quit+=2; if (VIEWER) quit++; } defaultChoice=quit; do { if (isInteractive) { if (POVRAY) { printf("\nNow you can: 1) Make"); if (VIEWER) printf(" and view"); puts(" a preview picture"); printf( " 2) Make a full-quality"); if (stereoPairCreated) printf(" (stereo)"); puts(" picture"); } if (VIEWER) puts ( " 3) View your picture"); if (!POVRAY && !VIEWER) printf("\nNow you can: "); else printf( " "); printf("%u) Make stereo pair\n",quit-2); printf( " %u) Adjust the View\n",quit-1); printf( " %u) Quit\n",quit); printf("Which [%u] ? ",defaultChoice); choice=( (Get_User_Input(tmp)[0]=='\0') ? defaultChoice : atoi(tmp) ); if (choice<1 || choice>quit) choice=defaultChoice; } else { if (choice==0 && DEFAULT_MAKE_STEREO_PAIR) choice=quit-2; else if (RUN_POVRAY && (choice==0 || choice==quit-2)) choice=2; else choice=quit; } if (POVRAY && choice==1) { switch (imageOrientation) { case LANDSCAPE: default: povWidth=240; break; case PORTRAIT: povWidth=180; break; case USER_DEFINED: if (WINDOW_RIGHT>WINDOW_UP) povWidth=240; else povWidth=180; break; } povHeight=Round((WINDOW_UP/WINDOW_RIGHT)*povWidth); sprintf(tmp,"%s %s -A +W%u +H%u +I%s +O%s", POVRAY,POV_OPTIONS,povWidth,povHeight, povName,tgaName); system(tmp); if (VIEWER) { if ((testTga=fopen(tgaName,"r"))!=NULL) { fclose(testTga); printf("\n%s present! Viewing...\n",tgaName); sprintf(tmp,"%s %s",VIEWER,tgaName); system(tmp); } else { sprintf(tmp,"\nCan't open your picture '%s' because",tgaName); perror(tmp); } } } else if (POVRAY && choice==2) { povWidth=POV_WIDTH; if (isInteractive) { printf("\nHow wide [%u] ? ",povWidth); povWidth=((Get_User_Input(tmp)[0]=='\0') ? povWidth : atoi(tmp)); } povHeight=Round((WINDOW_UP/WINDOW_RIGHT)*povWidth); if (stereoPairCreated) { sprintf(tmp,"%s %s +W%u +H%u +I%s +O%s", POVRAY,POV_OPTIONS,povWidth,povHeight, leftName,leftTga); system(tmp); sprintf(tmp,"%s %s +W%u +H%u +I%s +O%s", POVRAY,POV_OPTIONS,povWidth,povHeight, rightName,rightTga); system(tmp); } else { sprintf(tmp,"%s %s +W%u +H%u +I%s +O%s", POVRAY,POV_OPTIONS,povWidth,povHeight, povName,tgaName); system(tmp); } } else if (POVRAY && VIEWER && choice==3) { if ((testTga=fopen(tgaName,"r"))!=NULL) { fclose(testTga); printf("\n%s present! Viewing...\n",tgaName); sprintf(tmp,"%s %s",VIEWER,tgaName); system(tmp); } else { sprintf(tmp,"\nCan't open your picture '%s' because",tgaName); perror(tmp); puts("Did you forget to make the picture?"); } } else if (choice==(quit-1)) { Adjust_View(); } else if (choice==(quit-2)) { if (leftName==NULL) { strcpy(tmp,pdbName); *strrchr(tmp,'.')='\0'; strcat(tmp,"-left.pov"); New_String(&leftName,tmp); strcpy(tmp,pdbName); *strrchr(tmp,'.')='\0'; strcat(tmp,"-right.pov"); New_String(&rightName,tmp); if (POVRAY) { strcpy(tmp,leftName); *strrchr(tmp,'.')='\0'; strcat(tmp,".tga"); New_String(&leftTga,tmp); strcpy(tmp,rightName); *strrchr(tmp,'.')='\0'; strcat(tmp,".tga"); New_String(&rightTga,tmp); } } Write_POV_Header(LEFT_EYE,leftName); Write_POV_Header(RIGHT_EYE,rightName); stereoPairCreated=TRUE; } } while (choice!=quit); /* printf("\nAllocated a total of about %iKb memory. Bye!\n\n", (int)(memAlloced/1024.0+1)); */ puts(""); exit(EXIT_SUCCESS); }