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

DrawMolItemRibbons.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: DrawMolItemRibbons.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.141 $      $Date: 2011/06/16 14:37:01 $
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 "DrawMolecule.h"
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 #include "WKFUtils.h"
00041 
00042 #define RIBBON_ERR_NOTENOUGH            1
00043 #define RIBBON_ERR_PROTEIN_MESS         2
00044 #define RIBBON_ERR_MISSING_PHOSPHATE    4
00045 #define RIBBON_ERR_MISSING_O1P_O2P      8
00046 #define RIBBON_ERR_BADNUCLEIC          16
00047 #define RIBBON_ERR_BADPYRIMIDINE       32
00048 #define RIBBON_ERR_BADPURINE           64
00049 
00050 #if 0
00051 #define RIBBON_ERR_NOTENOUGH_STR         "Cannot draw a ribbon for a nucleic acid with only one element"
00052 #define RIBBON_ERR_PROTEIN_MESS_STR      "Someone's been messing around with the definition of a protein!\nThings are going to look messy"
00053 #define RIBBON_ERR_MISSING_PHOSPHATE_STR "Cannot find first phosphate of a nucleic acid, so it won't be drawn"
00054 #define RIBBON_ERR_MISSING_O1P_O2P_STR   "Cannot find both O1P and O2P of a nucleic acid, so it won't be drawn"
00055 #define RIBBON_ERR_BADNUCLEIC_STR        "Someone has redefined the nucleic acid atom names!"
00056 #define RIBBON_ERR_BADPYRIMIDINE_STR     "Tried to draw a nucleic acid residue I thought was a pyrimidine, but it doesn't have the right atom names."
00057 #define RIBBON_ERR_BADPURINE_STR         "Tried to draw a nucleic acid residue I thought was a purine, but it doesn't have the right atom names."
00058 #endif
00059 
00060 // draw a spline ribbon.  The math is the same as a tube. 
00061 // However, two splines are calculated, one for coords+perp
00062 // and the other along coords-perp.  Triangles are used to
00063 // connect the curves.  If cylinders are used,  they are
00064 // drawn along the spline paths.  The final result should
00065 // look something like:
00066 //    ooooooooooooooooooo   where the oooo are the (optional) cylinders
00067 //    ! /| /| /| /| /| /!   the ! are a control point, in the ribbon center
00068 //    |/ |/ |/ |/ |/ |/ |   the /| are the triangles  (size per interpolation)
00069 //    ooooooooooooooooooo   the edges go through coords[i] +/- offset[i]
00070 void DrawMolItem::draw_spline_ribbon(int num, float *coords,
00071         float *offset, int *idx, int use_cyl, float b_rad, int b_res) {
00072   ResizeArray<float> pickpointcoords;
00073   ResizeArray<int> pickpointindices;
00074   float q[4][3];
00075   // for the next 2 variables, the information for this residue
00076   // starts at element 2; elements 0 and 1 are copies of the last
00077   // two elements of the previous residue
00078   float pts[2][9][3];  // data for 2 edges, 7 points (+2 overlaps from
00079                        // the previous interpolation) and x,y,z
00080    
00081   //  contains the norms as [edge][interpolation point][x/y/z]
00082   float norms[2][8][3]; 
00083   // contains the summed norms of the sequential triangles
00084   // the first element (# 0) contains the info for what should have
00085   // been the last two triangles of the previous residue.  In other
00086   // words, tri_norm[0][0] contains the summed norm for the point
00087   // located at pts[1][0]
00088   float tri_norm[2][7][3]; 
00089 
00090   int last_loop = -10;
00091   int loop, i, j;
00092   float *tmp_ptr = (float *) malloc(2*(num+4) * sizeof(float) * 3);
00093   if (tmp_ptr == NULL) {
00094     msgErr << "Cannot make a ribbon; not enough memory!" << sendmsg;
00095     return;
00096   }
00097   float *edge[2];
00098 
00099   // XXX disgusting array math here, rewrite!
00100   // copy the coordinates +/- the offsets into the temp ("edge") arrays
00101   edge[0] = tmp_ptr + 2*3;
00102   memcpy(edge[0]-2*3, coords-2*3, (num+4)*sizeof(float)*3);
00103   edge[1] = edge[0] + (num+4)*3;
00104   memcpy(edge[1]-2*3, coords-2*3, (num+4)*sizeof(float)*3);
00105   for (j=-2*3; j<(num+2)*3-1; j++) {
00106     edge[0][j] += offset[j];
00107     edge[1][j] -= offset[j];
00108   }
00109 
00110   // go through the data points
00111   for (loop=-1; loop<num; loop++) {
00112     int j;
00113 
00114     // If I'm to draw anything....
00115     if ((idx[loop] >= 0 && atomSel->on[idx[loop]]) ||
00116         (idx[loop+1] >= 0 && atomSel->on[idx[loop+1]])) {
00117 
00118       // construct the interpolation points (into the "pts" array)
00119       // remember, these are offset by two to keep some information
00120       // about the previous residue in the first 2 elements
00121       for (i=0; i<=1; i++) {
00122         make_spline_Q_matrix(q, spline_basis, edge[i]+(loop-1)*3);
00123         make_spline_interpolation(pts[i][2], 0.0f/6.0f, q);
00124         make_spline_interpolation(pts[i][3], 1.0f/6.0f, q);
00125         make_spline_interpolation(pts[i][4], 2.0f/6.0f, q);
00126         make_spline_interpolation(pts[i][5], 3.0f/6.0f, q);
00127         make_spline_interpolation(pts[i][6], 4.0f/6.0f, q);
00128         make_spline_interpolation(pts[i][7], 5.0f/6.0f, q);
00129         make_spline_interpolation(pts[i][8], 6.0f/6.0f, q);
00130       }
00131 
00132       // make the normals for each new point.
00133       for (i=2; i<8; i++) {
00134         float diff1[3], diff2[3];
00135         vec_sub(diff1, pts[1][i+1], pts[1][i+0]);
00136         vec_sub(diff2, pts[1][i+1], pts[0][i+0]);
00137         cross_prod(norms[1][i], diff1, diff2);
00138         vec_sub(diff1, pts[0][i+1], pts[0][i+0]);
00139         cross_prod(norms[0][i], diff1, diff2);
00140       }
00141 
00142       // if this wasn't a continuation, I need to set the
00143       // first 2 elements properly so the norms are smooth
00144       if (last_loop != loop-1) {
00145         vec_copy(norms[0][0], norms[0][2]);
00146         vec_copy(norms[1][0], norms[1][2]);
00147         vec_copy(norms[0][1], norms[0][2]);
00148         vec_copy(norms[1][1], norms[1][2]);
00149       }
00150 
00151       // sum up the values of neighboring triangles to make
00152       // a smooth normal
00153       for (j=0; j<=1; j++) {
00154         for (i=0; i<8-1; i++) {
00155           vec_add(tri_norm[j][i], norms[j][i+1],     // "this" triangle
00156                   norms[1-j][i+1]);  // opposite strand
00157           vec_add(tri_norm[j][i], tri_norm[j][i],
00158                   norms[j][i]);      // prev on strand
00159         }
00160       }
00161 
00162       // pre-normalize the normals so we don't need to have
00163       // OpenGL doing this for us repetitively on every frame,
00164       // this allows use to use the GL_RESCALE_NORMAL extension
00165       for (j=0; j<=1; j++) {
00166         for (i=0; i<8-1; i++) {
00167           vec_normalize(tri_norm[j][i]);
00168         } 
00169       }
00170          
00171       // draw what I need for atom 'loop'
00172       if (idx[loop] >= 0 &&          // this is a real atom
00173           atomSel->on[idx[loop]]) {  // and it it turned on
00174 
00175         if (last_loop != loop - 1) {  
00176           // do prev. points exist? if not then I don't know if the color 
00177           // was properly set, so set it here
00178           cmdColorIndex.putdata(atomColor->color[idx[loop]], cmdList);
00179         }
00180 
00181         // draw the cylinders to finish off the last residue, if
00182         // need be, and draw the ones for this half of the residue
00183         // Cylinders are drawn on the top and bottom of the residues
00184         if (use_cyl) { // draw top/bot edge cylinders if need be
00185           // Special case the first cylinder because I want
00186           // it to be a smooth continuation from the previous
00187           // cylinder; assuming it exists
00188           if (last_loop != loop-1) {  // continue from previous?
00189             int ii;
00190             for (ii=0; ii<=1; ii++) {  // doesn't exist, so
00191               make_connection(NULL, pts[ii][2], pts[ii][3],
00192                               pts[ii][4], b_rad, b_res, use_cyl);
00193             }
00194           } else { // there was a previous cylinder, so be smooth
00195             for (i=0; i<=1; i++) {
00196                make_connection(pts[i][0], pts[i][1], pts[i][2], pts[i][3],
00197                                b_rad, b_res, use_cyl);
00198                make_connection(pts[i][1], pts[i][2], pts[i][3], pts[i][4],
00199                                b_rad, b_res, use_cyl);
00200             }
00201           }
00202                
00203           // and draw the rest of the cylinders for this 1/2 residue
00204           for (i=0; i<=1; i++) {
00205             make_connection(pts[i][2], pts[i][3], pts[i][4], pts[i][5],
00206                             b_rad, b_res, use_cyl);
00207             make_connection(pts[i][3], pts[i][4], pts[i][5], pts[i][6],
00208                             b_rad, b_res, use_cyl);
00209           }
00210         } // drew cylinders
00211 
00212         // Draw the triangles that connect the cylinders and make up the 
00213         // ribbon proper.  The funky start condition is so that it starts at
00214         // pts[][1] if I need to finish the previous residue, or
00215         // pts[][2] if I don't
00216         for (i= (last_loop == loop-1 ? 1: 2); i<5; i++) {
00217           cmdTriangle.putdata(pts[1][1+i], pts[1][0+i], pts[0][0+i],
00218                               tri_norm[1][0+i], tri_norm[1][-1+i],
00219                               tri_norm[0][-1+i], cmdList);
00220           cmdTriangle.putdata(pts[0][1+i], pts[1][1+i], pts[0][0+i],
00221                               tri_norm[0][0+i], tri_norm[1][0+i],
00222                               tri_norm[0][-1+i], cmdList);
00223 
00224           // indicate this atom can be picked
00225           // this spot is in the middle of the ribbon, both in length
00226           // and in width
00227           int pidx = loop * 3;
00228           pickpointcoords.append(coords[pidx    ]);
00229           pickpointcoords.append(coords[pidx + 1]);
00230           pickpointcoords.append(coords[pidx + 2]);
00231           pickpointindices.append(idx[loop]);
00232         }
00233       }  // atom 'loop' finished
00234          
00235       // draw what I can for atom 'loop+1'
00236       if (idx[loop+1] >= 0 && atomSel->on[idx[loop+1]]) {
00237         // If this is on, then I may have to change the color,
00238         // since I'm lazy, I won't check to see if I _have_ to change it
00239         // but assume I do
00240         cmdColorIndex.putdata(atomColor->color[idx[loop+1]], cmdList);
00241         // I can draw the first couple of cylinders, but I need knowledge
00242         // of what goes on next to get a smooth fit.  Thus, I won't
00243         // draw them here.
00244         // I can't draw the last two cylinders.
00245         if (use_cyl) {
00246           for (i=0; i<=1; i++) {
00247             make_connection(pts[i][4], pts[i][5], pts[i][6], pts[i][7],
00248                             b_rad, b_res, use_cyl);
00249             make_connection(pts[i][5], pts[i][6], pts[i][7], pts[i][8],
00250                             b_rad, b_res, use_cyl);
00251           }
00252         }
00253 
00254         // I can draw 3 of the four triangles, but I need
00255         // the normals to do the last one properly
00256         for (i= 5; i<8-1; i++) {
00257 /*
00258           cmdTriangle.putdata(pts[0][0+i], pts[1][0+i], pts[1][1+i],
00259                               tri_norm[0][-1+i], tri_norm[1][-1+i],
00260                               tri_norm[1][0+i], cmdList);
00261           cmdTriangle.putdata(pts[0][0+i], pts[1][1+i], pts[0][1+i],
00262                               tri_norm[0][-1+i], tri_norm[1][0+i],
00263                               tri_norm[0][0+i], cmdList);
00264 */
00265           cmdTriangle.putdata(pts[1][1+i], pts[1][0+i], pts[0][0+i],
00266                               tri_norm[1][0+i], tri_norm[1][-1+i],
00267                               tri_norm[0][-1+i], cmdList);
00268           cmdTriangle.putdata(pts[0][1+i], pts[1][1+i], pts[0][0+i],
00269                               tri_norm[0][0+i], tri_norm[1][0+i],
00270                               tri_norm[0][-1+i], cmdList);
00271         }
00272         last_loop = loop;
00273       } // atom 'loop+1' finished
00274          
00275       // save infor for next loop, for smoothing cylinders and normals
00276       for (i=0; i<=1; i++) {
00277         vec_copy(pts[i][0], pts[i][6]);  // remember, because of the spline,
00278         vec_copy(pts[i][1], pts[i][7]);  // loop pts[][8] is loop+1 pts[][2]
00279         vec_copy(norms[i][0], norms[i][6]);
00280         vec_copy(norms[i][1], norms[i][7]);
00281       }
00282     } 
00283   } // gone down the fragment
00284 
00285   free(tmp_ptr);
00286 
00287   // draw the pickpoints if we have any
00288   if (pickpointindices.num() > 0) {
00289     DispCmdPickPointArray pickPointArray;
00290     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
00291                            &pickpointcoords[0], cmdList);
00292   }
00293 }
00294 
00295 
00296 
00297 
00298 // draw ribbons along the protein backbone
00299 // part of this method taken (with permission) from the 'ribbon.f' in the
00300 // Raster3D package:
00301 //  Merritt, Ethan A. and Murphy, Michael E.P. (1994).
00302 //   "Raster3D Version 2.0, a Program for Photorealistic Molecular Graphics"
00303 //        Acta Cryst. D50, 869-873.
00304 
00305 // That method was based on ideas from  Carson & Bugg, J. Molec. Graphics
00306 //   4,121-122 (1986)
00307 void DrawMolItem::draw_ribbons(float *framepos, float brad, int bres, float thickness) {
00308   sprintf (commentBuffer,"MoleculeID: %d ReprID: %d Beginning Ribbons",
00309            mol->id(), repNumber);
00310   cmdCommentX.putdata(commentBuffer, cmdList);
00311 
00312   // find out if I'm using lines or cylinders
00313   int use_cyl = TRUE; // use cylinders by default
00314   int rc = 0;  // clear errors
00315   
00316   if (bres <= 2 || brad < 0.01f) { 
00317     use_cyl = FALSE; // the representation will be as lines
00318   } 
00319 
00320   append(DMATERIALON); // enable shading 
00321 
00322   float ribbon_width = thickness;
00323   if (ribbon_width < 1.0) 
00324     ribbon_width = 1.0;
00325   ribbon_width /= 7.0;
00326 
00327   rc |= draw_protein_ribbons_old(framepos, bres, brad, ribbon_width, use_cyl);
00328   rc |= draw_nucleic_ribbons(framepos, bres, brad, ribbon_width, use_cyl, 0, 0);
00329   rc |= draw_base_sugar_rings(framepos, bres, brad, ribbon_width, use_cyl);
00330 
00331   // XXX put the more specific error messages back in here if we feel
00332   //     that they are helpful to the user.
00333   if (rc != 0) {
00334     if (emitstructwarning())
00335       msgErr << "Warning: ribbons code encountered an unusual structure, geometry may not look as expected." << sendmsg;
00336   }
00337 }
00338 
00339 
00340 
00341 
00342 int DrawMolItem::draw_protein_ribbons_old(float *framepos, int b_res, float b_rad,
00343                                 float ribbon_width, int use_cyl) {
00344   float *real_coords = NULL, *coords;
00345   float *real_perps = NULL, *perps;
00346   int *real_idx = NULL, *idx;
00347   float *capos, *last_capos;
00348   float *opos,  *last_opos;
00349   int onum, canum, res, frag, num;
00350   int rc=0;
00351 
00352   // these are the variables used in the Raster3D package
00353   float a[3], b[3], c[3], d[3], e[3], g[3];  
00354 
00355   // go through each protein and find the CA and O
00356   // from that, construct the offsets to use (called "perps")
00357   // then do two splines and connect them
00358   for (frag=0; frag<mol->pfragList.num(); frag++) {
00359     int loop;
00360 
00361     num = mol->pfragList[frag]->num();  // number of residues in this fragment
00362     if (num < 2) {
00363       rc |= RIBBON_ERR_NOTENOUGH;
00364       continue; // can't draw a ribbon with only one element, so skip
00365     }
00366 
00367     // check that we have a valid structure before continuing
00368       res = (*mol->pfragList[frag])[0];
00369     canum = mol->find_atom_in_residue("CA", res); // get CA 
00370      onum = mol->find_atom_in_residue("O", res);  // get O
00371 
00372     if (canum < 0 || onum < 0) {
00373       continue; // can't find 1st CA or O of the protein, so don't draw
00374     }
00375 
00376     if (real_coords) {
00377       free(real_coords);
00378       free(real_idx);
00379       free(real_perps);
00380       real_coords = NULL;
00381       real_idx = NULL;
00382       real_perps = NULL;
00383     }
00384 
00385     // allocate memory for control points, perps, and index storage
00386     // The arrays have 2 extra elements at the front and end to allow
00387     // duplication of control points necessary for the old ribbons code.
00388     real_coords = (float *) malloc((num+4) * sizeof(float)*3);
00389        real_idx =   (int *) malloc((num+4) * sizeof(int));
00390      real_perps = (float *) malloc((num+4) * sizeof(float)*3);
00391 
00392     coords = real_coords + 2*3;
00393        idx = real_idx + 2;
00394      perps = real_perps + 2*3;
00395 
00396     // initialize CA and O atom pointers
00397     capos = framepos + 3*canum;
00398     last_capos = capos;
00399     opos = framepos + 3*onum;
00400     last_opos = opos;
00401 
00402     // duplicate first control point data
00403     vec_copy(coords-6, capos);
00404     vec_copy(coords-3, capos);
00405     idx[-2] = idx[-1] = -1;
00406 
00407     // now go through and set the coordinates and the perps
00408     e[0] = e[1] = e[2] = 0.0;
00409     vec_copy(g, e);
00410     for (loop=0; loop<num; loop++) {
00411       res = (*mol->pfragList[frag])[loop];
00412       canum = mol->find_atom_in_residue("CA", res);
00413       if (canum >= 0) {
00414         capos = framepos + 3*canum;
00415       }
00416       onum = mol->find_atom_in_residue("O", res);
00417       if (onum < 0) {
00418         onum = mol->find_atom_in_residue("OT1", res);
00419       }
00420 
00421       if (onum >= 0) {
00422         opos = framepos + 3*onum;
00423       } else {
00424         rc |= RIBBON_ERR_PROTEIN_MESS;
00425         opos = last_opos; // try to cope if we have no oxygen index
00426       }
00427 
00428       vec_copy(coords+loop*3, capos);
00429       idx[loop] = canum;
00430 
00431       // now I need to figure out where the ribbon goes
00432       vec_sub(a, capos, last_capos);     // A=(pos(CA(res)) - pos(CA(res-1)))
00433       vec_sub(b, last_opos, last_capos); // B=(pos(O(res-1)) - pos(CA(res-1)))
00434       cross_prod(c, a, b);               // C=AxB, define res-1's peptide plane
00435       cross_prod(d, c, a);               // D=CxA, normal to plane and backbone
00436       // if normal is > 90 deg from  previous one, negate the new one
00437       if (dot_prod(d, g) < 0) {
00438         vec_negate(b, d);
00439       } else {
00440         vec_copy(b, d);
00441       }
00442       vec_add(e, g, b);   // average to the sum of the previous vectors
00443       vec_normalize(e);
00444       vec_scale(&perps[3*loop], ribbon_width, e); // compute perps from normal
00445       vec_copy(g, e);     // make a cumulative sum; cute, IMHO
00446       last_capos = capos;
00447       last_opos = opos;
00448     }
00449 
00450     // duplicate the last control point into two extra copies
00451     vec_copy(coords+3*num, capos);
00452     vec_copy(coords+3*(num+1), capos);
00453     idx[num] = idx[num+1] = -1;
00454 
00455     // copy the second perp to the first since the first one didn't have
00456     // enough data to construct a good perpendicular.
00457     vec_copy(perps, perps+3);
00458 
00459     // duplicate the first and last perps into the extra control points
00460     vec_copy(perps-3, perps);
00461     vec_copy(perps-6, perps);
00462     vec_copy(perps+3*num, perps+3*num-3);
00463     vec_copy(perps+3*num+3, perps+3*num);
00464 
00465     draw_spline_ribbon(num, coords, perps, idx, use_cyl, b_rad, b_res);
00466 
00467     if (real_coords) {
00468       free(real_coords);
00469       free(real_idx);
00470       free(real_perps);
00471       real_coords = NULL;
00472       real_idx = NULL;
00473       real_perps = NULL;
00474     }
00475   } // drew the protein fragment ribbon
00476 
00477   return rc;
00478 }
00479 
00480 
00481 int DrawMolItem::draw_protein_ribbons_new(float *framepos, int b_res, float b_rad,
00482                                 float ribbon_width, int use_cyl) {
00483   float *coords = NULL;
00484   float *perps = NULL;
00485   int *idx = NULL;
00486   float *capos, *last_capos, *opos, *last_opos;
00487   int onum, canum, frag, num, res;
00488   const char *modulatefield = NULL; // XXX this needs to become a parameter
00489   const float *modulatedata = NULL; // data field to use for width modulation
00490   float *modulate = NULL;           // per-control point width values
00491   int rc=0;
00492 
00493   // these are the variables used in the Raster3D package
00494   float a[3], b[3], c[3], d[3], e[3], g[3];  
00495 
00496   // Lookup atom typecodes ahead of time so we can use the most 
00497   // efficient variant of find_atom_in_residue() in the main loop.
00498   // If we can't find the atom types we need, bail out immediately
00499   int CAtypecode  = mol->atomNames.typecode("CA");
00500   int Otypecode   = mol->atomNames.typecode("O");
00501   int OT1typecode = mol->atomNames.typecode("OT1");
00502 
00503   if (CAtypecode < 0 || ((Otypecode < 0) && (OT1typecode < 0))) {
00504     return rc; // can't draw a ribbon without CA and O atoms for guidance
00505   }
00506 
00507   // allocate for the maximum possible per-residue control points, perps, 
00508   // and indices so we don't have to reallocate for every fragment
00509   coords = (float *) malloc((mol->nResidues) * sizeof(float)*3);
00510      idx =   (int *) malloc((mol->nResidues) * sizeof(int));
00511    perps = (float *) malloc((mol->nResidues) * sizeof(float)*3);
00512 
00513 #if 1
00514   // XXX hack to let users try various stuff
00515   modulatefield = getenv("VMDMODULATERIBBON");
00516 #endif
00517   if (modulatefield != NULL) {
00518     if (!strcmp(modulatefield, "user")) {
00519       modulatedata = mol->current()->user;
00520       // XXX user field can be NULL on some timesteps
00521     } else {
00522       modulatedata = mol->extraflt.data(modulatefield);
00523     } 
00524 
00525     // allocate for the maximum possible per-residue modulation values
00526     // so we don't have to reallocate for every fragment
00527     // XXX the modulate array is allocated cleared to zeros so that
00528     // in the case we get a NULL modulatedata pointer (user field for
00529     // example, we'll just end up with no modulation.
00530     modulate = (float *) calloc(1, mol->nResidues * sizeof(float));
00531   }
00532 
00533   // go through each protein and find the CA and O
00534   // from that, construct the offsets to use (called "perps")
00535   for (frag=0; frag<mol->pfragList.num(); frag++) {
00536     int cyclic=mol->pfragCyclic[frag];  // whether fragment is cyclic
00537     num = mol->pfragList[frag]->num();  // number of residues in this fragment
00538     if (num < 2) {
00539       rc |= RIBBON_ERR_NOTENOUGH;
00540       continue; // can't draw a ribbon with only one element, so skip
00541     }
00542 
00543     // check that we have a valid structure before continuing
00544       res = (*mol->pfragList[frag])[0];
00545     canum = mol->find_atom_in_residue(CAtypecode, res);
00546      onum = mol->find_atom_in_residue(Otypecode, res);
00547 
00548     if (canum < 0 || onum < 0) {
00549       continue; // can't find 1st CA or O of the protein, so don't draw
00550     }
00551 
00552     // initialize CA and O atom pointers
00553     capos = framepos + 3*canum;
00554     last_capos = capos;
00555     opos = framepos + 3*onum;
00556     last_opos = opos;
00557 
00558     // now go through and set the coordinates and the perps
00559     e[0] = e[1] = e[2] = 0.0;
00560     vec_copy(g, e);
00561 
00562     // for a cyclic structure, we use the positions of the last residue
00563     // to seed the initial direction vectors for the ribbon
00564     if (cyclic) {
00565       int lastres = (*mol->pfragList[frag])[num-1];
00566       int lastcanum = mol->find_atom_in_residue(CAtypecode, lastres);
00567       last_capos = framepos + 3*lastcanum;
00568 
00569       int lastonum = mol->find_atom_in_residue(Otypecode, lastres);
00570       if (lastonum < 0 && OT1typecode >= 0) {
00571         lastonum = mol->find_atom_in_residue(OT1typecode, lastres);
00572       }
00573       last_opos = framepos + 3*lastonum;
00574 
00575       // now I need to figure out where the ribbon goes
00576       vec_sub(a, capos, last_capos);     // A=(pos(CA(res)) - pos(CA(res-1)))
00577       vec_sub(b, last_opos, last_capos); // B=(pos(O(res-1)) - pos(CA(res-1)))
00578       cross_prod(c, a, b);               // C=AxB, define res-1's peptide plane
00579       cross_prod(d, c, a);               // D=CxA, normal to plane and backbone
00580       // if normal is > 90 deg from  previous one, negate the new one
00581       if (dot_prod(d, g) < 0) {
00582         vec_negate(b, d);
00583       } else {
00584         vec_copy(b, d);
00585       }
00586       vec_add(e, g, b);            // average to the sum of the previous vectors
00587       vec_normalize(e);
00588       vec_copy(g, e);              // make a cumulative sum; cute, IMHO
00589     }
00590 
00591     int loop;
00592     for (loop=0; loop<num; loop++) {
00593       res = (*mol->pfragList[frag])[loop];
00594 
00595       canum = mol->find_atom_in_residue(CAtypecode, res);
00596       if (canum >= 0) {
00597         capos = framepos + 3*canum;
00598       }
00599 
00600       onum = mol->find_atom_in_residue(Otypecode, res);
00601       if (onum < 0 && OT1typecode >= 0) {
00602         onum = mol->find_atom_in_residue(OT1typecode, res);
00603       }
00604       if (onum >= 0) {
00605         opos = framepos + 3*onum;
00606       } else {
00607         rc |= RIBBON_ERR_PROTEIN_MESS;
00608         opos = last_opos; // try to cope if we have no oxygen index
00609       }
00610 
00611       // copy the CA coordinate into the control point array
00612       vec_copy(coords+loop*3, capos);
00613 
00614       // modulate the ribbon width by user-specified per-atom data
00615       if (modulatedata != NULL)
00616         modulate[loop] = modulatedata[canum];
00617  
00618       idx[loop] = canum;
00619 
00620       // now I need to figure out where the ribbon goes
00621       vec_sub(a, capos, last_capos);     // A=(pos(CA(res)) - pos(CA(res-1)))
00622       vec_sub(b, last_opos, last_capos); // B=(pos(O(res-1)) - pos(CA(res-1)))
00623       cross_prod(c, a, b);               // C=AxB, define res-1's peptide plane
00624       cross_prod(d, c, a);               // D=CxA, normal to plane and backbone
00625       // if normal is > 90 deg from  previous one, negate the new one
00626       if (dot_prod(d, g) < 0) {
00627         vec_negate(b, d);
00628       } else {
00629         vec_copy(b, d);
00630       }
00631       vec_add(e, g, b);            // average to the sum of the previous vectors
00632       vec_normalize(e);
00633       vec_copy(&perps[3*loop], e); // compute perps from the normal
00634       vec_copy(g, e);              // make a cumulative sum; cute, IMHO
00635       last_capos = capos;
00636       last_opos = opos;
00637     }
00638 
00639     if (!cyclic) {
00640       // copy the second perp to the first since the first one didn't have
00641       // enough data to construct a good perpendicular.
00642       vec_copy(perps, perps+3);
00643     } 
00644 
00645     if (modulate != NULL) {
00646       // modulate ribbon width by user-specified per-atom field value
00647       float *widths = (float *) malloc(num * sizeof(float));
00648       float *heights = (float *) malloc(num * sizeof(float));
00649       float m_fac;
00650       int i;
00651       for (i=0; i<num; i++) {
00652 #if 1
00653         // only allow modulation values > 0. otherwise fall back on old scheme.
00654         m_fac = modulate[i];
00655         if (m_fac <= 0.0f)
00656            m_fac = 1.0f;
00657 
00658         // modulate both width and height
00659         widths[i] = 7 * ribbon_width * b_rad * m_fac;
00660         heights[i] = b_rad*m_fac;
00661 #else
00662         // modulate only width, and only additively
00663         widths[i] = 7 * ribbon_width * b_rad + m_fac;
00664         heights[i] = b_rad;
00665 #endif
00666       }
00667       draw_spline_new(num, coords, perps, idx, widths, heights, num, b_res, cyclic);
00668       free(widths);
00669       free(heights);
00670     } else {
00671       // draw normal unmodulated ribbons
00672       float widths = 7 * ribbon_width * b_rad;
00673       float heights = b_rad;
00674       draw_spline_new(num, coords, perps, idx, &widths, &heights, 1, b_res, cyclic);
00675     }
00676   } // drew the protein fragment ribbon
00677 
00678   if (coords) {
00679     free(coords);
00680     free(idx);
00681     free(perps);
00682   }
00683 
00684   if (modulate) {
00685     free(modulate);
00686   }
00687 
00688   return rc;
00689 }
00690 
00691 
00692 
00693 int DrawMolItem::draw_nucleic_ribbons(float *framepos, int b_res, float b_rad,
00694                                 float ribbon_width, int use_cyl, int use_new, 
00695                                 int use_carb) {
00697   float *real_coords = NULL, *coords;
00698   float *real_perps = NULL, *perps;
00699   int *real_idx = NULL, *idx;
00700   float *ppos, *last_ppos;
00701   float opos[3], last_opos[3];
00702   int cpnum;         // index of spline control point atom
00703   int o1num, o2num;  // indices of atoms used to construct perps
00704   int frag, num;
00705   int rc=0;
00706 
00707   // these are the variables used in the Raster3D package
00708   float a[3], b[3], c[3], d[3], e[3], g[3];  
00709 
00710   // go through each nucleic acid and find the phospate
00711   // then find the O1P/O2P, or OP1/OP2 (new nomenclature). 
00712   // From those construct the perps then do two splines and connect them
00713   for (frag=0; frag<mol->nfragList.num(); frag++) {
00714     int loop;
00715 
00716     num = mol->nfragList[frag]->num(); // number of residues in this fragment
00717     if (num < 2) {
00718       rc |= RIBBON_ERR_NOTENOUGH;
00719       continue;
00720     }
00721     if (real_coords) {
00722       free(real_coords);
00723       free(real_idx);
00724       free(real_perps);
00725       real_coords = NULL;
00726       real_idx = NULL;
00727       real_perps = NULL;
00728     }
00729     real_coords = (float *) malloc((num+4)*sizeof(float)*3);
00730     real_idx = (int *) malloc((num+4) * sizeof(int));
00731     real_perps = (float *) malloc((num+4) * sizeof(float)*3);
00732     coords = real_coords + 2*3;
00733        idx = real_idx + 2;
00734      perps = real_perps + 2*3;
00735          
00736     // okay, I've got space for the coordinates, the index translations,
00737     // and the perpendiculars, now initialize everything
00738     int res = (*mol->nfragList[frag])[0];
00739 
00740     // get atoms to use as the spline control points
00741     if (use_carb) {
00742       // use the carbons
00743       cpnum = mol->find_atom_in_residue("C3'", res);
00744       if (cpnum < 0)
00745         cpnum = mol->find_atom_in_residue("C3*", res);
00746       // use the phosphates if no carbon control points found
00747       if (cpnum < 0)
00748         cpnum = mol->find_atom_in_residue("P", res);
00749     } else { 
00750       // use the phosphates 
00751       cpnum = mol->find_atom_in_residue("P", res);
00752     }
00753 
00754     // if no P found, check the terminal atom names
00755     if (cpnum < 0) {
00756       cpnum = mol->find_atom_in_residue("H5T", res);
00757     }
00758     if (cpnum < 0) {
00759       cpnum = mol->find_atom_in_residue("H3T", res);
00760     }
00761 
00762     if (cpnum < 0) {
00763       rc |= RIBBON_ERR_MISSING_PHOSPHATE;
00764       continue;
00765     }
00766    
00767     o1num = mol->find_atom_in_residue("O1P", res);   //  and an oxygen
00768     if (o1num < 0)
00769       o1num = mol->find_atom_in_residue("OP1", res); //  and an oxygen
00770 
00771     o2num = mol->find_atom_in_residue("O2P", res);   //  and an oxygen
00772     if (o2num < 0)
00773       o2num = mol->find_atom_in_residue("OP2", res); //  and an oxygen
00774 
00775     // if we failed to find these on the terminal residue, try the next one..
00776     if (o1num  < 0 || o2num < 0) {
00777       int nextres = (*mol->nfragList[frag])[1];
00778       o1num = mol->find_atom_in_residue("O1P", nextres);   //  and an oxygen
00779       if (o1num < 0) 
00780         o1num = mol->find_atom_in_residue("OP1", nextres); //  and an oxygen
00781 
00782       o2num = mol->find_atom_in_residue("O2P", nextres);   //  and an oxygen
00783       if (o2num < 0)
00784         o2num = mol->find_atom_in_residue("OP2", nextres); //  and an oxygen
00785 
00786       if (o1num  < 0 || o2num < 0) {
00787         rc |= RIBBON_ERR_MISSING_O1P_O2P;
00788         continue;
00789       }
00790     }
00791 
00792     ppos = framepos + 3*cpnum;
00793     vec_add(opos, framepos + 3*o1num, framepos + 3*o2num);  // along the bisector
00794     vec_copy(last_opos, opos);
00795     last_ppos = ppos;
00796 
00797     vec_copy(coords-6, ppos);
00798     vec_copy(coords-3, ppos);
00799     idx[-2] = idx[-1] = -1;
00800 
00801     // now go through and set the coordinates and the perps
00802     e[0] = e[1] = e[2] = 0.0;
00803     vec_copy(g, e);
00804     int abortfrag=0;
00805     for (loop=0; (loop<num && abortfrag==0); loop++) {
00806       res = (*mol->nfragList[frag])[loop];
00807 
00808       // get atoms to use as the spline control points
00809       if (use_carb) {
00810         // use the carbons
00811         cpnum = mol->find_atom_in_residue("C3'", res);
00812         if (cpnum < 0)
00813           cpnum = mol->find_atom_in_residue("C3*", res);
00814         // use the phosphates if no carbon control points found
00815         if (cpnum < 0)
00816           cpnum = mol->find_atom_in_residue("P", res);
00817       } else { 
00818         // use the phosphates 
00819         cpnum = mol->find_atom_in_residue("P", res);
00820       }
00821 
00822       // if no P found, check the terminal atom names
00823       if (cpnum < 0) {
00824         cpnum = mol->find_atom_in_residue("H5T", res);
00825       }
00826       if (cpnum < 0) {
00827         cpnum = mol->find_atom_in_residue("H3T", res);
00828       }
00829 
00830       // cpnum must be set to a valid atom or we'll crash
00831       if (cpnum >= 0) {
00832         ppos = framepos + 3*cpnum;
00833         idx[loop] = cpnum;
00834       } else {
00835         rc |= RIBBON_ERR_MISSING_PHOSPHATE;
00836         abortfrag = 1;
00837         break;
00838       }
00839         
00840       o1num = mol->find_atom_in_residue("O1P", res);   //  and an oxygen
00841       if (o1num < 0)
00842         o1num = mol->find_atom_in_residue("OP1", res); //  and an oxygen
00843 
00844       o2num = mol->find_atom_in_residue("O2P", res);   //  and an oxygen
00845       if (o2num < 0)
00846         o2num = mol->find_atom_in_residue("OP2", res); //  and an oxygen
00847 
00848       if (o1num < 0 || o2num < 0) {
00849         rc |= RIBBON_ERR_PROTEIN_MESS;
00850         vec_copy(opos, last_opos);
00851       } else {
00852         float tmp[3];
00853         vec_sub(tmp, framepos + 3*o1num, ppos);
00854         vec_sub(opos, framepos + 3*o2num, ppos);
00855         vec_add(opos, tmp, opos);  // along the bisector
00856       }
00857       vec_copy(coords+loop*3, ppos);
00858  
00859       // now I need to figure out where the ribbon goes
00860       vec_sub(a, ppos, last_ppos);      // A=(pos(P(res)) - pos(P(res-1)))
00861 //    vec_sub(b, last_opos, last_ppos); // B=(pos(Obisector(res-1)) - pos(P(res-1)))
00862 //    cross_prod(c, a, b); // C=AxB defines res-1's peptide plane
00863       vec_copy(c, opos);   // already have the normal to the ribbon
00864       cross_prod(d, c, a); // D=CxA normal to plane and backbone
00865 
00866       // if normal is > 90 deg from  previous one, invert the new one
00867       if (dot_prod(d, g) < 0) { 
00868         vec_negate(b, d);
00869       } else {
00870         vec_copy(b, d);
00871       }
00872       vec_add(e, g, b);   // average to the sum of the previous vectors
00873       vec_normalize(e);
00874       vec_scale(&perps[3*loop], ribbon_width, e); // compute perps from normal
00875       vec_copy(g, e);     // make a cumulative sum; cute, IMHO
00876       last_ppos = ppos;
00877       vec_copy(last_opos, opos);
00878     }
00879 
00880     // abort drawing the entire fragment if unfixable problems occur
00881     if (abortfrag)
00882       continue;
00883 
00884     // and set the final points to the last element
00885     vec_copy(coords+3*num, ppos);
00886     vec_copy(coords+3*(num+1), ppos);
00887     idx[num] = idx[num+1] = -1;
00888 
00889     // copy the second perp to the first since the first one didn't have
00890     // enough data to construct a good perpendicular.
00891     vec_copy(perps, perps+3);
00892 
00893     // now set the first and last perps correctly
00894     vec_copy(perps-3, perps);
00895     vec_copy(perps-6, perps);
00896     vec_copy(perps+3*num, perps+3*num-3);
00897     vec_copy(perps+3*num+3, perps+3*num);
00898 
00899     // draw the nucleic acid fragment ribbon
00900     if (use_new) {
00901       float widths = 7 * ribbon_width * b_rad;
00902       float heights = b_rad; 
00903       draw_spline_new(num, coords, perps, idx, &widths, &heights, 1, b_res, 0);
00904     } else {
00905       draw_spline_ribbon(num, coords, perps, idx, use_cyl, b_rad, b_res);
00906     }
00907   }
00908 
00909   if (real_coords) {
00910     free(real_coords);
00911     free(real_idx);
00912     free(real_perps);
00913     real_coords = NULL;
00914     real_idx = NULL;
00915     real_perps = NULL;
00916   }
00917 
00918   return rc;
00919 }
00920 
00921 
00922 
00923 
00924 int DrawMolItem::draw_base_sugar_rings(float *framepos, int b_res, float b_rad,
00925                               float ribbon_width, int use_cyl) {
00926   float *real_coords = NULL;
00927   int frag, num;
00928   int rc=0;
00929 
00931   // ribose 
00932   //   O4',C1',C2',C3'C4' or
00933   //   O4*,C1*,C2*,C3*C4*
00934   // purines (CYT,THY,URA) 
00935   //    N1,C2,N3,C4,C5,C6
00936   // pyrimidines (ADE,GUA) 
00937   //      N1,C2,N3,C4,C5,C6,N7,N8,N9
00938   // O4',C1' and C4' are define the ribose plane and 
00939   // C2' and C3' then define the pucker of the ring
00940   // sugar -- base bonds
00941   //    pyrimidine      C1' to N9 
00942   //    purine          C1' to N1  
00943   float *o4ppos=NULL, *c1ppos=NULL, *c2ppos=NULL; 
00944   float *c3ppos=NULL, *c4ppos=NULL;
00945   int o4pnum, c1pnum, c2pnum, c3pnum, c4pnum;
00946   float *n1pos,*c2pos,*n3pos,*c4pos,*c5pos,*c6pos,*n7pos,*c8pos,*n9pos;
00947   int n1num,c2num,n3num,c4num,c5num,c6num,n7num,c8num,n9num;
00948   float rescentra[3], rescentrb[3];
00949   float midptc1pc4p[3];
00950       
00951   for (frag=0; frag<mol->nfragList.num(); frag++) {
00952     int loop;
00953 
00954     num = mol->nfragList[frag]->num(); // num of residues in this fragment
00955     if (real_coords) {
00956       free(real_coords);
00957       real_coords = NULL;
00958     }
00959 
00960     // 5atoms for the ribose but only 4 triangles
00961     // 9atoms max for a base
00962     real_coords = (float *) malloc( 14 * num * sizeof(float)*3);
00963          
00964     // okay, I've got space for the coordinates now go
00965     for (loop=0; loop<num; loop++) {
00966       // the furanose
00967       int res = (*mol->nfragList[frag])[loop];
00968 
00969       c1pnum = mol->find_atom_in_residue("C1'", res);
00970       if (c1pnum < 0) {
00971         c1pnum = mol->find_atom_in_residue("C1*", res);
00972         if (c1pnum < 0) {
00973           rc |= RIBBON_ERR_BADNUCLEIC;
00974           continue;
00975         }
00976       } 
00977       c1ppos = framepos + 3*c1pnum;
00978 
00979       if (atomSel->on[c1pnum]) { // switch drawing by C1' atom
00980         o4pnum = mol->find_atom_in_residue("O4'", res);
00981         if (o4pnum < 0) {
00982           o4pnum = mol->find_atom_in_residue("O4*", res);
00983         }
00984         c2pnum = mol->find_atom_in_residue("C2'", res); 
00985         if (c2pnum < 0) {
00986           c2pnum = mol->find_atom_in_residue("C2*", res);
00987         }
00988         c3pnum = mol->find_atom_in_residue("C3'", res); 
00989         if (c3pnum < 0) {
00990           c3pnum = mol->find_atom_in_residue("C3*", res);
00991         }
00992         c4pnum = mol->find_atom_in_residue("C4'", res);
00993         if (c4pnum < 0) {
00994           c4pnum = mol->find_atom_in_residue("C4*", res);
00995         }
00996 
00997         if (o4pnum < 0 || c2pnum < 0 || c3pnum < 0 || c4pnum < 0) {
00998           rc |= RIBBON_ERR_BADNUCLEIC;
00999           continue;
01000         }
01001 
01002         o4ppos = framepos + 3*o4pnum;
01003         c2ppos = framepos + 3*c2pnum;
01004         c3ppos = framepos + 3*c3pnum;
01005         c4ppos = framepos + 3*c4pnum;
01006          
01007         midpoint(midptc1pc4p, c1ppos, c4ppos);
01008          
01009         // now display triangles 
01010         cmdColorIndex.putdata(atomColor->color[c1pnum], cmdList);
01011         
01012         cmdTriangle.putdata(c4ppos,c1ppos,o4ppos,cmdList);
01013         cmdTriangle.putdata(c3ppos,midptc1pc4p,c4ppos,cmdList);
01014         cmdTriangle.putdata(c2ppos,midptc1pc4p,c3ppos,cmdList);
01015         cmdTriangle.putdata(c1ppos,midptc1pc4p,c2ppos,cmdList);
01016       } 
01017 
01018       // begin bases
01019       rescentra[0]=rescentra[1]=rescentra[2]=0.0;
01020       rescentrb[0]=rescentrb[1]=rescentrb[2]=0.0;
01021  
01022       // check for purine and pyrimidine specific atoms
01023       n9num = mol->find_atom_in_residue("N9", res);     
01024       n1num = mol->find_atom_in_residue("N1", res);
01025 
01026       // if there is a N9, then this is a pyrimidine
01027       if ((n9num >= 0) && (atomSel->on[n9num])) {
01028         c8num = mol->find_atom_in_residue("C8", res); 
01029         n7num = mol->find_atom_in_residue("N7", res); 
01030         c6num = mol->find_atom_in_residue("C6", res); 
01031         c5num = mol->find_atom_in_residue("C5", res);
01032         c4num = mol->find_atom_in_residue("C4", res);
01033         n3num = mol->find_atom_in_residue("N3", res);
01034         c2num = mol->find_atom_in_residue("C2", res);
01035         n1num = mol->find_atom_in_residue("N1", res);
01036 
01037         if (c8num < 0 || n7num < 0 || c6num < 0 || c5num < 0 ||
01038             c4num < 0 || n3num < 0 || c2num < 0 || n1num < 0) {
01039           rc |= RIBBON_ERR_BADPYRIMIDINE;
01040           continue;
01041         }
01042 
01043         n9pos = framepos + 3*n9num;     
01044         vec_add(rescentra,rescentra,n9pos);
01045         c8pos = framepos + 3*c8num; 
01046         vec_add(rescentra,rescentra,c8pos);
01047         n7pos = framepos + 3*n7num; 
01048         vec_add(rescentra,rescentra,n7pos);
01049 
01050         c5pos = framepos + 3*c5num;
01051         vec_add(rescentra,rescentra,c5pos);
01052         vec_add(rescentrb,rescentrb,c5pos);
01053         c4pos = framepos + 3*c4num;
01054         vec_add(rescentra,rescentra,c5pos);
01055         vec_add(rescentrb,rescentrb,c5pos);
01056 
01057         c6pos = framepos + 3*c6num; 
01058         vec_add(rescentrb,rescentrb,c6pos);
01059         n3pos = framepos + 3*n3num;
01060         vec_add(rescentrb,rescentrb,n3pos);
01061         c2pos = framepos + 3*c2num;
01062         vec_add(rescentrb,rescentrb,c2pos);
01063         n1pos = framepos + 3*n1num;
01064         vec_add(rescentrb,rescentrb,n1pos);
01065                 
01066         rescentrb[0] = rescentrb[0]/6.0f;
01067         rescentrb[1] = rescentrb[1]/6.0f;
01068         rescentrb[2] = rescentrb[2]/6.0f;
01069 
01070         rescentra[0] = rescentra[0]/5.0f;
01071         rescentra[1] = rescentra[1]/5.0f;
01072         rescentra[2] = rescentra[2]/5.0f;
01073 
01074         // draw bond from ribose to base
01075         cmdCylinder.putdata(c1ppos, n9pos, b_rad, b_res, 0, cmdList);
01076         // now display triangles
01077         cmdColorIndex.putdata(atomColor->color[n9num], cmdList);
01078 
01079         cmdTriangle.putdata(n1pos,rescentrb,c2pos,cmdList);
01080         cmdTriangle.putdata(c2pos,rescentrb,n3pos,cmdList);
01081         cmdTriangle.putdata(n3pos,rescentrb,c4pos,cmdList);
01082         cmdTriangle.putdata(c4pos,rescentrb,c5pos,cmdList);
01083         cmdTriangle.putdata(c5pos,rescentrb,c6pos,cmdList);
01084         cmdTriangle.putdata(c6pos,rescentrb,n1pos,cmdList);
01085 
01086         cmdTriangle.putdata(n9pos,rescentra,c8pos,cmdList);
01087         cmdTriangle.putdata(c8pos,rescentra,n7pos,cmdList);
01088         cmdTriangle.putdata(n7pos,rescentra,c5pos,cmdList);
01089         cmdTriangle.putdata(c5pos,rescentra,c4pos,cmdList);
01090         cmdTriangle.putdata(c5pos,rescentra,c4pos,cmdList);
01091         cmdTriangle.putdata(c4pos,rescentra,n9pos,cmdList);
01092       }     
01093       else if (( n1num >= 0) && (atomSel->on[n1num])){
01094         // residue is purine and turned on
01095         c6num = mol->find_atom_in_residue("C6", res); 
01096         c5num = mol->find_atom_in_residue("C5", res);
01097         c4num = mol->find_atom_in_residue("C4", res);
01098         n3num = mol->find_atom_in_residue("N3", res);
01099         c2num = mol->find_atom_in_residue("C2", res);
01100 
01101         if (c6num < 0 || c5num < 0 || c4num < 0 || n3num < 0 || c2num < 0) {
01102           rc |= RIBBON_ERR_BADPURINE; 
01103           continue;
01104         }
01105 
01106         c6pos = framepos + 3*c6num; 
01107         vec_add(rescentrb,rescentrb,c6pos);
01108         c5pos = framepos + 3*c5num;
01109         vec_add(rescentrb,rescentrb,c5pos);
01110         c4pos = framepos + 3*c4num;
01111         vec_add(rescentrb,rescentrb,c5pos);
01112         n3pos = framepos + 3*n3num;
01113         vec_add(rescentrb,rescentrb,n3pos);
01114         c2pos = framepos + 3*c2num;
01115         vec_add(rescentrb,rescentrb,c2pos);
01116         n1pos = framepos + 3*n1num;
01117         vec_add(rescentrb,rescentrb,n1pos);
01118 
01119         rescentrb[0] = rescentrb[0]/6.0f;
01120         rescentrb[1] = rescentrb[1]/6.0f;
01121         rescentrb[2] = rescentrb[2]/6.0f;
01122 
01123         // draw bond from ribose to base
01124         cmdCylinder.putdata(c1ppos, n1pos, b_rad, b_res, 0, cmdList);
01125         cmdColorIndex.putdata(atomColor->color[n1num], cmdList);
01126 
01127         cmdTriangle.putdata(n1pos,rescentrb,c2pos,cmdList);
01128         cmdTriangle.putdata(c2pos,rescentrb,n3pos,cmdList);
01129         cmdTriangle.putdata(n3pos,rescentrb,c4pos,cmdList);
01130         cmdTriangle.putdata(c4pos,rescentrb,c5pos,cmdList);
01131         cmdTriangle.putdata(c5pos,rescentrb,c6pos,cmdList);
01132         cmdTriangle.putdata(c6pos,rescentrb,n1pos,cmdList);
01133       }
01134     }
01135   }
01136 
01137   return rc;
01138 }
01139 
01140 int DrawMolItem::draw_nucleotide_cylinders(float *framepos, int b_res, float b_rad, float ribbon_width, int use_cyl) {
01141   int frag, num;
01142   int rc=0;
01143   int lastcolor = -1; // only emit color changes when necessary
01144 
01145   b_rad *= 1.5; // hack to make it look a little better by default
01146  
01147   int nuctypes[10];
01148   nuctypes[0] = mol->resNames.typecode((char *) "A");
01149   nuctypes[1] = mol->resNames.typecode((char *) "ADE");
01150   nuctypes[2] = mol->resNames.typecode((char *) "C");
01151   nuctypes[3] = mol->resNames.typecode((char *) "CYT");
01152   nuctypes[4] = mol->resNames.typecode((char *) "G");
01153   nuctypes[5] = mol->resNames.typecode((char *) "GUA");
01154   nuctypes[6] = mol->resNames.typecode((char *) "T");
01155   nuctypes[7] = mol->resNames.typecode((char *) "THY");
01156   nuctypes[8] = mol->resNames.typecode((char *) "U");
01157   nuctypes[9] = mol->resNames.typecode((char *) "URA");
01158 
01159   ResizeArray<float> centers;
01160   ResizeArray<float> radii;
01161   ResizeArray<float> colors;
01162   ResizeArray<float> pickpointcoords;
01163   ResizeArray<int>   pickpointindices;
01164 
01165   // draw all fragments
01166   for (frag=0; frag<mol->nfragList.num(); frag++) {
01167     int loop;
01168 
01169     num = mol->nfragList[frag]->num(); // number of residues in this fragment
01170 
01171     // loop over all of the residues drawing nucleotide cylinders where we can
01172     for (loop=0; loop<num; loop++) {
01173       int istart = -1;  // init to invalid atom index
01174       int iend = -1;    // init to invalid atom index
01175       int res = (*mol->nfragList[frag])[loop];
01176       int myatom = mol->residueList[res]->atoms[0];
01177       int resnameindex = mol->atom(myatom)->resnameindex;
01178 
01179       if (istart < 0)
01180         istart = mol->find_atom_in_residue("C3'", res);
01181       if (istart < 0)
01182         istart = mol->find_atom_in_residue("C3*", res);
01183       if (istart < 0)
01184         istart = mol->find_atom_in_residue("C1'", res);
01185       if (istart < 0)
01186         istart = mol->find_atom_in_residue("C1*", res);
01187 
01188       // catch single nucleotides of interest
01189       if (istart < 0)
01190         istart = mol->find_atom_in_residue("P", res);
01191 
01192       // skip this nucleotide if a starting atom can't be found
01193       if (istart < 0)
01194         continue;
01195 
01196       // XXX now that we're using the C3 atoms as the backbone,
01197       // we'll only use the end atoms to select the nucleotide
01198       // cylinder, otherwise one can't use "backbone" and "not backbone"
01199       // selections to color the ribbon and nucleotide cylinders separately.  
01200 #if 0
01201       // skip this nucleotide if the starting atom is turned off
01202       if (!(atomSel->on[istart]))
01203         continue;
01204 #endif
01205 
01206       // assign end atom
01207       if (resnameindex == nuctypes[0] || resnameindex == nuctypes[1]) {
01208         // ADE 
01209         int n1num = mol->find_atom_in_residue("N1", res);
01210         if (n1num < 0 || !(atomSel->on[n1num]))
01211           continue;
01212         iend = n1num;
01213       } else if (resnameindex == nuctypes[2] || resnameindex == nuctypes[3]) {
01214         // CYT
01215         int n3num = mol->find_atom_in_residue("N3", res);
01216         if (n3num < 0 || !(atomSel->on[n3num]))
01217           continue;
01218         iend = n3num;
01219       } else if (resnameindex == nuctypes[4] || resnameindex == nuctypes[5]) {
01220         // GUA
01221         int n1num = mol->find_atom_in_residue("N1", res);
01222         if (n1num < 0 || !(atomSel->on[n1num]))
01223           continue;
01224         iend = n1num;
01225       } else if (resnameindex == nuctypes[6] || resnameindex == nuctypes[7]) {
01226         // THY
01227         int o4num = mol->find_atom_in_residue("O4", res);
01228         if (o4num < 0 || !(atomSel->on[o4num]))
01229           continue;
01230         iend = o4num;
01231       } else if (resnameindex == nuctypes[8] || resnameindex == nuctypes[9]) {
01232         // URA
01233         int o4num = mol->find_atom_in_residue("O4", res);
01234         if (o4num < 0 || !(atomSel->on[o4num]))
01235           continue;
01236         iend = o4num;
01237       } else {
01238         // catch single nucleotides of interest
01239         // this is a desperation move to identify modified bases that
01240         // use random residue names that will never be recognized by VMD.
01241         int o4num = mol->find_atom_in_residue("O4", res);
01242         if (o4num < 0 || !(atomSel->on[o4num]))
01243           continue;
01244         iend = o4num;
01245       }
01246   
01247       // add pick points for nucleotides 
01248       int pidx = 3 * istart;
01249       pickpointcoords.append(framepos[pidx    ]);
01250       pickpointcoords.append(framepos[pidx + 1]);
01251       pickpointcoords.append(framepos[pidx + 2]);
01252 
01253       pidx = 3 * iend;
01254       pickpointcoords.append(framepos[pidx    ]);
01255       pickpointcoords.append(framepos[pidx + 1]);
01256       pickpointcoords.append(framepos[pidx + 2]);
01257 
01258       pickpointindices.append(istart);
01259       pickpointindices.append(iend);
01260 
01261       // only emit color command if it has changed
01262       if (lastcolor != atomColor->color[istart]) {
01263         lastcolor = atomColor->color[istart]; 
01264         cmdColorIndex.putdata(lastcolor, cmdList);
01265       }
01266 
01267       // get coordinates of start and end atom
01268       float *cstart = framepos + 3*istart;
01269       float *cend = framepos + 3*iend;
01270 
01271       // draw the nucleotide cylinder
01272       cmdCylinder.putdata(cstart, cend, b_rad, b_res, 0, cmdList); 
01273 
01274       // cap ends with spheres
01275       const float *cp = scene->color_value(lastcolor);
01276 
01277 #if 0
01278       // only draw the ribbon-end sphere the starting atom is turned off
01279       // (thus no ribbon is being drawn..)  If the ribbon is being drawn,
01280       // we'll leave that end of the cylinder totally open.
01281       if (!(atomSel->on[istart])) {
01282         centers.append(cstart[0]);
01283         centers.append(cstart[1]);
01284         centers.append(cstart[2]);
01285 
01286         radii.append(b_rad);
01287 
01288         colors.append(cp[0]);
01289         colors.append(cp[1]);
01290         colors.append(cp[2]);
01291       }
01292 #endif
01293 
01294       centers.append(cend[0]);
01295       centers.append(cend[1]);
01296       centers.append(cend[2]);
01297 
01298       radii.append(b_rad);
01299 
01300       colors.append(cp[0]);
01301       colors.append(cp[1]);
01302       colors.append(cp[2]);
01303     } 
01304   } 
01305 
01306   // draw the spheres if we have any
01307   if (radii.num() > 0) {
01308     cmdSphereArray.putdata((float *) &centers[0],
01309                            (float *) &radii[0],
01310                            (float *) &colors[0],
01311                            radii.num(),
01312                            b_res,
01313                            cmdList);
01314   }
01315 
01316   // draw the pickpoints if we have any
01317   if (pickpointindices.num() > 0) {
01318     DispCmdPickPointArray pickPointArray;
01319     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
01320                            &pickpointcoords[0], cmdList);
01321   }
01322 
01323   return rc;
01324 }
01325 
01326 
01327 // draw ribbons along the protein backbone
01328 void DrawMolItem::draw_ribbons_new(float *framepos, float brad, int bres, int use_bspline, float thickness) {
01329   sprintf(commentBuffer,"MoleculeID: %d ReprID: %d Beginning Ribbons",
01330           mol->id(), repNumber);
01331   cmdCommentX.putdata(commentBuffer, cmdList);
01332 
01333   // find out if I'm using lines or cylinders
01334   int use_cyl = TRUE; // use cylinders by default
01335   int rc = 0;  // clear errors
01336   
01337   if (bres <= 2 || brad < 0.01f) {
01338     use_cyl = FALSE; // the representation will be as lines
01339   } 
01340 
01341   if (use_bspline) {
01342     create_Bspline_basis(spline_basis);
01343   }
01344 
01345   append(DMATERIALON); // enable shading 
01346 
01347   float ribbon_width = thickness;
01348   if (ribbon_width < 1.0) 
01349     ribbon_width = 1.0;
01350   ribbon_width /= 7.0;
01351 
01352   rc |= draw_protein_ribbons_new(framepos, bres, brad, ribbon_width, use_cyl);
01353   rc |= draw_nucleic_ribbons(framepos, bres, brad, ribbon_width, use_cyl, 1, 1);
01354   rc |= draw_base_sugar_rings(framepos, bres, brad, ribbon_width, use_cyl);
01355 
01356   if (rc != 0) {
01357     if (emitstructwarning())
01358       msgErr << "Warning: ribbons code encountered an unusual structure, geometry may not look as expected." << sendmsg;
01359   }
01360 
01361   // set the spline basis back to the default CR so that if we change rep
01362   // styles the other reps don't get messed up.  Ahh, the joys of one big
01363   // monolithic class...
01364   if (use_bspline) {
01365     create_modified_CR_spline_basis(spline_basis, 1.25f);
01366   }
01367 }
01368 
01369 
01370 
01371 //
01372 // The new ribbons code more clearly separates the calculation of
01373 // the ribbon spline itself and the construction of the extrusion along
01374 // the spline.  Future implementations may be able to perform the 
01375 // extrusion work on graphics accelerator hardware rather than on the host
01376 // CPU.
01377 //
01378 void DrawMolItem::draw_spline_new(int num, const float *coords,
01379                                   const float *offset, const int *idx, 
01380                                   const float *cpwidths, const float *cpheights,
01381                                   int cpscalefactors, int b_res, int cyclic) {
01382   float q[4][3]; // spline Q matrix
01383   float *pts = NULL;
01384   float *prps = NULL;
01385   int   *cols = NULL;
01386   float *interpwidths = NULL;
01387   float *interpheights = NULL;
01388   const float *widths = NULL;
01389   const float *heights = NULL;
01390   int i, j;
01391   int state, spanlen, numscalefactors;
01392   ResizeArray<float> pickpointcoords;
01393   ResizeArray<int> pickpointindices;
01394 
01395   // number of spline interpolations between control points
01396   int splinedivs = b_res + 2; 
01397   float invsplinedivs = 1.0f / ((float) splinedivs);
01398   int hdivs = splinedivs / 2;
01399 
01400   // allocations for interpolated spline points, perps, and color indices 
01401   // there can be a max of splinedivs * (num + 1) sections rendered, so
01402   // we allocate the max we'll ever need and go from there.
01403   pts  = (float *) malloc(sizeof(float) * splinedivs * 3 * (num + 1));
01404   prps = (float *) malloc(sizeof(float) * splinedivs * 3 * (num + 1));
01405   cols = (int *)   malloc(sizeof(int)   * splinedivs * (num + 1));
01406 
01407   // allocate memory for per-section interpolated width/height values
01408   if (cpscalefactors == num) {
01409     numscalefactors = splinedivs * (num + 1);
01410     interpwidths  = (float *) malloc(sizeof(float) * numscalefactors);
01411     interpheights = (float *) malloc(sizeof(float) * numscalefactors);
01412     widths = interpwidths;
01413     heights = interpheights;
01414   } else if (cpscalefactors == 1) {
01415     numscalefactors = 1;
01416     widths  = cpwidths;
01417     heights = cpheights;
01418   } else {
01419     return; // XXX error, this should never happen
01420   }
01421 
01422   if ((pts != NULL) && (prps != NULL) && (cols != NULL)) { 
01423     // place pick points for CA atoms
01424     for (i=0; i<num; i++) {
01425       if (atomSel->on[idx[i]]) {
01426         int pidx = 3 * i;
01427         pickpointcoords.append(coords[pidx    ]);
01428         pickpointcoords.append(coords[pidx + 1]);
01429         pickpointcoords.append(coords[pidx + 2]);
01430         pickpointindices.append(idx[i]);
01431       }
01432     }
01433 
01434     // draw spans of connected 'on' segments 
01435     state=0;     // no 'on' segments yet
01436     spanlen = 0; // reset span length, no segs yet (# of splinedivs) 
01437     for (i=0; i<num; i++) {
01438       if (atomSel->on[idx[i]]) {
01439         int cindex;
01440         int iminus1min, iminus2min, iplus1max;
01441         if (!cyclic) {
01442           // duplicate start/end control points for non-cyclic fragments
01443           iminus1min = ((i - 1) >= 0) ? (i - 1) : 0;
01444           iminus2min = ((i - 2) >= 0) ? (i - 2) : 0;
01445           iplus1max  = (i + 1 < num) ? (i + 1) : (num-1);
01446         } else {
01447           // cyclic structures use modular control point indexing
01448           iminus1min = (num + i - 1) % num;
01449           iminus2min = (num + i - 2) % num;
01450           iplus1max  = (num + i + 1) % num;
01451         }
01452 
01453         // reset and start a new span
01454         if (state == 0) {
01455           state = 1;   // a new span is in progress
01456           spanlen = 0; // reset span length, no segs yet (# of splinedivs) 
01457         }
01458 
01459         // construct the interpolation points (into the "pts" array)
01460         // remember, these are offset by two to keep some information
01461         // about the previous residue in the first 2 elements
01462         make_spline_Q_matrix_noncontig(q, spline_basis, 
01463                           &coords[iminus2min * 3],
01464                           &coords[iminus1min * 3],
01465                           &coords[i          * 3],
01466                           &coords[iplus1max  * 3]);
01467 
01468         // calculated interpolated spline points
01469         for (j=0; j<splinedivs; j++) {    
01470           make_spline_interpolation(&pts[(spanlen + j) * 3], 
01471                                     j * invsplinedivs, q);
01472         }
01473 
01474         // range-clamp control point index since the first few are copies
01475         int cpind = iminus1min * 3;
01476 
01477         // interpolate perps
01478         for (j=0; j<splinedivs; j++) {  
01479           int ind = (spanlen + j)*3;
01480           float v = j * invsplinedivs;
01481           float vv = (1.0f - v); 
01482           prps[ind    ] = vv * offset[cpind    ] + v * offset[cpind + 3];
01483           prps[ind + 1] = vv * offset[cpind + 1] + v * offset[cpind + 4];
01484           prps[ind + 2] = vv * offset[cpind + 2] + v * offset[cpind + 5];
01485           vec_normalize(&prps[ind]);
01486         }
01487 
01488         // interpolate width/height values
01489         if (cpscalefactors == num) {
01490           float wminus1 = cpwidths[iminus1min];
01491           float w = cpwidths[i];
01492           float hminus1 = cpheights[iminus1min];
01493           float h = cpheights[i];
01494 
01495           if (w >= 0 && wminus1 >= 0) {
01496             // if both widths are positive, interpolate between them, 
01497             // otherwise take the previous width value rather than 
01498             // interpolating, used for drawing beta arrows.
01499             for (j=0; j<splinedivs; j++) {  
01500               int ind = spanlen + j;
01501               float v = j * invsplinedivs;
01502               float vv = (1.0f - v); 
01503               interpwidths[ind]  = vv * wminus1 + v * w;
01504               interpheights[ind] = vv * hminus1 + v * h;
01505             }
01506           } else {
01507             float wplus1 = cpwidths[iplus1max];
01508             if (wplus1 < 0) 
01509               wplus1 = -wplus1; // undo width negation
01510 
01511             if (w < 0) {
01512               w = -w; // undo width negation
01513 
01514               for (j=0; j<hdivs; j++) {  
01515                 int ind = spanlen + j;
01516                 float v = j * invsplinedivs;
01517                 interpwidths[ind]  = wminus1;
01518                 interpheights[ind] = (1.0f - v) * hminus1 + v * h;
01519               }
01520               for (j=hdivs; j<splinedivs; j++) {  
01521                 int ind = spanlen + j;
01522                 float v = j * invsplinedivs;
01523                 float nv = (j-hdivs) * invsplinedivs;
01524                 interpwidths[ind]  = (1.0f - nv) * w + nv * wplus1;
01525                 interpheights[ind] = (1.0f - v) * hminus1 + v * h;
01526               }
01527             } else {
01528               wminus1 = -wminus1; // undo width negation
01529 
01530               for (j=0; j<hdivs; j++) {  
01531                 int ind = spanlen + j;
01532                 float v = j * invsplinedivs;
01533                 float nv = (j + (splinedivs - hdivs)) * invsplinedivs;
01534                 interpwidths[ind]  = (1.0f - nv) * wminus1 + nv * w;
01535                 interpheights[ind] = (1.0f - v) * hminus1 + v * h;
01536               }
01537               for (j=hdivs; j<splinedivs; j++) {  
01538                 int ind = spanlen + j;
01539                 float v = j * invsplinedivs;
01540                 interpwidths[ind]  = w;
01541                 interpheights[ind] = (1.0f - v) * hminus1 + v * h;
01542               }
01543             }
01544           }
01545         }
01546 
01547         // lookup atom color indices and assign to the interpolated points
01548         cindex = atomColor->color[idx[iminus1min]]; 
01549         for (j=0; j<hdivs; j++) {    
01550           cols[spanlen + j] = cindex; // set color index for each point
01551         } 
01552 
01553         cindex = atomColor->color[idx[i]]; 
01554         for (j=hdivs; j<splinedivs; j++) {    
01555           cols[spanlen + j] = cindex; // set color index for each point
01556         }
01557   
01558         spanlen += splinedivs; // number of segments rendered (cp * splinediv)
01559       } else {
01560         // render a finished span
01561         if (state == 1) {
01562           state = 0; // span is finished, ready to start a new one
01563 
01564           // draw last section to connect with other reps...
01565           int iminus1min, iminus2min, iplus1max;
01566           if (!cyclic) {
01567             // duplicate start/end control points for non-cyclic fragments
01568             iminus1min = ((i - 1) >= 0) ? (i - 1) : 0;
01569             iminus2min = ((i - 2) >= 0) ? (i - 2) : 0;
01570             iplus1max  = (i + 1 < num) ? (i + 1) : (num-1);
01571           } else {
01572             // cyclic structures use modular control point indexing
01573             iminus1min = (num + i - 1) % num;
01574             iminus2min = (num + i - 2) % num;
01575             iplus1max  = (num + i + 1) % num;
01576           }
01577 
01578           int ind = spanlen * 3;
01579           make_spline_interpolation(&pts[ind], 1.0, q);
01580           int cpind = iminus2min * 3;
01581           prps[ind    ] = offset[cpind + 3]; 
01582           prps[ind + 1] = offset[cpind + 4]; 
01583           prps[ind + 2] = offset[cpind + 5]; 
01584           vec_normalize(&prps[ind]);
01585 
01586           if (cpscalefactors == num) {
01587             float w = cpwidths[i];
01588             float wminus1 = cpwidths[iminus1min];
01589             // float h = cpheights[i];
01590             float hminus1 = cpheights[iminus1min];
01591 
01592             if (w >= 0 && wminus1 >= 0) {
01593               interpwidths[spanlen] = wminus1;
01594               interpheights[spanlen] = hminus1; 
01595             } else {
01596               float wplus1 = cpwidths[iplus1max];
01597               if (wplus1 < 0) 
01598                 wplus1 = -wplus1; // undo width negation
01599 
01600               if (w < 0) {
01601                 w = -w; // undo width negation
01602 
01603                 float nv = (splinedivs - hdivs) * invsplinedivs;
01604                 interpwidths[spanlen]  = (1.0f - nv) * w + nv * wplus1;
01605                 interpheights[spanlen] = wminus1;
01606               } else {
01607                 if (w < 0) 
01608                   w = -w; // undo width negation
01609                 wminus1 = -wminus1; // undo width negation
01610                 interpwidths[spanlen]  = w;
01611                 interpheights[spanlen] = hminus1;
01612               }
01613             }
01614           }
01615 
01616           cols[spanlen] = atomColor->color[idx[iminus1min]];
01617           spanlen++;
01618 
01619           // draw a ribbon with the spline data
01620           int scalenum = (cpscalefactors == num) ? spanlen : 1;
01621           draw_ribbon_from_points(spanlen, pts, prps, cols, 
01622                                   b_res, widths, heights, scalenum);
01623         }
01624       }
01625     } 
01626 
01627     if (state == 1) {
01628       // draw last section to connect with other reps...
01629       int iminus1min, iminus2min, iplus1max;
01630       if (!cyclic) {
01631         // duplicate start/end control points for non-cyclic fragments
01632         iminus1min = ((i - 1) >= 0) ? (i - 1) : 0;
01633         iminus2min = ((i - 2) >= 0) ? (i - 2) : 0;
01634         iplus1max  = (i + 1 < num) ? (i + 1) : (num-1);
01635       } else {
01636         // cyclic structures use modular control point indexing
01637         iminus1min = (num + i - 1) % num;
01638         iminus2min = (num + i - 2) % num;
01639         iplus1max  = (num + i + 1) % num;
01640       }
01641 
01642       int ind = spanlen * 3;
01643       make_spline_interpolation(&pts[ind], 1.0, q);
01644       int cpind = iminus2min * 3;
01645       prps[ind    ] = offset[cpind + 3]; 
01646       prps[ind + 1] = offset[cpind + 4]; 
01647       prps[ind + 2] = offset[cpind + 5]; 
01648       vec_normalize(&prps[ind]);
01649 
01650       if (cpscalefactors == num) {
01651         float w = cpwidths[iminus1min];
01652         float wminus1 = cpwidths[iminus1min];
01653         // float h = cpheights[iminus1min];
01654         float hminus1 = cpheights[iminus1min];
01655 
01656         if (w >= 0 && wminus1 >= 0) {
01657           interpwidths[spanlen] = wminus1;
01658           interpheights[spanlen] = hminus1; 
01659         } else {
01660           float wplus1 = cpwidths[iplus1max];
01661           if (wplus1 < 0) 
01662             wplus1 = -wplus1; // undo width negation
01663 
01664           if (w < 0) {
01665             w = -w; // undo width negation
01666 
01667             float nv = (splinedivs - hdivs) * invsplinedivs;
01668             float nvv = (1.0f - nv); 
01669             interpwidths[spanlen]  = nvv * w + nv * wplus1;
01670             interpheights[spanlen] = wminus1;
01671           } else {
01672             if (w < 0) 
01673               w = -w; // undo width negation
01674             wminus1 = -wminus1; // undo width negation
01675             interpwidths[spanlen]  = w;
01676             interpheights[spanlen] = hminus1;
01677           }
01678         }
01679       }
01680 
01681       cols[spanlen] = atomColor->color[idx[iminus1min]];
01682       spanlen++;
01683 
01684       // draw a ribbon with the spline data
01685       int scalenum = (cpscalefactors == num) ? spanlen : 1;
01686       draw_ribbon_from_points(spanlen, pts, prps, cols, 
01687                               b_res, widths, heights, scalenum);
01688     }
01689   }
01690 
01691   if (pts != NULL)
01692     free(pts);
01693 
01694   if (prps != NULL) 
01695     free(prps);
01696 
01697   if (cols != NULL)
01698     free(cols);
01699 
01700   // deallocate memory for per-section interpolated width/height values
01701   if (interpwidths != NULL)
01702     free(interpwidths);
01703 
01704   if (interpheights != NULL)
01705     free(interpheights);
01706 
01707   // draw the pickpoints if we have any
01708   if (pickpointindices.num() > 0) {
01709     DispCmdPickPointArray pickPointArray;
01710     pickPointArray.putdata(pickpointindices.num(), &pickpointindices[0],
01711                            &pickpointcoords[0], cmdList);
01712   }
01713 }
01714 
01715 
01716 // 
01717 // Routine to render a ribbon or tube based on an array of points that
01718 // define the backbone spline on which an extrusion is built.  The 
01719 // cross-section of the extrusion can be any oval shape described by
01720 // height/width parameters that are used to scale a circular cross 
01721 // section.
01722 //
01723 void DrawMolItem::draw_ribbon_from_points(int numpoints, const float *points, 
01724                   const float *perps, const int *cols, int numpanels, 
01725                   const float *widths, const float *heights, 
01726                   int numscalefactors) {
01727   int numverts, numsections;
01728   int point, section, panel, panelindex;
01729   int index; // array index of the point coordinate we're working with
01730   float *vertexarray, *normalarray, *colorarray;
01731   float *panelshape, *panelverts, *panelnorms;
01732   float curdir[3], perpdir[3], updir[3];
01733   float width, height, lastwidth, lastheight;
01734 
01735   // we must have at least two points and two panels per section
01736   if (numpoints < 2 || numpanels < 2)
01737     return; // skip drawing if basic requirements are not met
01738  
01739   numsections = numpoints - 1;
01740   numverts = numpoints * numpanels;  
01741 
01742   // storage for final vertex array data
01743   vertexarray = (float *) malloc(numverts * 3 * sizeof(float));
01744   normalarray = (float *) malloc(numverts * 3 * sizeof(float));
01745    colorarray = (float *) malloc(numverts * 3 * sizeof(float));
01746 
01747   // storage for panel cross-section data
01748   panelverts = (float *) malloc(numpanels * 2 * sizeof(float));
01749   panelnorms = (float *) malloc(numpanels * 2 * sizeof(float));
01750   panelshape = (float *) malloc(numpanels * 2 * sizeof(float));
01751 
01752   // bail out if any of the memory allocations fail
01753   if (!vertexarray || !normalarray || !colorarray ||
01754       !panelverts  || !panelnorms  || !panelshape) {
01755     if (vertexarray) free(vertexarray);
01756     if (normalarray) free(normalarray);
01757     if (colorarray)  free(colorarray);
01758     if (panelverts)  free(panelverts);
01759     if (panelnorms)  free(panelnorms);
01760     if (panelshape)  free(panelshape);
01761     return;
01762   }
01763 
01764   // Pre-calculate cross-section template shape.  The current template shape 
01765   // is a unit circle, but it could be any shape that can be scaled efficiently
01766   // by the code that generates section offsets and normals.
01767   for (panel=0; panel<numpanels; panel++) {
01768     int pidx = panel * 2;
01769     float radangle = (float) ((VMD_TWOPI) * (panel / ((float) numpanels)));
01770     panelshape[pidx    ] = sinf(radangle);
01771     panelshape[pidx + 1] = cosf(radangle);
01772   }
01773 
01774   // initialize with invalid values so we recalculate the section 
01775   // vertex offsets and normals the first time through.
01776   lastwidth  = -999;
01777   lastheight = -999;
01778 
01779   // setup first width/height values
01780   width  =  widths[0];
01781   height = heights[0];
01782 
01783   // calculate each cross section from the spline points and "perp" vectors
01784   // that are given to us by caller, combined with the vertex offsets and
01785   // normals we've already calculated.
01786   for (point=0, index=0; point<numpoints; point++, index+=3) {
01787 
01788     // If we are provided with width/height values for every section, we
01789     // have to update the cross-sections each time the values change.
01790     // Otherwise we just re-use the same cross-section vertex offsets and
01791     // normals over and over.
01792     if (numscalefactors == numpoints) {
01793       width  = widths[point];
01794       height = heights[point];
01795     }
01796 
01797     // Calculate cross-section vertex offsets and normals. For Cartoon 
01798     // reps these have to be recalculated for each section, but for
01799     // a plain ribbon rep, the width/height are constant and the data can
01800     // be re-used over and over again for all sections.   We test to see
01801     // if width or height have changed, and only recalculate when necessary.
01802     if (width != lastwidth || height != lastheight) {
01803       float invwidth, invheight;
01804 
01805       lastwidth = width;
01806       lastheight = width;
01807 
01808       // calculate inverse scaling coefficients which are applied to normals
01809       invwidth = 1.0f / width;
01810       invheight = 1.0f / height;
01811 
01812 #if 1
01813       // Standard elliptical cross-section created by scaling a unit circle
01814       // and applying inverse scaling to the normals.  Simple to calculate,
01815       // generates decent looking results.
01816       for (panel=0; panel<numpanels; panel++) {
01817         int pidx = panel * 2;
01818         int pidy = pidx + 1;
01819         float xn, yn, invlen;
01820 
01821         // calculate vertices for a cross-section with a given width/height
01822         panelverts[pidx] = width  * panelshape[pidx];
01823         panelverts[pidy] = height * panelshape[pidy];
01824 
01825         // calculate normals for a cross-section with a given width/height
01826         xn = invwidth  * panelshape[pidx];
01827         yn = invheight * panelshape[pidy];
01828         invlen = 1.0f / sqrtf(xn*xn + yn*yn);
01829         panelnorms[pidx] = xn * invlen;
01830         panelnorms[pidy] = yn * invlen;
01831       }
01832 #else
01833       // Alternative cross-section shape built from a completely flat ribbon 
01834       // capped by two semi-circles at the edges.  Looks similar to the
01835       // elliptical ribbon, but has a flat face, so vertices are concentrated
01836       // at the edges, giving better looking shading results for the same number
01837       // of drawn vertices in many cases.  This might be worth trying out 
01838       // in place of the elliptical implementation, though it requires a 
01839       // few more more calculations.
01840   
01841       // calculate vertices for semi-circle cap on the right hand side
01842       for (panel=0; panel<(numpanels/2); panel++) {
01843         int pidx = panel * 2;
01844         int pidy = pidx + 1;
01845 
01846         // calculate vertices for a cross-section with a given width/height
01847         panelverts[pidx] = (width  / 2.0f) + panelshape[pidx];
01848         panelverts[pidy] = height * panelshape[pidy];
01849       }
01850 
01851       // calculate vertices for semi-circle cap on the left hand side
01852       for (panel=(numpanels/2); panel<numpanels; panel++) {
01853         int pidx = panel * 2;
01854         int pidy = pidx + 1;
01855 
01856         // calculate vertices for a cross-section with a given width/height
01857         panelverts[pidx] = (-width  / 2.0f) + panelshape[pidx];
01858         panelverts[pidy] = height * panelshape[pidy];
01859       }
01860 
01861       // calculate normals for both sides
01862       for (panel=0; panel<numpanels; panel++) {
01863         int pidx = panel * 2;
01864         int pidy = pidx + 1;
01865         float xn, yn, invlen;
01866     
01867         // calculate normals for a cross-section with a given width/height
01868         xn = invwidth  * panelshape[pidx];
01869         yn = invheight * panelshape[pidy];
01870         invlen = 1.0f / sqrtf(xn*xn + yn*yn);
01871         panelnorms[pidx] = xn * invlen;
01872         panelnorms[pidy] = yn * invlen;
01873       }
01874 #endif
01875     } // end of vertex offset and normal calculations
01876 
01877 
01878     // calculate and normalize the direction vector, but avoid going out
01879     // of bounds on the last point/index we calculate.
01880     if (point != (numpoints - 1)) {
01881       vec_sub(curdir, &points[index], &points[index + 3]); 
01882     } else {
01883       // handle the endpoint case where we re-use previous direction
01884       // this code requires that numpoints > 1.
01885       vec_sub(curdir, &points[index-3], &points[index]); 
01886     }
01887     vec_normalize(curdir);                 
01888 
01889     // calculate "up" from direction and perp vectors
01890     vec_copy(perpdir, &perps[index]);    // copy perpdir
01891     cross_prod(updir, curdir, perpdir);  // find section "up" direction   
01892     vec_normalize(updir);                // normalize the up direction
01893 
01894     // create vertices for the cross-section
01895     for (panel=0; panel<numpanels; panel++) {
01896       panelindex = (point * numpanels + panel) * 3;
01897       int pidx = panel * 2;
01898       float xv = panelverts[pidx    ];
01899       float yv = panelverts[pidx + 1];
01900 
01901       // use the unnormalized vertex direction vectors to construct the 
01902       // vertex position by offsetting from the original spline point.
01903       vertexarray[panelindex    ] = 
01904         (xv * perpdir[0]) + (yv * updir[0]) + points[index    ]; 
01905       vertexarray[panelindex + 1] = 
01906         (xv * perpdir[1]) + (yv * updir[1]) + points[index + 1]; 
01907       vertexarray[panelindex + 2] = 
01908         (xv * perpdir[2]) + (yv * updir[2]) + points[index + 2]; 
01909     }
01910 
01911     // create the normals for the cross-section
01912     for (panel=0; panel<numpanels; panel++) {
01913       panelindex = (point * numpanels + panel) * 3;
01914       int pidx = panel * 2;
01915       float xn = panelnorms[pidx    ];
01916       float yn = panelnorms[pidx + 1];
01917 
01918       // basis vectors are all pre-normalized so no re-normalization of the
01919       // resulting calculations needs to be done here inside this loop.
01920       normalarray[panelindex    ] = (xn * perpdir[0]) + (yn * updir[0]); 
01921       normalarray[panelindex + 1] = (xn * perpdir[1]) + (yn * updir[1]); 
01922       normalarray[panelindex + 2] = (xn * perpdir[2]) + (yn * updir[2]); 
01923     }
01924 
01925     // assign colors to the vertices in this cross-section
01926     const float *fp = scene->color_value(cols[point]);
01927     for (panel=0; panel<numpanels; panel++) {
01928       panelindex = ((point * numpanels) + panel) * 3;
01929       colorarray[panelindex    ] = fp[0]; // Red
01930       colorarray[panelindex + 1] = fp[1]; // Green
01931       colorarray[panelindex + 2] = fp[2]; // Blue
01932     }
01933   } 
01934 
01935   // generate facet lists from the vertex, normal, and color arrays
01936   // using the triangle strip primitive in VMD.
01937   int numstripverts = numsections * ((numpanels * 2) + 2);
01938   unsigned int * faces = new unsigned int[numstripverts];
01939   int * vertsperstrip = new int[numsections];
01940   int numstrips = numsections;
01941 
01942   int l=0;  
01943   for (section=0; section<numsections; section++) {
01944     vertsperstrip[section] = (numpanels * 2) + 2;
01945 
01946     int panel;
01947     for (panel=0; panel<numpanels; panel++) {
01948       // create a 2 triangles for each panel 
01949       int index = ((section * numpanels) + panel);
01950       faces[l    ] = index + numpanels;
01951       faces[l + 1] = index;
01952       l+=2;
01953     }
01954 
01955     // create a 2 triangles for each panel 
01956     int index  = section * numpanels;
01957     faces[l    ] = index + numpanels;
01958     faces[l + 1] = index;
01959 
01960     l+=2;
01961   }
01962 
01963   // Draw the ribbon!
01964   append(DMATERIALON);         // enable materials and lighting
01965 
01966   // draw triangle strips using single-sided lighting for best speed
01967   DispCmdTriStrips cmdTriStrips;  
01968   cmdTriStrips.putdata(vertexarray, normalarray, colorarray, numverts, 
01969                        vertsperstrip, numstrips, faces, numstripverts, 
01970                        0, cmdList);
01971 
01972   delete [] faces;
01973   delete [] vertsperstrip;
01974 
01975   free(vertexarray);
01976   free(normalarray);
01977   free(colorarray);
01978   free(panelshape);
01979   free(panelverts);
01980   free(panelnorms);
01981 }
01982 
01983 int DrawMolItem::draw_cartoon_ribbons(float *framepos, int b_res, float b_rad,
01984                              float ribbon_width, int use_cyl, int use_bspline) {
01985   float *coords = NULL;
01986   float *perps = NULL;
01987   int *idx = NULL;
01988   float *capos, *last_capos, *opos, *last_opos;
01989   int onum, canum, frag, num, res;
01990   float *widths, *heights;          // per-control point widths/heights
01991   int rc=0;
01992 
01993 #if defined(VMDFASTRIBBONS)
01994   wkf_timerhandle tm = wkf_timer_create();
01995   wkf_timerhandle tm2 = wkf_timer_create();
01996   wkf_timerhandle tm3 = wkf_timer_create();
01997   wkf_timerhandle tm4 = wkf_timer_create();
01998   wkf_timer_start(tm);
01999   wkf_timer_start(tm3);
02000   double foo=0.0;
02001 #endif
02002 
02003   // these are the variables used in the Raster3D package
02004   float a[3], b[3], c[3], d[3], e[3], g[3];  
02005 
02006   if (use_bspline) {
02007     create_Bspline_basis(spline_basis);
02008   }
02009 
02010   // draw nucleic acid ribbons and nucleotide cylinders
02011   rc |= draw_nucleic_ribbons(framepos, b_res, b_rad, ribbon_width / 7.0f, use_cyl, 1, 1);
02012   rc |= draw_nucleotide_cylinders(framepos, b_res, b_rad, ribbon_width / 7.0f, use_cyl);
02013 
02014   // Lookup atom typecodes ahead of time so we can use the most 
02015   // efficient variant of find_atom_in_residue() in the main loop.
02016   // If we can't find the atom types we need, bail out immediately
02017   int CAtypecode  = mol->atomNames.typecode("CA");
02018   int Otypecode   = mol->atomNames.typecode("O");
02019   int OT1typecode = mol->atomNames.typecode("OT1");
02020 
02021   if (CAtypecode < 0 || ((Otypecode < 0) && (OT1typecode < 0))) {
02022     return rc; // can't draw a ribbon without CA and O atoms for guidance
02023   }
02024 #if defined(VMDFASTRIBBONS)
02025   wkf_timer_stop(tm3);
02026   msgInfo << "Cartoon nucleotide time: " << wkf_timer_time(tm3) << sendmsg;
02027 #endif
02028 
02029   // Indicate that I need secondary structure information
02030   mol->need_secondary_structure(1);  // calculate 2ndary structure if need be
02031 
02032 #if defined(VMDFASTRIBBONS)
02033   wkf_timer_start(tm2);
02034 #endif
02035 
02036   // allocate for the maximum possible per-residue control points, perps, 
02037   // and indices so we don't have to reallocate for every fragment
02038   coords = (float *) malloc((mol->nResidues) * sizeof(float)*3);
02039      idx =   (int *) malloc((mol->nResidues) * sizeof(int));
02040    perps = (float *) malloc((mol->nResidues) * sizeof(float)*3);
02041 
02042   // cross section widths and heights
02043   widths = (float *) malloc(mol->nResidues * sizeof(float));
02044  heights = (float *) malloc(mol->nResidues * sizeof(float));
02045 
02046   // go through each protein and find the CA and O
02047   // from that, construct the offsets to use (called "perps")
02048   for (frag=0; frag<mol->pfragList.num(); frag++) {
02049     int cyclic=mol->pfragCyclic[frag];  // whether fragment is cyclic
02050     num = mol->pfragList[frag]->num();  // number of residues in this fragment
02051     if (num < 2) {
02052       rc |= RIBBON_ERR_NOTENOUGH;
02053       continue; // can't draw a ribbon with only one element, so skip
02054     }
02055 
02056     // check that we have a valid structure before continuing
02057       res = (*mol->pfragList[frag])[0];
02058     canum = mol->find_atom_in_residue(CAtypecode, res);
02059      onum = mol->find_atom_in_residue(Otypecode, res);
02060 
02061     if (canum < 0 || onum < 0) {
02062       continue; // can't find 1st CA or O of the protein, so don't draw
02063     }
02064 
02065     // initialize last 4 CA indices for use by helix rendering code
02066     int ca2, ca3, ca4;
02067     ca2=ca3=ca4=canum;
02068 
02069     int starthelix=-1;
02070     int endhelix=-1;    
02071     float starthelixperp[3];
02072 
02073     // initialize CA and O atom pointers
02074     capos = framepos + 3*canum;
02075     last_capos = capos;
02076     opos = framepos + 3*onum;
02077     last_opos = opos;
02078       
02079     // now go through and set the coordinates and the perps
02080     e[0] = e[1] = e[2] = 0.0;
02081     vec_copy(g, e);
02082 
02083     // for a cyclic structure, we use the positions of the last residue
02084     // to seed the initial direction vectors for the ribbon
02085     if (cyclic) {
02086       int lastres = (*mol->pfragList[frag])[num-1];
02087       int lastcanum = mol->find_atom_in_residue(CAtypecode, lastres);
02088       last_capos = framepos + 3*lastcanum;
02089 
02090       int lastonum = mol->find_atom_in_residue(Otypecode, lastres);
02091       if (lastonum < 0 && OT1typecode >= 0) {
02092         lastonum = mol->find_atom_in_residue(OT1typecode, lastres);
02093       }
02094       last_opos = framepos + 3*lastonum;
02095 
02096       // now I need to figure out where the ribbon goes
02097       vec_sub(a, capos, last_capos);     // A=(pos(CA(res)) - pos(CA(res-1)))
02098       vec_sub(b, last_opos, last_capos); // B=(pos(O(res-1)) - pos(CA(res-1)))
02099       cross_prod(c, a, b);               // C=AxB, define res-1's peptide plane
02100       cross_prod(d, c, a);               // D=CxA, normal to plane and backbone
02101       // if normal is > 90 deg from  previous one, negate the new one
02102       if (dot_prod(d, g) < 0) {
02103         vec_negate(b, d);
02104       } else {
02105         vec_copy(b, d);
02106       }
02107       vec_add(e, g, b);            // average to the sum of the previous vectors
02108       vec_normalize(e);
02109       vec_copy(g, e);              // make a cumulative sum; cute, IMHO
02110     }
02111 
02112     int loop;
02113     for (loop=0; loop<num; loop++) {
02114       res = (*mol->pfragList[frag])[loop];
02115       const int ss = mol->residue(res)->sstruct;
02116       float helixpos[3]; // storage for modified control point position
02117 
02118       int newcanum = mol->find_atom_in_residue(CAtypecode, res);
02119       if (newcanum >= 0) {
02120         ca4 = ca3;
02121         ca3 = ca2;
02122         ca2 = canum;
02123         canum = newcanum; 
02124         capos = framepos + 3*canum;
02125       }
02126 
02127       onum = mol->find_atom_in_residue(Otypecode, res);
02128       if (onum < 0 && OT1typecode >= 0) {
02129         onum = mol->find_atom_in_residue(OT1typecode, res);
02130       }
02131       if (onum >= 0) {
02132         opos = framepos + 3*onum;
02133       } else {
02134         rc |= RIBBON_ERR_PROTEIN_MESS;
02135         opos = last_opos; // try to cope if we have no oxygen index
02136       }
02137       idx[loop] = canum;
02138 
02139       // calculate ribbon cross section 
02140       int drawhelixwithrods = 0;
02141       int drawbetawithribbons = 0;
02142 #if 0
02143       drawhelixwithrods = (getenv("VMDHELIXRODS") != NULL);
02144       drawbetawithribbons = (getenv("VMDBETARIBBONS") != NULL);
02145 #endif
02146 
02147       switch (ss) {
02148         case SS_HELIX_ALPHA:
02149         case SS_HELIX_3_10:
02150         case SS_HELIX_PI:
02151           if (drawhelixwithrods) { 
02152             // if we just entered the helix, find the end of the helix so 
02153             // we can generate usable perps by interpolation.
02154             if (starthelix == -1) {
02155               int helind;
02156               starthelix = loop;
02157               for (helind=loop; helind<num; helind++) {
02158                 int hres = (*mol->pfragList[frag])[helind];
02159                 const int hss = mol->residue(hres)->sstruct;
02160                 if (hss == SS_HELIX_ALPHA ||
02161                     hss == SS_HELIX_3_10 || 
02162                     hss == SS_HELIX_PI) {
02163                   endhelix = helind;
02164                 } else {
02165                   break; // stop when this helix ends
02166                 }
02167               }
02168 
02169               // save starting perp and ending perp, for interpolation later
02170             }
02171 
02172             widths[loop] = 4 * b_rad;
02173             heights[loop] = 4 * b_rad;
02174             vec_copy(helixpos, framepos + 3 * canum); 
02175             vec_add(helixpos, helixpos, framepos + 3 * ca2); 
02176             vec_add(helixpos, helixpos, framepos + 3 * ca3); 
02177             vec_add(helixpos, helixpos, framepos + 3 * ca4); 
02178             vec_scale(helixpos, 0.25, helixpos);
02179 
02180             // copy the helix coordinate into the control point array
02181             vec_copy(coords+loop*3, helixpos);
02182             capos = helixpos; // perps are calculated from modified position 
02183           } else {
02184             widths[loop] = ribbon_width * b_rad;
02185             heights[loop] = b_rad;
02186             // copy the CA coordinate into the control point array
02187             vec_copy(coords+loop*3, capos);
02188           }
02189           break;
02190 
02191         case SS_TURN:
02192         case SS_COIL:
02193         case SS_BRIDGE:
02194         default:
02195           widths[loop] = b_rad;
02196           heights[loop] = b_rad;
02197           // copy the CA coordinate into the control point array
02198           vec_copy(coords+loop*3, capos);
02199           break;
02200 
02201         case SS_BETA:
02202           widths[loop] = ribbon_width * b_rad;
02203           heights[loop] = b_rad;
02204 
02205           if (drawbetawithribbons) {
02206             // copy the CA coordinate into the control point array
02207             vec_copy(coords+loop*3, capos);
02208           } else {
02209             float betapos[3]; 
02210             int drawarrowhead = 0;
02211 
02212             // filter the control point positions, giving equal weight
02213             // to the current CA atom and it's two neighbors, so they
02214             // ideally cancel out the wiggles in the beta sheet, without
02215             // having to switch spline basis etc.
02216             int caplus1 = -1; // make invalid initially
02217 
02218             if ((loop+1) < num) {
02219               int nextres = (*mol->pfragList[frag])[loop+1];
02220               caplus1 = mol->find_atom_in_residue(CAtypecode, nextres);
02221 
02222               // draw directionality arrow if we're at the end
02223               if (mol->residue(nextres)->sstruct != SS_BETA) 
02224                 drawarrowhead = 1;
02225             } else {
02226               // draw directionality arrow if we're at the end
02227               drawarrowhead = 1;
02228             }
02229 
02230             // mark this width to avoid interpolating it, and make
02231             // the arrowhead wider than the beta sheet body
02232             // XXX non-interpolated control points are negated
02233             if (drawarrowhead) 
02234               widths[loop] = -ribbon_width * b_rad * 1.75f;
02235 
02236             if (caplus1 < 0)
02237               caplus1 = canum;
02238 
02239             vec_copy(betapos, framepos + 3 * canum); 
02240             vec_scale(betapos, 2.0f, betapos);
02241             vec_add(betapos, betapos, framepos + 3 * caplus1); 
02242             vec_add(betapos, betapos, framepos + 3 * ca2); 
02243             vec_scale(betapos, 0.25f, betapos);
02244 
02245             // copy the beta coordinate into the control point array
02246             vec_copy(coords+loop*3, betapos);
02247 
02248             // perps will be generated using the original CA position
02249           }
02250           break;
02251       }
02252 
02253 
02254       // now I need to figure out where the ribbon goes
02255       vec_sub(a, capos, last_capos);     // A=(pos(CA(res)) - pos(CA(res-1)))
02256       vec_sub(b, last_opos, last_capos); // B=(pos(O(res-1)) - pos(CA(res-1)))
02257       cross_prod(c, a, b);               // C=AxB, define res-1's peptide plane
02258       cross_prod(d, c, a);               // D=CxA, normal to plane and backbone
02259 
02260       // if normal is > 90 deg from  previous one, negate the new one
02261       if (dot_prod(d, g) < 0) {
02262         vec_negate(b, d);
02263       } else {
02264         vec_copy(b, d);
02265       }
02266       vec_add(e, g, b);            // average to the sum of the previous vectors
02267       vec_normalize(e);
02268       vec_copy(&perps[3*loop], e); // compute perps from the normal
02269       vec_copy(g, e);              // make a cumulative sum; cute, IMHO
02270       last_capos = capos;
02271       last_opos = opos;
02272 
02273       if (drawhelixwithrods) {
02274         if (loop == starthelix) {
02275           // save starting helix perp
02276           vec_copy(starthelixperp, e); 
02277         } else  if (loop > starthelix && loop < endhelix) {
02278           // should interpolate rather than copy, but this is just for testing
02279           vec_copy(&perps[3*loop], starthelixperp);
02280         } else if (loop == endhelix) {
02281           // reset helix start and end if we've reached the end
02282           starthelix = -1; 
02283           endhelix = -1; 
02284         }
02285       }
02286                      
02287     }
02288 
02289     if (!cyclic) {
02290       // copy the second perp to the first since the first one didn't have
02291       // enough data to construct a good perpendicular.
02292       vec_copy(perps, perps+3);
02293     }
02294 
02295 #if defined(VMDFASTRIBBONS)
02296     wkf_timer_start(tm4);
02297 #endif
02298     // draw the cartoon 
02299     draw_spline_new(num, coords, perps, idx, widths, heights, num, b_res, cyclic);
02300 #if defined(VMDFASTRIBBONS)
02301     wkf_timer_stop(tm4);
02302     foo+=wkf_timer_time(tm4);
02303 #endif
02304   } // drew the protein fragment ribbon
02305 
02306   if (coords) {
02307     free(coords);
02308     free(idx);
02309     free(perps);
02310     free(widths);
02311     free(heights);
02312   }
02313 
02314   // set the spline basis back to the default CR so that if we change rep
02315   // styles the other reps don't get messed up.  Ahh, the joys of one big
02316   // monolithic class...
02317   if (use_bspline) {
02318     create_modified_CR_spline_basis(spline_basis, 1.25f);
02319   }
02320 
02321 #if defined(VMDFASTRIBBONS)
02322   wkf_timer_stop(tm2);
02323   msgInfo << "Cartoon spline time: " << wkf_timer_time(tm2) << sendmsg;
02324   msgInfo << "     subspline time: " << foo << sendmsg;
02325 #endif
02326 #if defined(VMDFASTRIBBONS)
02327   wkf_timer_stop(tm);
02328   msgInfo << "Cartoon regen time: " << wkf_timer_time(tm) << sendmsg;
02329   wkf_timer_destroy(tm);
02330   wkf_timer_destroy(tm2);
02331   wkf_timer_destroy(tm3);
02332 #endif
02333 
02334   return rc;
02335 }
02336 

Generated on Sat May 26 01:47:57 2012 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002