Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

DrawMolItem.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2011 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: DrawMolItem.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.340 $      $Date: 2013/04/09 14:16:18 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  * Child Displayable component of a molecule; this is responsible for doing
00020  * the actual drawing of a molecule.  It contains an atom color, atom
00021  * selection, and atom representation object to specify how this component
00022  * should look.
00023  *
00024  ***************************************************************************/
00025 
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <sys/types.h>
00029 #include <math.h>
00030 #include <ctype.h>              // for isdigit()
00031 
00032 #include "DrawMolItem.h"
00033 #include "Molecule.h"  // for file_in_progress()
00034 #include "DispCmds.h"
00035 #include "Inform.h"
00036 #include "Scene.h"
00037 #include "TextEvent.h"
00038 #include "BondSearch.h"
00039 #include "DisplayDevice.h"
00040 #ifdef VMDMSMS
00041 #include "MSMSInterface.h" // Interface to MSMS surface generation program
00042 #endif
00043 #ifdef VMDSURF
00044 #include "Surf.h"          // this is an interface to the SURF program
00045 #endif
00046 #include "VMDApp.h"        // for vmd_alloc/vmd_dealloc
00047 #include "VolumetricData.h"
00048 
00050 DrawMolItem::DrawMolItem(const char *nm, DrawMolecule *dm, AtomColor *ac, 
00051         AtomRep *ar, AtomSel *as) 
00052         : Displayable(dm) {
00053 
00054   // save data and pointers to drawing method objects
00055   mol = dm;
00056   avg = new float[3*mol->nAtoms];
00057   avgsize = 0;
00058   atomColor = ac;
00059   atomRep = ar;
00060   atomSel = as;
00061   structwarningcount = 0;
00062 
00063   name = stringdup(nm);
00064   framesel = stringdup("now");
00065   tubearray = NULL;
00066 
00067   colorlookups = new ColorLookup[MAXCOLORS]; // for color sorted line drawing
00068 
00069   // use non-standard Catmull-Rom spline, with slope of 1.25 instead of 2.0 
00070   create_modified_CR_spline_basis(spline_basis, 1.25f);
00071 
00072   // set orbital and volumetric data objects sentinel values or to NULL
00073   waveftype = -1;
00074   wavefspin = -1;
00075   wavefexcitation = -1;
00076   gridorbid = -1;
00077   orbgridspacing = -1.0f; 
00078   orbgridisdensity = -1;
00079   orbvol = NULL;
00080 
00081   // initialize volume texture data to invalid values
00082   voltexVolid = -1;
00083   voltexColorMethod = -1;
00084   voltexDataMin = 0;
00085   voltexDataMax = 0;
00086 
00087   // signal that we need a complete refresh next prepare
00088   needRegenerate = MOL_REGEN | COL_REGEN | SEL_REGEN;
00089   update_pbc = 0;
00090   update_ss = 0;
00091   update_ts = 0;
00092   update_traj = 0;
00093   isOn = TRUE;     // newly created reps default to on
00094 }
00095 
00097 DrawMolItem::~DrawMolItem(void) {
00098   if (tubearray) {
00099     for (int i=0; i<tubearray->num(); i++) 
00100       delete (*tubearray)[i];
00101     delete tubearray;
00102     tubearray = NULL;
00103   }
00104 
00105   delete atomColor;
00106   delete atomRep;
00107   delete atomSel;
00108   delete [] colorlookups; // color/atomid lookup for color sorted line drawing
00109   delete [] name;
00110   delete [] avg;
00111   delete [] framesel;
00112   delete orbvol;
00113 }
00114 
00115 int DrawMolItem::emitstructwarning(void) {
00116   if (structwarningcount < 30) {
00117     structwarningcount++;
00118     return 1;
00119   } 
00120 
00121   if (structwarningcount == 30) {
00122     msgErr << "Maximum structure display warnings reached, further warnings will be suppressed" << sendmsg; 
00123     structwarningcount++;
00124     return 0;
00125   }
00126 
00127   structwarningcount++;
00128   return 0;
00129 }
00130 
00131 void DrawMolItem::update_lookups(AtomColor *ac, AtomSel *sel, 
00132                                     ColorLookup *lookups) {
00133   int i;
00134   for (i=0; i<MAXCOLORS; i++) {
00135     lookups[i].num = 0;
00136   }
00137   for (i=0; i<sel->num_atoms; i++) {
00138     if (sel->on[i]) {
00139       int color = ac->color[i];
00140       lookups[color].append(i);
00141     }
00142   }
00143 }
00144   
00145 void DrawMolItem::set_pbc(int pbc) {
00146   cmdList->pbc = pbc;
00147   change_pbc(); // tell rep to update PBC transformation matrices next prepare
00148 
00149   // tell the molecule to notify its geometry monitors that the number of
00150   // periodic images has changed so that they can be turned off or on if
00151   // necessary.  XXX This may not be currently implemented in GeometryMol.
00152   mol->notify();
00153 }
00154 
00155 int DrawMolItem::get_pbc() const {
00156   return cmdList->pbc;
00157 }
00158 
00159 void DrawMolItem::set_pbc_images(int n) {
00160   if (n < 1) return;
00161   cmdList->npbc = n;
00162   need_matrix_recalc();
00163   mol->notify();
00164 }
00165 int DrawMolItem::get_pbc_images() const {
00166   return cmdList->npbc;
00167 }
00168 
00169 static void parse_frames(const char *beg, int len, int maxframe, 
00170     ResizeArray<int>& frames) {
00171   if (!len) {
00172     msgErr << "parse_frames got zero-length string!" << sendmsg;
00173     return;
00174   }
00175   const char *firstsep = NULL, *secondsep = NULL;
00176   int i;
00177   for (i=0; i<len; i++) {
00178     if (beg[i] == ':') {
00179       firstsep = beg+i;
00180       break;
00181     }
00182   }
00183   for (++i; i < len; i++) {
00184     if (beg[i] == ':') {
00185       secondsep = beg+i;
00186       break;
00187     }
00188   }
00189   char *endptr;
00190   long first, second, third;
00191   first = strtol(beg, &endptr, 0);
00192   if (endptr == beg) {
00193     msgErr << "frame element is invalid: " << beg << sendmsg;
00194     return;
00195   }
00196   second = third = first; // quiet compiler warnings
00197   if (firstsep) {
00198     firstsep++;
00199     // if no text after separator, then assume last frame.
00200     if (!(*firstsep)) {
00201       second = maxframe;
00202     } else {
00203       second = strtol(firstsep, &endptr, 0);
00204       if (endptr == firstsep) {
00205         msgErr << "frame element is invalid: " << beg << sendmsg;
00206         return;
00207       }
00208     }
00209   }
00210   if (secondsep) {
00211     secondsep++;
00212     if (!(*secondsep)) {
00213       third = maxframe;
00214     } else {
00215       third = strtol(secondsep, &endptr, 0);
00216       if (endptr == secondsep) {
00217         msgErr << "frame element is invalid: " << beg << sendmsg;
00218         return;
00219       }
00220     }
00221   }
00222   // append frames based on what we got:
00223   if (!firstsep) {
00224     //printf("adding frame %d\n", (int)first);
00225     frames.append((int)first);
00226   } else if (!secondsep) {
00227     for (long i=first; i <= second; i++) {
00228       //printf("adding frame %d\n", (int)i);
00229       frames.append((int)i);
00230     }
00231   } else {
00232     for (long i=first; i <= third; i += second) {
00233       //printf("adding frame %d\n", (int)i);
00234       frames.append((int)i);
00235     } 
00236   }
00237 }
00238 
00239 void DrawMolItem::set_drawframes(const char *frames) {
00240   if (!frames) return;
00241   while (isspace(*frames)) frames++;
00242   if (!strncmp(frames, "now", 3) && !strncmp(framesel, "now", 3)) return;
00243   delete [] framesel;
00244   framesel = stringdup(frames);
00245   needRegenerate |= MOL_REGEN;
00246 }
00247 
00249 
00250 void DrawMolItem::create_cmdlist() {
00251   int i;
00252 
00253   // do we need to recreate everything?
00254   if (needRegenerate) {
00255     // Identify the representation number for output comments
00256     repNumber = -1;   // Nonsense default number
00257     for (i = 0; i < mol->components(); i++) {
00258       if (this == mol->component(i))
00259         repNumber = i;
00260     }
00261 
00262     if (needRegenerate & COL_REGEN)
00263       atomColor->find(mol); // execute any pending color updates
00264 
00265     if (needRegenerate & SEL_REGEN)
00266       atomSel->change(NULL, mol); // update atom selection onoff flags
00267 
00268     if (needRegenerate & REP_REGEN) {
00269       // dup cmdStr to prevent overlapping strcpy() buffer pointers
00270       char *newcmdstr = stringdup(atomRep->cmdStr);
00271       atomRep->change(newcmdstr); // update the representation text
00272       delete [] newcmdstr;
00273     }
00274  
00275     reset_disp_list(); // regenerate both data block and display commands
00276 
00277     //
00278     // Record the start of a new representation geometry group
00279     // This can only be called once per rep, as the generated 
00280     // group names need to be unique.
00281     char repbuf[2048];
00282     sprintf(repbuf, "vmd_mol%d_rep%d", mol->id(), repNumber);
00283     cmdBeginRepGeomGroup.putdata(repbuf, cmdList);
00284 
00285     // If coloring by a volume texture, setup the graphics state
00286     //
00287     // We want to generate the fewest number of volume textures possible,
00288     // so we actually put this code outside all per-timestep and
00289     // per-pbc image drawing loops.  That way we'll minimize the number of
00290     // OpenGL texture downloads.  I might be able to make low level renderer
00291     // smart enough to recognize textures by some unique serial number, but
00292     // that code doesn't exist yet, so the next best thing is to be extra 
00293     // careful in generating them in the first place.
00294     //
00295     // XXX One gotcha WRT volume textures, is that since we clamp them at
00296     //     the edge of the texture, there is no provision to make PBC images
00297     //     of volumes render nicely.  Since the PBC box isn't necessarily the
00298     //     same coordinate system as the volume set, not much we can do there.
00299     //     We'd have to implement a separate PBC transform feature for the
00300     //     volumetric data in order to correctly solve this.  For now, we'll
00301     //     just live without it.
00302     if (atomColor->method() == AtomColor::VOLUME ||
00303         atomRep->method() == AtomRep::VOLSLICE) {
00304 
00305       updateVolumeTexture();
00306       // Pass a pointer to the texture map, retained in shared memory
00307       float v0[3], v1[3], v2[3], v3[3];
00308       volumeTexture.calculateTexgenPlanes(v0, v1, v2, v3);
00309       DispCmdVolumeTexture cmdVolTexture;
00310       cmdVolTexture.putdata(
00311           volumeTexture.getTextureID(),
00312           volumeTexture.getTextureSize(),
00313           volumeTexture.getTextureMap(),
00314           v0, v1, v2, v3,
00315           cmdList);
00316     }
00317 
00318     if (strcmp(framesel, "now")) {
00319       // parse the frame selection into a list of frames to process 
00320       ResizeArray<int> frames;
00321       const char *endptr = framesel;
00322       int maxframe = mol->numframes() - 1;
00323       do {
00324         const char *begptr = endptr;
00325         while (isspace(*begptr)) begptr++;
00326         endptr = begptr;
00327         while (*endptr && !isspace(*endptr)) endptr++;
00328         parse_frames(begptr, endptr-begptr, maxframe, frames);
00329       } while (*endptr);
00330 
00331       int curframe = mol->frame(); // save current frame for later
00332 
00333       // loop over the selected frames and render them all
00334       for (i=0; i<frames.num(); i++) {
00335         int frame = frames[i];
00336         if (frame < 0 || frame > maxframe) 
00337           continue;
00338 
00339         mol->override_current_frame(frame);  // override current frame
00340 
00341         // force timestep color to update, redraw or recolor affected geometry
00342         // XXX for the POS[XYZ] coloring methods, the user will need to 
00343         // select "update color every frame" in order to get time-varying color
00344         switch (atomColor->method()) {
00345           case AtomColor::USER:
00346           case AtomColor::USER2:
00347           case AtomColor::USER3:
00348           case AtomColor::USER4:
00349           case AtomColor::PHYSICALTIME:
00350           case AtomColor::TIMESTEP:
00351           case AtomColor::VELOCITY:
00352             atomColor->find(mol);  // recalc atom colors and redraw
00353             break;
00354         }   
00355         do_create_cmdlist();     // draw selected timestep(s)
00356       }
00357       mol->override_current_frame(curframe); // restore previous frame
00358     } else {
00359       do_create_cmdlist();       // draw the current timestep only
00360     }
00361 
00362     // If we have a volume texture enabled, we must turn it back off here
00363     if (atomColor->method() == AtomColor::VOLUME ||
00364         atomRep->method() == AtomRep::VOLSLICE) {
00365       append(DVOLTEXOFF);
00366     }
00367 
00368     cacheskip(1);
00369   } else {
00370     cacheskip(0);
00371   }
00372 
00373   // XXX work around a bug with texturing and display list caching
00374   //     The OpenGL texture object is not stored in the display list 
00375   //     unless the rep was created while display list caching was enabled.
00376   //     If the rep was created beforehand, then the display list will not
00377   //     contain the texture data, and thus things will be drawn incorrectly.
00378   //     For now we'll take the easy way out and disable caching of geometry
00379   //     that is textured.  The right thing to do would be to add a new 
00380   //     flag to tell the lowest level render() routine to regen the textures
00381   //     at the time display lists are created.  This is a reasonable short
00382   //     term solution however.
00383   if (atomColor->method() == AtomColor::VOLUME ||
00384       atomRep->method() == AtomRep::VOLSLICE) {
00385     cacheskip(1); // don't allow this rep to be cached
00386   }
00387 
00388   needRegenerate = NO_REGEN; // done regenerating
00389 }
00390 
00391 
00392 // regenerate the command list
00393 void DrawMolItem::do_create_cmdlist(void) {
00394   float *framepos;
00395   int i;
00396 
00397   // only render frame-oriented reps if there is a current frame
00398   if (mol->current()) {
00399     if (atomSel->do_update)
00400       atomSel->change(NULL, mol);     // update atom selection if necessary
00401 
00402     if (atomColor->do_update)
00403       atomColor->find(mol);           // update colors if necessary
00404 
00405     framepos = (mol->current())->pos; // get atom coordinates for this frame
00406 
00407     // average atom coordinates using a window of size ((2*avgsize) + 1)
00408     if (avgsize && atomSel->selected > 0) {
00409       const int curframe = mol->frame();
00410       const int begframe = curframe - avgsize;
00411       const int endframe = curframe + avgsize;
00412       const int lastframe = mol->numframes() - 1;
00413 #if 0
00414       // Only smooth coordinates of selected atoms
00415       // XXX This would be a big performance gain in most cases, but
00416       //     we can't use it if we're drawing a rep that references
00417       //     coordinates for atoms outside the selection...
00418       //     In order to use this, we'll have to validate that the
00419       //     active representation doesn't touch non-selected atom coords
00420       const int atomloopfirst = atomSel->firstsel;
00421       const int atomlooplast  = 3*(atomSel->lastsel+1);
00422 #else
00423       // Smooth all atom coordinates
00424       const int atomloopfirst = 0;
00425       const int atomlooplast  = 3*mol->nAtoms;
00426 #endif
00427       const float rescale = 1.0f/(2.0f*avgsize+1.0f);
00428 
00429 #define VMDPBCSMOOTH 1
00430 #ifdef VMDPBCSMOOTH
00431       // for pbc aware averaging
00432       int isortho,usepbc;
00433       float a,b,c,alpha,beta,gamma;
00434 
00435       // set some arbitrary defaults
00436       isortho=usepbc=1;
00437       a=b=c=9999999.0f;
00438       alpha=beta=gamma=90.0f;
00439 
00440       // reference timestep for pbc wrap
00441       const float *ref = mol->get_frame(curframe)->pos;
00442 #endif      
00443 
00444       memset(avg, 0, 3*mol->nAtoms*sizeof(float)); // clear average array
00445 
00446       for (i=begframe; i<=endframe; i++) {
00447         int ind = i;
00448         if (ind < 0) 
00449           ind = 0;
00450         else if (ind > lastframe) 
00451           ind = lastframe;
00452         const float *ts = mol->get_frame(ind)->pos;
00453 
00454 #ifdef VMDPBCSMOOTH
00455         // get periodic cell information for current frame
00456         a = mol->get_frame(ind)->a_length;
00457         b = mol->get_frame(ind)->b_length;
00458         c = mol->get_frame(ind)->c_length;
00459         alpha = mol->get_frame(ind)->alpha;
00460         beta = mol->get_frame(ind)->beta;
00461         gamma = mol->get_frame(ind)->gamma;
00462 
00463         // check validity of PBC cell side lengths
00464         if (fabsf(a*b*c) < 0.0001)
00465           usepbc=0;
00466 
00467         // check PBC unit cell shape to select proper low level algorithm.
00468         if ((alpha != 90.0) || (beta != 90.0) || (gamma != 90.0))
00469           isortho=0;
00470         
00471         // until we can handle non-orthogonal periodic cells turn off pbc handling
00472         if (!isortho)
00473           usepbc=0;
00474 
00475         if (usepbc) {
00476           const float ahalf=a*0.5f;
00477           const float bhalf=b*0.5f;
00478           const float chalf=c*0.5f;
00479 
00480           // smooth by offsetting with computed ifs using the current frame as reference.
00481           // (in the hope that the compiler can optimize this better than regular nested ifs).
00482           // only works for orthogonal cells.
00483           for (int j=atomloopfirst; j<atomlooplast; j += 3) {
00484             float adiff=ts[j  ]-ref[j  ];
00485             avg[j  ] += ts[j  ] + ((adiff < -ahalf) ? a : ( (adiff > ahalf) ? -a : 0));
00486             float bdiff=ts[j+1]-ref[j+1];
00487             avg[j+1] += ts[j+1] + ((bdiff < -bhalf) ? b : ( (bdiff > bhalf) ? -b : 0));
00488             float cdiff=ts[j+2]-ref[j+2];
00489             avg[j+2] += ts[j+2] + ((cdiff < -chalf) ? c : ( (cdiff > chalf) ? -c : 0));
00490           }
00491         } else {                // normal smoothing
00492           for (int j=atomloopfirst; j<atomlooplast; j++) {
00493             avg[j] += ts[j];
00494           }
00495         }
00496 #else 
00497         for (int j=atomloopfirst; j<atomlooplast; j++) {
00498           avg[j] += ts[j];
00499         }
00500 #endif
00501   
00502       }
00503 
00504       // scale results appropriately
00505       for (int j=atomloopfirst; j<atomlooplast; j++)
00506         avg[j] *= rescale;
00507 
00508       framepos = avg; // framepos points to the new average values
00509     }
00510 
00511     // now put in drawing commands, which index into the above block
00512     switch (atomRep->method()) {
00513       case AtomRep::LINES:        
00514         draw_lines(framepos, 
00515           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00516           atomRep->get_data(AtomRep::ISOLINETHICKNESS));
00517         place_picks(framepos);
00518         break;
00519 
00520       case AtomRep::BONDS:
00521         draw_bonds(framepos, 
00522           atomRep->get_data(AtomRep::BONDRAD),
00523           (int)atomRep->get_data(AtomRep::BONDRES),
00524           atomRep->get_data(AtomRep::ISOLINETHICKNESS));
00525         place_picks(framepos);
00526         break;
00527 
00528       case AtomRep::DYNAMICBONDS: 
00529         draw_dynamic_bonds(framepos, 
00530           atomRep->get_data(AtomRep::BONDRAD),
00531           (int)atomRep->get_data(AtomRep::BONDRES),
00532           atomRep->get_data(AtomRep::SPHERERAD)); 
00533         break;
00534 
00535       case AtomRep::HBONDS:
00536         draw_hbonds(framepos,
00537           atomRep->get_data(AtomRep::SPHERERAD),
00538           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00539           atomRep->get_data(AtomRep::BONDRAD)); 
00540         break;
00541 
00542       case AtomRep::POINTS:       
00543         draw_points(framepos, atomRep->get_data(AtomRep::LINETHICKNESS)); 
00544         place_picks(framepos);
00545         break;
00546 
00547       case AtomRep::VDW:          
00548         draw_solid_spheres(framepos,
00549           (int)atomRep->get_data(AtomRep::SPHERERES),
00550           atomRep->get_data(AtomRep::SPHERERAD),
00551           0.0);
00552         place_picks(framepos);
00553         break;
00554 
00555       case AtomRep::CPK:          
00556         draw_cpk_licorice(framepos, 1, 
00557           atomRep->get_data(AtomRep::BONDRAD),           // bond rad
00558           (int)atomRep->get_data(AtomRep::BONDRES),      // bond res
00559           atomRep->get_data(AtomRep::SPHERERAD),         // scaled VDW rad
00560           (int)atomRep->get_data(AtomRep::SPHERERES),    // sphere res
00561           (int)atomRep->get_data(AtomRep::LINETHICKNESS),// line thickness
00562           atomRep->get_data(AtomRep::ISOLINETHICKNESS)); // bonds cutoff
00563         place_picks(framepos);
00564         break;
00565 
00566       case AtomRep::LICORICE:     
00567         draw_cpk_licorice(framepos, 0, 
00568           atomRep->get_data(AtomRep::BONDRAD),            // bond rad
00569           (int)atomRep->get_data(AtomRep::BONDRES),       // bond res
00570           atomRep->get_data(AtomRep::SPHERERAD),          // scaled VDW rad
00571           (int)atomRep->get_data(AtomRep::SPHERERES),     // sphere res
00572           (int)atomRep->get_data(AtomRep::LINETHICKNESS), // line thickness
00573           atomRep->get_data(AtomRep::ISOLINETHICKNESS));  // bonds cutoff
00574         place_picks(framepos);
00575         break;
00576 
00577 #ifdef VMDPOLYHEDRA
00578       case AtomRep::POLYHEDRA:     
00579         draw_polyhedra(framepos, atomRep->get_data(AtomRep::SPHERERAD)); 
00580         break;
00581 #endif
00582 
00583       case AtomRep::TRACE:
00584         draw_trace(framepos,
00585           atomRep->get_data(AtomRep::BONDRAD),
00586           (int)atomRep->get_data(AtomRep::BONDRES),
00587           (int)atomRep->get_data(AtomRep::LINETHICKNESS));
00588         place_picks(framepos);
00589         break;
00590 
00591       case AtomRep::TUBE:
00592         draw_tube(framepos, 
00593           atomRep->get_data(AtomRep::BONDRAD),
00594           (int)atomRep->get_data(AtomRep::BONDRES));
00595         break;
00596 
00597       case AtomRep::RIBBONS:
00598         draw_ribbons(framepos, 
00599           atomRep->get_data(AtomRep::BONDRAD) / 3.0f,
00600           (int)atomRep->get_data(AtomRep::BONDRES),
00601           atomRep->get_data(AtomRep::LINETHICKNESS));
00602         break;
00603 
00604       case AtomRep::NEWRIBBONS:
00605         draw_ribbons_new(framepos,
00606           atomRep->get_data(AtomRep::BONDRAD) / 3.0f,
00607           (int)atomRep->get_data(AtomRep::BONDRES),
00608           (int)atomRep->get_data(AtomRep::SPHERERAD), // use bspline or not
00609           atomRep->get_data(AtomRep::LINETHICKNESS));
00610         break;
00611 
00612 #ifdef VMDWITHCARBS
00613       case AtomRep::RINGS_PAPERCHAIN:
00614         draw_rings_paperchain(framepos,
00615                               atomRep->get_data(AtomRep::LINETHICKNESS), // bipyramid_height
00616                               (int)atomRep->get_data(AtomRep::ISOSTEPSIZE) // maximum ring size
00617                               ); 
00618         place_picks(framepos); // XXX add better pick points in the ring traversal code
00619         break;
00620 
00621       case AtomRep::RINGS_TWISTER:
00622         draw_rings_twister(framepos,
00623                            (int)atomRep->get_data(AtomRep::LINETHICKNESS), // start_end_centroid
00624                            (int)atomRep->get_data(AtomRep::BONDRES), // hide_shared_links
00625                            (int)atomRep->get_data(AtomRep::SPHERERAD), // rib_steps
00626                            atomRep->get_data(AtomRep::BONDRAD), // rib_width
00627                            atomRep->get_data(AtomRep::SPHERERES), // rib_height
00628                            (int)atomRep->get_data(AtomRep::ISOSTEPSIZE), // maximum ring size
00629                            (int)atomRep->get_data(AtomRep::ISOLINETHICKNESS) // maximum link length
00630                            );
00631         place_picks(framepos); // XXX add better pick points in the ring traversal code
00632         break;
00633 #endif
00634 
00635       case AtomRep::NEWCARTOON:   
00636         draw_cartoon_ribbons(framepos, 
00637                              (int)atomRep->get_data(AtomRep::BONDRES),
00638                              atomRep->get_data(AtomRep::BONDRAD), 
00639                              (float) atomRep->get_data(AtomRep::LINETHICKNESS),
00640                              1,
00641                              (int)atomRep->get_data(AtomRep::SPHERERAD));
00642         break;
00643 
00644       case AtomRep::STRUCTURE:
00645         draw_structure(framepos,
00646           atomRep->get_data(AtomRep::BONDRAD),
00647           (int)atomRep->get_data(AtomRep::BONDRES),
00648           (int)atomRep->get_data(AtomRep::LINETHICKNESS));
00649         break;
00650 
00651 #ifdef VMDMSMS
00652       case AtomRep::MSMS:
00653         draw_msms(framepos, 
00654           (int)atomRep->get_data(AtomRep::BONDRES),    // draw wireframe
00655           (atomRep->get_data(AtomRep::LINETHICKNESS) < 0.5), // all / selected
00656           atomRep->get_data(AtomRep::SPHERERAD),  // probe radius
00657           atomRep->get_data(AtomRep::SPHERERES)); // point density 
00658         break;
00659 #endif
00660 #ifdef VMDSURF   
00661       case AtomRep::SURF:
00662         draw_surface(framepos,
00663           (int)atomRep->get_data(AtomRep::BONDRES),    // draw wireframe
00664           atomRep->get_data(AtomRep::SPHERERAD)); // probe radius
00665         break;
00666 #endif
00667 #ifdef VMDQUICKSURF   
00668       case AtomRep::QUICKSURF:
00669         draw_quicksurf(framepos,
00670           int(atomRep->get_data(AtomRep::BONDRES)), // quality level
00671           atomRep->get_data(AtomRep::SPHERERAD),    // sphere radius scale
00672           atomRep->get_data(AtomRep::BONDRAD),      // density isovalue
00673           atomRep->get_data(AtomRep::GRIDSPACING)); // grid spacing
00674         break;
00675 #endif
00676 
00677       case AtomRep::VOLSLICE:
00678         draw_volslice((int)atomRep->get_data(AtomRep::SPHERERES),
00679           atomRep->get_data(AtomRep::SPHERERAD),
00680           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00681           (int)atomRep->get_data(AtomRep::BONDRES));
00682         break;
00683 
00684       case AtomRep::FIELDLINES:
00685         draw_volume_field_lines((int)atomRep->get_data(AtomRep::SPHERERES),
00686           atomRep->get_data(AtomRep::SPHERERAD),
00687           atomRep->get_data(AtomRep::BONDRAD),
00688           atomRep->get_data(AtomRep::BONDRES),
00689           atomRep->get_data(AtomRep::LINETHICKNESS));
00690         break;
00691 
00692       case AtomRep::ISOSURFACE:   
00693         draw_isosurface((int)atomRep->get_data(AtomRep::SPHERERES),
00694           atomRep->get_data(AtomRep::SPHERERAD),
00695           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00696           (int)atomRep->get_data(AtomRep::BONDRES),
00697           (int)atomRep->get_data(AtomRep::ISOSTEPSIZE),
00698           (int)atomRep->get_data(AtomRep::ISOLINETHICKNESS));
00699         break;
00700 
00701       case AtomRep::ORBITAL:   
00702         draw_orbital(
00703 #if 1
00704                      (getenv("VMDMODENSITY") != NULL),
00705 #else
00706                      0,
00707 #endif
00708                      (int)atomRep->get_data(AtomRep::WAVEFNCTYPE),
00709                      (int)atomRep->get_data(AtomRep::WAVEFNCSPIN),
00710                      (int)atomRep->get_data(AtomRep::WAVEFNCEXCITATION),
00711                      (int)atomRep->get_data(AtomRep::SPHERERES),
00712                      atomRep->get_data(AtomRep::SPHERERAD),
00713                      (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00714                      (int)atomRep->get_data(AtomRep::BONDRES),
00715                      atomRep->get_data(AtomRep::GRIDSPACING),
00716                      (int)atomRep->get_data(AtomRep::ISOSTEPSIZE),
00717                      (int)atomRep->get_data(AtomRep::ISOLINETHICKNESS));
00718         break;
00719 
00720       case AtomRep::BEADS:
00721         draw_residue_beads(framepos,
00722           (int)atomRep->get_data(AtomRep::SPHERERES),
00723           atomRep->get_data(AtomRep::SPHERERAD));
00724         break;
00725 
00726       case AtomRep::DOTTED:       
00727         draw_dotted_spheres(framepos, 
00728           atomRep->get_data(AtomRep::SPHERERAD),
00729           (int)atomRep->get_data(AtomRep::SPHERERES)); 
00730         place_picks(framepos);
00731         break;
00732 
00733       case AtomRep::SOLVENT:
00734         draw_dot_surface(framepos,
00735           atomRep->get_data(AtomRep::SPHERERAD),
00736           (int)atomRep->get_data(AtomRep::SPHERERES),
00737           (int)atomRep->get_data(AtomRep::LINETHICKNESS) - 1); // method
00738         place_picks(framepos);
00739         break;
00740       default:
00741         msgErr << "Illegal atom representation in DrawMolecule." << sendmsg;
00742     }
00743   } else {
00744     // do reps that don't require a current frame
00745     switch (atomRep->method()) {
00746       case AtomRep::VOLSLICE:
00747         draw_volslice((int)atomRep->get_data(AtomRep::SPHERERES),
00748           atomRep->get_data(AtomRep::SPHERERAD),
00749           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00750           (int)atomRep->get_data(AtomRep::BONDRES));
00751         break;
00752 
00753       case AtomRep::ISOSURFACE:   
00754         draw_isosurface((int)atomRep->get_data(AtomRep::SPHERERES),
00755           atomRep->get_data(AtomRep::SPHERERAD),
00756           (int)atomRep->get_data(AtomRep::LINETHICKNESS),
00757           (int)atomRep->get_data(AtomRep::BONDRES),
00758           (int)atomRep->get_data(AtomRep::ISOSTEPSIZE),
00759           (int)atomRep->get_data(AtomRep::ISOLINETHICKNESS));
00760         break;
00761 
00762       case AtomRep::FIELDLINES:
00763         draw_volume_field_lines((int)atomRep->get_data(AtomRep::SPHERERES),
00764           atomRep->get_data(AtomRep::SPHERERAD),
00765           atomRep->get_data(AtomRep::BONDRAD),
00766           atomRep->get_data(AtomRep::BONDRES),
00767           atomRep->get_data(AtomRep::LINETHICKNESS));
00768         break;
00769     }
00770   }
00771 }
00772 
00773 
00774 // enable pick points for all atoms that are turned on
00775 void DrawMolItem::place_picks(float *pos) {
00776   DispCmdPickPointArray cmdPickPointArray;
00777 
00778   // optimize pick point generation by taking advantage of
00779   // the firstsel/lastsel indices for the active selection
00780   int selseglen = atomSel->lastsel-atomSel->firstsel+1;
00781   cmdPickPointArray.putdata(selseglen, atomSel->selected, atomSel->firstsel,
00782                             &atomSel->on[atomSel->firstsel], 
00783                             pos + 3*atomSel->firstsel, cmdList);
00784 }
00785 
00786 
00788 
00789 void DrawMolItem::do_color_changed(int ccat) {
00790   // check Display and Axes categories, since volumetric and other
00791   // representations that uses these colors will need regeneration
00792   if ((ccat == scene->category_index("Display")) ||
00793       (ccat == scene->category_index("Axes"))) {
00794     needRegenerate |= COL_REGEN; // recalc colors and redraw
00795   }
00796 
00797   // check the atom category ... see if it's for the current coloring method
00798   if (atomColor && atomColor->current_color_use(ccat)) {
00799     change_color(atomColor);    // recalc atom colors and redraw
00800   }
00801 }
00802 
00803 void DrawMolItem::do_color_rgb_changed(int color) {
00804   int ccat;
00805 
00806   // check Display and Axes categories, since volumetric and other
00807   // representations that uses these colors will need regeneration
00808   ccat = scene->category_index("Display");
00809   if ((color == scene->category_item_value(ccat, "Background")) ||
00810       (color == scene->category_item_value(ccat, "Foreground")))
00811     needRegenerate |= COL_REGEN; // recalc colors and redraw
00812 
00813   ccat = scene->category_index("Axes");
00814   if ((color == scene->category_item_value(ccat, "X")) ||
00815       (color == scene->category_item_value(ccat, "Y")) ||
00816       (color == scene->category_item_value(ccat, "Z")) ||
00817       (color == scene->category_item_value(ccat, "Origin")) ||
00818       (color == scene->category_item_value(ccat, "Labels")))
00819     needRegenerate |= COL_REGEN; // recalc colors and redraw
00820 
00821   // check all atom colors 
00822   const int *colors = atomColor->color;
00823   for (int i=0; i<mol->nAtoms; i++) {
00824     if (colors[i] == color) {
00825       change_color(atomColor);   // recalc atom colors and redraw
00826       break;
00827     }
00828   }
00829 }
00830 
00831 void DrawMolItem::do_color_scale_changed() {
00832   if (atomColor->uses_colorscale()) {
00833     atomColor->find(mol);
00834     needRegenerate |= COL_REGEN; // recalc atom colors and redraw
00835   }
00836 }
00837   
00839 
00840 // change the atom coloring method.  Return success.
00841 int DrawMolItem::change_color(AtomColor *ac) {
00842   if (ac) {
00843     *atomColor = *ac;
00844     needRegenerate |= COL_REGEN;
00845     return TRUE;
00846   } else 
00847     return FALSE;
00848 }
00849 
00850 // return which representation this is, i.e. an index from 0 ... # reps - 1
00851 int DrawMolItem::representation_index(void) {
00852   int totalreps = mol->components();
00853   for (int i=0; i < totalreps; i++) 
00854     if (mol->component(i) == this)
00855       return i;
00856 
00857   msgErr << "Unknown molecular representation found." << sendmsg;
00858   return 0; // couldn't find anything that made sense.
00859 }
00860 
00861 // prepare for drawing ... do any updates needed right before draw.
00862 void DrawMolItem::prepare() {
00863   // if the rep is off, do nothing; leave the state flags alone and
00864   // do all required actions when we're turned back on again.
00865   if (!displayed()) return;
00866 
00867   // force throb atom colors to update, recoloring affected geometry 
00868   if (atomColor->method() == AtomColor::THROB) { 
00869     needRegenerate |= COL_REGEN; // recalc atom colors and redraw    
00870   }   
00871 
00872   // update periodic image display
00873   if ((update_pbc || update_ts) && cmdList->pbc) {
00874     update_pbc_transformations();
00875     update_pbc = 0;
00876     need_matrix_recalc(); // sets the _needUpdate flag
00877   }
00878 
00879   // update secondary structure
00880   if (update_ss) {
00881     if (atomRep->method() == AtomRep::STRUCTURE ||
00882         atomRep->method() == AtomRep::NEWCARTOON)
00883       needRegenerate |= MOL_REGEN;
00884 
00885     if (atomColor->method() == AtomColor::STRUCTURE)
00886       needRegenerate |= COL_REGEN;
00887 
00888     update_ss = 0;
00889   }
00890 
00891   // update timestep
00892   if (update_ts) {
00893     // If we're drawing the current frame, treat the same as MOL_REGEN.
00894     // Otherwise, we're just using cached geometry and there's nothing to do.
00895     if (!strcmp(framesel, "now")) {
00896       needRegenerate |= MOL_REGEN;
00897 
00898       // force timestep color to update, redraw or recolor affected geometry
00899       // XXX for the POS[XYZ] coloring methods, the user will need to 
00900       // select "update color every frame" in order to get time-varying color
00901       switch (atomColor->method()) {
00902         case AtomColor::USER:
00903         case AtomColor::USER2:
00904         case AtomColor::USER3:
00905         case AtomColor::USER4:
00906         case AtomColor::PHYSICALTIME:
00907         case AtomColor::TIMESTEP:
00908         case AtomColor::VELOCITY:
00909           needRegenerate |= COL_REGEN; // recalc atom colors and redraw
00910           break;
00911       }   
00912     }
00913 
00914     update_ts = 0; // update complete
00915   }
00916 
00917   // new trajectory frames, update reps that draw multiple frames
00918   if (update_traj) {
00919     // If we're drawing multiple frames, wait for any molecule I/O to finish
00920     // before recalculating the entire set of geometry.
00921     if (strcmp(framesel, "now")) {  // we have a frame selection...
00922       // XXX ick - cast to Molecule so that we can access Molecule method
00923       Molecule *m = (Molecule *)mol; 
00924       if (!m->file_in_progress()) { // ...and file I/O is done.
00925         needRegenerate |= MOL_REGEN;
00926         update_traj = 0; // update complete
00927       }
00928     }
00929   }
00930 
00931   create_cmdlist();
00932 }
00933 
00934 void DrawMolItem::update_pbc_transformations() {
00935   const Timestep *ts = mol->current();
00936   if (!ts) return;
00937 
00938   cmdList->transX.identity();
00939   cmdList->transY.identity();
00940   cmdList->transZ.identity();
00941   ts->get_transforms(cmdList->transX, cmdList->transY, cmdList->transZ);
00942   cmdList->transXinv = cmdList->transX;
00943   cmdList->transYinv = cmdList->transY;
00944   cmdList->transZinv = cmdList->transZ;
00945   cmdList->transXinv.inverse();
00946   cmdList->transYinv.inverse();
00947   cmdList->transZinv.inverse();
00948 }
00949 
00950 
00951 // change the atom rep method.  Return success.
00952 int DrawMolItem::change_rep(AtomRep *ar) {
00953   if (!ar) return FALSE;  // XX does this actually occur?
00954 
00955 #ifdef VMDMSMS
00956   // If we're changing from MSMS to something else, free MSMS memory
00957   if (atomRep->method() == AtomRep::MSMS && ar->method() != AtomRep::MSMS)
00958     msms.clear();
00959 #endif
00960 
00961 #ifdef VMDSURF
00962   // If we're changing from Surf to something else, free Surf memory
00963   if (atomRep->method() == AtomRep::SURF && ar->method() != AtomRep::SURF)
00964     surf.clear();
00965 #endif
00966 
00967   // If we're changing from Orbital to something else, free the grid
00968   if (atomRep->method() == AtomRep::ORBITAL && ar->method() != AtomRep::ORBITAL) {
00969     waveftype = -1;
00970     wavefspin = -1;
00971     wavefexcitation = -1;
00972     gridorbid=-1;
00973     orbgridspacing = -1.0f; 
00974     delete orbvol;
00975     orbvol=NULL;
00976   }   
00977 
00978   // Free tubearray if changing from tube or cartoon to something else.
00979   // We need to check for cartoon because cartoon uses draw_tube to draw
00980   // its coils. 
00981   if ((atomRep->method() == AtomRep::TUBE ||
00982        atomRep->method() == AtomRep::STRUCTURE) && 
00983       (ar->method() != AtomRep::TUBE &&
00984        ar->method() != AtomRep::STRUCTURE)) {
00985     if (tubearray) {
00986       for (int i=0; i<tubearray->num(); i++) 
00987         delete (*tubearray)[i];
00988       delete tubearray;
00989       tubearray = NULL;
00990     }
00991   }
00992     
00993   *atomRep = *ar;
00994   needRegenerate |= REP_REGEN;
00995   return TRUE;
00996 }
00997 
00998 
00999 // change the atom selection method.  Return success.
01000 int DrawMolItem::change_sel(const char *cmdStr) {
01001   if (!cmdStr) return TRUE; // no problem
01002   if (atomSel->change(cmdStr, mol) == AtomSel::NO_PARSE) return FALSE;
01003   mol->notify(); // _after_ changing atomSel, tell molecule to update
01004                  // geometry monitors since they need to be switched off
01005                  // if they point to atoms that are no longer on.
01006   needRegenerate |= SEL_REGEN;
01007   return TRUE;
01008 }
01009 
01010 // force a recalculation
01011 void DrawMolItem::force_recalc(int r) {
01012   needRegenerate |= r;
01013 }
01014 
01015 // return whether the Nth atom is displayed in this representation
01016 int DrawMolItem::atom_displayed(int n) {
01017   return (atomSel != NULL && mol != NULL && 
01018           n >= 0 && n < mol->nAtoms && atomSel->on[n]);
01019 }
01020 
01022 void DrawMolItem::draw_lines(float *framepos, int thickness, float cutoff) {
01023   update_lookups(atomColor, atomSel, colorlookups); // update line color table
01024   int *nbonds = NULL;
01025   int *bondlists = NULL;
01026   if (cutoff > 0) {
01027     nbonds = new int[mol->nAtoms];
01028     memset(nbonds, 0, mol->nAtoms*sizeof(int));
01029     bondlists = new int[MAXATOMBONDS * mol->nAtoms];
01030     GridSearchPair *pairlist = vmd_gridsearch1(framepos, mol->nAtoms, atomSel->on, 
01031         cutoff, 0, mol->nAtoms * 27);
01032     GridSearchPair *p, *tmp;
01033     for (p=pairlist; p != NULL; p=tmp) {
01034       MolAtom *atom1 = mol->atom(p->ind1);
01035       MolAtom *atom2 = mol->atom(p->ind2);
01036   
01037       // don't bond atoms that aren't part of the same conformation    
01038       // or that aren't in the all-conformations part of the structure 
01039       if ((atom1->altlocindex != atom2->altlocindex) &&
01040           ((mol->altlocNames.name(atom1->altlocindex)[0] != '\0') &&
01041           (mol->altlocNames.name(atom2->altlocindex)[0] != '\0'))) {
01042         tmp = p->next;
01043         free(p);
01044         continue;
01045       }
01046       // Prevent hydrogens from bonding with each other.
01047       // Use atomType info derived during initial molecule analysis for speed.
01048       if (atom1->atomType == ATOMHYDROGEN &&
01049           atom2->atomType == ATOMHYDROGEN) {
01050         tmp = p->next;
01051         free(p);
01052         continue;
01053       }
01054       bondlists[p->ind1 * MAXATOMBONDS + nbonds[p->ind1]] = p->ind2;
01055       bondlists[p->ind2 * MAXATOMBONDS + nbonds[p->ind2]] = p->ind1;
01056       nbonds[p->ind1]++;
01057       nbonds[p->ind2]++;
01058       tmp = p->next;
01059       free(p);
01060       continue;
01061     }
01062   }
01063 
01064   sprintf(commentBuffer, "MoleculeID: %d ReprID: %d Beginning Lines",
01065           mol->id(), repNumber);
01066   cmdCommentX.putdata(commentBuffer, cmdList);
01067 
01068   // turn off material characteristics, set line style
01069   append(DMATERIALOFF);
01070   cmdLineType.putdata(SOLIDLINE, cmdList);
01071   cmdLineWidth.putdata(thickness, cmdList);
01072 
01073   for (int i=0; i<MAXCOLORS; i++) {
01074     const ColorLookup &cl = colorlookups[i];
01075 
01076     if (cl.num == 0) continue; // skip if no bonds of this color
01077     
01078     cmdColorIndex.putdata(i, cmdList); // set color for these half-bonds
01079   
01080     ResizeArray<float> verts;
01081     // loop over all half-bonds to be drawn in this color
01082     for (int j=0; j<cl.num; j++) { 
01083       const int id = cl.idlist[j];
01084       float *fp1 = framepos + 3*id;
01085       const MolAtom *a1 = mol->atom(id);
01086       int bondsdrawn = 0;
01087       
01088       // draw half-bond to each bonded, displayed partner
01089       // NOTE: bond mid-points are calculated TWICE, once for each atom.
01090       // Why? So each atom is drawn entirely, including all it's half-bonds,
01091       // after a single set-color command, instead of having a set-color
01092       // command for each bond.  This reduces total graphics state changes
01093       // considerably, at the cost of extra calculation here.
01094       int n = cutoff > 0 ? nbonds[id] : a1->bonds;
01095       for (int k=0; k < n; k++) {
01096         int a2n = cutoff > 0 ? bondlists[MAXATOMBONDS*id + k] : a1->bondTo[k];
01097         if (atomSel->on[a2n]) {    // bonded atom displayed?
01098           float *fp2 = framepos + 3*a2n;
01099           if (atomColor->color[a2n] == i) {
01100             // same color, so just draw a whole bond, but only if the atom
01101             // id of the other atom is higher to avoid drawing it twice.
01102             if (a2n > id) {
01103               verts.append(fp1[0]);
01104               verts.append(fp1[1]);
01105               verts.append(fp1[2]);
01106               verts.append(fp2[0]);
01107               verts.append(fp2[1]);
01108               verts.append(fp2[2]);
01109             }
01110           } else {
01111             float mid[3];
01112             mid[0] = 0.5f * (fp1[0] + fp2[0]); 
01113             mid[1] = 0.5f * (fp1[1] + fp2[1]); 
01114             mid[2] = 0.5f * (fp1[2] + fp2[2]); 
01115             verts.append(fp1[0]);
01116             verts.append(fp1[1]);
01117             verts.append(fp1[2]);
01118             verts.append(mid[0]);
01119             verts.append(mid[1]);
01120             verts.append(mid[2]);
01121           }
01122           bondsdrawn++;                // increment counter of bonds to atom i
01123         }
01124       }
01125      
01126       // if atom is all alone (no bonds drawn) draw a point 
01127       if (!bondsdrawn) {  
01128         DispCmdPoint cmdPoint;
01129         cmdPoint.putdata(fp1, cmdList);
01130       }
01131     }
01132 
01133     if (verts.num()) {
01134       float *v = &(verts[0]);
01135       DispCmdLineArray cmdLineArray;
01136       cmdLineArray.putdata(v, verts.num()/6, cmdList);
01137     }
01138   }
01139   if (cutoff > 0) {
01140     delete [] nbonds;
01141     delete [] bondlists;
01142   }
01143 }
01144 
01145 
01146 // draw all atoms as spheres 
01147 // radius is set to vdw radius * radscale + fixrad
01148 // This allows same code to be used for VDW, CPK, Licorice, etc.
01149 void DrawMolItem::draw_solid_spheres(float * framepos, int res, 
01150                                      float radscale, float fixrad) {
01151   int i;
01152 
01153   // set sphere type, resolution, and shading characteristics
01154   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning VDW",
01155           mol->id(), repNumber);
01156   cmdCommentX.putdata(commentBuffer, cmdList);
01157   append(DMATERIALON); // turn on lighting
01158   cmdSphres.putdata(res, cmdList);
01159   cmdSphtype.putdata(SOLIDSPHERE, cmdList);
01160 
01161   const char *modulatefield = NULL; // XXX this needs to become a parameter
01162   const float *modulatedata = NULL; // data field to use for width modulation
01163   int  modulateoffs  = 0;    // data offset
01164   int  modulatemult  = 1;    // data multiples
01165 
01166 #if 1
01167   // XXX hack to let users try various stuff
01168   modulatefield = getenv("VMDMODULATERADIUS");
01169 #endif
01170   if (modulatefield != NULL) {
01171     if (!strcmp(modulatefield, "user")) {
01172       modulatedata = mol->current()->user;
01173       // XXX user field can be NULL on some timesteps
01174     } else if (!strcmp(modulatefield, "user2")) {
01175       modulatedata = mol->current()->user2;
01176       // XXX user2 field can be NULL on some timesteps
01177     } else if (!strcmp(modulatefield, "user3")) {
01178       modulatedata = mol->current()->user3;
01179       // XXX user3 field can be NULL on some timesteps
01180     } else if (!strcmp(modulatefield, "user4")) {
01181       modulatedata = mol->current()->user4;
01182       // XXX user1 field can be NULL on some timesteps
01183     } else if (!strcmp(modulatefield, "vx")) {
01184       modulatedata = mol->current()->vel;
01185       // XXX vx field can be NULL on some timesteps
01186       modulateoffs = 0; modulatemult = 3;
01187     } else if (!strcmp(modulatefield, "vy")) {
01188       modulatedata = mol->current()->vel;
01189       // XXX vy field can be NULL on some timesteps
01190       modulateoffs = 1; modulatemult = 3;
01191     } else if (!strcmp(modulatefield, "vz")) {
01192       modulatedata = mol->current()->vel;
01193       // XXX vz field can be NULL on some timesteps
01194       modulateoffs = 2; modulatemult = 3;
01195     } else {
01196       modulatedata = mol->extraflt.data(modulatefield);
01197     } 
01198   }
01199 
01200   // draw spheres using new sphere array primitive
01201   if ((radscale + fixrad) > 0) {           // don't draw zero scaled spheres
01202     int ind = 0;
01203     ResizeArray<float> centers;
01204     ResizeArray<float> radii;
01205     ResizeArray<float> colors;
01206     const float *radius = mol->radius();
01207 
01208     ind = atomSel->firstsel * 3;    
01209     for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01210       // draw a sphere for each selected atom
01211       if (atomSel->on[i]) {
01212         float *fp = framepos + ind;
01213         const float *cp; 
01214 
01215         centers.append(fp[0]);
01216         centers.append(fp[1]);
01217         centers.append(fp[2]);
01218     
01219         float my_rad = 1.0f;
01220         if (modulatedata != NULL) {
01221           my_rad = modulatedata[i*modulatemult + modulateoffs];
01222           if (my_rad <= 0.0f)
01223             my_rad = 1.0f;
01224         }
01225         radii.append(radius[i]*radscale*my_rad + fixrad);
01226      
01227         cp = scene->color_value(atomColor->color[i]);
01228         colors.append(cp[0]);
01229         colors.append(cp[1]);
01230         colors.append(cp[2]);
01231       }
01232       ind += 3;
01233     }
01234    
01235     if (radii.num() > 0) {
01236       cmdSphereArray.putdata((float *) &centers[0], 
01237                              (float *) &radii[0], 
01238                              (float *) &colors[0], 
01239                              radii.num(), 
01240                              res,
01241                              cmdList);
01242     }
01243   }
01244 }
01245 
01246 // draw residues as beads
01247 // radius is set to vdw radius * radscale + fixrad
01248 // This allows same code to be used for VDW, CPK, Licorice, etc.
01249 void DrawMolItem::draw_residue_beads(float * framepos, int sres, float radscale) {
01250   int i, resid, numres;
01251 
01252   // set sphere type, resolution, and shading characteristics
01253   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning beads",
01254           mol->id(), repNumber);
01255   cmdCommentX.putdata(commentBuffer, cmdList);
01256   append(DMATERIALON); // turn on lighting
01257   cmdSphres.putdata(sres, cmdList);
01258   cmdSphtype.putdata(SOLIDSPHERE, cmdList);
01259 
01260   // draw spheres using new sphere array primitive
01261   ResizeArray<float> centers;
01262   ResizeArray<float> radii;
01263   ResizeArray<float> colors;
01264 
01265   // draw a bead for each residue
01266   numres = mol->residueList.num();
01267   for (resid=0; resid<numres; resid++) {
01268     float com[3] = {0.0, 0.0, 0.0};
01269     const ResizeArray<int> &atoms = mol->residueList[resid]->atoms;
01270     int numatoms = atoms.num();
01271     int oncount = 0;
01272     int pickindex=-1;
01273     
01274     // find COM for residue
01275     for (i=0; i<numatoms; i++) {
01276       int idx = atoms[i];
01277       if (atomSel->on[idx]) {
01278         oncount++;
01279         vec_add(com, com, framepos + 3*idx);
01280       }
01281     }
01282 
01283     if (oncount < 1)
01284       continue; // exit if there weren't any atoms
01285 
01286     vec_scale(com, 1.0f / (float) oncount, com);
01287 
01288     // find radius of bounding sphere and save last atom index for color
01289     int atomcolorindex=0; // initialize, to please compilers
01290     float boundradsq = 0.0f;
01291 #ifdef BEADELLIPSOID
01292     float avdist = 0.0f;
01293     float majoraxis[3] = {0.0, 0.0, 0.0};
01294 #endif
01295     for (i=0; i<numatoms; i++) {
01296       int idx = atoms[i];
01297       if (atomSel->on[idx]) {
01298         float tmpdist[3];
01299         atomcolorindex = idx;
01300         vec_sub(tmpdist, com, framepos + 3*idx);
01301         float distsq = dot_prod(tmpdist, tmpdist);
01302         if (distsq > boundradsq) {
01303 #ifdef BEADELLIPSOID
01304           // XXX if we want to draw an ellipsoid rather than a sphere,
01305           // we could keep track of the direction from the COM to the 
01306           // most distant atom so far, and use that as the major axis of
01307           // the ellipsoid, and use the average distance as the minor axis
01308           // for a rotationally symmetric ellipsoid.  
01309           vec_copy(majoraxis, tmpdist);
01310           avdist += sqrtf(distsq);
01311 #endif
01312           boundradsq = distsq;
01313         }
01314       }
01315     }
01316 
01317 #ifdef BEADELLIPSOID
01318     avdist /= (float) oncount;
01319     float cep1[3], cep2[3];
01320     vec_copy(cep1, majoraxis);
01321     vec_scale(cep2, -1, majoraxis);
01322     vec_add(cep1, cep1, com);
01323     vec_add(cep2, cep2, com);
01324 
01325     cmdColorIndex.putdata(atomColor->color[atomcolorindex], cmdList);
01326 
01327     // we'd draw a real ellipsoid here rather than a cylinder, but
01328     // drawing a cylinder is a pretty representative test of the idea.
01329     cmdCylinder.putdata(cep1, cep2, avdist, 8, 0, cmdList);
01330 #endif
01331 
01332     centers.append(com[0]);
01333     centers.append(com[1]);
01334     centers.append(com[2]);
01335 
01336     radii.append(radscale * (sqrtf(boundradsq) + 1.0f));
01337 
01338     const float *cp = scene->color_value(atomColor->color[atomcolorindex]);
01339     colors.append(cp[0]);
01340     colors.append(cp[1]);
01341     colors.append(cp[2]);
01342 
01343     // Add a pick point
01344     // for proteins use the CA atom
01345     if (pickindex < 0)
01346       pickindex = mol->find_atom_in_residue("CA", resid); 
01347 
01348     // for nucleic acids, use the C3', C3*, or P atom
01349     if (pickindex < 0)
01350       pickindex = mol->find_atom_in_residue("C3'", resid); 
01351     if (pickindex < 0)
01352       pickindex = mol->find_atom_in_residue("C3*", resid); 
01353     if (pickindex < 0)
01354       pickindex = mol->find_atom_in_residue("P", resid);
01355 
01356     // if nothing better is found, use the first atom
01357     if (pickindex < 0)
01358       pickindex = atoms[0];
01359 
01360     pickPoint.putdata(com, pickindex, cmdList);
01361   }
01362 
01363 #if !defined(BEADELLIPSOID)
01364   if (radii.num() > 0) {
01365     cmdSphereArray.putdata((float *) &centers[0],
01366                            (float *) &radii[0],
01367                            (float *) &colors[0],
01368                            radii.num(),
01369                            sres,
01370                            cmdList);
01371   }
01372 #endif
01373 }
01374 
01375 
01376 
01377 // draw all atoms as dotted spheres of vdw radius
01378 void DrawMolItem::draw_dotted_spheres(float * framepos, float srad, int sres) {
01379   float radscale;
01380   int i;
01381   const float *radius = mol->radius();
01382 
01383   // set sphere type, resolution, and shading characteristics
01384   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning Dotted VDW",
01385           mol->id(), repNumber);
01386   cmdCommentX.putdata(commentBuffer, cmdList);
01387   append(DMATERIALOFF); // turn off lighting
01388   cmdSphres.putdata(sres, cmdList);
01389   cmdSphtype.putdata(POINTSPHERE, cmdList);
01390 
01391   // XXX we should teach this code to sort by color and by radius, 
01392   //     which would allow us to avoid dynamic scaling of the spheres at
01393   //     display time.
01394   radscale = srad;
01395   if (radscale > 0) {                  // don't draw zero scaled spheres
01396     int lastcolor = -1;
01397     for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01398       // draw a sphere for each selected atom
01399       if (atomSel->on[i]) {
01400         if (lastcolor != atomColor->color[i]) {   // set color
01401           lastcolor = atomColor->color[i];
01402           cmdColorIndex.putdata(lastcolor, cmdList);
01403         }
01404       
01405         cmdSphere.putdata(framepos+3*i, radius[i]*radscale,cmdList);
01406       }
01407     }
01408   }
01409 }
01410 
01411 
01412 // draws each atom as a dot
01413 void DrawMolItem::draw_points(float *framepos, float pointsize) {
01414   sprintf(commentBuffer, "MoleculeID: %d ReprID: %d Beginning Points",
01415           mol->id(), repNumber);
01416   cmdCommentX.putdata(commentBuffer, cmdList);
01417   append(DMATERIALOFF);
01418 
01419   if (atomSel->selected > 0) {
01420     // optimize pick point generation by taking advantage of
01421     // the firstsel/lastsel indices for the active selection
01422     int selseglen = atomSel->lastsel-atomSel->firstsel+1;
01423 
01424     // draw spheres and pick points
01425     cmdPointArray.putdata(framepos + 3*atomSel->firstsel,
01426                           atomColor->color + atomSel->firstsel,
01427                           scene,
01428                           pointsize,
01429                           selseglen,
01430                           atomSel->on + atomSel->firstsel,
01431                           atomSel->selected,
01432                           cmdList);
01433   }
01434 
01435 }
01436 
01437 
01438 void DrawMolItem::draw_cpk_licorice(float *framepos, int cpk, float brad, int bres, float srad, int sres, int linethickness, float cutoff) {
01439   MolAtom *a1;
01440   int i, j, a2n;
01441   float radscale, fixrad; 
01442   int lastcolor = -1;
01443   int use_bonds = TRUE;
01444 
01445   if (cpk) { 
01446         brad *= 0.25f;
01447     radscale = srad * 0.25f; // scaled VDW rad
01448       fixrad = 0.0; // no fixed radius
01449   } else {
01450     radscale = 0.0;  // no VDW scaling
01451       fixrad = brad; // used fixed radius spheres only
01452 
01453     // special case -- if there is no thickness, call the wireframe
01454     if (brad == 0) {
01455       draw_lines(framepos, linethickness, cutoff);
01456       return;
01457     }
01458   }
01459 
01460   if (bres <= 2 || brad < 0.01) { 
01461     use_bonds = FALSE; // draw bonds using lines
01462   }
01463 
01464   int *nbonds = NULL;
01465   int *bondlists = NULL;
01466   if (cutoff > 0) {
01467     nbonds = new int[mol->nAtoms];
01468     memset(nbonds, 0, mol->nAtoms*sizeof(int));
01469     bondlists = new int[MAXATOMBONDS * mol->nAtoms];
01470     GridSearchPair *pairlist = vmd_gridsearch1(framepos, mol->nAtoms, atomSel->on, 
01471         cutoff, 0, mol->nAtoms * 27);
01472     GridSearchPair *p, *tmp;
01473     for (p=pairlist; p != NULL; p=tmp) {
01474       MolAtom *atom1 = mol->atom(p->ind1);
01475       MolAtom *atom2 = mol->atom(p->ind2);
01476   
01477       // don't bond atoms that aren't part of the same conformation    
01478       // or that aren't in the all-conformations part of the structure 
01479       if ((atom1->altlocindex != atom2->altlocindex) &&
01480           ((mol->altlocNames.name(atom1->altlocindex)[0] != '\0') &&
01481           (mol->altlocNames.name(atom2->altlocindex)[0] != '\0'))) {
01482         tmp = p->next;
01483         free(p);
01484         continue;
01485       }
01486       // Prevent hydrogens from bonding with each other.
01487       // Use atomType info derived during initial molecule analysis for speed.
01488       if (atom1->atomType == ATOMHYDROGEN &&
01489           atom2->atomType == ATOMHYDROGEN) {
01490         tmp = p->next;
01491         free(p);
01492         continue;
01493       }
01494       bondlists[p->ind1 * MAXATOMBONDS + nbonds[p->ind1]] = p->ind2;
01495       bondlists[p->ind2 * MAXATOMBONDS + nbonds[p->ind2]] = p->ind1;
01496       nbonds[p->ind1]++;
01497       nbonds[p->ind2]++;
01498       tmp = p->next;
01499       free(p);
01500       continue;
01501     }
01502   }
01503   sprintf (commentBuffer,"MoleculeID: %d ReprID: %d Beginning CPK",
01504          mol->id(), repNumber);
01505   cmdCommentX.putdata(commentBuffer, cmdList);
01506 
01507   append(DMATERIALON);
01508  
01509   // only draw spheres if either radscale or fixrad is nonzero 
01510   if ((radscale + fixrad) > 0) {   
01511     draw_solid_spheres(framepos, sres, radscale, fixrad);
01512   }
01513 
01514   if (use_bonds) {
01515     // draw bonds between atoms with cylinders
01516     for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01517       // for each atom, draw half-bond to other displayed atoms
01518       if (atomSel->on[i]) {
01519         float mid[3], *fp1, *fp2;
01520         fp1 = framepos + 3*i; // position of atom 'i'
01521         a1 = mol->atom(i);
01522 
01523         if (lastcolor != atomColor->color[i]) {
01524           lastcolor = atomColor->color[i];
01525           cmdColorIndex.putdata(lastcolor, cmdList);
01526         }
01527 
01528         // draw half-bond to each bonded, displayed partner
01529         int n = cutoff > 0 ? nbonds[i] : a1->bonds;
01530         for (j=0; j < n; j++) {
01531           a2n = cutoff > 0 ? bondlists[MAXATOMBONDS*i + j] : a1->bondTo[j];
01532           if (atomSel->on[a2n]) {      // bonded atom 'a2n' displayed?
01533             fp2 = framepos + 3*a2n;   // position of atom 'a2n'
01534             // find the bond midpoint 'mid' between atoms 'i' and 'a2n'
01535             mid[0] = 0.5f * (fp1[0] + fp2[0]);
01536             mid[1] = 0.5f * (fp1[1] + fp2[1]);
01537             mid[2] = 0.5f * (fp1[2] + fp2[2]);
01538 
01539             cmdCylinder.putdata(fp1, mid, brad, bres, 0, cmdList);
01540           }
01541         }
01542       }
01543     }
01544   }
01545   if (cutoff > 0) {
01546     delete [] nbonds;
01547     delete [] bondlists;
01548   }
01549 }
01550 
01551 
01552 
01553 // This draws a cylindrical bond from atom 'i' to atom 'k', but
01554 // only if both are selected.  Each end of the cylinder is colored
01555 // appropriately.  If there are bonds connected to the ends of this
01556 // i-k bond, then the i-k bond is extended so that the bonds have
01557 // no gap between them
01558 void DrawMolItem::draw_bonds(float *framepos, float brad, int bres, float cutoff) {
01559   MolAtom *a1, *a2;
01560   int g=0, h=0, i=0, j=0, k=0, l=0, m=0;
01561   int lastcolor = -1;
01562   int use_cyl;
01563   const float *bondorders = mol->bondorders();
01564 
01565   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning Bonds",
01566           mol->id(), repNumber);
01567   cmdCommentX.putdata(commentBuffer, cmdList);
01568 
01569   if (bres <= 2 || brad < 0.01 ) {
01570     // draw bonds with lines
01571     use_cyl = FALSE;
01572     append(DMATERIALOFF);
01573     cmdLineType.putdata(SOLIDLINE, cmdList);
01574     cmdLineWidth.putdata(2, cmdList);
01575   } else {
01576     use_cyl = TRUE;
01577     // set up general drawing characteristics for this drawing method
01578     // turn on material characteristics
01579     append(DMATERIALON);
01580   }
01581 
01582   for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01583     // Only bonds to 'on' atoms are considered.
01584     if (atomSel->on[i]) {
01585       float *p2 = framepos + 3*i;   // set p2 to atom 'i'
01586       a1 = mol->atom(i);            // find a selected atom
01587       float idouble[3], kdouble[3]; 
01588       float itriple[3], ktriple[3]; 
01589       const float *bondorderi = bondorders + (i * MAXATOMBONDS);
01590 
01591       for (j=0; j<a1->bonds; j++) {
01592         k = a1->bondTo[j];          // find a bonded atom that is turned on
01593         if (k > i && atomSel->on[k]) {
01594           float *p1, p3[3], *p4, *p5;
01595           a2 = mol->atom(k);
01596           p4 = framepos + 3*k;      // set p4 to atom 'k'
01597 
01598           // find the bond midpoint 'p3' between atoms 'i' and 'k' 
01599           p3[0] = 0.5f * (p2[0] + p4[0]);
01600           p3[1] = 0.5f * (p2[1] + p4[1]);
01601           p3[2] = 0.5f * (p2[2] + p4[2]);
01602 
01603           // We extend the ends of the bond to touch neighboring bonds
01604           // Look for atom 'm' bonded to 'k' that isn't 'i', to extend to
01605           p5 = NULL;                // only used if not NULL
01606           for (l=a2->bonds-1; l>=0; l--) {
01607             m = a2->bondTo[l];
01608             if (m != i && atomSel->on[m]) 
01609               p5 = framepos + 3*m; // set p5 to atom 'm'
01610               break;
01611           }
01612   
01613           // Look for atom 'g' bonded to 'i' that isn't 'k', to extend to
01614           p1 = NULL;                // only used if not NULL
01615           for (h=a1->bonds-1; h>=0; h--) {
01616             g = a1->bondTo[h];
01617             if (g != k && atomSel->on[g]) 
01618               p1 = framepos + 3*g; // set p1 to atom 'g'
01619               break;
01620           }
01621 
01622           // determine the bond order (-1 (unset), 1, 2, or 3)
01623           float order = 0;
01624           if (bondorders != NULL) {
01625             order = bondorderi[j];
01626             if (order > 1) {
01627               int lv;
01628               for (lv=0; lv<3; lv++) {
01629                 idouble[lv] = p2[lv] + 0.333f * (p4[lv] - p2[lv]);
01630                 kdouble[lv] = p2[lv] + 0.666f * (p4[lv] - p2[lv]);
01631                 itriple[lv] = p2[lv] + 0.450f * (p4[lv] - p2[lv]);
01632                 ktriple[lv] = p2[lv] + 0.550f * (p4[lv] - p2[lv]);
01633               } 
01634             }
01635           }
01636 
01637           // now I can draw the bonds, setting the color for each
01638           if (lastcolor != atomColor->color[i]) {
01639             lastcolor = atomColor->color[i];
01640             cmdColorIndex.putdata(lastcolor, cmdList);
01641           }
01642           make_connection(p1, p2, p3, p4, brad, bres, use_cyl);
01643           if (order > 1) {
01644             make_connection(NULL, idouble, p3, kdouble, brad * 1.5f, bres, use_cyl);
01645             if (order > 2)
01646               make_connection(NULL, itriple, p3, ktriple, brad * 2.0f, bres, use_cyl);
01647           }
01648 
01649           if (lastcolor != atomColor->color[k]) {
01650             lastcolor = atomColor->color[k];
01651             cmdColorIndex.putdata(lastcolor, cmdList);
01652           }
01653           make_connection(p2, p3, p4, p5, brad, bres, use_cyl);
01654           if (order > 1) {
01655             make_connection(idouble, p3, kdouble, NULL, brad * 1.5f, bres, use_cyl);
01656             if (order > 2)
01657               make_connection(itriple, p3, ktriple, NULL, brad * 2.0f, bres, use_cyl);
01658           }
01659         } // found k atom
01660       } // searching along i's bonds
01661     } // found i
01662   } // searching each atom
01663 }
01664 
01665 
01666 // given the current cylinder and the previous and next cylinders,
01667 // find and draw the cylinder (or line) that best fits without making
01668 // an overlap.  prev can be NULL, in which case it acts as if the
01669 // previous cylinder was linear with the one to draw.  Ditto (but
01670 // opposite) for next.
01671 //
01672 // The problem is that if two cylinders touch head to tail,
01673 // but at an angle, there is a gap between them.  This routine
01674 // eliminates the gap by extending the cylinder endpoints.
01675 void DrawMolItem::make_connection(float *prev, float *start, float *end,
01676                                   float *next, float rad, int res, int use_cyl)
01677 {
01678   if (!start || !end) {
01679     msgErr << "Trying to make an extended cylinder with NULL end point(s)"
01680            << sendmsg;
01681     return;
01682   }
01683 
01684   if (!use_cyl) {
01685     cmdLine.putdata(start, end, cmdList);
01686     return;
01687   }
01688 
01689   float new_start[3], new_end[3];
01690   float cthis[3];
01691   float cnext[3];
01692   float cprev[3];
01693   float length;
01694   float costheta;
01695 
01696   vec_sub(cthis, end, start);    // cthis = end - start, cylinder (b)
01697   vec_normalize(cthis);
01698 
01699   length = 0.0f;                 // initialize to zero length by default
01700   if (prev != NULL) {            // previous cylinder (a)
01701     vec_sub(cprev, start, prev); // cprev = start - prev
01702     vec_normalize(cprev);
01703 
01704     // Compute length for aligning this cylinder with previous
01705     costheta = dot_prod(cprev, cthis);
01706 
01707     // does this exceed threshhold to start extension (might speed things up)
01708     if ((costheta > 0.0f) && (costheta < 0.9999f)) 
01709       length = rad * sqrtf((1.0f-costheta) / (1.0f+costheta));
01710   }
01711   new_start[0] = start[0] - length * cthis[0];
01712   new_start[1] = start[1] - length * cthis[1];  
01713   new_start[2] = start[2] - length * cthis[2];
01714 
01715   length = 0.0f;                 // initialize to zero length by default
01716   if (next != NULL) {            // along the next cylinder (a)
01717     vec_sub(cnext, next, end);   // cnext = next - end
01718     vec_normalize(cnext);
01719 
01720     // Now move the ending point to line up with the next cylinder.
01721     costheta = dot_prod(cthis,cnext);
01722 
01723     // does this exceed threshhold to start extension (might speed things up)
01724     if ((costheta > 0.0f) && (costheta < 0.9999f)) 
01725       length = rad * sqrtf((1.0f-costheta) / (1.0f+costheta));
01726   }
01727   new_end[0] = end[0] + length * cthis[0];
01728   new_end[1] = end[1] + length * cthis[1];
01729   new_end[2] = end[2] + length * cthis[2];
01730 
01731   cmdCylinder.putdata(new_start, new_end, rad, res, 0, cmdList);
01732 }
01733 
01734 
01735 // Make a spline curve from a set of coordinates.  There are 'num' coords.
01736 // Coords goes from -2 to num+1 and has 3 floats per coord
01737 //    idx goes from -2 to num+1 and has either the atom number or -1
01738 void DrawMolItem::draw_spline_curve(int num, float *coords, int *idx,
01739                                     int use_cyl, float b_rad, int b_res) {
01740   float q[4][3];       // spline Q matrix
01741   float prev[2][3];    // the last two points for the previous spline
01742   float final[7][3];   // the points of the current approximation
01743   int last_loop = -10; // what is this value for?
01744   int loop;
01745 
01746   ResizeArray<float> pickpointcoords;
01747   ResizeArray<int> pickpointindices;
01748 
01749   const char *modulatefield = NULL; // XXX this needs to become a parameter
01750   const float *modulatedata = NULL; // data field to use for width modulation
01751   float *modulate = NULL;           // per-control point width values
01752 
01753 #if 1
01754   // XXX hack to let users try various stuff
01755   modulatefield = getenv("VMDMODULATERIBBON");
01756 #endif
01757   if (modulatefield != NULL) {
01758     if (!strcmp(modulatefield, "user")) {
01759       modulatedata = mol->current()->user;
01760       // XXX user field can be NULL on some timesteps
01761     } else {
01762       modulatedata = mol->extraflt.data(modulatefield);
01763     } 
01764 
01765     // allocate for the maximum possible per-residue modulation values
01766     // so we don't have to reallocate for every fragment
01767     // XXX the modulate array is allocated cleared to zeros so that
01768     // in the case we get a NULL modulatedata pointer (user field for
01769     // example, we'll just end up with no modulation.
01770     modulate = (float *) calloc(1, mol->nResidues * sizeof(float));
01771   }
01772 
01773 
01774   for (loop=-1; loop<num; loop++) { // go through the array
01775     // check if we need to do any computations, makes code faster
01776     // but its a bit more of a headache later on
01777     if ((idx[loop  ] >=0 && atomSel->on[idx[loop  ]]) ||
01778         (idx[loop+1] >=0 && atomSel->on[idx[loop+1]])) {
01779       make_spline_Q_matrix(q, spline_basis, coords+(loop-1)*3);
01780          
01781       // evaluate the interpolation between atom "loop" and
01782       // "loop+1".  I'll make this in 6 pieces, 7 coords.
01783       make_spline_interpolation(final[0], 0.0f/6.0f, q);
01784       make_spline_interpolation(final[1], 1.0f/6.0f, q);
01785       make_spline_interpolation(final[2], 2.0f/6.0f, q);
01786       make_spline_interpolation(final[3], 3.0f/6.0f, q);
01787       make_spline_interpolation(final[4], 4.0f/6.0f, q);
01788       make_spline_interpolation(final[5], 5.0f/6.0f, q);
01789       make_spline_interpolation(final[6], 6.0f/6.0f, q);
01790          
01791       // the possible values of 'on' are 0 (off), 1 (on),
01792       // 2 (draw 1st half), and 3 (draw 2nd half)
01793       // However, if the 1st half is drawn, the prev atom must
01794       // be on, and if the 2nd half is drawn, the new one must
01795       // be on.
01796 
01797       // draw what I need for atom 'loop'
01798       if (idx[loop] >= 0 &&          // this is a real atom
01799           (atomSel->on[idx[loop  ]] == 1 ||    // and it is turned on
01800           (atomSel->on[idx[loop  ]] == 3 && idx[loop+1] >= 0 &&
01801            atomSel->on[idx[loop+1]]))) {
01802     
01803         // here is the trickiness I was talking about
01804         // I need to see if there was a computation for
01805         // the first part of this atom
01806         if (last_loop != loop - 1) {  // no there wasn't
01807            cmdColorIndex.putdata(atomColor->color[idx[loop]], cmdList);
01808            make_connection(NULL, final[0], final[1], final[2],
01809                            b_rad, b_res, use_cyl);
01810         } else {
01811           // finish up drawing the previous part of this residue (I couldn't
01812           // do this before since I didn't know how to angle the final cylinder
01813           // to get a nice match up to the first of the current cylinders)
01814           make_connection(prev[0], prev[1], final[0], final[1],
01815                           b_rad, b_res, use_cyl);
01816           make_connection(prev[1], final[0], final[1], final[2],
01817                           b_rad, b_res, use_cyl);
01818         }
01819         make_connection(final[0], final[1], final[2], final[3],
01820                         b_rad, b_res, use_cyl);
01821         make_connection(final[1], final[2], final[3], final[4],
01822                         b_rad, b_res, use_cyl);
01823 
01824         // indicate this atom can be picked
01825         int pidx = 3 * loop;
01826         pickpointcoords.append(coords[pidx    ]);
01827         pickpointcoords.append(coords[pidx + 1]);
01828         pickpointcoords.append(coords[pidx + 2]);
01829         pickpointindices.append(idx[loop]);
01830       }
01831          
01832       // draw what I can for atom 'loop+1'
01833       if (idx[loop+1] >= 0 &&
01834           (atomSel->on[idx[loop+1]] == 1 ||
01835           (atomSel->on[idx[loop+1]] == 2 && idx[loop] >= 0 &&
01836            atomSel->on[idx[loop]]))) {
01837         cmdColorIndex.putdata(atomColor->color[idx[loop+1]], cmdList);
01838         make_connection(final[2], final[3], final[4], final[5],
01839                         b_rad, b_res, use_cyl);
01840         make_connection(final[3], final[4], final[5], final[6],
01841                         b_rad, b_res, use_cyl);
01842         last_loop = loop;
01843       }
01844       vec_copy(prev[0], final[4]);  // save for the
01845       vec_copy(prev[1], final[5]);  // next interation
01846     } 
01847   } // gone down the fragment
01848 
01849   // draw the pickpoints if we have any
01850   if (pickpointindices.num() > 0) {
01851     DispCmdPickPointArray pickPointArray;
01852     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0], 
01853                            &pickpointcoords[0], cmdList);
01854   }
01855 }
01856 
01857 
01858 void DrawMolItem::generate_tubearray() {
01859   if (tubearray) {
01860     for (int i=0; i<tubearray->num(); i++) delete (*tubearray)[i];
01861     delete tubearray;
01862   }
01863   tubearray = new ResizeArray<TubeIndexList *>;
01864 
01865   int frag;
01866   // start with protein
01867   for (frag = 0; frag < mol->pfragList.num(); frag++) {
01868     int num = mol->pfragList[frag]->num(); // number of residues
01869     if (num < 2) continue;
01870     TubeIndexList *idx = new TubeIndexList;
01871     // I'll go ahead and pad the beginning and end with two -1's for now.
01872     // It shouldn't be necessary but that's how draw_spline_curve works.
01873     idx->append(-1);
01874     idx->append(-1);
01875     for (int loop = 0; loop < num; loop++) {
01876       int res = (*mol->pfragList[frag])[loop];
01877       // Find the CA in this residue.  If not found, use the first atom
01878       // in the residue, since there has to be at least one atom.
01879       int atomnum = mol->find_atom_in_residue("CA", res);
01880       if (atomnum < 0) {
01881         atomnum = mol->atom_residue(res)->atoms[0];
01882       }
01883       idx->append(atomnum);
01884     }
01885     idx->append(-1);
01886     idx->append(-1);
01887     tubearray->append(idx);
01888   }
01889   // If there were no pfrags, check for a molecule with only CA's, 
01890   // and draw tubes connecting CA's with consecutive resid's.
01891 
01892   if (mol->pfragList.num() == 0) {
01893     int num = mol->nAtoms;
01894     int ca_num = mol->atomNames.typecode("CA"); // for fast lookup
01895     int last_resid = -10000;
01896     int resid;
01897     MolAtom *atm = NULL;
01898     
01899     TubeIndexList *idx = NULL;
01900     for (int i=0; i<=num; i++) {
01901       if (i != num) {
01902         atm = mol->atom(i);
01903         resid = atm->resid;
01904       } else {
01905         resid = -1000; // want it be out of range, but not equal to -10000.
01906       }
01907       // find a sequential CA
01908       if (atm->nameindex == ca_num && resid == last_resid + 1) {
01909         if (idx == NULL) { 
01910           msgErr << "Internal error in draw_tube for CA atoms: last_resid = "
01911             << last_resid << " but no first atom was added." << sendmsg;
01912           msgErr << "Tubes may be incomplete." << sendmsg;
01913           return;
01914         }
01915         idx->append(i);
01916       } else {
01917         if (idx) { // were there any points?
01918           idx->append(-1);
01919           idx->append(-1);
01920           tubearray->append(idx);
01921           idx = NULL;
01922         }
01923         if (atm->nameindex == ca_num && i != num) {
01924           idx = new TubeIndexList;
01925           idx->append(-1);
01926           idx->append(-1);
01927           idx->append(i);
01928         }
01929       }
01930       last_resid = resid;
01931     }
01932   }
01933 
01934   // Now for nucleic acid backbone
01935   // This is just like we did for protein, except that instead of looking
01936   // for CA we look for P, or C5', or C5*.
01937   for (frag = 0; frag < mol->nfragList.num(); frag++) {
01938     int num = mol->nfragList[frag]->num(); // number of residues
01939     if (num < 2) continue;
01940     TubeIndexList *idx = new TubeIndexList;
01941     // I'll go ahead and pad the beginning and end with two -1's for now.
01942     // It shouldn't be necessary.
01943     idx->append(-1);
01944     idx->append(-1);
01945     for (int loop = 0; loop < num; loop++) {
01946       int res = (*mol->nfragList[frag])[loop];
01947       // Find the P, C5', or C5* in this residue.  
01948       // If not found, use the first atom
01949       // in the residue, since there has to be at least one atom.
01950       int atomnum = mol->find_atom_in_residue("P", res);
01951       if (atomnum < 0) {
01952         atomnum = mol->find_atom_in_residue("C5'", res);
01953         if (atomnum < 0) {
01954           atomnum = mol->find_atom_in_residue("C5*", res);
01955           if (atomnum < 0) { 
01956             atomnum = mol->atom_residue(res)->atoms[0];
01957           }
01958         }
01959       }
01960       idx->append(atomnum);
01961     }
01962     idx->append(-1);
01963     idx->append(-1);
01964     tubearray->append(idx);
01965   }
01966 
01967   // If there were no nfrags, check for a molecule with only P's, 
01968   // and draw tubes connecting P's with consecutive resid's.
01969   // This is duplicated from what we did for CA's - the only line that's
01970   // changed is the definition of ca_num.
01971 
01972   if (mol->nfragList.num() == 0) {
01973     int num = mol->nAtoms;
01974     int ca_num = mol->atomNames.typecode("P"); // for fast lookup
01975     int last_resid = -10000;
01976     int resid;
01977     MolAtom *atm = NULL;
01978     
01979     TubeIndexList *idx = NULL;
01980     for (int i=0; i<=num; i++) {
01981       if (i != num) {
01982         atm = mol->atom(i);
01983         resid = atm->resid;
01984       } else {
01985         resid = -1000; // want it be out of range, but not equal to -10000.
01986       }
01987       // find a sequential P
01988       if (atm->nameindex == ca_num && resid == last_resid + 1) {
01989         if (idx == NULL) { 
01990           msgErr << "Internal error in draw_tube for P atoms: last_resid = "
01991             << last_resid << " but no first atom was added." << sendmsg;
01992           msgErr << "Tubes may be incomplete." << sendmsg;
01993           return;
01994         }
01995         idx->append(i);
01996       } else {
01997         if (idx) { // were there any points?
01998           idx->append(-1);
01999           idx->append(-1);
02000           tubearray->append(idx);
02001           idx = NULL;
02002         }
02003         if (atm->nameindex == ca_num && i != num) {
02004           idx = new TubeIndexList;
02005           idx->append(-1);
02006           idx->append(-1);
02007           idx->append(i);
02008         }
02009       }
02010       last_resid = resid;
02011     }
02012   }
02013 }
02014 
02015 
02016 // draw a cubic spline (in this case, using a modified Catmull-Rom basis)
02017 // through the C alpha of the protein and the P of the nucleic acids
02018 void DrawMolItem::draw_tube(float *framepos, float b_rad, int b_res) {
02019   // If we don't have tubearray yet, we need to generate it.  We don't
02020   // have to worry about selection or color because that's handled by
02021   // draw_spline_curve().
02022   if (!tubearray) {
02023     generate_tubearray();
02024   }
02025 
02026   sprintf (commentBuffer,"MoleculeID: %d ReprID: %d Beginning Tube",
02027            mol->id(), repNumber);
02028   cmdCommentX.putdata(commentBuffer, cmdList);
02029 
02030   // find out if I'm using lines or cylinders
02031   int use_cyl = FALSE;
02032   if (b_res <= 2 || b_rad < 0.01) { // then going to do lines
02033     append(DMATERIALOFF);
02034     cmdLineType.putdata(SOLIDLINE, cmdList);
02035     cmdLineWidth.putdata(2, cmdList);
02036   } else {
02037     use_cyl = TRUE;
02038     append(DMATERIALON);
02039   }
02040 
02041   for (int i=0; i<tubearray->num(); i++) {
02042     // XXX copy the current coordinates into a temporary array.
02043     TubeIndexList &indxlist = *(*tubearray)[i];
02044     int num = indxlist.num();
02045     float *coords = new float[3*num];
02046     // First two and last two points are always just the third and third to
02047     // last real control points.
02048     int firstind = indxlist[2];
02049     int lastind  = indxlist[num-3];
02050     memcpy(coords,   framepos+3*firstind, 3*sizeof(float));
02051     memcpy(coords+3, framepos+3*firstind, 3*sizeof(float));
02052     for (int i=2; i<num-2; i++) {
02053       memcpy(coords+3*i, framepos+3*indxlist[i], 3*sizeof(float));
02054     }
02055     memcpy(coords+3*(num-2), framepos+3*lastind, 3*sizeof(float));
02056     memcpy(coords+3*(num-1), framepos+3*lastind, 3*sizeof(float));
02057     draw_spline_curve(num-4, coords+6, &(indxlist[0])+2, use_cyl, b_rad, b_res);
02058     delete [] coords;
02059   }
02060 }
02061 
02062 
02063 //  This draws a hydrogen bond from atom 'i' to the hydrogen of atom 'k', only
02064 //  if both are selected and meet the hbond distance and/or angle criteria
02065 //  distance is measured from heavy atom to heavy atom
02066 //  the angle is from heavy atom to hydrogen to heavy atom
02067 //  coloring is by the donor atom color specification
02068 void DrawMolItem::draw_hbonds(float *framepos, float maxangle, int thickness, float maxdist) {
02069   int i, k;
02070   float  donortoH[3],Htoacceptor[3];
02071   float cosmaxangle2 = cosf(maxangle); cosmaxangle2 *= cosmaxangle2; 
02072   ResizeArray<float> pickpointcoords;
02073   ResizeArray<int> pickpointindices;
02074 
02075   // protect against ridiculous values which might crash us or
02076   // run the system out of memory.
02077   if (maxdist <= 0.1 || atomSel->selected == 0) {
02078     return;
02079   }
02080    
02081   int *onlist = (int *) calloc(1, mol->nAtoms * sizeof(int)); // clear to zeros
02082   for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
02083     if (atomSel->on[i] && mol->atom(i)->atomType != ATOMHYDROGEN)
02084       onlist[i] = 1;
02085   } 
02086 
02087   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning Hbonds",
02088           mol->id(), repNumber);
02089   cmdCommentX.putdata(commentBuffer, cmdList);
02090    
02091   // set up general drawing characteristics for this drawing method
02092   append(DMATERIALOFF);
02093   cmdLineType.putdata(DASHEDLINE, cmdList);
02094   cmdLineWidth.putdata(thickness, cmdList);
02095 
02096   // XXX the actual code for measuring hbonds doesn't belong here, it should
02097   //     be moved into Measure.[Ch] where it really belongs.  This file
02098   //     only implements the rendering interface, and should not be doing the
02099   //     hard core math, particularly since we want to expose the same
02100   //     feature via scripting interfaces etc.  Also, having a single
02101   //     implementation avoids having different bugs in the
02102   //     long-term.  Too late to do anything about this now, but should be
02103   //     addressed for the next major version when time allows.
02104 
02105   // XXX This code is similar, but no identical to the code in TclMeasure.C
02106   //     It would be better written if rather than emitting lines and 
02107   //     pickpoints piecemeal, if it instead got a complete list of HBonds
02108   //     and then built a single vertex array instead. 
02109  
02110   // loop over all SELECTED atoms, inspite of the fact that non-hbonding
02111   // atoms may actually be selected, searching for 
02112   // any pair that are closer than distance, not bonded,
02113   // and have a hydrogen that forms an acceptable angle.. all said 
02114   // and done it works pretty well only catching C-H---O hbonds 
02115   // only when the distance and angle are set rather unrealistic
02116   GridSearchPair *pairlist = vmd_gridsearch1(framepos, mol->nAtoms, onlist, maxdist, 0, mol->nAtoms * 27);
02117   GridSearchPair *p, *tmp;
02118   for (p=pairlist; p != NULL; p=tmp) {
02119     MolAtom *a1 = mol->atom(p->ind1);
02120     MolAtom *a2 = mol->atom(p->ind2);
02121 
02122     // ignore if bonded
02123     if (!a2->bonded(p->ind1)) {
02124       int b1 = a1->bonds;
02125       int b2 = a2->bonds;
02126       float *coor1 = framepos + 3*p->ind1; 
02127       float *coor2 = framepos + 3*p->ind2; 
02128   
02129       for (k=0; k < b2; k++) {
02130         if (mol->atom(a2->bondTo[k])->atomType == ATOMHYDROGEN) {
02131           float *hydrogen = framepos + 3*a2->bondTo[k];
02132           vec_sub(donortoH,hydrogen,coor2);
02133           vec_sub(Htoacceptor,coor1,hydrogen);
02134           if (angle(donortoH, Htoacceptor)  < maxangle ) {
02135             cmdColorIndex.putdata(atomColor->color[p->ind2], cmdList);
02136             cmdLine.putdata(coor1,hydrogen, cmdList); // draw line
02137 
02138             // indicate the bonded atoms can be picked
02139             int pidx = 3 * a2->bondTo[k];
02140             pickpointcoords.append(framepos[pidx    ]);
02141             pickpointcoords.append(framepos[pidx + 1]);
02142             pickpointcoords.append(framepos[pidx + 2]);
02143 
02144             pidx = 3 * p->ind1;
02145             pickpointcoords.append(framepos[pidx    ]);
02146             pickpointcoords.append(framepos[pidx + 1]);
02147             pickpointcoords.append(framepos[pidx + 2]);
02148 
02149             pickpointindices.append(a2->bondTo[k]);
02150             pickpointindices.append(p->ind1);
02151           }
02152         }
02153       }
02154       for (k=0; k < b1; k++){
02155         if (mol->atom(a1->bondTo[k])->atomType == ATOMHYDROGEN) {
02156           float *hydrogen = framepos + 3*a1->bondTo[k];
02157           vec_sub(donortoH,hydrogen,coor1);
02158           vec_sub(Htoacceptor,coor2,hydrogen);
02159           if (angle(donortoH, Htoacceptor)  < maxangle ) {
02160             cmdColorIndex.putdata(atomColor->color[p->ind1], cmdList);
02161             cmdLine.putdata(hydrogen,coor2, cmdList); // draw line
02162 
02163             // indicate the bonded atoms can be picked
02164             int pidx = 3 * a1->bondTo[k];
02165             pickpointcoords.append(framepos[pidx    ]);
02166             pickpointcoords.append(framepos[pidx + 1]);
02167             pickpointcoords.append(framepos[pidx + 2]);
02168 
02169             pidx = 3 * p->ind2;
02170             pickpointcoords.append(framepos[pidx    ]);
02171             pickpointcoords.append(framepos[pidx + 1]);
02172             pickpointcoords.append(framepos[pidx + 2]);
02173 
02174             pickpointindices.append(a1->bondTo[k]);
02175             pickpointindices.append(p->ind2);
02176           }
02177         }
02178       }
02179     }
02180     tmp = p->next;
02181     free(p); 
02182   }
02183   free(onlist);
02184 
02185   // draw the pickpoints if we have any
02186   if (pickpointindices.num() > 0) {
02187     DispCmdPickPointArray pickPointArray;
02188     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
02189                            &pickpointcoords[0], cmdList);
02190   }
02191 }
02192 
02193 
02194 void DrawMolItem::draw_dynamic_bonds(float *framepos, float brad, int bres, float maxdist) {
02195   int lastcolor = -1;
02196   ResizeArray<float> pickpointcoords;
02197   ResizeArray<int> pickpointindices;
02198 
02199   // protect against ridiculous values which might crash us or
02200   // run the system out of memory.
02201   if (maxdist <= 0.1 || atomSel->selected == 0) {
02202     return;
02203   }
02204    
02205   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning dynamic bonds",
02206           mol->id(), repNumber);
02207   cmdCommentX.putdata(commentBuffer, cmdList);
02208    
02209   // set up general drawing characteristics for this drawing method
02210   append(DMATERIALON);
02211  
02212   // loop over all SELECTED atoms, in spite of the fact that non-bonded
02213   // atoms may actually be selected, searching for 
02214   // any pair that are closer than distance, not bonded,
02215   GridSearchPair *pairlist = vmd_gridsearch1(framepos, mol->nAtoms, atomSel->on, maxdist, 0, mol->nAtoms * 27);
02216   GridSearchPair *p, *tmp;
02217   for (p=pairlist; p != NULL; p=tmp) {
02218     MolAtom *atom1 = mol->atom(p->ind1);
02219     MolAtom *atom2 = mol->atom(p->ind2);
02220 
02221     // don't bond atoms that aren't part of the same conformation    
02222     // or that aren't in the all-conformations part of the structure 
02223     if ((atom1->altlocindex != atom2->altlocindex) &&
02224         ((mol->altlocNames.name(atom1->altlocindex)[0] != '\0') &&
02225          (mol->altlocNames.name(atom2->altlocindex)[0] != '\0'))) {
02226       tmp = p->next;
02227       free(p);
02228       continue;
02229     }
02230 
02231     // Prevent hydrogens from bonding with each other.
02232     // Use atomType info derived during initial molecule analysis for speed.
02233     if (!(atom1->atomType == ATOMHYDROGEN) ||
02234         !(atom2->atomType == ATOMHYDROGEN)) {
02235       float *coor1 = framepos + 3*p->ind1; 
02236       float *coor2 = framepos + 3*p->ind2; 
02237       float mid[3];
02238 #if 0
02239       if (cutoff < 0) { // Do atom-specific distance check
02240         float d2 = distance2(coor1, coor2);
02241         float r1 = atom1->extra[ATOMRAD];
02242         float r2 = atom2->extra[ATOMRAD];
02243         float cut = 0.6f * (r1 + r2);
02244         if (d2 < cut*cut)
02245           // mol->add_bond(p->ind1, p->ind2);
02246       } else
02247 #endif
02248       // mol->add_bond(p->ind1, p->ind2);
02249 
02250       // draw half-bond to each bonded, displayed partner
02251       // find the bond midpoint 'mid' between atoms 'i' and 'a2n'
02252       mid[0] = 0.5f * (coor1[0] + coor2[0]);
02253       mid[1] = 0.5f * (coor1[1] + coor2[1]);
02254       mid[2] = 0.5f * (coor1[2] + coor2[2]);
02255 
02256       if (lastcolor != atomColor->color[p->ind1]) {
02257         lastcolor = atomColor->color[p->ind1];
02258         cmdColorIndex.putdata(lastcolor, cmdList);
02259       }
02260       cmdCylinder.putdata(coor1, mid, brad, bres, 0, cmdList);
02261 
02262       if (lastcolor != atomColor->color[p->ind2]) {
02263         lastcolor = atomColor->color[p->ind2];
02264         cmdColorIndex.putdata(lastcolor, cmdList);
02265       }
02266       cmdCylinder.putdata(mid, coor2, brad, bres, 0, cmdList);
02267 
02268       // indicate the bonded atoms can be picked
02269       int pidx = 3 * p->ind1;
02270       pickpointcoords.append(framepos[pidx    ]);
02271       pickpointcoords.append(framepos[pidx + 1]);
02272       pickpointcoords.append(framepos[pidx + 2]);
02273 
02274       pidx = 3 * p->ind2;
02275       pickpointcoords.append(framepos[pidx    ]);
02276       pickpointcoords.append(framepos[pidx + 1]);
02277       pickpointcoords.append(framepos[pidx + 2]);
02278 
02279       pickpointindices.append(p->ind1);
02280       pickpointindices.append(p->ind2);
02281     }
02282   
02283     tmp = p->next;
02284     free(p); 
02285   }
02286 
02287   // draw the pickpoints if we have any
02288   if (pickpointindices.num() > 0) {
02289     DispCmdPickPointArray pickPointArray;
02290     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
02291                            &pickpointcoords[0], cmdList);
02292   }
02293 }
02294 
02295 
02296 #ifdef VMDPOLYHEDRA
02297 // Based on original code by Dr. Francois-Xavier Coudert<f.coudert@ucl.ac.uk>
02298 #define PLYMAXNB 16
02299 void DrawMolItem::draw_polyhedra(float *framepos, float maxdist) {
02300   int i;
02301   int lastcolor = -1;
02302 
02303   // protect against ridiculous values which might crash us or
02304   // run the system out of memory.
02305   if (maxdist <= 0.1) {
02306     return;
02307   }
02308 
02309   // turn on all atoms for grid search (not using a real selection presently)
02310   int natoms = mol->nAtoms;
02311   int *onlist = new int[natoms];
02312   for (i=0; i<natoms; i++)
02313     onlist[i] = 1;
02314 
02315   // allocate array for selected neighbor list
02316   int *nblist = (int *) malloc(natoms * sizeof(int) * PLYMAXNB);
02317   if (nblist == NULL)
02318     return;
02319   memset(nblist, 0, natoms * sizeof(int) * PLYMAXNB);
02320 
02321   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning polyhedra",
02322           mol->id(), repNumber);
02323   cmdCommentX.putdata(commentBuffer, cmdList);
02324 
02325   // set up general drawing characteristics for this drawing method
02326   append(DMATERIALON);
02327 
02328   // loop over all SELECTED atoms, inspite of the fact that non-bonded
02329   // atoms may actually be selected, searching for 
02330   // any pair that are closer than distance, not bonded,
02331   GridSearchPair *pairlist = vmd_gridsearch1(framepos, natoms, onlist, maxdist, 0, natoms * (PLYMAXNB - 1));
02332   delete [] onlist;
02333 
02334   GridSearchPair *p, *tmp;
02335 
02336   for (p=pairlist; p != NULL; p=tmp) {
02337     MolAtom *atom1 = mol->atom(p->ind1);
02338     MolAtom *atom2 = mol->atom(p->ind2);
02339 
02340     // delete pairs where neither atom is selected
02341     if (!(atomSel->on[p->ind1] || atomSel->on[p->ind2])) {
02342       tmp = p->next;
02343       free(p);
02344       continue;
02345     }
02346 
02347     // don't bond atoms that aren't part of the same conformation    
02348     // or that aren't in the all-conformations part of the structure 
02349     if ((atom1->altlocindex != atom2->altlocindex) &&
02350         ((mol->altlocNames.name(atom1->altlocindex)[0] != '\0') &&
02351          (mol->altlocNames.name(atom2->altlocindex)[0] != '\0'))) {
02352       tmp = p->next;
02353       free(p);
02354       continue;
02355     }
02356 
02357     // record selected neighbors in the array
02358     // increment neighbor counts, clamping at PLYMAXNB-1 neighbors each
02359     int idx1 = p->ind1 * PLYMAXNB;
02360     int idx2 = p->ind2 * PLYMAXNB;
02361     if (nblist[idx1] < (PLYMAXNB-1) && nblist[idx2] < (PLYMAXNB-1)) {
02362       // update neighbor count 
02363       nblist[idx1]++; 
02364       nblist[idx2]++;
02365 
02366       // record new neighbors
02367       nblist[idx1 + nblist[idx1]] = p->ind2;
02368       nblist[idx2 + nblist[idx2]] = p->ind1;
02369     }
02370 
02371     tmp = p->next;
02372     free(p);
02373     continue;
02374   }
02375 
02376   // draw polyhedra composed of triangles for each combination
02377   // of three neighbors, using the neighbor atom coords as the vertices
02378   // XXX we should not be generating a massive list of independent 
02379   //     triangles as this is very wasteful of memory.  Instead, we
02380   //     should be building an indexed list of vertices, and generate
02381   //     one or more vertex array tokens for each of the colors that
02382   //     we'll ultimately draw.  This would save a massive amount of 
02383   //     display list memory that is wasted on needlessly 
02384   //     replicated vertex coordinates in the current implementation.
02385   for (i=0; i < natoms; i++) {
02386     int idx = i*PLYMAXNB;
02387 
02388     // don't draw a polyhedron if the atom isn't selected or
02389     // there are less than three neighbors within the user-selected 
02390     // cutoff distance
02391     if (!atomSel->on[i] || nblist[idx] < 4) 
02392       continue;
02393 
02394     // emit a color command only if necessary
02395     if (lastcolor != atomColor->color[i]) {
02396       lastcolor = atomColor->color[i];
02397       cmdColorIndex.putdata(lastcolor, cmdList);
02398     }
02399 
02400     int index=nblist[idx];
02401     for (int i1=1; i1 <= index - 2; i1++)
02402       for (int i2=i1; i2 <= index - 1; i2++)
02403         for (int i3=i2; i3 <= index; i3++)
02404           cmdTriangle.putdata(framepos + 3*nblist[idx+i1],
02405                               framepos + 3*nblist[idx+i2],
02406                               framepos + 3*nblist[idx+i3], cmdList);
02407   }
02408 
02409   free(nblist);
02410 }
02411 #endif
02412 
02413 
02414 // find x = a*i + b where i = 0..n-1
02415 static void least_squares(int n, const float *x, float *a, float *b) {
02416   float sum = 0;
02417   int i;
02418   for (i=0; i<n; i++) {    // find the sum of x
02419     sum += x[i];
02420   }
02421   float d = (float(n)-1.0f) / 2.0f;
02422   float t, sum_t2 = 0.0f;
02423   *a = 0.0f;
02424   for (i=0; i<n; i++) {
02425     t = (i - d);
02426     sum_t2 += t*t;
02427     *a += t*x[i];
02428   }
02429   *a /= sum_t2;
02430   *b = (sum/float(n) - d*(*a));
02431 }
02432 
02433 // given the info about the helix (length, color, etc.), draw it
02434 // This computes a best fit line long the given points.  For each "on"
02435 // atom, a cylinder is drawn from 1/2 a residue before to 1/2 a residue
02436 // beyond, in the right color.
02437 void DrawMolItem::draw_alpha_helix_cylinders(ResizeArray<float> &x,
02438        ResizeArray<float> &y, ResizeArray<float> &z, ResizeArray<int> &atom_on,
02439        int *color, float bond_rad, int bond_res,
02440        float *start_coord, float *end_coord)
02441 {
02442   // compute the best fit line for each direction
02443   float a[3], b[3];
02444   ResizeArray<float> pickpointcoords;
02445   ResizeArray<int> pickpointindices;
02446 
02447   int num = x.num();
02448   least_squares(num, &(x[0]), a+0, b+0);
02449   least_squares(num, &(y[0]), a+1, b+1);
02450   least_squares(num, &(z[0]), a+2, b+2);
02451   
02452   // draw the cylinder(s)
02453   float start[3], end[3];
02454   
02455   start[0] = a[0] * (-0.5f) + b[0];
02456   start[1] = a[1] * (-0.5f) + b[1];
02457   start[2] = a[2] * (-0.5f) + b[2];
02458   vec_copy(start_coord, start);
02459   for (int i=0; i<x.num(); i++) {
02460     end[0] = a[0] * (float(i)+0.5f) + b[0];
02461     end[1] = a[1] * (float(i)+0.5f) + b[1];
02462     end[2] = a[2] * (float(i)+0.5f) + b[2];
02463     if (atom_on[i] >= 0) {
02464       // indicate this atom can be picked
02465       pickpointcoords.append(x[i]);
02466       pickpointcoords.append(y[i]);
02467       pickpointcoords.append(z[i]);
02468       pickpointindices.append(atom_on[i]);
02469       cmdColorIndex.putdata(color[atom_on[i]], cmdList);
02470       int caps = 0;
02471       if (i == 0           || (i>0         && atom_on[i-1] < 0)) 
02472         caps |= CYLINDER_TRAILINGCAP;
02473       if (i == x.num() - 1 || (i<x.num()-1 && atom_on[i+1] < 0))
02474         caps |= CYLINDER_LEADINGCAP;
02475       if (bond_res <= 2 || bond_rad < 0.01) {
02476         // the representation will be as lines
02477         cmdLine.putdata(start, end, cmdList);
02478       } else {
02479         // draw the cylinder with closed ends
02480         cmdCylinder.putdata(start, end, bond_rad, bond_res, caps, cmdList);
02481       }
02482     }
02483     memcpy(start, end, 3*sizeof(float));
02484   }
02485   vec_copy(end_coord, start);
02486 
02487   // draw the pickpoints if we have any
02488   if (pickpointindices.num() > 0) {
02489     DispCmdPickPointArray pickPointArray;
02490     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
02491                            &pickpointcoords[0], cmdList);
02492   }
02493 
02494   // reset for the next round
02495   x.clear();
02496   y.clear();
02497   z.clear();
02498   atom_on.clear();
02499 }
02500 
02501 #define SCALEADD(vec, offset, byscale) {   \
02502     vec[0] += (*(offset  ))*byscale;       \
02503     vec[1] += (*(offset+1))*byscale;       \
02504     vec[2] += (*(offset+2))*byscale;       \
02505 }
02506 #define SCALESUM(vec, term1, term2, byscale) {   \
02507     vec[0] = (*(term1  )) + (*(term2  ))*byscale;       \
02508     vec[1] = (*(term1+1)) + (*(term2+1))*byscale;       \
02509     vec[2] = (*(term1+2)) + (*(term2+2))*byscale;       \
02510 }
02511 #define BETASCALE (0.2f * ribbon_width)
02512 
02513 
02514 
02515 
02516 void DrawMolItem::draw_beta_sheet(ResizeArray<float> &x,
02517        ResizeArray<float> &y, ResizeArray<float> &z, ResizeArray<int> &atom_on,
02518        int *color, float ribbon_width, float *start_coord, float *end_coord)
02519 {
02520   ResizeArray<float> pickpointcoords;
02521   ResizeArray<int> pickpointindices;
02522 
02523   // compute the points between the atoms, and do a linear extrapolation
02524   // to get the first and last point.  (I should base it on something better
02525   // than a linear fit ... ).
02526   //   I am gauranteed at least three residues for initial data
02527   int num = x.num();
02528   int i;
02529   float *centers = new float[3*(num+1)];
02530   for (i=1; i<num; i++) {   // compute the centers
02531     centers[3*i+0] = (x[i-1]+x[i])/2;
02532     centers[3*i+1] = (y[i-1]+y[i])/2;
02533     centers[3*i+2] = (z[i-1]+z[i])/2;
02534   }
02535   // and the linear extrapolation
02536   centers[0] = 2*centers[3] - centers[6];
02537   centers[1] = 2*centers[4] - centers[7];
02538   centers[2] = 2*centers[5] - centers[8];
02539 
02540   centers[3*num+0] = 2*centers[3*num-3] - centers[3*num-6];
02541   centers[3*num+1] = 2*centers[3*num-2] - centers[3*num-5];
02542   centers[3*num+2] = 2*centers[3*num-1] - centers[3*num-4];
02543 
02544   // now do the normals
02545   float *norms = new float[3*(num+1)];  // along the width
02546   float *perps = new float[3*(num+1)];  // along the height
02547   float d[2][3]; // deltas between successive points
02548   d[0][0] = x[1]-x[0];  d[0][1] = y[1]-y[0];  d[0][2] = z[1]-z[0];
02549   for (i=1; i<num-1; i++) {
02550     d[1][0] = x[i+1]-x[i];
02551     d[1][1] = y[i+1]-y[i];
02552     d[1][2] = z[i+1]-z[i];
02553     cross_prod(norms+3*i, d[0], d[1]);
02554     vec_normalize(norms+3*i);
02555     if (i%2) { // flip every other normal so they are aligned
02556       norms[3*i+0] = -norms[3*i+0];
02557       norms[3*i+1] = -norms[3*i+1];
02558       norms[3*i+2] = -norms[3*i+2];
02559     }
02560     vec_copy(d[0], d[1]);
02561   }
02562   // for the first one and last two normals, copy the end norms.
02563   vec_copy(norms, norms+3);
02564   vec_copy(norms+3*num-3, norms+3*num-6);
02565   vec_copy(norms+3*num  , norms+3*num-6);
02566 
02567   // and the perpendiculars
02568   for (i=0; i<num; i++) {
02569     vec_sub(d[0], centers+3*i, centers+3*i+3);
02570     cross_prod(perps+3*i, d[0], norms+3*i);
02571     vec_normalize(perps+3*i);
02572   }
02573   vec_copy(perps+3*num, perps+3*num-3);
02574 
02575   // Draw rectangular blocks for each segment
02576   //  upper/lower, left/right corners;  this is the beginning of the block
02577   float ul[2][3], ur[2][3], ll[2][3], lr[2][3];
02578   SCALESUM(ul[0], centers, norms, ribbon_width);
02579   vec_copy    (ll[0], ul[0]);
02580   SCALEADD(ul[0], perps, BETASCALE);
02581   SCALEADD(ll[0], perps, -BETASCALE);
02582 
02583   SCALESUM(ur[0] , centers, norms, -ribbon_width);
02584   vec_copy    (lr[0], ur[0]);
02585   SCALEADD(ur[0], perps, BETASCALE);
02586   SCALEADD(lr[0], perps, -BETASCALE);
02587 
02588   // save the initial and final coordinate
02589   vec_copy(start_coord, centers);
02590   vec_copy(end_coord, centers+3*num);
02591 
02592   int prev_on = 0;
02593   float dot=0.0f;
02594   for (i=0; i<num-1; i++) {  // go down the list of residues
02595     dot = dot_prod(perps+3*i, perps+3*i+3);
02596     // check the amount of rotation
02597     if (dot < 0) { // ribbons switched orientation
02598       // swap normals
02599       perps[3*i+3] = -perps[3*i+3];
02600       perps[3*i+4] = -perps[3*i+4];
02601       perps[3*i+5] = -perps[3*i+5];
02602       norms[3*i+3] = -norms[3*i+3];
02603       norms[3*i+4] = -norms[3*i+4];
02604       norms[3*i+5] = -norms[3*i+5];
02605     }
02606 
02607     // calculate the corners of the end of the blocks
02608     SCALESUM(ul[1], centers+3*i+3, norms+3*i+3, ribbon_width);
02609     vec_copy    (ll[1], ul[1]);
02610     SCALEADD(ul[1], perps+3*i+3, BETASCALE);
02611     SCALEADD(ll[1], perps+3*i+3, -BETASCALE);
02612     
02613     SCALESUM(ur[1] , centers+3*i+3, norms+3*i+3, -ribbon_width);
02614     vec_copy    (lr[1], ur[1]);
02615     SCALEADD(ur[1], perps+3*i+3, BETASCALE);
02616     SCALEADD(lr[1], perps+3*i+3, -BETASCALE);
02617 
02618 
02619     // draw this section, if it is on
02620     if (atom_on[i] >= 0) {
02621       // indicate this atom can be picked
02622       pickpointcoords.append(x[i]);
02623       pickpointcoords.append(y[i]);
02624       pickpointcoords.append(z[i]);
02625       pickpointindices.append(atom_on[i]);
02626 
02627       cmdColorIndex.putdata(color[atom_on[i]], cmdList);
02628 
02629       if (ribbon_width != 0) {
02630         // draw 1 or 2 segments based on how much curvature there was
02631         if (i == 0 || dot > .9 || dot < -.9) {
02632           // small flip, so I just need one segment
02633           // draw the sides
02634           cmdTriangle.putdata(ur[0], ul[0], ul[1],  // top
02635                               perps+3*i, perps+3*i, perps+3*i+3, cmdList);
02636           cmdTriangle.putdata(ur[0], ul[1], ur[1],
02637                               perps+3*i, perps+3*i+3, perps+3*i+3, cmdList);
02638           cmdTriangle.putdata(lr[0], ll[0], ll[1],  // bottom
02639                               perps+3*i, perps+3*i, perps+3*i+3, cmdList);
02640           cmdTriangle.putdata(lr[0], ll[1], lr[1],
02641                               perps+3*i, perps+3*i+3, perps+3*i+3, cmdList);
02642           
02643           // and the other sides
02644           cmdTriangle.putdata(ul[0], ll[0], ll[1], 
02645                               norms+3*i, norms+3*i, norms+3*i+3, cmdList);
02646           cmdTriangle.putdata(ul[0], ll[1], ul[1], 
02647                               norms+3*i, norms+3*i+3, norms+3*i+3, cmdList);
02648           cmdTriangle.putdata(ur[0], lr[0], lr[1], 
02649                               norms+3*i, norms+3*i, norms+3*i+3, cmdList);
02650           cmdTriangle.putdata(ur[0], lr[1], ur[1], 
02651                               norms+3*i, norms+3*i+3, norms+3*i+3, cmdList);
02652         } else {
02653           // big turn, so make more lines
02654           // given the construction, I know the first and last residues
02655           // don't twist (the perps are just copies) so I just do a simple
02656           // spline along the centers
02657           float centers_q[4][3];
02658           make_spline_Q_matrix(centers_q, spline_basis, centers + 3*i-3);
02659           
02660           // make the spline for the perps and norms
02661           float perps_q[4][3];
02662           make_spline_Q_matrix(perps_q, spline_basis, perps + 3*i-3);
02663           float norms_q[4][3];
02664           make_spline_Q_matrix(norms_q, spline_basis, norms + 3*i-3);
02665           
02666           // break the section into two parts, so compute the new middle
02667           float new_center[3];
02668           float new_perp[3];
02669           float new_norm[3];
02670           make_spline_interpolation(new_center, 0.5, centers_q);
02671           make_spline_interpolation(new_perp  , 0.5, perps_q);
02672           vec_normalize(new_perp);  // rescale correctly
02673           make_spline_interpolation(new_norm  , 0.5, norms_q);
02674           vec_normalize(new_norm);  // rescale correctly
02675           
02676           // and make the new corners
02677           float ul2[3], ur2[3], ll2[3], lr2[3];
02678           SCALESUM(ul2 , new_center, new_norm, ribbon_width);
02679           vec_copy    (ll2, ul2);
02680           SCALEADD(ul2, new_perp, BETASCALE);
02681           SCALEADD(ll2, new_perp, -BETASCALE);
02682           
02683           SCALESUM(ur2 , new_center, new_norm, -ribbon_width);
02684           vec_copy    (lr2, ur2);
02685           SCALEADD(ur2, new_perp, BETASCALE);
02686           SCALEADD(lr2, new_perp, -BETASCALE);
02687           
02688           // draw the new, intermediate blocks
02689           cmdTriangle.putdata(ur[0], ul[0], ul2,  // top
02690                               perps+3*i, perps+3*i, new_perp, cmdList);
02691           cmdTriangle.putdata(ur[0], ul2, ur2,
02692                               perps+3*i, new_perp, new_perp, cmdList);
02693           cmdTriangle.putdata(lr[0], ll[0], ll2,  // bottom
02694                               perps+3*i, perps+3*i, new_perp, cmdList);
02695           cmdTriangle.putdata(lr[0], ll2, lr2,
02696                               perps+3*i, new_perp, new_perp, cmdList);
02697 
02698         // and the other sides
02699         cmdTriangle.putdata(ul[0], ll[0], ll2, 
02700                             norms+3*i, norms+3*i, new_norm, cmdList);
02701         cmdTriangle.putdata(ul[0], ll2, ul2, 
02702                             norms+3*i, new_norm, new_norm, cmdList);
02703         cmdTriangle.putdata(ur[0], lr[0], lr2, 
02704                             norms+3*i, norms+3*i, new_norm, cmdList);
02705         cmdTriangle.putdata(ur[0], lr2, ur2, 
02706                             norms+3*i, new_norm, new_norm, cmdList);
02707         
02708         // get the 1/2 half
02709         cmdTriangle.putdata(ur2, ul2, ul[1],  // top
02710                             new_perp, new_perp, perps+3*i+3, cmdList);
02711         cmdTriangle.putdata(ur2, ul[1], ur[1],
02712                             new_perp, perps+3*i+3, perps+3*i+3, cmdList);
02713         cmdTriangle.putdata(lr2, ll2, ll[1],  // bottom
02714                             new_perp, new_perp, perps+3*i+3, cmdList);
02715         cmdTriangle.putdata(lr2, ll[1], lr[1],
02716                             new_perp, perps+3*i+3, perps+3*i+3, cmdList);
02717         
02718         // and the other sides
02719         cmdTriangle.putdata(ul2, ll2, ll[1], 
02720                             new_norm, new_norm, norms+3*i+3, cmdList);
02721         cmdTriangle.putdata(ul2, ll[1], ul[1], 
02722                             new_norm, norms+3*i+3, norms+3*i+3, cmdList);
02723         cmdTriangle.putdata(ur2, lr2, lr[1], 
02724                             new_norm, new_norm, norms+3*i+3, cmdList);
02725         cmdTriangle.putdata(ur2, lr[1], ur[1], 
02726                             new_norm, norms+3*i+3, norms+3*i+3, cmdList);
02727         }
02728         
02729         if (!prev_on) {
02730           // draw the base
02731           cmdTriangle.putdata(ul[0], ll[0], ur[0], cmdList);
02732           cmdTriangle.putdata(ll[0], lr[0], ur[0], cmdList);
02733           prev_on = 1;
02734         }
02735       } else {  // ribbon_width == 0
02736         cmdLine.putdata(centers+3*i, centers+3*i+3, cmdList);
02737       }
02738     } else { // atom_on[i] >= 0
02739       // this isn't on.  Was prev?  If so, draw its base
02740       if (prev_on && ribbon_width != 0) {
02741         cmdTriangle.putdata(ul[0], ll[0], ur[0], cmdList);
02742         cmdTriangle.putdata(ll[0], lr[0], ur[0], cmdList);
02743         prev_on = 0;
02744       }
02745     }
02746 
02747     vec_copy(ur[0], ur[1]);
02748     vec_copy(ul[0], ul[1]);
02749     vec_copy(lr[0], lr[1]);
02750     vec_copy(ll[0], ll[1]);
02751   } // end of loop though centers
02752   
02753   // but if the second to last one was on, then its end won't be visible
02754   if (prev_on && ribbon_width != 0) {
02755     cmdTriangle.putdata(ul[0], ll[0], ur[0], cmdList);
02756     cmdTriangle.putdata(ll[0], lr[0], ur[0], cmdList);
02757   }
02758 
02759   // swap the normals back, if they were swapped
02760   if (dot < 0) {
02761     perps[3*i+0] = -perps[3*i+0];
02762     perps[3*i+1] = -perps[3*i+1];
02763     perps[3*i+2] = -perps[3*i+2];
02764     norms[3*i+0] = -norms[3*i+0];
02765     norms[3*i+1] = -norms[3*i+1];
02766     norms[3*i+2] = -norms[3*i+2];
02767   }
02768 
02769   // what remains is the last one, which will be drawn as an arrow head
02770   if (atom_on[num-1] >= 0) {
02771     // indicate this atom can be picked
02772     pickpointcoords.append(x[num-1]);
02773     pickpointcoords.append(y[num-1]);
02774     pickpointcoords.append(z[num-1]);
02775     pickpointindices.append(atom_on[num-1]);
02776 
02777     // recompute the base and tip information correctly
02778     // the 'norms' direction is 50% longer in each direction
02779     norms[3*num-3] *= 1.5;
02780     norms[3*num-2] *= 1.5;
02781     norms[3*num-1] *= 1.5;
02782 
02783     SCALESUM(ul[0], centers+3*num-3, norms+3*num-3, ribbon_width);
02784     vec_copy    (ll[0], ul[0]);
02785     SCALEADD(ul[0], perps+3*num-3, BETASCALE);
02786     SCALEADD(ll[0], perps+3*num-3, -BETASCALE);
02787 
02788     SCALESUM(ur[0], centers+3*num-3, norms+3*num-3, -ribbon_width);
02789     vec_copy    (lr[0], ur[0]);
02790     SCALEADD(ur[0], perps+3*num-3, BETASCALE);
02791     SCALEADD(lr[0], perps+3*num-3, -BETASCALE);
02792 
02793     // and the tip has no norms component
02794     vec_copy    (ur[1], centers+3*num);
02795     vec_copy    (lr[1], ur[1]);
02796     SCALEADD(ur[1], perps+3*num, BETASCALE);
02797     SCALEADD(lr[1], perps+3*num, -BETASCALE);
02798 
02799     // draw the arrow
02800     cmdColorIndex.putdata(color[atom_on[num-1]], cmdList);
02801 
02802     if (ribbon_width > 0) {
02803       // the normals are the same (from a copy) so I don't worry about them
02804       cmdTriangle.putdata(ul[0], ur[1], ur[0], cmdList);    // top
02805       // but that means here I _do_ have to switch the normal direction
02806       cmdTriangle.putdata(ll[0], lr[0], lr[1], cmdList);    // bottom
02807     
02808       cmdTriangle.putdata(ul[0], ll[0], lr[1], cmdList);  // sides
02809       cmdTriangle.putdata(ul[0], lr[1], ur[1], cmdList);
02810       cmdTriangle.putdata(ur[0], lr[0], lr[1], cmdList);
02811       cmdTriangle.putdata(ur[0], lr[1], ur[1], cmdList);
02812 
02813       cmdTriangle.putdata(ul[0], ll[0], ur[0], cmdList);  // and base
02814       cmdTriangle.putdata(ll[0], lr[0], ur[0], cmdList);
02815     } else {// ribbon_width == 0
02816       cmdLine.putdata(centers+3*num-3, centers+3*num, cmdList);
02817     } // special case for ribbon_width == 0
02818   }
02819   delete [] perps;
02820   delete [] norms;
02821   delete [] centers;
02822 
02823   // draw the pickpoints if we have any
02824   if (pickpointindices.num() > 0) {
02825     DispCmdPickPointArray pickPointArray;
02826     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
02827                            &pickpointcoords[0], cmdList);
02828   }
02829 
02830   // reset for the next round
02831   x.clear();
02832   y.clear();
02833   z.clear();
02834   atom_on.clear();
02835 }
02836 
02837 // draw based on secondary structure elements
02838 void DrawMolItem::draw_structure(float *framepos, float brad, int bres, int linethickness) {
02839   sprintf (commentBuffer,"MoleculeID: %d ReprID: %d Beginning Cartoon",
02840          mol->id(), repNumber);
02841   cmdCommentX.putdata(commentBuffer, cmdList);
02842 
02843   // Indicate that I need secondary structure information
02844   mol->need_secondary_structure(1);  // calculate 2ndary structure if need be
02845 
02846   int h_start, b_start; // atom indices that start beta sheets and helices
02847   int atom=0;
02848   int frag, res;
02849 
02850   ResizeArray<float> resx;      // coord of start/end of structure
02851   ResizeArray<float> resy;
02852   ResizeArray<float> resz;
02853   ResizeArray<int> CA_num;      // CA of start/end structure
02854   ResizeArray<int> extra_resid; // CA of very short residues
02855 
02856   ResizeArray<float> x;         // coords (x,y,z) of the residues
02857   ResizeArray<float> y;
02858   ResizeArray<float> z;
02859   ResizeArray<int> atom_on;     // turn on/off unselected parts of the helix
02860 
02861 
02862   //
02863   // draw protein alpha helices as cylinders
02864   //
02865   if (bres <= 2 || brad < 0.01) {
02866     cmdLineType.putdata(SOLIDLINE, cmdList);
02867     cmdLineWidth.putdata(2, cmdList);
02868     append(DMATERIALOFF);
02869   } else {
02870     append(DMATERIALON);
02871   }
02872   
02873   h_start = -1; // reset the helix starting atom index
02874   for (frag=0; frag<mol->pfragList.num(); frag++) {
02875     int num = mol->pfragList[frag]->num();
02876     for (int resindex=0; resindex < num; resindex++) {
02877       res = (*mol->pfragList[frag])[resindex];
02878       const int ss = mol->residue(res)->sstruct;
02879       int residue_is_helix = 
02880         (ss == SS_HELIX_ALPHA || ss == SS_HELIX_3_10 ||
02881          ss == SS_HELIX_PI);
02882 
02883       if (residue_is_helix) {
02884         atom = mol->find_atom_in_residue("CA", res);
02885         if (atom >= 0) {
02886           if (h_start == -1) {
02887             h_start = atom;             // just started a helix
02888           }
02889 
02890           x.append(framepos[3*atom+0]); // add CA atom to the coordinate arrays
02891           y.append(framepos[3*atom+1]);
02892           z.append(framepos[3*atom+2]);
02893 
02894           if (atomSel->on[atom]) {      // atom_on contains either
02895             atom_on.append(atom);       // the atom index (if on)
02896           } else {
02897             atom_on.append(-1);         // or -1 (if off)
02898           }
02899         } else {
02900           msgErr << "Missing a CA in a protein residue!!" << sendmsg;
02901         }
02902       }
02903 
02904       // If we got something that wasn't helix, or if we're on the last
02905       // residue of the fragment, draw what we have.
02906       if (!residue_is_helix || resindex == num-1) {
02907         if (x.num() <= 1) {
02908           // msgWarn << "Cartoon will not draw a helix of length 1" << sendmsg;
02909           if (x.num() == 1) {
02910             extra_resid.append(atom);  // keep track of these for the coil
02911           }
02912           x.clear();                   // clear the coordinate arrays
02913           y.clear();
02914           z.clear();
02915         } else {
02916           float ends[2][3];
02917           CA_num.append(h_start);      // save the residues which start
02918           CA_num.append(atom);         // and end the helix structure
02919           draw_alpha_helix_cylinders(x, y, z, atom_on, atomColor->color, 
02920                                      brad, bres, ends[0], ends[1]);
02921 
02922           resx.append(ends[0][0]);     // save helix start/end coordinates
02923           resy.append(ends[0][1]);
02924           resz.append(ends[0][2]);
02925           resx.append(ends[1][0]);
02926           resy.append(ends[1][1]);
02927           resz.append(ends[1][2]);
02928         }
02929 
02930         h_start = -1; // reset the helix starting atom index
02931       } // drew helix
02932     } // went through residues
02933   } // went through pfrag
02934 
02935   //
02936   // now do beta sheets via another pass through the pfrags
02937   //
02938   if (linethickness == 0) {
02939     cmdLineType.putdata(SOLIDLINE, cmdList);
02940     cmdLineWidth.putdata(2, cmdList);
02941     append(DMATERIALOFF);
02942   } else {
02943     append(DMATERIALON);
02944   }
02945 
02946   b_start = -1; // reset the beta sheet starting atom index
02947   for (frag=0; frag<mol->pfragList.num(); frag++) {
02948     int num = mol->pfragList[frag]->num();
02949     for (int resindex=0; resindex < num; resindex++) {
02950       int res = (*mol->pfragList[frag])[resindex];
02951       const int ss = mol->residue(res)->sstruct;
02952       int is_beta = (ss == SS_BETA || ss == SS_BRIDGE);
02953       if (is_beta) {
02954         atom = mol->find_atom_in_residue("CA", res);
02955         if (atom >= 0) {
02956           if (b_start == -1) {
02957             b_start = atom;             // just started a sheet
02958           }
02959           x.append(framepos[3*atom+0]); // add CA atom to the coordinate arrays
02960           y.append(framepos[3*atom+1]);
02961           z.append(framepos[3*atom+2]);
02962           if (atomSel->on[atom]) {      // atom_on contains either
02963             atom_on.append(atom);       // the atom index (if on)
02964           } else {
02965             atom_on.append(-1);         // or -1 (if off)
02966           }
02967         } else {
02968           msgErr << "Missing a CA in a protein residue!!" << sendmsg;
02969         }
02970       }
02971 
02972       // Draw what we have if we got to a non-beta residue, or if this was
02973       // the last residue.
02974       if (b_start != -1 && (!is_beta || resindex == num-1)) {
02975         if (x.num() <= 2) {
02976           // msgWarn << "Cartoon will not draw a sheet with less than "
02977           //         << "3 residues" << sendmsg;
02978 
02979           extra_resid.append(b_start); // keep track of these for the coil
02980           if (x.num() == 2) {
02981             extra_resid.append(atom);  // keep track of these for the coil
02982           }
02983           x.clear();                   // clear the coordinate arrays
02984           y.clear();
02985           z.clear();
02986           atom_on.clear();             // clear the 'on' array
02987         } else {
02988           float ends[2][3];
02989           CA_num.append(b_start);      // save the residues which start
02990           CA_num.append(atom);         // and end the beta sheet structure
02991           draw_beta_sheet(x, y, z, atom_on, atomColor->color,
02992                linethickness / 5.0f,
02993                ends[0], ends[1]);
02994           resx.append(ends[0][0]);     // save beta sheet start/end coordinates
02995           resy.append(ends[0][1]);
02996           resz.append(ends[0][2]);
02997           resx.append(ends[1][0]);
02998           resy.append(ends[1][1]);
02999           resz.append(ends[1][2]);
03000         }
03001 
03002         b_start = -1; // reset the beta sheet starting atom index
03003       }
03004     }  // went through the residues
03005   } // went through pfrag
03006 
03007 
03008   // Finally, everything left I can draw as a spline.  The tricky part
03009   // is I need to get the start/end coordinates of the cylinders and
03010   // sheets correct, for otherwise  the tube doesn't meet with them nicely.
03011   // Luckily, I kept track of the start/end coordinates as I went along, so
03012   // I swap the coords of that array with the frampos data, replace atomSel'
03013   // 'on' with a new 'on', and call draw_tube.  Code reuse, right?  
03014   // Anyway, when I return I swap everything back
03015   {
03016     // finding the atoms is easy; search for 'protein not (sheet or helix)'
03017     // and not those atoms in the same residue as the extra_resid list.
03018     int nres, i;
03019 
03020     // allocate and clear a new 'on' list
03021     int *on = new int[mol->nAtoms];
03022     memset(on, 0, mol->nAtoms*sizeof(int));
03023 
03024     // find "protein and not (sheet or helix)"
03025     nres=mol->residueList.num();
03026     for (i=0; i<nres; i++) {
03027       const Residue *res = mol->residueList[i];
03028       if (res->residueType == RESPROTEIN) {
03029         const int ss = res->sstruct;
03030         if (ss == SS_TURN || ss == SS_COIL) {
03031           int numatoms = res->atoms.num();
03032           for (int j=0; j<numatoms; j++) {
03033             on[res->atoms[j]] = 1;
03034           }
03035         }
03036       }
03037     }
03038     
03039     // collect the leftover residues in "short" sheets or cylinders
03040     // and draw them as tubes as well
03041     nres=extra_resid.num();
03042     for (i=0; i<nres; i++) {
03043       int res = mol->atom(extra_resid[i])->uniq_resid;
03044       Residue *r = mol->residue(res);
03045       int numatoms = r->atoms.num();
03046       for (int j=0; j<numatoms; j++) {
03047         on[r->atoms[j]] = 1;
03048       }
03049     }
03050 
03051     // swap the real CA atom coordinates with calculated 
03052     // control point coordinates so that draw_tube generates
03053     // the right shape.
03054     float tmp_coords[3];
03055     int atom; 
03056     int numcaatoms = CA_num.num();
03057     for (i=0; i<numcaatoms; i++) {
03058       atom = 3*CA_num[i];
03059       vec_copy(tmp_coords, framepos+atom);
03060       framepos[atom+0] = resx[i];
03061       framepos[atom+1] = resy[i];
03062       framepos[atom+2] = resz[i];
03063       resx[i] = tmp_coords[0];
03064       resy[i] = tmp_coords[1];
03065       resz[i] = tmp_coords[2];
03066       // turn CA of the start/end on as well
03067       on[CA_num[i]] = 2+i%2;    
03068       // the "2" is a special case for draw_spline_curve which will only
03069       // draw the first 1/2 if it is a 2 or second half if it is a 3
03070     }
03071 
03072     // replace the list of "on" atoms with the new list
03073     for (i=0; i<mol->nAtoms; i++) {
03074       if (!atomSel->on[i]) { // mask the coil against the selected atoms
03075         on[i] = 0;
03076       }
03077     }
03078     int *temp_on = atomSel->on;
03079     atomSel->on = on;
03080 
03081     // make the tube
03082     draw_tube(framepos, 
03083               brad / 7.0f,
03084               int(float(bres) * 2.0f/3.0f + 0.5f));
03085 
03086     // and revert to the original coordinate and 'on' arrays
03087     atomSel->on = temp_on;
03088     for (i=0; i<numcaatoms; i++) {
03089       atom = 3*CA_num[i];
03090       framepos[atom+0] = resx[i];
03091       framepos[atom+1] = resy[i];
03092       framepos[atom+2] = resz[i];
03093     }
03094 
03095     delete [] on; // delete the temporary 'on' array
03096   }
03097 
03098   append(DMATERIALOFF);
03099 }
03100 
03101 int DrawMolItem::pickable_on() {
03102   return displayed() && mol && mol->displayed();
03103 }
03104 

Generated on Tue Jun 18 01:47:33 2013 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002