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

TclMeasure.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: TclMeasure.C,v $
00013  *      $Author: akohlmey $     $Locker:  $             $State: Exp $
00014  *      $Revision: 1.145 $      $Date: 2011/01/28 01:16:21 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  * These are essentially just Tcl wrappers for the measure commands in
00019  * Measure.C.
00020  *
00021  ***************************************************************************/
00022 
00023 #include <stdlib.h>
00024 #include <tcl.h>
00025 #include <ctype.h>
00026 #include "TclCommands.h"
00027 #include "AtomSel.h"
00028 #include "Matrix4.h"
00029 #include "SymbolTable.h"
00030 #include "VMDApp.h"
00031 #include "MoleculeList.h"
00032 #include "utilities.h"
00033 #include "config.h"
00034 #include "Measure.h"
00035 #include "MeasureSymmetry.h"
00036 #include "SpatialSearch.h"
00037 #include "Atom.h"
00038 #include "Molecule.h"
00039 
00040 
00041 // compute the center of mass
00042 // Takes an atom selection and one of three possible weights
00043 //  1) if weight == NULL, uses weights of 1
00044 //  2) if num == sel.selected ; assumes there is one weight per
00045 //           selected atom
00046 //  3) if num == sel.num_atoms; assumes weight[i] is for atom[i]
00047 // returns center coordinate in float com[3]
00048 // returns 0 if valid data or an appropriate MEASURE_ERR_xxx code otherwise.
00049 
00050 // get weights from a Tcl item.  data must have space to hold sel->selected
00051 // items.
00052 // If there is no item, or if it's "none", return a list of ones.
00053 // If the item matches an atom selection keyword, and the keyword returns 
00054 // floating point data, return that data, otherwise error.
00055 // Otherwise, the item must be a list of floats.  Set the selected items
00056 // from the list.   
00057 int tcl_get_weights(Tcl_Interp *interp, VMDApp *app, AtomSel *sel, 
00058                        Tcl_Obj *weight_obj, float *data) {
00059 
00060   char *weight_string = NULL;
00061   if (!sel) return MEASURE_ERR_NOSEL;
00062   if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
00063   if (weight_obj)
00064     weight_string = Tcl_GetStringFromObj(weight_obj, NULL);
00065 
00066   if (!weight_string || !strcmp(weight_string, "none")) {
00067     for (int i=0; i<sel->selected; i++) {
00068       data[i] = 1.0;
00069     }
00070     return 0;
00071   }
00072   // if a selection string was given, check the symbol table
00073   SymbolTable *atomSelParser = app->atomSelParser; 
00074   // weights must return floating point values, so the symbol must not 
00075   // be a singleword, so macro is NULL.
00076   atomsel_ctxt context(atomSelParser, 
00077                        app->moleculeList->mol_from_id(sel->molid()), 
00078                        sel->which_frame, NULL);
00079 
00080   int fctn = atomSelParser->find_attribute(weight_string);
00081   if (fctn >= 0) {
00082     // the keyword exists, so get the data
00083     // first, check to see that the function returns floats.
00084     // if it doesn't, it makes no sense to use it as a weight
00085     if (atomSelParser->fctns.data(fctn)->returns_a != SymbolTableElement::IS_FLOAT) {
00086       Tcl_AppendResult(interp, 
00087         "weight attribute must have floating point values", NULL);
00088       return MEASURE_ERR_BADWEIGHTPARM;  // can't understand weight parameter 
00089     }
00090     double *tmp_data = new double[sel->num_atoms];
00091     atomSelParser->fctns.data(fctn)->keyword_double(
00092         &context, sel->num_atoms,tmp_data, sel->on);
00093     int j=0;
00094     for (int i=0; i<sel->num_atoms; i++) {
00095       if (sel->on[i])
00096         data[j++] = (float)tmp_data[i];
00097     }
00098     delete [] tmp_data;
00099     return 0;
00100   }
00101   // and see if this is a Tcl list with the right number of atoms
00102   int list_num;
00103   Tcl_Obj **list_data;
00104   if (Tcl_ListObjGetElements(interp, weight_obj, &list_num, &list_data) 
00105       != TCL_OK) {
00106     return MEASURE_ERR_BADWEIGHTPARM;
00107   }
00108   if (list_num != sel->selected && list_num != sel->num_atoms) 
00109     return MEASURE_ERR_BADWEIGHTNUM;
00110   
00111   int j = 0;
00112   for (int i=0; i<list_num; i++) {
00113     double tmp_data;
00114     if (Tcl_GetDoubleFromObj(interp, list_data[i], &tmp_data) != TCL_OK) 
00115       return MEASURE_ERR_NONNUMBERPARM;
00116     if (list_num == sel->selected) {
00117       //assert(i < sel->selected);
00118       data[i] = (float)tmp_data;
00119     } else {
00120       if (sel->on[i]) {
00121         //assert(j < sel->selected);
00122         data[j++] = (float)tmp_data;
00123       }
00124     }
00125   }
00126   return 0;
00127 }
00128 
00129 // get the  atom index re-ordering list for use by measure_fit
00130 int tcl_get_orders(Tcl_Interp *interp, int selnum, 
00131                        Tcl_Obj *order_obj, int *data) {
00132   int list_num;
00133   Tcl_Obj **list_data;
00134 
00135   if (Tcl_ListObjGetElements(interp, order_obj, &list_num, &list_data)
00136       != TCL_OK) {
00137     return MEASURE_ERR_NOSEL;
00138   }
00139 
00140   // see if this is a Tcl list with the right number of atom indices
00141   if (list_num != selnum) return MEASURE_ERR_NOSEL;
00142 
00143   for (int i=0; i<list_num; i++) {
00144     if (Tcl_GetIntFromObj(interp, list_data[i], &data[i]) != TCL_OK)
00145       return MEASURE_ERR_NONNUMBERPARM;
00146 
00147     // order indices are 0-based
00148     if (data[i] < 0 || data[i] >= selnum)
00149       return MEASURE_ERR_BADORDERINDEX; // order index is out of range
00150   }
00151 
00152   return 0;
00153 }
00154 
00155 /*
00156 Function:  vmd_measure_center
00157 Parameters:  <selection>               // computes with weight == 1
00158 Parameters:  <selection> weight [none|atom value|string array] 
00159   computes with the weights based on the following:
00160       none   => weights all 1
00161       atom value => value from atomSelParser (eg
00162          mass  => use weight based on mass
00163          index => use weight based on atom index (0 to n-1)
00164       string => use string to get weights for each atom.  The string can
00165          have number == number of selected atoms or total number of atoms
00166 
00167  Examples: 
00168     vmd_measure_center atomselect12
00169     vmd_measure_center atomselect12 weight mass
00170     vmd_measure_center atomselect12 {12 13} [atomselect top "index 2 3"]
00171  If no weight is given, no weight term is used (computes center of number)
00172  */
00173 static int vmd_measure_center(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp)
00174 {
00175   if (argc != 2 && argc != 4 ) {
00176     Tcl_WrongNumArgs(interp, 2, objv-1, 
00177       (char *)"<sel> [weight <weights>]");
00178     return TCL_ERROR;
00179   }
00180   if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00181     Tcl_SetResult(interp, (char *) "measure center: parameter can only be 'weight'", TCL_STATIC);
00182     return TCL_ERROR;
00183   }
00184   
00185   // get the selection
00186   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00187   if (!sel) {
00188     Tcl_SetResult(interp, (char *) "measure center: no atom selection", TCL_STATIC);
00189     return TCL_ERROR;
00190   }
00191 
00192   // get the weight
00193   float *weight = new float[sel->selected];
00194   {
00195     int ret_val;
00196     if (argc == 2) {          // only from atom selection, so weight is 1
00197       ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00198     } else {
00199       ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00200     }
00201     if (ret_val < 0) {
00202       Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val),
00203                        NULL);
00204       delete [] weight;
00205       return TCL_ERROR;
00206     }
00207   }
00208 
00209   // compute the center of "mass"
00210   {
00211     float com[3];
00212     const float *framepos = sel->coordinates(app->moleculeList);
00213     int ret_val = measure_center(sel, framepos, weight, com);
00214     delete [] weight;
00215     if (ret_val < 0) {
00216       Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val),
00217                        NULL);
00218       return TCL_ERROR;
00219     }
00220     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00221     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[0]));
00222     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[1]));
00223     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[2]));
00224     Tcl_SetObjResult(interp, tcl_result);
00225   }
00226 
00227   return TCL_OK;
00228 }
00229 
00230 
00231 // measure sum of weights for selected atoms
00232 static int vmd_measure_sumweights(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00233   if (argc != 4) {
00234     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> weight <weights>");
00235     return TCL_ERROR;
00236   }
00237   if (strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00238     Tcl_SetResult(interp, (char *) "measure sumweights: parameter can only be 'weight'", TCL_STATIC);
00239     return TCL_ERROR;
00240   }
00241  
00242   // get the selection
00243   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00244 );
00245   if (!sel) {
00246     Tcl_SetResult(interp, (char *) "measure sumweights: no atom selection", TCL_STATIC);
00247     return TCL_ERROR;
00248   }
00249 
00250   // get the weight
00251   float *weight = new float[sel->selected];
00252   {
00253     int ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00254     if (ret_val < 0) {
00255       Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val),
00256                        NULL);
00257       delete [] weight;
00258       return TCL_ERROR;
00259     }
00260   }
00261 
00262   // compute the sum of the weights
00263   {
00264     float weightsum;
00265     int ret_val = measure_sumweights(sel, sel->selected, weight, &weightsum);
00266     delete [] weight;
00267     if (ret_val < 0) {
00268       Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val),
00269                        NULL);
00270       return TCL_ERROR;
00271     }
00272     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00273     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(weightsum));
00274     Tcl_SetObjResult(interp, tcl_result);
00275   }
00276 
00277   return TCL_OK;
00278 }
00279 
00280 
00281 // Function: vmd_measure_avpos <selection> first <first> last <last> step <step>
00282 //  Returns: the average position of the selected atoms over the selected frames
00283 //  Example: measure avpos atomselect76 0 20 1
00284 static int vmd_measure_avpos(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00285   int first = 0;  // start with first frame by default
00286   int last = -1;  // finish with last frame by default
00287   int step = 1;   // use all frames by default
00288 
00289   if (argc < 2 || argc > 8) {
00290     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [first <first>] [last <last>] [step <step>]");
00291     return TCL_ERROR;
00292   }
00293   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00294 );
00295   if (!sel) {
00296     Tcl_AppendResult(interp, "measure avpos: no atom selection", NULL);
00297     return TCL_ERROR;
00298   }
00299 
00300   int i;
00301   for (i=2; i<argc; i+=2) {
00302     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00303     if (!strupncmp(argvcur, "first", CMDLEN)) {
00304       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00305         Tcl_AppendResult(interp, "measure avpos: bad first frame value", NULL);
00306         return TCL_ERROR;
00307       }
00308     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00309       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00310         Tcl_AppendResult(interp, "measure avpos: bad last frame value", NULL);
00311         return TCL_ERROR;
00312       }
00313     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00314       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00315         Tcl_AppendResult(interp, "measure avpos: bad frame step value", NULL);
00316         return TCL_ERROR;
00317       }
00318     } else {
00319       Tcl_AppendResult(interp, "measure avpos: invalid syntax, no such keyword: ", argvcur, NULL);
00320       return TCL_ERROR;
00321     }
00322   }
00323 
00324   float *avpos = new float[3*sel->selected];
00325   int ret_val = measure_avpos(sel, app->moleculeList, first, last, step, avpos);
00326   if (ret_val < 0) {
00327     Tcl_AppendResult(interp, "measure avpos: ", measure_error(ret_val), NULL);
00328     delete [] avpos;
00329     return TCL_ERROR;
00330   }
00331 
00332   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00333   for (i=0; i<sel->selected; i++) {
00334     Tcl_Obj *atom = Tcl_NewListObj(0, NULL);
00335     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3    ]));
00336     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3 + 1]));
00337     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3 + 2]));
00338     Tcl_ListObjAppendElement(interp, tcl_result, atom);
00339   }
00340 
00341   Tcl_SetObjResult(interp, tcl_result);
00342 
00343   delete [] avpos;
00344 
00345   return TCL_OK;
00346 }
00347 
00348 
00349 // Function: vmd_measure_dipole <selection>
00350 //  Returns: the dipole moment for the selected atoms
00351 static int vmd_measure_dipole(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00352   const char *opt;
00353   int unitsdebye=0; // default units are elementary charges/Angstrom
00354   int usecenter=1;  // remove net charge at the center of mass (-1), geometrical center (1), don't (0)
00355   
00356 
00357   if ((argc < 2) || (argc > 4)) {
00358     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]");
00359     return TCL_ERROR;
00360   }
00361   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL)
00362 );
00363   if (!sel) {
00364     Tcl_AppendResult(interp, "measure dipole: no atom selection", NULL);
00365     return TCL_ERROR;
00366   }
00367 
00368   int i;
00369   for (i=0; i < (argc-2); ++i) {
00370     opt = Tcl_GetStringFromObj(objv[2+i], NULL);
00371     if (!strcmp(opt, "-debye"))
00372       unitsdebye=1; 
00373     if (!strcmp(opt, "-elementary"))
00374       unitsdebye=0; 
00375 
00376     if (!strcmp(opt, "-geocenter"))
00377       usecenter=1; 
00378     if (!strcmp(opt, "-masscenter"))
00379       usecenter=-1; 
00380     if (!strcmp(opt, "-origincenter"))
00381       usecenter=0; 
00382   }
00383 
00384   float dipole[3];
00385   int ret_val = measure_dipole(sel, app->moleculeList, dipole, unitsdebye, usecenter);
00386   if (ret_val < 0) {
00387     Tcl_AppendResult(interp, "measure dipole: ", measure_error(ret_val), NULL);
00388     return TCL_ERROR;
00389   }
00390 
00391   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00392   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[0]));
00393   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[1]));
00394   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[2]));
00395   Tcl_SetObjResult(interp, tcl_result);
00396 
00397   return TCL_OK;
00398 }
00399 
00400 
00401 
00402 // Function: vmd_measure_dihed {4 atoms as {<atomid> ?<molid>?}} ?molid <default molid>?
00403 //                             ?frame [f|all|last]? | ?first <first>? ?last <last>?
00404 //  Returns: the dihedral angle for the specified atoms
00405 static int vmd_measure_dihed(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00406   int first=-1, last=-1, frame=-1;
00407 
00408   if(argc<2) {
00409     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]");
00410     return TCL_ERROR;
00411   }
00412 
00413   int molid[4];
00414   int atmid[4];
00415   int defmolid = -1;
00416   bool allframes = false;
00417 
00418   // Get the geometry type dihed/imprp
00419   char *geomname = Tcl_GetStringFromObj(objv[0],NULL);
00420 
00421 
00422   // Read the atom list
00423   int numatms;
00424   Tcl_Obj **data;
00425   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00426     Tcl_AppendResult(interp, " measure ", geomname, ": bad syntax", NULL);
00427     Tcl_AppendResult(interp, " Usage: measure ", geomname, " {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00428 
00429     return TCL_ERROR;
00430   }
00431 
00432   if (numatms!=4) {
00433     Tcl_AppendResult(interp, " measure dihed: must specify exactly four atoms in a list", NULL);
00434     return TCL_ERROR;
00435   }
00436     
00437 
00438   if (argc>3) {
00439     int i;
00440     for (i=2; i<argc; i+=2) {
00441       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00442       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00443         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00444           Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00445           return TCL_ERROR;
00446         }
00447       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00448         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00449           Tcl_AppendResult(interp, " measure ", geomname, ": bad first frame value", NULL);
00450           return TCL_ERROR;
00451         }
00452       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00453         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00454           Tcl_AppendResult(interp, " measure ", geomname, ": bad last frame value", NULL);
00455           return TCL_ERROR;
00456         }
00457      } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00458         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00459            allframes = true;
00460         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00461           frame=-2;
00462         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00463           Tcl_AppendResult(interp, " measure ", geomname, ": bad frame value", NULL);
00464           return TCL_ERROR;
00465         }
00466       } else {
00467         Tcl_AppendResult(interp, "measure ", geomname, ": invalid syntax, no such keyword: ", argvcur, NULL);
00468         return TCL_ERROR;
00469       }
00470     }
00471   }
00472 
00473   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00474     Tcl_AppendResult(interp, "measure ", geomname, ": Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00475     Tcl_AppendResult(interp, "\nUsage:\nmeasure ", geomname, " {<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]} {<atomid4> [<molid4>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00476     return TCL_ERROR;    
00477   }
00478 
00479   if (allframes) first=0;
00480 
00481   // If no molecule was specified use top as default
00482   if (defmolid<0) defmolid = app->molecule_top();
00483 
00484   // Assign atom IDs and molecule IDs
00485   int i,numelem;
00486   Tcl_Obj **atmmol;
00487   for (i=0; i<numatms; i++) {
00488     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00489       return TCL_ERROR;
00490     }
00491 
00492     if (!numelem) {
00493       Tcl_AppendResult(interp, " measure ", geomname, ": empty atom index", NULL);
00494       return TCL_ERROR;
00495     }
00496 
00497     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00498       Tcl_AppendResult(interp, " measure ", geomname, ": bad atom index", NULL);
00499       return TCL_ERROR;
00500     }
00501     
00502     if (numelem==2) {
00503       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00504         Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00505         return TCL_ERROR;
00506       }
00507     } else molid[i] = defmolid;
00508   }
00509   
00510 
00511   // Compute the value
00512   ResizeArray<float> gValues(1024);
00513   int ret_val;
00514   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, frame, first, last,
00515                          defmolid, MEASURE_DIHED);
00516   if (ret_val<0) {
00517     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00518     return TCL_ERROR;
00519   }
00520 
00521   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00522   int numvalues = gValues.num();
00523   for (int count = 0; count < numvalues; count++) {
00524     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00525   }
00526   Tcl_SetObjResult(interp, tcl_result);
00527 
00528   return TCL_OK;
00529 }
00530 
00531 // Function: vmd_measure_angle {3 atoms as {<atomid> ?<molid>?}} ?molid <default molid>? 
00532 //                             ?frame [f|all|last]? | ?first <first>? ?last <last>?
00533 //  Returns: the bond angle for the specified atoms
00534 static int vmd_measure_angle(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00535   int first=-1, last=-1, frame=-1;
00536 
00537   if(argc<2) {
00538     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]");
00539     return TCL_ERROR;
00540   }
00541 
00542   int molid[3];
00543   int atmid[3];
00544   int defmolid = -1;
00545   bool allframes = false;
00546 
00547   // Read the atom list
00548   int numatms;
00549   Tcl_Obj **data;
00550   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00551     Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00552     Tcl_AppendResult(interp, " Usage: measure angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00553     return TCL_ERROR;
00554   }
00555 
00556   if (numatms!=3) {
00557     Tcl_AppendResult(interp, " measure angle: must specify exactly three atoms in a list", NULL);
00558     return TCL_ERROR;
00559   }
00560     
00561 
00562   if (argc>3) {
00563     int i;
00564     for (i=2; i<argc; i+=2) {
00565       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00566       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00567         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00568           Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00569           return TCL_ERROR;
00570         }
00571       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00572         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00573           Tcl_AppendResult(interp, " measure angle: bad first frame value", NULL);
00574           return TCL_ERROR;
00575         }
00576       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00577         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00578           Tcl_AppendResult(interp, " measure angle: bad last frame value", NULL);
00579           return TCL_ERROR;
00580         }
00581       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00582         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00583           allframes = true;
00584         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00585           frame=-2;
00586         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00587           Tcl_AppendResult(interp, " measure angle: bad frame value", NULL);
00588           return TCL_ERROR;
00589         }
00590       } else {
00591         Tcl_AppendResult(interp, " measure angle: invalid syntax, no such keyword: ", argvcur, NULL);
00592         return TCL_ERROR;
00593       }
00594     }
00595   }
00596 
00597   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00598     Tcl_AppendResult(interp, "measure angle: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00599     Tcl_AppendResult(interp, "\nUsage:\nmeasure angle {<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00600     return TCL_ERROR;    
00601   }
00602 
00603   if (allframes) first=0;
00604 
00605   // If no molecule was specified use top as default
00606   if (defmolid<0) defmolid = app->molecule_top();
00607 
00608   // Assign atom IDs and molecule IDs
00609   int i,numelem;
00610   Tcl_Obj **atmmol;
00611   for (i=0; i<numatms; i++) {
00612     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00613       return TCL_ERROR;
00614     }
00615 
00616     if (!numelem) {
00617       Tcl_AppendResult(interp, " measure angle: empty atom index", NULL);
00618       return TCL_ERROR;
00619     }
00620 
00621     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00622       Tcl_AppendResult(interp, " measure angle: bad atom index", NULL);
00623       return TCL_ERROR;
00624     }
00625     
00626     if (numelem==2) {
00627       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00628         Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00629         return TCL_ERROR;
00630       }
00631     } else molid[i] = defmolid;
00632   }
00633   
00634 
00635   // Compute the value
00636   ResizeArray<float> gValues(1024);
00637   int ret_val;
00638   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, frame, first, last,
00639                          defmolid, MEASURE_ANGLE);
00640   if (ret_val<0) {
00641     printf("ERROR\n %s\n", measure_error(ret_val));
00642     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00643     return TCL_ERROR;
00644   }
00645 
00646   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00647   int numvalues = gValues.num();
00648   for (int count = 0; count < numvalues; count++) {
00649     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00650   }
00651   Tcl_SetObjResult(interp, tcl_result);
00652 
00653   return TCL_OK;
00654 }
00655 
00656 // Function: vmd_measure_bond {2 atoms as {<atomid> ?<molid>?}} ?molid <molid>? ?frame [f|all|last]? | ?first <first>? ?last <last>?
00657 //  Returns: the bond length for the specified atoms
00658 static int vmd_measure_bond(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00659   int first=-1, last=-1, frame=-1;
00660 
00661   if (argc<2) {
00662     Tcl_WrongNumArgs(interp, 2, objv-1,
00663                      (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]]");
00664     return TCL_ERROR;
00665   }
00666   
00667   int molid[2];
00668   int atmid[2];
00669   int defmolid = -1;
00670   bool allframes = false;
00671 
00672   // Read the atom list
00673   int numatms;
00674   Tcl_Obj **data;
00675   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00676     Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00677     Tcl_AppendResult(interp, " Usage: measure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]]", NULL);
00678     return TCL_ERROR;
00679   }
00680 
00681   if (numatms!=2) {
00682     Tcl_AppendResult(interp, " measure bond: must specify exactly two atoms in a list", NULL);
00683     return TCL_ERROR;
00684   }
00685     
00686 
00687   if (argc>3) {
00688     int i;
00689     for (i=2; i<argc; i+=2) {
00690       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00691       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00692         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00693           Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00694           return TCL_ERROR;
00695         }
00696       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00697         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00698           Tcl_AppendResult(interp, " measure bond: bad first frame value", NULL);
00699           return TCL_ERROR;
00700         }
00701       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00702         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00703           Tcl_AppendResult(interp, " measure bond: bad last frame value", NULL);
00704           return TCL_ERROR;
00705         }
00706       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00707         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00708           allframes = true;
00709         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00710           frame=-2;
00711         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00712           Tcl_AppendResult(interp, " measure bond: bad frame value", NULL);
00713           return TCL_ERROR;
00714         }
00715       } else {
00716         Tcl_AppendResult(interp, " measure bond: invalid syntax, no such keyword: ", argvcur, NULL);
00717         return TCL_ERROR;
00718       }
00719     }
00720   }
00721 
00722   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00723     Tcl_AppendResult(interp, "measure bond: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00724     Tcl_AppendResult(interp, "\nUsage:\nmeasure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00725     return TCL_ERROR;    
00726   }
00727 
00728   if (allframes) first=0;
00729 
00730   // If no molecule was specified use top as default
00731   if (defmolid<0) defmolid = app->molecule_top();
00732 
00733   // Assign atom IDs and molecule IDs
00734   int i,numelem;
00735   Tcl_Obj **atmmol;
00736   for (i=0; i<numatms; i++) {
00737     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00738       return TCL_ERROR;
00739     }
00740 
00741     if (!numelem) {
00742       Tcl_AppendResult(interp, " measure bond: empty atom index", NULL);
00743       return TCL_ERROR;
00744     }
00745 
00746     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00747       Tcl_AppendResult(interp, " measure bond: bad atom index", NULL);
00748       return TCL_ERROR;
00749     }
00750     
00751     if (numelem==2) {
00752       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00753         Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00754         return TCL_ERROR;
00755       }
00756     } else molid[i] = defmolid;
00757   }
00758   
00759 
00760   // Compute the value
00761   ResizeArray<float> gValues(1024);
00762   int ret_val;
00763   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, frame, first, last,
00764                          defmolid, MEASURE_BOND);
00765   if (ret_val<0) {
00766     printf("ERROR\n %s\n", measure_error(ret_val));
00767     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00768     return TCL_ERROR;
00769   }
00770 
00771   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00772   int numvalues = gValues.num();
00773   for (int count = 0; count < numvalues; count++) {
00774     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00775   }
00776   Tcl_SetObjResult(interp, tcl_result);
00777 
00778   return TCL_OK;
00779 }
00780 
00781 // Function: vmd_measure_rmsf <selection> first <first> last <last> step <step>
00782 //  Returns: position variance of the selected atoms over the selected frames
00783 //  Example: measure rmsf atomselect76 0 20 1
00784 static int vmd_measure_rmsf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00785   int first = 0;  // start with first frame by default
00786   int last = -1;  // finish with last frame by default
00787   int step = 1;   // use all frames by default
00788 
00789   if (argc < 2 || argc > 8) {
00790     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [first <first>] [last <last>] [step <step>]");
00791     return TCL_ERROR;
00792   }
00793   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00794 );
00795   if (!sel) {
00796     Tcl_AppendResult(interp, "measure rmsf: no atom selection", NULL);
00797     return TCL_ERROR;
00798   }
00799  
00800   int i;
00801   for (i=2; i<argc; i+=2) {
00802     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00803     if (!strupncmp(argvcur, "first", CMDLEN)) {
00804       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00805         Tcl_AppendResult(interp, "measure rmsf: bad first frame value", NULL);
00806         return TCL_ERROR;
00807       }
00808     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00809       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00810         Tcl_AppendResult(interp, "measure rmsf: bad last frame value", NULL);
00811         return TCL_ERROR;
00812       }
00813     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00814       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00815         Tcl_AppendResult(interp, "measure rmsf: bad frame step value", NULL);
00816         return TCL_ERROR;
00817       }
00818     } else {
00819       Tcl_AppendResult(interp, "measure rmsf: invalid syntax, no such keyword: ", argvcur, NULL);
00820       return TCL_ERROR;
00821     }
00822   }
00823 
00824   float *rmsf = new float[sel->selected];
00825   int ret_val = measure_rmsf(sel, app->moleculeList, first, last, step, rmsf);
00826   if (ret_val < 0) {
00827     Tcl_AppendResult(interp, "measure rmsf: ", measure_error(ret_val), NULL);
00828     delete [] rmsf;
00829     return TCL_ERROR;
00830   }
00831 
00832   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00833 
00834   for (i=0; i<sel->selected; i++) {
00835     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsf[i]));
00836   }
00837 
00838   Tcl_SetObjResult(interp, tcl_result);
00839 
00840   delete [] rmsf;
00841 
00842   return TCL_OK;
00843 }
00844 
00845 
00846 // measure radius of gyration for selected atoms
00847 static int vmd_measure_rgyr(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp)
00848 {
00849   if (argc != 2 && argc != 4 ) {
00850     Tcl_WrongNumArgs(interp, 2, objv-1,
00851       (char *)"<selection> [weight <weights>]");
00852     return TCL_ERROR;
00853   }
00854   if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00855     Tcl_SetResult(interp, (char *) "measure rgyr: parameter can only be 'weight'", TCL_STATIC);
00856     return TCL_ERROR;
00857   }
00858 
00859   // get the selection
00860   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00861   if (!sel) {
00862     Tcl_SetResult(interp, (char *) "measure rgyr: no atom selection", TCL_STATIC);
00863     return TCL_ERROR;
00864   }
00865 
00866   // get the weight
00867   float *weight = new float[sel->selected];
00868   {
00869     int ret_val;
00870     if (argc == 2) {          // only from atom selection, so weight is 1
00871       ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00872     } else {
00873       ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00874     }
00875     if (ret_val < 0) {
00876       Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val),
00877                        NULL);
00878       delete [] weight;
00879       return TCL_ERROR;
00880     }
00881   }
00882   float rgyr;
00883   int ret_val = measure_rgyr(sel, app->moleculeList, weight, &rgyr);
00884   delete [] weight;
00885   if (ret_val < 0) {
00886     Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val),
00887                      NULL);
00888     return TCL_ERROR;
00889   }
00890   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rgyr));
00891   return TCL_OK;
00892 }
00893 
00894 
00895 // Function: vmd_measure_minmax <selection>
00896 //  Returns: the cartesian range of a selection (min/max){x,y,z}
00897 //  Example: vmd_measure_minmax atomselect76
00898 //     {-5 0 0} {15 10 11.2}
00899 static int vmd_measure_minmax(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00900   const float *radii = NULL;
00901   if (argc != 2 && argc != 3) {
00902     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<selection> [-withradii]");
00903     return TCL_ERROR;
00904   }
00905   if (argc == 3 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "-withradii")) {
00906     Tcl_SetResult(interp, (char *) "measure minmax: parameter can only be '-withradii'", TCL_STATIC);
00907     return TCL_ERROR;
00908   }
00909 
00910   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00911   if (!sel) {
00912     Tcl_AppendResult(interp, "measure minmax: no atom selection", NULL);
00913     return TCL_ERROR;
00914   }
00915 
00916   float min_coord[3], max_coord[3];
00917   const float *framepos = sel->coordinates(app->moleculeList);
00918   if (!framepos) return TCL_ERROR;
00919 
00920   // get atom radii if requested
00921   if (argc == 3) {
00922     Molecule *mol = app->moleculeList->mol_from_id(sel->molid());
00923     radii = mol->extraflt.data("radius");
00924   } 
00925 
00926   int ret_val = measure_minmax(sel->num_atoms, sel->on, framepos, radii, 
00927                                min_coord, max_coord);
00928   if (ret_val < 0) {
00929     Tcl_AppendResult(interp, "measure minmax: ", measure_error(ret_val));
00930     return TCL_ERROR;
00931   }
00932 
00933   Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
00934   Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
00935   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00936 
00937   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[0]));
00938   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[1]));
00939   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[2]));
00940 
00941   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[0]));
00942   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[1]));
00943   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[2]));
00944 
00945   Tcl_ListObjAppendElement(interp, tcl_result, list1);
00946   Tcl_ListObjAppendElement(interp, tcl_result, list2);
00947   Tcl_SetObjResult(interp, tcl_result);
00948 
00949   return TCL_OK;
00950 }
00951 
00952 /* Function: vmd_measure_rmsd <selection1> <selection2> 
00953                        {weight [none|atom value|string array]}
00954 
00955    Returns the RMSD between the two selection, taking the weight (if
00956 any) into account.  If number of elements in the weight != num_atoms
00957 in sel1 then (num in weight = num selected in sel1 = num selected in
00958 sel2) else (num in weight = total num in sel1 = total num in sel2).
00959 The weights are taken from the FIRST selection, if needed
00960 
00961 Examples:
00962   set sel1 [atomselect 0 all]
00963   set sel2 [atomselect 1 all]
00964   measure rmsd $sel1 $sel2
00965   measure rmsd $sel1 $sel2 weight mass
00966   set sel3 [atomselect 0 "index 3 4 5"]
00967   set sel4 [atomselect 1 "index 8 5 9"]    # gets turned to 5 8 9
00968   measure rmsd $sel3 $sel4 weight occupancy
00969 */
00970 static int vmd_measure_rmsd(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
00971 {
00972   if (argc !=3 && argc != 5) {
00973     Tcl_WrongNumArgs(interp, 2, objv-1, 
00974       (char *)"<sel1> <sel2> [weight <weights>]");
00975     return TCL_ERROR;
00976   }
00977   // get the selections
00978   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00979   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
00980   if (!sel1 || !sel2) {
00981     Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
00982     return TCL_ERROR;
00983   }
00984 
00985   if (sel1->selected != sel2->selected) {
00986     Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
00987     return TCL_ERROR;
00988   }
00989   if (!sel1->selected) {
00990     Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
00991     return TCL_ERROR;
00992   }
00993   float *weight = new float[sel1->selected];
00994   {
00995     int ret_val;
00996     if (argc == 3) {
00997       ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
00998     } else {
00999       ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01000     }
01001     if (ret_val < 0) {
01002       Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val),
01003                        NULL);
01004       delete [] weight;
01005       return TCL_ERROR;
01006     }
01007   }
01008   // compute the rmsd
01009   {
01010     float rmsd = 0;
01011     const float *x = sel1->coordinates(app->moleculeList);
01012     const float *y = sel2->coordinates(app->moleculeList);
01013     if (!x || !y) {
01014       delete [] weight;
01015       return TCL_ERROR;
01016     }
01017     int ret_val = measure_rmsd(sel1, sel2, sel1->selected, x, y, weight, &rmsd);
01018     delete [] weight;
01019     if (ret_val < 0) {
01020       Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val),
01021                        NULL);
01022       return TCL_ERROR;
01023     }
01024     Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rmsd));
01025   }
01026   return TCL_OK;
01027 }
01028 
01030 // measure fit $sel1 $sel2 [weight <weights>][
01031 static int vmd_measure_fit(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
01032 {
01033   AtomSel *sel1, *sel2;
01034   int *order = NULL;
01035   float *weight = NULL;
01036   int rc;
01037 
01038   if (argc != 3 && argc != 5 
01039       && argc != 7) {
01040     Tcl_WrongNumArgs(interp, 2, objv-1, 
01041        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01042     return TCL_ERROR;
01043   } else if (argc == 5
01044              && strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) 
01045              && strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01046     Tcl_WrongNumArgs(interp, 2, objv-1, 
01047        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01048     return TCL_ERROR;
01049   } else if (argc == 7 && 
01050              (strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) || 
01051               strcmp("order", Tcl_GetStringFromObj(objv[5], NULL)))) {
01052     Tcl_WrongNumArgs(interp, 2, objv-1, 
01053        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01054     return TCL_ERROR;
01055   }
01056 
01057   // get the selections
01058   sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01059   sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01060 
01061   if (!sel1 || !sel2) {
01062     Tcl_AppendResult(interp, "measure fit: no atom selection", NULL);
01063     return TCL_ERROR;
01064   }
01065 
01066   int num = sel1->selected;
01067   if (!num) {
01068     Tcl_AppendResult(interp, "measure fit: no atoms selected", NULL);
01069     return TCL_ERROR;
01070   }
01071   if (num != sel2->selected) {
01072     Tcl_AppendResult(interp, "measure fit: selections must have the same number of atoms", NULL);
01073     return TCL_ERROR;
01074   }
01075 
01076   // get the weights
01077   weight = new float[num]; 
01078   if (argc > 3 && !strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL))) {
01079     // get user weight parameter
01080     rc = tcl_get_weights(interp, app, sel1, objv[4], weight);
01081   } else {
01082     // default to weights of 1.0
01083     rc = tcl_get_weights(interp, app, sel1, NULL, weight);
01084   }
01085   if (rc < 0) {
01086     Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01087     delete [] weight;
01088     return TCL_ERROR;
01089   }
01090 
01091 
01092   int orderparam = 0;
01093   if (argc == 5 && !strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01094     orderparam = 4;
01095   } else if (argc == 7 && !strcmp("order", Tcl_GetStringFromObj(objv[5], NULL))) {
01096     orderparam = 6;
01097   }
01098 
01099   if (orderparam != 0) {
01100     // get the atom order
01101     order = new int[num];
01102     rc = tcl_get_orders(interp, num, objv[orderparam], order);
01103     if (rc < 0) {
01104       Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01105       delete [] order;
01106       return TCL_ERROR;
01107     }
01108   }
01109 
01110   // compute the transformation matrix
01111   Matrix4 T;
01112   const float *x = sel1->coordinates(app->moleculeList);
01113   const float *y = sel2->coordinates(app->moleculeList);
01114 
01115   int ret_val = MEASURE_ERR_NOMOLECULE;
01116   if (x && y) 
01117     ret_val = measure_fit(sel1, sel2, x, y, weight, order, &T);
01118 
01119   delete [] weight;
01120 
01121   if (order != NULL)
01122     delete [] order;
01123 
01124   if (ret_val < 0) {
01125     Tcl_AppendResult(interp, "measure fit: ", measure_error(ret_val), NULL);
01126     return TCL_ERROR;
01127   }
01128 
01129   // and return the matrix
01130   tcl_append_matrix(interp, T.mat);
01131   return TCL_OK;
01132 }
01133 
01135 // measure inverse 4x4matrix
01136 static int vmd_measure_inverse(int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01137   // make there there is exactly one matrix
01138   if (argc != 2) {
01139     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<matrix>");
01140     return TCL_ERROR;
01141   }
01142   // Get the first matrix
01143   Matrix4 inv;
01144   if (tcl_get_matrix("measure inverse: ",interp,objv[1],inv.mat) != TCL_OK) {
01145     return TCL_ERROR;
01146   }
01147   if (inv.inverse()) {
01148     Tcl_AppendResult(interp, "Singular Matrix; inverse not computed", NULL);
01149     return TCL_ERROR;
01150   }
01151   tcl_append_matrix(interp, inv.mat);
01152   return TCL_OK;
01153 }
01154 
01155 // Find all atoms p in sel1 and q in sel2 within the cutoff.  
01156 static int vmd_measure_contacts(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01157   
01158   // Cutoff, and either one or two atom selections
01159   if (argc != 3 && argc != 4) {
01160     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <sel1> [<sel2>]");
01161     return TCL_ERROR;
01162   }
01163   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01164   if (!sel1) {
01165     Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01166     return TCL_ERROR;
01167   }
01168   AtomSel *sel2 = NULL;
01169   if (argc == 4) {
01170     sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
01171     if (!sel2) {
01172       Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01173       return TCL_ERROR;
01174     }
01175   }
01176   if (!sel2) sel2 = sel1;
01177   
01178   double cutoff;
01179   if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK)
01180     return TCL_ERROR;
01181 
01182   const float *pos1 = sel1->coordinates(app->moleculeList);
01183   const float *pos2 = sel2->coordinates(app->moleculeList);
01184   if (!pos1 || !pos2) {
01185     Tcl_AppendResult(interp, "measure contacts: error, molecule contains no coordinates", NULL);
01186     return TCL_ERROR;
01187   }
01188   Molecule *mol1 = app->moleculeList->mol_from_id(sel1->molid());
01189   Molecule *mol2 = app->moleculeList->mol_from_id(sel2->molid());
01190 
01191   GridSearchPair *pairlist = vmd_gridsearch3(pos1, sel1->num_atoms, sel1->on, pos2, sel2->num_atoms, sel2->on, (float) cutoff, -1, (sel1->num_atoms + sel2->num_atoms) * 27);
01192   GridSearchPair *p, *tmp;
01193   Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
01194   Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
01195   for (p=pairlist; p != NULL; p=tmp) {
01196     // throw out pairs that are already bonded
01197     MolAtom *a1 = mol1->atom(p->ind1);
01198     if (mol1 != mol2 || !a1->bonded(p->ind2)) {
01199       Tcl_ListObjAppendElement(interp, list1, Tcl_NewIntObj(p->ind1));
01200       Tcl_ListObjAppendElement(interp, list2, Tcl_NewIntObj(p->ind2));
01201     }
01202     tmp = p->next;
01203     free(p);
01204   }
01205   Tcl_Obj *result = Tcl_NewListObj(0, NULL);
01206   Tcl_ListObjAppendElement(interp, result, list1);
01207   Tcl_ListObjAppendElement(interp, result, list2);
01208   Tcl_SetObjResult(interp, result);
01209   return TCL_OK;
01210 }
01211 
01212 
01213 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01214 // frame parameters the code will compute the normalized histogram.
01215 static int vmd_measure_gofr(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01216   int i;
01217   // initialize optional arguments to default values
01218   double rmax=10.0;
01219   double delta=0.1;
01220   int usepbc=0;
01221   int selupdate=0;
01222   int first=-1, last=-1, step=1;
01223   int rc;
01224 
01225   // argument error message
01226   const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01227 
01228   // Two atom selections and optional keyword/value pairs.
01229   if ((argc < 3) || (argc > 17) || (argc % 2 == 0) )  {
01230     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01231     return TCL_ERROR;
01232   }
01233 
01234   // check atom selections
01235   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01236   if (!sel1) {
01237     Tcl_AppendResult(interp, "measure gofr: invalid first atom selection", NULL);
01238     return TCL_ERROR;
01239   }
01240 
01241   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01242   if (!sel2) {
01243     Tcl_AppendResult(interp, "measure gofr: invalid second atom selection", NULL);
01244     return TCL_ERROR;
01245   }
01246 
01247   // parse optional arguments
01248   for (i=3; i<argc; i+=2) {
01249     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01250     if (i==(argc-1)) {
01251       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01252       return TCL_ERROR;
01253     }
01254     if (!strcmp(opt, "delta")) {
01255       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01256         return TCL_ERROR;
01257       if (delta <= 0.0) {
01258         Tcl_AppendResult(interp, "measure gofr: invalid 'delta' value", NULL);
01259         return TCL_ERROR;
01260       }
01261     } else if (!strcmp(opt, "rmax")) {
01262       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01263         return TCL_ERROR;
01264       if (rmax <= 0.0) {
01265         Tcl_AppendResult(interp, "measure gofr: invalid 'rmax' value", NULL);
01266         return TCL_ERROR;
01267       }
01268     } else if (!strcmp(opt, "usepbc")) {
01269       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01270         return TCL_ERROR;
01271     } else if (!strcmp(opt, "selupdate")) {
01272       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01273         return TCL_ERROR;
01274     } else if (!strcmp(opt, "first")) {
01275       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01276         return TCL_ERROR;
01277     } else if (!strcmp(opt, "last")) {
01278       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01279         return TCL_ERROR;
01280     } else if (!strcmp(opt, "step")) {
01281       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01282         return TCL_ERROR;
01283     } else { // unknown keyword.
01284       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure gofr ", argerrmsg,NULL);
01285       return TCL_ERROR;
01286     }
01287   }
01288 
01289   // allocate and initialize histogram arrays
01290   int    count_h = (int)(rmax / delta + 1.0);
01291   double *gofr   = new double[count_h];
01292   double *numint = new double[count_h];
01293   double *histog = new double[count_h];
01294   int *framecntr = new int[3];
01295 
01296   // do the gofr calculation
01297   rc = measure_gofr(sel1, sel2, app->moleculeList,
01298                count_h, gofr, numint, histog,
01299                (float) delta,
01300                first, last, step, framecntr,
01301                usepbc, selupdate);
01302 
01303   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01304   if (rc != MEASURE_NOERR) { 
01305     Tcl_AppendResult(interp, "measure gofr: error during g(r) calculation.", NULL);
01306     return TCL_ERROR;
01307   }
01308 
01309   // convert the results of the lowlevel call to tcl lists
01310   // and build a list from them as return value.
01311   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01312   Tcl_Obj *tcl_rlist  = Tcl_NewListObj(0, NULL);
01313   Tcl_Obj *tcl_gofr   = Tcl_NewListObj(0, NULL);
01314   Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01315   Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01316   Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01317 
01318   // build lists with results ready for plotting
01319   for (i=0; i<count_h; i++) { 
01320     Tcl_ListObjAppendElement(interp, tcl_rlist,  Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01321     Tcl_ListObjAppendElement(interp, tcl_gofr,   Tcl_NewDoubleObj(gofr[i]));
01322     Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01323     Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01324   }
01325 
01326   // build list with number of frames: 
01327   // total, skipped and processed (one entry for each algorithm).
01328   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01329   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01330   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01331 
01332   // build final list-of-lists as return value
01333   Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01334   Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01335   Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01336   Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01337   Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01338   Tcl_SetObjResult(interp, tcl_result);
01339 
01340   delete [] gofr;
01341   delete [] numint;
01342   delete [] histog;
01343   delete [] framecntr;
01344   return TCL_OK;
01345 }
01346 
01347 
01348 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01349 // frame parameters the code will compute the normalized histogram.
01350 static int vmd_measure_rdf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01351   int i;
01352   // initialize optional arguments to default values
01353   double rmax=10.0;
01354   double delta=0.1;
01355   int usepbc=0;
01356   int selupdate=0;
01357   int first=-1, last=-1, step=1;
01358   int rc;
01359 
01360   // argument error message
01361   const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01362 
01363   // Two atom selections and optional keyword/value pairs.
01364   if ((argc < 3) || (argc > 17) || (argc % 2 == 0) )  {
01365     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01366     return TCL_ERROR;
01367   }
01368 
01369   // check atom selections
01370   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01371   if (!sel1) {
01372     Tcl_AppendResult(interp, "measure rdf: invalid first atom selection", NULL);
01373     return TCL_ERROR;
01374   }
01375 
01376   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01377   if (!sel2) {
01378     Tcl_AppendResult(interp, "measure rdf: invalid second atom selection", NULL);
01379     return TCL_ERROR;
01380   }
01381 
01382   // parse optional arguments
01383   for (i=3; i<argc; i+=2) {
01384     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01385     if (i==(argc-1)) {
01386       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01387       return TCL_ERROR;
01388     }
01389     if (!strcmp(opt, "delta")) {
01390       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01391         return TCL_ERROR;
01392       if (delta <= 0.0) {
01393         Tcl_AppendResult(interp, "measure rdf: invalid 'delta' value", NULL);
01394         return TCL_ERROR;
01395       }
01396     } else if (!strcmp(opt, "rmax")) {
01397       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01398         return TCL_ERROR;
01399       if (rmax <= 0.0) {
01400         Tcl_AppendResult(interp, "measure rdf: invalid 'rmax' value", NULL);
01401         return TCL_ERROR;
01402       }
01403     } else if (!strcmp(opt, "usepbc")) {
01404       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01405         return TCL_ERROR;
01406     } else if (!strcmp(opt, "selupdate")) {
01407       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01408         return TCL_ERROR;
01409     } else if (!strcmp(opt, "first")) {
01410       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01411         return TCL_ERROR;
01412     } else if (!strcmp(opt, "last")) {
01413       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01414         return TCL_ERROR;
01415     } else if (!strcmp(opt, "step")) {
01416       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01417         return TCL_ERROR;
01418     } else { // unknown keyword.
01419       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure rdf ", argerrmsg,NULL);
01420       return TCL_ERROR;
01421     }
01422   }
01423 
01424   // allocate and initialize histogram arrays
01425   int    count_h = (int)(rmax / delta + 1.0);
01426   double *gofr   = new double[count_h];
01427   double *numint = new double[count_h];
01428   double *histog = new double[count_h];
01429   int *framecntr = new int[3];
01430 
01431   // do the gofr calculation
01432   rc = measure_rdf(app, sel1, sel2, app->moleculeList,
01433                    count_h, gofr, numint, histog,
01434                    (float) delta,
01435                    first, last, step, framecntr,
01436                    usepbc, selupdate);
01437 
01438   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01439   if (rc != MEASURE_NOERR) { 
01440     Tcl_AppendResult(interp, "measure rdf: error during rdf calculation.", NULL);
01441     return TCL_ERROR;
01442   }
01443 
01444   // convert the results of the lowlevel call to tcl lists
01445   // and build a list from them as return value.
01446   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01447   Tcl_Obj *tcl_rlist  = Tcl_NewListObj(0, NULL);
01448   Tcl_Obj *tcl_gofr   = Tcl_NewListObj(0, NULL);
01449   Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01450   Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01451   Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01452 
01453   // build lists with results ready for plotting
01454   for (i=0; i<count_h; i++) { 
01455     Tcl_ListObjAppendElement(interp, tcl_rlist,  Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01456     Tcl_ListObjAppendElement(interp, tcl_gofr,   Tcl_NewDoubleObj(gofr[i]));
01457     Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01458     Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01459   }
01460 
01461   // build list with number of frames: 
01462   // total, skipped and processed (one entry for each algorithm).
01463   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01464   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01465   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01466 
01467   // build final list-of-lists as return value
01468   Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01469   Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01470   Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01471   Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01472   Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01473   Tcl_SetObjResult(interp, tcl_result);
01474 
01475   delete [] gofr;
01476   delete [] numint;
01477   delete [] histog;
01478   delete [] framecntr;
01479   return TCL_OK;
01480 }
01481 
01482 
01484 static int vmd_measure_cluster(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01485   int i,j;
01486   // initialize optional arguments to default values
01487   int algorithm=0; // will be MEASURE_CLUSTER_QT when finished
01488   int likeness=MEASURE_DIST_FITRMSD;
01489   int numcluster=5;
01490   double cutoff=1.0;
01491   float *weights=NULL;
01492   int selupdate=0;
01493   int first=0, last=-1, step=1;
01494   int rc;
01495 
01496   // argument error message
01497   const char *argerrmsg = "<sel> [num <#clusters>] [distfunc <flag>] "
01498     "[cutoff <cutoff>] [first <first>] [last <last>] [step <step>] "
01499     "[selupdate <bool>] [weight <weights>]";
01500 
01501   // Two atom selections and optional keyword/value pairs.
01502   if ((argc < 2) || (argc > 19) || ((argc-1) % 2 == 0) )  {
01503     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01504     return TCL_ERROR;
01505   }
01506 
01507   // check atom selection
01508   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01509   if (!sel) {
01510     Tcl_AppendResult(interp, "measure cluster: invalid atom selection", NULL);
01511     return TCL_ERROR;
01512   }
01513   if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
01514 
01515   // parse optional arguments
01516   for (i=2; i<argc; i+=2) {
01517     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01518     if (i==(argc-1)) {
01519       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01520       return TCL_ERROR;
01521     }
01522     if (!strcmp(opt, "num")) {
01523       if (Tcl_GetIntFromObj(interp, objv[i+1], &numcluster) != TCL_OK)
01524         return TCL_ERROR;
01525       if (numcluster < 1) {
01526         Tcl_AppendResult(interp, "measure cluster: invalid 'num' value (cannot be smaller than 1)", NULL);
01527         return TCL_ERROR;
01528       }
01529     } else if (!strcmp(opt, "cutoff")) {
01530       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
01531         return TCL_ERROR;
01532       if (cutoff <= 0.0) {
01533         Tcl_AppendResult(interp, "measure cluster: invalid 'cutoff' value (should be larger than 0.0)", NULL);
01534         return TCL_ERROR;
01535       }
01536     } else if (!strcmp(opt, "distfunc")) {
01537       char *argstr = Tcl_GetStringFromObj(objv[i+1], NULL);
01538       if (!strcmp(argstr,"rmsd")) {
01539         likeness = MEASURE_DIST_RMSD;
01540       } else if (!strcmp(argstr,"fitrmsd")) {
01541         likeness = MEASURE_DIST_FITRMSD;
01542       } else if (!strcmp(argstr,"rgyrd")) {
01543         likeness = MEASURE_DIST_RGYRD;
01544       } else {
01545         Tcl_AppendResult(interp, "measure cluster: unknown distance function (supported are 'rmsd', 'rgyrd' and 'fitrmsd')", NULL);
01546         return TCL_ERROR;
01547       }
01548     } else if (!strcmp(opt, "selupdate")) {
01549       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01550         return TCL_ERROR;
01551     } else if (!strcmp(opt, "weight")) {
01552       // NOTE: we cannot use tcl_get_weights here, since we have to 
01553       // get a full (all atoms) list of weights as we may be updating
01554       // the selection in the process. Also we don't support explicit
01555       // lists of weights for now.
01556       const char *weight_string = Tcl_GetStringFromObj(objv[i+1], NULL);
01557       weights = new float[sel->num_atoms];
01558 
01559       if (!weight_string || !strcmp(weight_string, "none")) {
01560         for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
01561       } else {
01562         // if a selection string was given, check the symbol table
01563         SymbolTable *atomSelParser = app->atomSelParser; 
01564         // weights must return floating point values, so the symbol must not 
01565         // be a singleword, so macro is NULL.
01566         atomsel_ctxt context(atomSelParser,
01567                              app->moleculeList->mol_from_id(sel->molid()), 
01568                              sel->which_frame, NULL);
01569 
01570         int fctn = atomSelParser->find_attribute(weight_string);
01571         if (fctn >= 0) {
01572           // the keyword exists, so get the data
01573           // first, check to see that the function returns floats.
01574           // if it doesn't, it makes no sense to use it as a weight
01575           if (atomSelParser->fctns.data(fctn)->returns_a != SymbolTableElement::IS_FLOAT) {
01576             Tcl_AppendResult(interp, "weight attribute must have floating point values", NULL);
01577             delete [] weights;
01578             return MEASURE_ERR_BADWEIGHTPARM;  // can't understand weight parameter 
01579           }
01580 
01581           double *tmp_data = new double[sel->num_atoms];
01582           int *all_on = new int[sel->num_atoms];
01583           for (j=0; j<sel->num_atoms; j++) all_on[j]=1;
01584 
01585           atomSelParser->fctns.data(fctn)->keyword_double(
01586             &context, sel->num_atoms, tmp_data, all_on);
01587           
01588           for (j=0; j<sel->num_atoms; j++) weights[j] = (float)tmp_data[j];
01589           
01590           // clean up.
01591           delete [] tmp_data;
01592           delete [] all_on;
01593         }
01594       }
01595     } else if (!strcmp(opt, "first")) {
01596       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01597         return TCL_ERROR;
01598     } else if (!strcmp(opt, "last")) {
01599       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01600         return TCL_ERROR;
01601     } else if (!strcmp(opt, "step")) {
01602       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01603         return TCL_ERROR;
01604     } else { // unknown keyword.
01605       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure cluster ", argerrmsg,NULL);
01606       return TCL_ERROR;
01607     }
01608   }
01609 
01610   // set default for weights if not already defined
01611   if (!weights) {
01612     weights = new float[sel->num_atoms];
01613     for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
01614   }
01615   
01616   // Allocate temporary result storage. we add one more cluster 
01617   // slot for collecting unclustered frames in an additional "cluster".
01618   // NOTE: the individual cluster lists are going to
01619   //       allocated in the ancilliary code.
01620   int  *clustersize = new int  [numcluster+1];
01621   int **clusterlist = new int *[numcluster+1];
01622   
01623   // do the cluster analysis
01624   rc = measure_cluster(sel, app->moleculeList, numcluster, algorithm, likeness, cutoff, 
01625                        clustersize, clusterlist, first, last, step, selupdate, weights);
01626 
01627   if (weights) delete [] weights;
01628 
01629   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01630   if (rc != MEASURE_NOERR) { 
01631     Tcl_AppendResult(interp, "measure cluster: error during cluster analysis calculation.", NULL);
01632     return TCL_ERROR;
01633   }
01634 
01635   // convert the results of the lowlevel call to tcl lists
01636   // and build a list from them as return value.
01637   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01638 
01639   for (i=0; i <= numcluster; ++i) { 
01640     int j;
01641     Tcl_Obj *tcl_clist  = Tcl_NewListObj(0, NULL);
01642     for (j=0; j < clustersize[i]; ++j) {
01643       Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clusterlist[i][j]));
01644     }
01645     Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
01646   }
01647   Tcl_SetObjResult(interp, tcl_result);
01648 
01649   // free temporary result storage
01650   for (i=0; i <= numcluster; ++i)
01651     delete[] clusterlist[i];
01652 
01653   delete[] clusterlist;
01654   delete[] clustersize;
01655 
01656   return TCL_OK;
01657 }
01658 
01660 static int vmd_measure_clustsize(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01661   int i;
01662   // initialize optional arguments to default values
01663   double cutoff=3.0; // arbitrary. took hbond cutoff.
01664   char *storesize=NULL;
01665   char *storenum=NULL;
01666   int usepbc=0;
01667   int minsize=2;
01668   int numshared=1;
01669   int rc;
01670 
01671   // argument error message
01672   const char *argerrmsg = "<sel> [cutoff <float>] [minsize <num>] [numshared <num>] "
01673     "[usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]";
01674 
01675   // Two atom selections and optional keyword/value pairs.
01676   if ((argc < 2) || (argc > 13) || ((argc-1) % 2 == 0) )  {
01677     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01678     return TCL_ERROR;
01679   }
01680 
01681   // check atom selection
01682   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01683   if (!sel) {
01684     Tcl_AppendResult(interp, "measure clustsize: invalid atom selection", NULL);
01685     return TCL_ERROR;
01686   }
01687   if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
01688 
01689   // parse optional arguments
01690   for (i=2; i<argc; i+=2) {
01691     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01692     if (i==(argc-1)) {
01693       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01694       return TCL_ERROR;
01695     } else if (!strcmp(opt, "cutoff")) {
01696       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
01697         return TCL_ERROR;
01698       if (cutoff <= 0.0) {
01699         Tcl_AppendResult(interp, "measure clustsize: invalid 'cutoff' value", NULL);
01700         return TCL_ERROR;
01701       }
01702     } else if (!strcmp(opt, "minsize")) {
01703       if (Tcl_GetIntFromObj(interp, objv[i+1], &minsize) != TCL_OK)
01704         return TCL_ERROR;
01705       if (minsize < 2) {
01706         Tcl_AppendResult(interp, "measure clustsize: invalid 'minsize' value", NULL);
01707         return TCL_ERROR;
01708       }
01709     } else if (!strcmp(opt, "numshared")) {
01710       if (Tcl_GetIntFromObj(interp, objv[i+1], &numshared) != TCL_OK)
01711         return TCL_ERROR;
01712       if (numshared < 0) {
01713         Tcl_AppendResult(interp, "measure clustsize: invalid 'numshared' value", NULL);
01714         return TCL_ERROR;
01715       }
01716     } else if (!strcmp(opt, "usepbc")) {
01717       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01718         return TCL_ERROR;
01719     } else if (!strcmp(opt, "storenum")) {
01720       storenum = Tcl_GetStringFromObj(objv[i+1], NULL);
01721     } else if (!strcmp(opt, "storesize")) {
01722       storesize = Tcl_GetStringFromObj(objv[i+1], NULL);
01723     } else { // unknown keyword.
01724       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure clustsize ", argerrmsg,NULL);
01725       return TCL_ERROR;
01726     }
01727   }
01728 
01729   if (usepbc) { 
01730     Tcl_AppendResult(interp, "measure clustsize: does not support periodic boundaries yet.", NULL);
01731     return TCL_ERROR;
01732   }
01733 
01734   // allocate temporary result storage
01735   // NOTE: the individual cluster lists are going to
01736   //       allocated in the ancilliary code.
01737   int num_selected=sel->selected;
01738   int *clustersize = new int[num_selected];
01739   int *clusternum= new int [num_selected];
01740   int *clusteridx= new int [num_selected];
01741   for (i=0; i < num_selected; i++) {
01742     clustersize[i] = 0;
01743     clusternum[i]  = -1;
01744     clusteridx[i]  = -1;
01745   }
01746   
01747   // do the cluster analysis
01748   rc = measure_clustsize(sel, app->moleculeList, cutoff, 
01749                          clustersize, clusternum, clusteridx,
01750                          minsize, numshared, usepbc);
01751 
01752   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01753   if (rc != MEASURE_NOERR) { 
01754     Tcl_AppendResult(interp, "measure clustsize: error during cluster size analysis calculation.", NULL);
01755     return TCL_ERROR;
01756   }
01757 
01758 
01759   if (storenum || storesize) {
01760     // field names were given to store the results. check the keywords and so on.
01761     SymbolTable *atomSelParser = app->atomSelParser; 
01762     atomsel_ctxt context(atomSelParser, 
01763                          app->moleculeList->mol_from_id(sel->molid()), 
01764                          sel->which_frame, NULL);
01765 
01766     // the keyword exists, set the data
01767     if (storenum) {
01768       int fctn = atomSelParser->find_attribute(storenum);
01769       if (fctn >= 0) {
01770         if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
01771           double *tmp_data = new double[sel->num_atoms];
01772           int j=0;
01773           for (int i=0; i<sel->num_atoms; i++) {
01774             if (sel->on[i])
01775               tmp_data[i] = (double) clusternum[j++];
01776           }
01777           atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
01778                                                               sel->num_atoms,
01779                                                               tmp_data, sel->on);
01780           delete[] tmp_data;
01781           
01782         } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
01783           int *tmp_data = new int[sel->num_atoms];
01784           int j=0;
01785           for (int i=0; i<sel->num_atoms; i++) {
01786             if (sel->on[i])
01787               tmp_data[i] = clusternum[j++];
01788           }
01789           atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
01790                                                            sel->num_atoms,
01791                                                            tmp_data, sel->on);
01792           delete[] tmp_data;
01793         } else {
01794           Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
01795           return TCL_ERROR;
01796         }
01797       } else {
01798         Tcl_AppendResult(interp, "measure clustsize: invalid field name for storenum", NULL);
01799         return TCL_ERROR;
01800       }
01801     }
01802 
01803     // the keyword exists, set the data
01804     if (storesize) {
01805       int fctn = atomSelParser->find_attribute(storesize);
01806       if (fctn >= 0) {
01807         if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
01808           double *tmp_data = new double[sel->num_atoms];
01809           int j=0;
01810           for (int i=0; i<sel->num_atoms; i++) {
01811             if (sel->on[i])
01812               tmp_data[i] = (double) clustersize[j++];
01813           }
01814           atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
01815                                                               sel->num_atoms,
01816                                                               tmp_data, sel->on);
01817           delete[] tmp_data;
01818           
01819         } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
01820           int *tmp_data = new int[sel->num_atoms];
01821           int j=0;
01822           for (int i=0; i<sel->num_atoms; i++) {
01823             if (sel->on[i])
01824               tmp_data[i] = clustersize[j++];
01825           }
01826           atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
01827                                                            sel->num_atoms,
01828                                                            tmp_data, sel->on);
01829           delete[] tmp_data;
01830         } else {
01831           Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
01832           return TCL_ERROR;
01833         }
01834       } else {
01835         Tcl_AppendResult(interp, "measure clustsize: invalid field name for storesize", NULL);
01836         return TCL_ERROR;
01837       }
01838     }
01839   } else {
01840     // convert the results of the lowlevel call to tcl lists
01841     // and build a list from them as return value.
01842     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01843 
01844     Tcl_Obj *tcl_ilist  = Tcl_NewListObj(0, NULL);
01845     Tcl_Obj *tcl_clist  = Tcl_NewListObj(0, NULL);
01846     Tcl_Obj *tcl_nlist  = Tcl_NewListObj(0, NULL);
01847     for (i=0; i<num_selected; i++) { 
01848       Tcl_ListObjAppendElement(interp, tcl_ilist, Tcl_NewIntObj(clusteridx[i]));
01849       Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clustersize[i]));
01850       Tcl_ListObjAppendElement(interp, tcl_nlist, Tcl_NewIntObj(clusternum[i]));
01851     }
01852     Tcl_ListObjAppendElement(interp, tcl_result, tcl_ilist);
01853     Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
01854     Tcl_ListObjAppendElement(interp, tcl_result, tcl_nlist);
01855     Tcl_SetObjResult(interp, tcl_result);
01856   }
01857   
01858   delete[] clustersize;
01859   delete[] clusternum;
01860   delete[] clusteridx;
01861 
01862   return TCL_OK;
01863 }
01864 
01865 static int vmd_measure_hbonds(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01866   
01867   // Cutoff, angle, and either one or two atom selections
01868   if (argc != 4 && argc != 5) {
01869     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <angle> <selection1> [<selection2>]");
01870     return TCL_ERROR;
01871   }
01872   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
01873   if (!sel1) {
01874     Tcl_AppendResult(interp, "measure hbonds: invalid first atom selection", NULL);
01875     return TCL_ERROR;
01876   }
01877 
01878   AtomSel *sel2 = NULL;
01879   if (argc == 5) {
01880     sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[4],NULL));
01881     if (!sel2) {
01882       Tcl_AppendResult(interp, "measure hbonds: invalid second atom selection", NULL);
01883       return TCL_ERROR;
01884     }
01885   }
01886   if (sel2 && sel2->molid() != sel1->molid()) {
01887     Tcl_AppendResult(interp, "measure hbonds: error, atom selections must come from same molecule.", NULL);
01888     return TCL_ERROR;
01889   }
01890   double cutoff;
01891   if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK) 
01892     return TCL_ERROR;
01893 
01894   double maxangle;
01895   if (Tcl_GetDoubleFromObj(interp, objv[2], &maxangle) != TCL_OK) 
01896     return TCL_ERROR;
01897   
01898   const float *pos = sel1->coordinates(app->moleculeList);
01899   if (!pos) {
01900     Tcl_AppendResult(interp, "measure bondsearch: error, molecule contains no coordinates", NULL);
01901     return TCL_ERROR;
01902   }
01903 
01904   // XXX the actual code for measuring hbonds doesn't belong here, it should
01905   //     be moved into Measure.[Ch] where it really belongs.  This file
01906   //     only implements the Tcl interface, and should not be doing the
01907   //     hard core math, particularly if we want to expose the same
01908   //     feature via other scripting interfaces.  Also, having a single
01909   //     implementation avoids having different Tcl/Python bugs in the 
01910   //     long-term.  Too late to do anything about this now, but should be
01911   //     addressed for the next major version when time allows.
01912 
01913   // XXX This code is close, but not identical to the HBonds code in 
01914   //     DrawMolItem.  Is there any good reason they aren't identical?
01915   //     This version does a few extra tests that the other does not.
01916 
01917   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
01918 
01919   const int *A = sel1->on;
01920   const int *B = sel2 ? sel2->on : sel1->on;
01921  
01922   GridSearchPair *pairlist = vmd_gridsearch2(pos, sel1->num_atoms, A, B, (float) cutoff, sel1->num_atoms * 27);
01923   GridSearchPair *p, *tmp;
01924   float donortoH[3], Htoacceptor[3];
01925   Tcl_Obj *donlist = Tcl_NewListObj(0, NULL);
01926   Tcl_Obj *hydlist = Tcl_NewListObj(0, NULL);
01927   Tcl_Obj *acclist = Tcl_NewListObj(0, NULL);
01928   for (p=pairlist; p != NULL; p=tmp) {
01929     MolAtom *a1 = mol->atom(p->ind1); 
01930     MolAtom *a2 = mol->atom(p->ind2); 
01931     
01932     // neither the donor nor acceptor may be hydrogens
01933     if (mol->atom(p->ind1)->atomType == ATOMHYDROGEN ||
01934         mol->atom(p->ind2)->atomType == ATOMHYDROGEN) {
01935       tmp = p->next;
01936       free(p);
01937       continue;
01938     } 
01939     if (!a1->bonded(p->ind2)) {
01940       int b1 = a1->bonds;
01941       int b2 = a2->bonds;
01942       const float *coor1 = pos + 3*p->ind1;
01943       const float *coor2 = pos + 3*p->ind2;
01944       int k;
01945       // first treat sel1 as donor
01946       for (k=0; k<b1; k++) {
01947         const int hindex = a1->bondTo[k];
01948         if (mol->atom(hindex)->atomType == ATOMHYDROGEN) {         
01949           const float *hydrogen = pos + 3*hindex;
01950           vec_sub(donortoH,hydrogen,coor1);
01951           vec_sub(Htoacceptor,coor2,hydrogen);
01952           if (angle(donortoH, Htoacceptor)  < maxangle ) {
01953             Tcl_ListObjAppendElement(interp, donlist, Tcl_NewIntObj(p->ind1));
01954             Tcl_ListObjAppendElement(interp, acclist, Tcl_NewIntObj(p->ind2));
01955             Tcl_ListObjAppendElement(interp, hydlist, Tcl_NewIntObj(hindex));
01956           }
01957         }
01958       }
01959       // if only one atom selection was given, treat sel2 as a donor as well
01960       if (!sel2) {
01961         for (k=0; k<b2; k++) {
01962           const int hindex = a2->bondTo[k];
01963           if (mol->atom(hindex)->atomType == ATOMHYDROGEN) {
01964             const float *hydrogen = pos + 3*hindex;
01965             vec_sub(donortoH,hydrogen,coor2);
01966             vec_sub(Htoacceptor,coor1,hydrogen);
01967             if (angle(donortoH, Htoacceptor)  < maxangle ) {
01968               Tcl_ListObjAppendElement(interp, donlist, Tcl_NewIntObj(p->ind2));
01969               Tcl_ListObjAppendElement(interp, acclist, Tcl_NewIntObj(p->ind1));
01970               Tcl_ListObjAppendElement(interp, hydlist, Tcl_NewIntObj(hindex));
01971             }
01972           }
01973         }
01974       } 
01975     }
01976     tmp = p->next;
01977     free(p);
01978   }
01979   Tcl_Obj *result = Tcl_NewListObj(0, NULL);
01980   Tcl_ListObjAppendElement(interp, result, donlist);
01981   Tcl_ListObjAppendElement(interp, result, acclist);
01982   Tcl_ListObjAppendElement(interp, result, hydlist);
01983   Tcl_SetObjResult(interp, result);
01984   return TCL_OK;
01985 }
01986 
01987   
01988 static int vmd_measure_sasa(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01989 
01990   int i;
01991   // srad and one atom selection, plus additional options
01992   if (argc < 3) {
01993     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-points <varname>] [-restrict <restrictedsel>] [-samples <numsamples>]");
01994     return TCL_ERROR;
01995   }
01996   // parse options
01997   Tcl_Obj *ptsvar = NULL;
01998   AtomSel *restrictsel = NULL;
01999   int nsamples = -1;
02000   int *sampleptr = NULL;
02001   for (i=3; i<argc-1; i+=2) {
02002     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02003     if (!strcmp(opt, "-points")) {
02004       ptsvar = objv[i+1];
02005     } else if (!strcmp(opt, "-restrict")) {
02006       restrictsel = tcl_commands_get_sel(interp, 
02007           Tcl_GetStringFromObj(objv[i+1], NULL));
02008       if (!restrictsel) {
02009         Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02010         return TCL_ERROR;
02011       }
02012     } else if (!strcmp(opt, "-samples")) {
02013       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02014         return TCL_ERROR;
02015       sampleptr = &nsamples;
02016     } else {
02017       Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", 
02018           NULL);
02019       return TCL_ERROR;
02020     }
02021   }
02022 
02023   double srad;
02024   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02025     return TCL_ERROR;
02026   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02027   if (!sel1) {
02028     Tcl_AppendResult(interp, "measure sasa: invalid first atom selection", NULL);
02029     return TCL_ERROR;
02030   }
02031 
02032   const float *pos = sel1->coordinates(app->moleculeList);
02033   if (!pos) {
02034     Tcl_AppendResult(interp, "measure sasa: error, molecule contains no coordinates", NULL);
02035     return TCL_ERROR;
02036   }
02037   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02038   const float *radius = mol->extraflt.data("radius");
02039 
02040   ResizeArray<float> sasapts;
02041   float sasa = 0;
02042   int rc = measure_sasa(sel1, pos, radius, (float) srad, &sasa, &sasapts, 
02043                         restrictsel, sampleptr);
02044   if (rc < 0) {
02045     Tcl_AppendResult(interp, "measure: sasa: ", measure_error(rc), NULL);
02046     return TCL_ERROR;
02047   }
02048   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sasa));
02049   if (ptsvar) {
02050     // construct list from sasapts
02051     Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02052     i=0;
02053     while (i<sasapts.num()) {
02054       Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02055       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02056       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02057       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02058       Tcl_ListObjAppendElement(interp, listobj, elem);
02059     }
02060     Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02061   }
02062   return TCL_OK;
02063 }
02064 
02065 
02066 // Function: vmd_measure_energy 
02067 // Returns: the energy for the specified bond/angle/dihed/imprp/vdw/elec
02068 // FIXME -- usage doesn't match user guide
02069 static int vmd_measure_energy(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02070 
02071   if (argc<3) {
02072     Tcl_WrongNumArgs(interp, 2, objv-1,
02073                      (char *) "bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?}} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]");
02074     return TCL_ERROR;
02075   }
02076 
02077   int geomtype, reqatoms;
02078   char *geomname = Tcl_GetStringFromObj(objv[1],NULL);
02079   if        (!strncmp(geomname, "bond", 4)) {
02080     reqatoms = 2; geomtype = MEASURE_BOND;
02081   } else if (!strncmp(geomname, "angl", 4)) {
02082     reqatoms = 3; geomtype = MEASURE_ANGLE;
02083   } else if (!strncmp(geomname, "dihe", 4)) {
02084     reqatoms = 4; geomtype = MEASURE_DIHED;
02085   } else if (!strncmp(geomname, "impr", 4)) {
02086     reqatoms = 4; geomtype = MEASURE_IMPRP;
02087   } else if (!strncmp(geomname, "vdw",  3)) {
02088     reqatoms = 2; geomtype = MEASURE_VDW;
02089   } else if (!strncmp(geomname, "elec", 4)) {
02090     reqatoms = 2; geomtype = MEASURE_ELECT;
02091   } else {
02092     Tcl_AppendResult(interp, " measure energy: bad syntax (must specify bond|angle|dihed|imprp|vdw|elec)", NULL);
02093     return TCL_ERROR;
02094   }
02095 
02096   int molid[4];
02097   int atmid[4];
02098   int defmolid = -1;
02099   bool allframes = false;
02100   char errstring[200];
02101 
02102   // Read the atom list
02103   int numatms;
02104   Tcl_Obj **data;
02105   if (Tcl_ListObjGetElements(interp, objv[2], &numatms, &data) != TCL_OK) {
02106     Tcl_AppendResult(interp, " measure energy: bad syntax", NULL);
02107     Tcl_AppendResult(interp, " Usage: measure energy bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?} ...} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02108     return TCL_ERROR;
02109   }
02110 
02111   if (numatms!=reqatoms) {
02112     sprintf(errstring, " measure energy %s: must specify exactly %i atoms in list", geomname, reqatoms);
02113     Tcl_AppendResult(interp, errstring, NULL);
02114     return TCL_ERROR;
02115   }
02116     
02117   int first=-1, last=-1, frame=-1;
02118   double params[6];
02119   memset(params, 0, 6*sizeof(double));
02120 
02121   if (argc>4) {
02122     int i;
02123     for (i=3; i<argc-1; i+=2) {
02124       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02125       if (!strupncmp(argvcur, "molid", CMDLEN)) {
02126         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
02127           Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02128           return TCL_ERROR;
02129         }
02130       } else if (!strupncmp(argvcur, "k", CMDLEN)) {
02131         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02132           Tcl_AppendResult(interp, " measure energy: bad force constant value", NULL);
02133           return TCL_ERROR;
02134         }
02135       } else if (!strupncmp(argvcur, "x0", CMDLEN)) {
02136         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02137           Tcl_AppendResult(interp, " measure energy: bad equilibrium value", NULL);
02138           return TCL_ERROR;
02139         }
02140       } else if (!strupncmp(argvcur, "kub", CMDLEN)) {
02141         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02142           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley force constant value", NULL);
02143           return TCL_ERROR;
02144         }
02145       } else if (!strupncmp(argvcur, "s0", CMDLEN)) {
02146         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02147           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley equilibrium distance", NULL);
02148           return TCL_ERROR;
02149         }
02150       } else if (!strupncmp(argvcur, "n", CMDLEN)) {
02151         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02152           Tcl_AppendResult(interp, " measure energy: bad dihedral periodicity", NULL);
02153           return TCL_ERROR;
02154         }
02155       } else if (!strupncmp(argvcur, "delta", CMDLEN)) {
02156         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02157           Tcl_AppendResult(interp, " measure energy: bad dihedral phase shift", NULL);
02158           return TCL_ERROR;
02159         }
02160       } else if (!strupncmp(argvcur, "eps1", CMDLEN)) {
02161         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02162           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02163           return TCL_ERROR;
02164         }
02165       } else if (!strupncmp(argvcur, "rmin1", CMDLEN)) {
02166         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02167           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02168           return TCL_ERROR;
02169         }
02170       } else if (!strupncmp(argvcur, "eps2", CMDLEN)) {
02171         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02172           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02173           return TCL_ERROR;
02174         }
02175       } else if (!strupncmp(argvcur, "rmin2", CMDLEN)) {
02176         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02177           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02178           return TCL_ERROR;
02179         }
02180       } else if (!strupncmp(argvcur, "q1", CMDLEN)) {
02181         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02182           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02183           return TCL_ERROR;
02184         }
02185         params[2]=1.0;
02186       } else if (!strupncmp(argvcur, "q2", CMDLEN)) {
02187         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02188           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02189           return TCL_ERROR;
02190         }
02191         params[3]=1.0;
02192       } else if (!strupncmp(argvcur, "cutoff", CMDLEN)) {
02193         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+4) != TCL_OK) {
02194           Tcl_AppendResult(interp, " measure energy: bad electrostatic cutoff value", NULL);
02195           return TCL_ERROR;
02196         }
02197       } else if (!strupncmp(argvcur, "switchdist", CMDLEN)) {
02198         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+5) != TCL_OK) {
02199           Tcl_AppendResult(interp, " measure energy: bad switching distance value", NULL);
02200           return TCL_ERROR;
02201         }
02202       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
02203         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
02204           Tcl_AppendResult(interp, " measure energy: bad first frame value", NULL);
02205           return TCL_ERROR;
02206         }
02207       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
02208         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
02209           Tcl_AppendResult(interp, " measure energy: bad last frame value", NULL);
02210           return TCL_ERROR;
02211         }
02212       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02213         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
02214           allframes = true;
02215         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02216           Tcl_AppendResult(interp, " measure energy: bad frame value", NULL);
02217           return TCL_ERROR;
02218         }
02219       } else {
02220         Tcl_AppendResult(interp, " measure energy: invalid syntax, no such keyword: ", argvcur, NULL);
02221         return TCL_ERROR;
02222       }
02223     }
02224   }
02225 
02226   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
02227     Tcl_AppendResult(interp, "measure energy: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
02228     Tcl_AppendResult(interp, "\nUsage:\nmeasure bond <molid1>/<atomid1> <molid2>/<atomid2> [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02229     return TCL_ERROR;    
02230   }
02231 
02232   if (allframes) first=0;
02233 
02234   // If no molecule was specified use top as default
02235   if (defmolid<0) defmolid = app->molecule_top();
02236 
02237   // Assign atom IDs and molecule IDs
02238   int i,numelem;
02239   Tcl_Obj **atmmol;
02240   for (i=0; i<numatms; i++) {
02241     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
02242       return TCL_ERROR;
02243     }
02244 
02245     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
02246       Tcl_AppendResult(interp, " measure energy: bad atom index", NULL);
02247       return TCL_ERROR;
02248     }
02249     
02250     if (numelem==2) {
02251       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
02252         Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02253         return TCL_ERROR;
02254       }
02255     } else molid[i] = defmolid;
02256   }
02257   
02258 
02259   // Compute the value
02260   ResizeArray<float> gValues(1024);
02261   int ret_val;
02262   ret_val = measure_energy(app->moleculeList, molid, atmid, reqatoms, &gValues, frame, first, last,
02263                          defmolid, params, geomtype);
02264   if (ret_val<0) {
02265     printf("ERROR\n %s\n", measure_error(ret_val));
02266     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
02267     return TCL_ERROR;
02268   }
02269 
02270   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02271   int numvalues = gValues.num();
02272   for (int count = 0; count < numvalues; count++) {
02273     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
02274   }
02275   Tcl_SetObjResult(interp, tcl_result);
02276 
02277   return TCL_OK;
02278 }
02279 
02280 
02281 //
02282 // Function: vmd_measure_surface <selection> <gridsize> <radius> <depth>
02283 //
02284 static int vmd_measure_surface(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02285   if (argc!=5) {
02286     Tcl_WrongNumArgs(interp, 2, objv-1,
02287                      (char *) "<sel> <gridsize> <radius> <depth>");
02288     return TCL_ERROR;
02289   }
02290    
02291   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02292   if (!sel) {
02293     Tcl_AppendResult(interp, "measure surface: no atom selection", NULL);
02294     return TCL_ERROR;
02295   }
02296 
02297   const float *framepos = sel->coordinates(app->moleculeList);
02298   if (!framepos) return TCL_ERROR;
02299 
02300   double gridsz;
02301   if (Tcl_GetDoubleFromObj(interp, objv[2], &gridsz) != TCL_OK) 
02302      return TCL_ERROR;
02303 
02304   double radius;
02305   if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) 
02306      return TCL_ERROR;
02307 
02308   double depth;
02309   if (Tcl_GetDoubleFromObj(interp, objv[4], &depth) != TCL_OK) 
02310      return TCL_ERROR;
02311   
02312   int *surface;
02313   int n_surf;
02314   
02315   int ret_val = measure_surface(sel, app->moleculeList, framepos,
02316                                gridsz, radius, depth, &surface, &n_surf);
02317   if (ret_val < 0) {
02318     Tcl_AppendResult(interp, "measure surface: ", measure_error(ret_val));
02319     return TCL_ERROR;
02320   }
02321 
02322   Tcl_Obj *surf_list = Tcl_NewListObj(0, NULL);
02323 
02324   int i;
02325   for(i=0; i < n_surf; i++) {
02326     Tcl_ListObjAppendElement(interp, surf_list, Tcl_NewIntObj(surface[i]));
02327   }
02328   Tcl_SetObjResult(interp, surf_list);
02329   delete [] surface;
02330   
02331   return TCL_OK;
02332 }
02333 
02334 
02335 // Function: vmd_measure_pbc2onc <center> ?molid <default>? ?frame <frame|last>?
02336 //  Returns: the transformation matrix to wrap a nonorthogonal pbc unicell
02337 //           into an orthonormal cell.
02338 static int vmd_measure_pbc2onc_transform(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02339   if (argc < 2) {
02340     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> [molid <default>] [frame <frame|last>]");
02341     return TCL_ERROR;
02342   }
02343 
02344   // Read the center
02345   int ndim;
02346   Tcl_Obj **centerObj;
02347   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
02348     Tcl_AppendResult(interp, " measure pbc2onc: bad syntax", NULL);
02349     Tcl_AppendResult(interp, " Usage: measure pbc2onc <center> [molid <default>] [frame <frame|last>]", NULL);
02350     return TCL_ERROR;
02351   }
02352 
02353   if (ndim!=3) {
02354     Tcl_AppendResult(interp, " measure pbc2onc: need three numbers for a vector", NULL);
02355     return TCL_ERROR;
02356   }
02357     
02358   int i;
02359   double tmp;
02360   float center[3];
02361   for (i=0; i<3; i++) {
02362     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
02363       Tcl_AppendResult(interp, " measure pbc2onc: non-numeric in center", NULL);
02364       return TCL_ERROR;
02365     }
02366     center[i] = (float)tmp;
02367   }
02368 
02369   int molid = app->molecule_top();
02370   int frame = -2;
02371   if (argc>3) {
02372     for (i=2; i<argc; i+=2) {
02373       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02374       if (!strupncmp(argvcur, "molid", CMDLEN)) {
02375         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
02376           // top is already default
02377         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
02378           Tcl_AppendResult(interp, " measure pbc2onc: bad molid", NULL);
02379           return TCL_ERROR;
02380         }
02381       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02382         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
02383           frame=-1;
02384         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02385           Tcl_AppendResult(interp, " measure pbc2onc: bad frame value", NULL);
02386           return TCL_ERROR;
02387         }
02388       } else {
02389         Tcl_AppendResult(interp, " measure pbc2onc: invalid syntax, no such keyword: ", argvcur, NULL);
02390         return TCL_ERROR;
02391       }
02392     }
02393   }
02394 
02395 
02396   Matrix4 transform;
02397   int ret_val = measure_pbc2onc(app->moleculeList, molid, frame, center, transform);
02398   if (ret_val < 0) {
02399     Tcl_AppendResult(interp, "measure pbc2onc: ", measure_error(ret_val), NULL);
02400     return TCL_ERROR;
02401   }
02402 
02403   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02404   for (i=0; i<4; i++) {
02405     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
02406     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+0]));
02407     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+4]));
02408     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+8]));
02409     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+12]));
02410     Tcl_ListObjAppendElement(interp, tcl_result, rowListObj);
02411   }
02412   Tcl_SetObjResult(interp, tcl_result);
02413 
02414   return TCL_OK;
02415 }
02416 
02417 // Function: vmd_measure_pbc_neighbors <center> <cutoff>
02418 //  Returns: All image atoms that are within cutoff Angstrom of the pbc unitcell.
02419 //           Two lists are returned. The first list holds the atom coordinates while
02420 //           the second one is an indexlist mapping the image atoms to the atoms in
02421 //           the unitcell.
02422 
02423 //   Input:  Since the pbc cell <center> is not stored in DCDs and cannot be set in
02424 //           VMD it must be provided by the user as the first argument.
02425 //           The second argument <cutoff> is the maximum distance (in Angstrom) from
02426 //           the PBC unit cell for atoms to be considered.
02427 
02428 // Options:  ?sel <selection>? :
02429 //           If an atomselection is provided after the keyword 'sel' then only those
02430 //           image atoms are returned that are within cutoff of the selected atoms
02431 //           of the main cell. In case cutoff is a vector the largest value will be
02432 //           used.
02433 
02434 //           ?align <matrix>? :
02435 //           In case the molecule was aligned you can supply the alignment matrix
02436 //           which is then used to correct for the rotation and shift of the pbc cell.
02437 
02438 //           ?boundingbox PBC | {<mincoord> <maxcoord>}?
02439 //           With this option the atoms are wrapped into a rectangular bounding box.
02440 //           If you provide "PBC" as an argument then the bounding box encloses the
02441 //           PBC box but then the cutoff is added to the bounding box. Negative 
02442 //           values for the cutoff dimensions are allowed and lead to a smaller box.
02443 //           Instead you can also provide a custom bounding box in form of the 
02444 //           minmax coordinates (list containing two coordinate vectors such as 
02445 //           returned by the measure minmax command). Here, again, the cutoff is
02446 //           added to the bounding box.
02447 static int vmd_measure_pbc_neighbors(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02448   if (argc < 3) {
02449     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> <cutoff> ?sel <selection>? ?align <matrix>? ?molid <default>? ?frame <frame|last>? ?boundingbox PBC|{<mincoord> <maxcoord>}?");
02450     return TCL_ERROR;
02451   }
02452 
02453   // Read the center
02454   int ndim;
02455   Tcl_Obj **centerObj;
02456   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
02457     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
02458     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
02459     return TCL_ERROR;
02460   }
02461 
02462   if (ndim!=3) {
02463     Tcl_AppendResult(interp, " measure pbcneighbors: need three numbers for a vector", NULL);
02464     return TCL_ERROR;
02465   }
02466     
02467   int i;
02468   double tmp;
02469   float center[3];
02470   for (i=0; i<3; i++) {
02471     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
02472       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in center", NULL);
02473       return TCL_ERROR;
02474     }
02475     center[i] = float(tmp);
02476   }
02477 
02478   // Read the cutoff
02479   Tcl_Obj **cutoffObj;
02480   if (Tcl_ListObjGetElements(interp, objv[2], &ndim, &cutoffObj) != TCL_OK) {
02481     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
02482     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
02483     return TCL_ERROR;
02484   }
02485 
02486   if (ndim!=3 && ndim!=1) {
02487     Tcl_AppendResult(interp, " measure pbcneighbors: need either one or three numbers for cutoff", NULL);
02488     return TCL_ERROR;
02489   }
02490 
02491   float cutoff[3];
02492   for (i=0; i<ndim; i++) {
02493     if (Tcl_GetDoubleFromObj(interp, cutoffObj[i], &tmp) != TCL_OK) {
02494       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in cutoff", NULL);
02495       return TCL_ERROR;
02496     }
02497     cutoff[i] = float(tmp);
02498   }
02499 
02500   if (ndim==1) { cutoff[2] = cutoff[1] = cutoff[0]; }
02501 
02502   bool molidprovided=0;
02503   float *boxminmax=NULL;
02504   int molid = app->molecule_top();
02505   int frame = -2;
02506   AtomSel *sel = NULL;
02507   Matrix4 alignment;
02508   if (argc>4) {
02509     for (i=3; i<argc; i+=2) {
02510       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02511       if (!strupncmp(argvcur, "sel", CMDLEN)) {
02512         // Read the atom selection
02513         sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
02514         if (!sel) {
02515           Tcl_AppendResult(interp, "measure pbcneighbors: invalid atom selection", NULL);
02516           return TCL_ERROR;
02517         }
02518         if (!app->molecule_valid_id(sel->molid())) {
02519           Tcl_AppendResult(interp, "measure pbcneighbors: ",
02520                            measure_error(MEASURE_ERR_NOMOLECULE), NULL);
02521           return TCL_ERROR;
02522         }
02523         if (!sel->selected) {
02524           Tcl_AppendResult(interp, "measure pbcneighbors: selection contains no atoms.", NULL);
02525           return TCL_ERROR;
02526         }
02527       } else if (!strupncmp(argvcur, "molid", CMDLEN)) {
02528         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
02529           // top is already default
02530         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
02531           Tcl_AppendResult(interp, " measure pbcneighbors: bad molid", NULL);
02532           return TCL_ERROR;
02533         }
02534         molidprovided = 1;
02535       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02536         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
02537           frame=-1;
02538         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02539           Tcl_AppendResult(interp, " measure pbcneighbors: bad frame value", NULL);
02540           return TCL_ERROR;
02541         }
02542       } else if (!strupncmp(argvcur, "align", CMDLEN)) {
02543         // Get the alignment matrix (as returned by 'measure fit')
02544         if (tcl_get_matrix("measure pbcneighbors: ", interp, objv[i+1], alignment.mat) != TCL_OK) {
02545           return TCL_ERROR;
02546         }
02547       } else if (!strupncmp(argvcur, "boundingbox", CMDLEN)) {
02548         // Determine if the atoms shall be wrapped to the smallest rectangular
02549         // bounding box that still encloses the unitcell plus the given cutoff.
02550         char *argv2 = Tcl_GetStringFromObj(objv[i+1],NULL);
02551         if (!strupncmp(argv2, "on", CMDLEN) || !strupncmp(argv2, "pbc", CMDLEN)) {
02552           boxminmax = new float[6];
02553           compute_pbcminmax(app->moleculeList, molid, frame, center, &alignment,
02554                             boxminmax, boxminmax+3);
02555         } else {
02556           // Read the bounding box
02557           int j, k, ncoor;
02558           Tcl_Obj **boxListObj;
02559           if (Tcl_ListObjGetElements(interp, objv[i+1], &ncoor, &boxListObj) != TCL_OK) {
02560             Tcl_AppendResult(interp, " measure pbcneighbors: invalid bounding box parameter", NULL);
02561             return TCL_ERROR;
02562           }
02563           if (ncoor!=2) {
02564             Tcl_AppendResult(interp, " measure pbcneighbors: need 2 points for bounding box", NULL);
02565             return TCL_ERROR;
02566           }
02567           int ndim = 0;
02568           double tmp;
02569           Tcl_Obj **boxObj;
02570           boxminmax = new float[6];
02571           for (j=0; j<2; j++) {
02572             if (Tcl_ListObjGetElements(interp, boxListObj[j], &ndim, &boxObj) != TCL_OK) {
02573               Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax in boundingbox", NULL);
02574               return TCL_ERROR;
02575             }
02576 
02577             for (k=0; k<3; k++) {
02578               if (Tcl_GetDoubleFromObj(interp, boxObj[k], &tmp) != TCL_OK) {
02579                 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in boundingbox", NULL);
02580                 return TCL_ERROR;
02581               }
02582               boxminmax[3*j+k] = (float)tmp;
02583             }
02584           }
02585         }
02586 
02587       } else {
02588         Tcl_AppendResult(interp, " measure pbcneighbors: invalid syntax, no such keyword: ", argvcur, NULL);
02589         return TCL_ERROR;
02590       }
02591     }
02592   }
02593 
02594   // If no molid was provided explicitely but a selection was given
02595   // then use that molid.
02596   if (sel && !molidprovided) {
02597     molid = sel->molid();
02598   }
02599 
02600   ResizeArray<float> extcoord_array;
02601   ResizeArray<int> indexmap_array;
02602 
02603   int ret_val = measure_pbc_neighbors(app->moleculeList, sel, molid, frame, &alignment, center, cutoff,
02604                                       boxminmax, &extcoord_array, &indexmap_array);
02605   delete [] boxminmax;
02606 
02607   if (ret_val < 0) {
02608     Tcl_AppendResult(interp, "measure pbcneighbors: ", measure_error(ret_val), NULL);
02609     return TCL_ERROR;
02610   }
02611   printf("measure pbcneighbors: %i neighbor atoms found\n", indexmap_array.num());
02612 
02613   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02614   Tcl_Obj *coorListObj = Tcl_NewListObj(0, NULL);
02615   Tcl_Obj *indexListObj = Tcl_NewListObj(0, NULL);
02616   for (i=0; i<indexmap_array.num(); i++) {
02617     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
02618     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3*i]));
02619     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3*i+1]));
02620     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3*i+2]));
02621     Tcl_ListObjAppendElement(interp, coorListObj, rowListObj);
02622     Tcl_ListObjAppendElement(interp, indexListObj, Tcl_NewIntObj(indexmap_array[i]));
02623   }
02624   Tcl_ListObjAppendElement(interp, tcl_result, coorListObj);
02625   Tcl_ListObjAppendElement(interp, tcl_result, indexListObj);
02626   Tcl_SetObjResult(interp, tcl_result);
02627 
02628   return TCL_OK;
02629 }
02630 
02631 
02632 
02633 // Function: vmd_measure_inertia <selection> [moments] [eigenvals]
02634 //  Returns: The center of mass and the principles axes of inertia for the 
02635 //           selected atoms. 
02636 //  Options: -moments -- also return the moments of inertia tensor
02637 //           -eigenvals -- also return the corresponding eigenvalues
02638 static int vmd_measure_inertia(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02639   bool moments = FALSE;
02640   bool eigenvals = FALSE;
02641   if (argc < 2 || argc > 4) {
02642     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<selection> [moments] [eigenvals]");
02643     return TCL_ERROR;
02644   }
02645   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02646   if (!sel) {
02647     Tcl_AppendResult(interp, "measure inertia: no atom selection", NULL);
02648     return TCL_ERROR;
02649   }
02650   
02651   if (!app->molecule_valid_id(sel->molid())) {
02652     Tcl_AppendResult(interp, "measure inertia: ",
02653                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
02654     return TCL_ERROR;
02655   }
02656   
02657   if (argc>2) {
02658     int i;
02659     for (i=2; i<argc; i++) {
02660       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02661       // Allow syntax with and without leading dash
02662       if (argvcur[0]=='-') argvcur++;
02663 
02664       if (!strupncmp(argvcur, "moments", CMDLEN)) {
02665         moments = TRUE; 
02666       }
02667       else if (!strupncmp(argvcur, "eigenvals", CMDLEN)) {
02668         eigenvals = TRUE; 
02669       }
02670       else {
02671         Tcl_AppendResult(interp, " measure inertia: unrecognized option\n", NULL);
02672         Tcl_AppendResult(interp, " Usage: measure inertia <selection> [moments] [eigenvals]", NULL);
02673         return TCL_ERROR;
02674       }
02675     }
02676   }
02677 
02678   float priaxes[3][3];
02679   float itensor[4][4];
02680   float evalue[3], rcom[3];
02681   int ret_val = measure_inertia(sel, app->moleculeList, NULL, rcom, priaxes, itensor, evalue);
02682   if (ret_val < 0) {
02683     Tcl_AppendResult(interp, "measure inertia: ", measure_error(ret_val), NULL);
02684     return TCL_ERROR;
02685   }
02686 
02687   // return COM and list of 3 principal axes
02688   int i;
02689   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02690 
02691   Tcl_Obj *rcomObj = Tcl_NewListObj(0, NULL);
02692   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[0]));
02693   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[1]));
02694   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[2]));
02695   Tcl_ListObjAppendElement(interp, tcl_result, rcomObj);
02696 
02697   Tcl_Obj *axesListObj = Tcl_NewListObj(0, NULL);
02698   for (i=0; i<3; i++) {
02699     Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
02700     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][0]));
02701     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][1]));
02702     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][2]));
02703     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
02704   }
02705   Tcl_ListObjAppendElement(interp, tcl_result, axesListObj);
02706 
02707   if (moments) {
02708     Tcl_Obj *momListObj = Tcl_NewListObj(0, NULL);
02709     for (i=0; i<3; i++) {
02710       Tcl_Obj *momObj = Tcl_NewListObj(0, NULL);
02711       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][0]));
02712       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][1]));
02713       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][2]));
02714       Tcl_ListObjAppendElement(interp, momListObj, momObj);
02715     }
02716     Tcl_ListObjAppendElement(interp, tcl_result, momListObj);
02717   }
02718 
02719   if (eigenvals) {
02720       Tcl_Obj *eigvListObj = Tcl_NewListObj(0, NULL);
02721       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[0]));
02722       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[1]));
02723       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[2]));
02724       Tcl_ListObjAppendElement(interp, tcl_result, eigvListObj);
02725   }
02726   Tcl_SetObjResult(interp, tcl_result);
02727 
02728   return TCL_OK;
02729 }
02730 
02731 
02732 // measure symmetry <sel> [plane|I|C<n>|S<n> [<vector>]] [-tol <value>]
02733 //                  [-nobonds] [-verbose <level>]
02734 //
02735 // This function evaluates the molecular symmetry of an atom selection.
02736 // The underlying algorithm finds the symmetry elements such as 
02737 // inversion center, mirror planes, rotary axes and rotary reflections.
02738 // Based on the found elements it guesses the underlying point group.
02739 // The guess is fairly robust and can handle molecules whose
02740 // coordinatesthat deviate to a certain extent from the ideal symmetry.
02741 // The closest match with the highest symmetry will be returned.
02742 //
02743 // Options:
02744 // --------
02745 // -tol <value>
02746 //    Allows one to control tolerance of the algorithm when
02747 //    considering wether something is symmetric or not.
02748 //    A smaller value signifies a lower tolerance, the default
02749 //    is 0.1.
02750 // -nobonds 
02751 //    If this flag is set then the bond order and orientation
02752 //    are not considered when comparing structures.
02753 // -verbose <level>
02754 //    Controls the amount of console output.
02755 //    A level of 0 means no output, 1 gives some statistics at the
02756 //    end of the search (default). Level 2 gives additional info
02757 //    about each stage, level 3 more output for each iteration
02758 //    and 3, 4 yield yet more additional info.
02759 // -I
02760 //    Instead of guessing the symmetry pointgroup of the selection
02761 //    determine if the selection's center off mass represents an
02762 //    inversion center. The returned value is a score between 0
02763 //    and 1 where 1 denotes a perfect match.
02764 // -plane <vector>
02765 //    Instead of guessing the symmetry pointgroup of the selection
02766 //    determine if the plane with the defined by its normal
02767 //    <vector> is a mirror plane of the selection. The
02768 //    returned value is a score between 0 and 1 where 1 denotes
02769 //    a perfect match.
02770 // -Cn | -Sn  <vector>
02771 //    Instead of guessing the symmetry pointgroup of the selection
02772 //    determine if the rotation or rotary reflection axis Cn/Sn
02773 //    with order n defined by <vector> exists for the
02774 //    selection. E.g., if you want to query wether the Y-axis
02775 //    has a C3 rotational symmetry you specify "C3 {0 1 0}".
02776 //    The returned value is a score between 0 and 1 where 1
02777 //    denotes a perfect match.
02778 //
02779 // Result:
02780 // -------
02781 // The return value is a TCL list of pairs consisting of a label
02782 // string and a value or list. For each label the data following
02783 // it are described below:
02784 //
02785 // * [pointgroup] The guessed pointgroup:
02786 // * [order] Point group order (the n from above)
02787 // * [elements] Summary of found elements.
02788 //     For instance {(i) (C3) 3*(C2) (S6) 3*(sigma)} for D3d.
02789 // * [missing] Elements missing with respect to ideal number of
02790 //     elements (same format as above). If this is not an empty
02791 //     list then something has gone awfully wrong with the symmetry
02792 //     finding algorithm.
02793 // * [additional] Additional elements  that would not be expected
02794 //     for this point group (same format as above). If this is not
02795 //     an empty list then something has gone awfully wrong with the
02796 //     symmetry finding algorithm.
02797 // * [com] Center of mass of the selection based on the idealized
02798 //     coordinates.
02799 // * [inertia] List of the three axes of inertia, the eigenvalues
02800 //     of the moments of inertia tensor and a list of three 0/1 flags 
02801 //     specifying for each axis wether it is unique or not.
02802 // * [inversion] Flag 0/1 signifying if there is an inversion center.
02803 // * [axes] Normalized vectors defining rotary axes
02804 // * [rotreflect] Normalized vectors defining rotary reflections
02805 // * [planes] Normalized vectors defining mirror planes.
02806 // * [ideal]  Idealized symmetric coordinates for all atoms of
02807 //     the selection. The coordinates are listed in the order of 
02808 //     increasing atom indices (same order asa returned by the
02809 //     atomselect command ``get {x y z}''). Thus you can use the list
02810 //     to set the atoms of your selection to the ideal coordinates
02811 // * [unique] Index list defining a set of atoms with unique
02812 //     coordinates
02813 // * [orient] Matrix that aligns molecule with GAMESS standard
02814 //     orientation
02815 //
02816 // If a certain item is not present (e.g. no planes or no axes)
02817 // then the corresponding value is an empty list.
02818 // The pair format allows to use the result as a TCL array for
02819 // convenient access of the different return items.
02820 
02821 static int vmd_measure_symmetry(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02822   if (argc<2) {
02823     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]");
02824     return TCL_ERROR;
02825   }
02826   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02827   if (!sel) {
02828     Tcl_AppendResult(interp, "measure symmetry: no atom selection", NULL);
02829     return TCL_ERROR;
02830   }
02831   if (!app->molecule_valid_id(sel->molid())) {
02832     Tcl_AppendResult(interp, "measure symmetry: ",
02833                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
02834     return TCL_ERROR;
02835   }
02836 
02837   double sigma = 0.1;
02838   float axis[3];
02839   int checkelement = 0;
02840   int checkbonds = 1;
02841   int order = 0;
02842   int verbose = 1;
02843   int impose = 0;
02844   int imposeinvers = 0;
02845   int numimposeplan = 0;
02846   int numimposeaxes = 0;
02847   int numimposerref = 0;
02848   float *imposeplan = NULL;
02849   float *imposeaxes = NULL;
02850   float *imposerref = NULL;
02851   int *imposeaxesord = NULL;
02852   int *imposerreford = NULL;
02853   AtomSel *idealsel = NULL;
02854 
02855   if (argc>2) {
02856     int bailout = 0;
02857     int i;
02858     for (i=2; (i<argc && !bailout); i++) {
02859       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02860       // Allow syntax with and without leading dash
02861       if (argvcur[0]=='-') argvcur++;
02862       
02863       if (!strupncmp(argvcur, "tol", CMDLEN)) {
02864         if (Tcl_GetDoubleFromObj(interp, objv[i+1], &sigma) != TCL_OK) {
02865           Tcl_AppendResult(interp, " measure symmetry: bad tolerance value", NULL);
02866           bailout = 1; continue;
02867         }       
02868         i++;
02869       }
02870       else if (!strupncmp(argvcur, "verbose", CMDLEN)) {
02871         if (Tcl_GetIntFromObj(interp, objv[i+1], &verbose) != TCL_OK) {
02872           Tcl_AppendResult(interp, " measure symmetry: bad verbosity level value", NULL);
02873           bailout = 1; continue;
02874         }       
02875         i++;
02876       }
02877       else if (!strupncmp(argvcur, "nobonds", CMDLEN)) {
02878         checkbonds = 0;
02879       }
02880       else if (!strupcmp(argvcur, "I")) {
02881         checkelement = 1;
02882       }
02883       else if (!strupncmp(argvcur, "plane", CMDLEN)) {
02884         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
02885           bailout = 1; continue;
02886         }
02887         checkelement = 2;
02888         i++;
02889       }
02890       else if (!strupncmp(argvcur, "C", 1) || !strupncmp(argvcur, "S", 1)) {
02891         char *begptr = argvcur+1;
02892         char *endptr;
02893         order = strtol(begptr, &endptr, 10);
02894         if (endptr==begptr || *endptr!='\0') {
02895           Tcl_AppendResult(interp, " measure symmetry: bad symmetry element format (must be I, C*, S*, plane, where * is the order). ", NULL);
02896           bailout = 1; continue;
02897         }
02898 
02899         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
02900           bailout = 1; continue;
02901         }
02902         
02903         if (!strupncmp(argvcur, "C", 1)) checkelement = 3;
02904         if (!strupncmp(argvcur, "S", 1)) checkelement = 4;
02905         i++;
02906       }
02907       else if (!strupncmp(argvcur, "idealsel", CMDLEN)) {
02908         idealsel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
02909         if (!sel) {
02910           Tcl_AppendResult(interp, "measure symmetry: no atom selection for idealized coordinates", NULL);
02911           bailout = 1; continue;
02912         }
02913         if (idealsel->molid()!=sel->molid()) {
02914           Tcl_AppendResult(interp, "measure symmetry: selection and idealsel must be from the same molecule", NULL);
02915           bailout = 1; continue;
02916         }
02917         if (idealsel->selected<sel->selected) {
02918           Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
02919           bailout = 1; continue;
02920         }
02921         // make sure sel is subset of idealsel
02922         int j;
02923         for (j=0; j<sel->num_atoms; j++) {
02924           if (sel->on[j] && !idealsel->on[j]) {
02925             Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
02926             bailout = 1; continue;
02927           }
02928         }
02929 
02930         i++;
02931       }
02932       else if (!strupncmp(argvcur, "imposeinversion", CMDLEN)) {
02933         imposeinvers = 1;
02934         impose = 1;
02935       }
02936       else if (!strupncmp(argvcur, "imposeplanes", CMDLEN)) {
02937         // Format: list of normal vectors {{x y z} {x y z} ...}
02938         int nelem;
02939         Tcl_Obj **elemListObj;
02940         if (i+1>=argc ||
02941             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK) {
02942           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
02943           bailout = 1; continue;
02944         }
02945         float *elem = new float[3*nelem];
02946         int k;
02947         for (k=0; k<nelem; k++) {
02948           int nobj;
02949           Tcl_Obj **vecObj;
02950           if (Tcl_ListObjGetElements(interp, elemListObj[k], &nobj, &vecObj) != TCL_OK) {
02951             delete [] elem;
02952             Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
02953             bailout = 1; continue;
02954           }
02955           if (nobj!=3) {
02956             delete [] elem;
02957             Tcl_AppendResult(interp, " measure symmetry imposeplanes: vector must have 3 elements", NULL);
02958             bailout = 1; continue;
02959           }
02960           
02961           int m;
02962           for (m=0; m<3; m++) {
02963             double d;
02964             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
02965               delete [] elem;
02966               bailout = 1; continue;
02967             }
02968             elem[3*k+m] = (float)d;
02969           }
02970         }
02971         if (imposeplan) delete [] imposeplan;
02972         imposeplan = elem;
02973         numimposeplan = nelem;
02974         impose = 1;
02975         i++;
02976       }
02977       else if (!strupncmp(argvcur, "imposeaxes", CMDLEN) ||
02978                !strupncmp(argvcur, "imposerotref", CMDLEN)) {
02979         // Format: list of axes and orders {{x y z} 3 {x y z} 2 ...}
02980         int nelem;
02981         Tcl_Obj **elemListObj;
02982         if (i+1>=argc ||
02983             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK ||
02984             nelem%2) {
02985           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeaxes/imposerotref option", NULL);
02986           bailout = 1; continue;
02987         }
02988         nelem /= 2;
02989      
02990         if (nelem<=0) {
02991           i++;
02992           continue;
02993         }
02994         float *elem = new float[3*nelem];
02995         int *axorder = new int[nelem];
02996         int k;
02997         for (k=0; k<nelem; k++) {
02998           int nobj;
02999           Tcl_Obj **vecObj;
03000           if (Tcl_ListObjGetElements(interp, elemListObj[2*k], &nobj, &vecObj) != TCL_OK) {
03001             delete [] elem;
03002             delete [] axorder;
03003             Tcl_AppendResult(interp, " measure symmetry impose: bad syntax for axis vector", NULL);
03004             bailout = 1; continue;
03005           }
03006           if (Tcl_GetIntFromObj(interp, elemListObj[2*k+1], &axorder[k]) != TCL_OK) {
03007             delete [] elem;
03008             delete [] axorder;
03009             bailout = 1; continue;
03010           }
03011           if (nobj!=3) {
03012             delete [] elem;
03013             delete [] axorder;
03014             Tcl_AppendResult(interp, " measure symmetry impose: axis vector must have 3 elements", NULL);
03015             bailout = 1; continue;
03016           }
03017           
03018           int m;
03019           for (m=0; m<3; m++) {
03020             double d;
03021             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03022               delete [] elem;
03023               delete [] axorder;
03024               bailout = 1; continue;
03025             }
03026             elem[3*k+m] = (float)d;
03027           }
03028         }
03029         if (!strupncmp(argvcur, "imposeaxes", CMDLEN)) {
03030           if (imposeaxes)    delete [] imposeaxes;
03031           if (imposeaxesord) delete [] imposeaxesord;
03032           imposeaxes = elem;
03033           imposeaxesord = axorder;
03034           numimposeaxes = nelem;
03035         } else if (!strupncmp(argvcur, "imposerotref", CMDLEN)) {
03036           if (imposerref)    delete [] imposerref;
03037           if (imposerreford) delete [] imposerreford;
03038           imposerref = elem;
03039           imposerreford = axorder;
03040           numimposerref = nelem;
03041         }
03042       
03043         impose = 1;
03044         i++;
03045       }
03046       else {
03047         Tcl_AppendResult(interp, " measure symmetry: unrecognized option ", NULL);
03048         Tcl_AppendResult(interp, argvcur);
03049         Tcl_AppendResult(interp, ".\n Usage: measure symmetry <selection> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]", NULL);
03050         bailout = 1; continue;
03051       }
03052     }
03053 
03054     if (bailout) {
03055       if (imposeplan) delete [] imposeplan;
03056       if (imposeaxes) delete [] imposeaxes;
03057       if (imposerref) delete [] imposerref;
03058       if (imposeaxesord) delete [] imposeaxesord;
03059       if (imposerreford) delete [] imposerreford;
03060       return TCL_ERROR;
03061     }
03062   }
03063 
03064   // Initialize
03065   Symmetry sym = Symmetry(sel, app->moleculeList, verbose);
03066 
03067   // Set tolerance for atomic overlapping
03068   sym.set_overlaptol(float(sigma));
03069 
03070   // Wether to include bonds into the analysis
03071   sym.set_checkbonds(checkbonds);
03072 
03073   if (checkelement) {
03074     // We are interested only in the score for a specific
03075     // symmetry element.
03076     float overlap = 0.0;
03077     if (checkelement==1) {
03078       overlap = sym.score_inversion();
03079     }
03080     else if (checkelement==2) {
03081       overlap = sym.score_plane(axis);
03082     }
03083     else if (checkelement==3) {
03084       overlap = sym.score_axis(axis, order);
03085     }
03086     else if (checkelement==4) {
03087       overlap = sym.score_rotary_reflection(axis, order);
03088     }
03089 
03090     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03091     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03092     Tcl_SetObjResult(interp, tcl_result);
03093     return TCL_OK;
03094   }
03095 
03096   if (impose) {
03097     sym.impose(imposeinvers, numimposeplan, imposeplan,
03098                numimposeaxes, imposeaxes, imposeaxesord,
03099                numimposerref, imposerref, imposerreford);
03100     if (imposeplan) delete [] imposeplan;
03101     if (imposeaxes) delete [] imposeaxes;
03102     if (imposerref) delete [] imposerref;
03103     if (imposeaxesord) delete [] imposeaxesord;
03104     if (imposerreford) delete [] imposerreford;
03105   }
03106 
03107   int ret_val = sym.guess(float(sigma));
03108   if (ret_val < 0) {
03109     Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03110     return TCL_ERROR;
03111   }
03112 
03113   int natoms = sel->selected;
03114   Symmetry *s = &sym;
03115 
03116   if (idealsel) {
03117     Symmetry *isym = new Symmetry(idealsel, app->moleculeList, verbose);
03118     isym->set_overlaptol(float(sigma));
03119     isym->set_checkbonds(checkbonds);
03120     int j;
03121     float *plane = new float[3*sym.numplanes()];
03122     for (j=0; j<sym.numplanes(); j++) {
03123       vec_copy(&plane[3*j], sym.plane(j));
03124     }
03125     int *axisorder = new int[sym.numaxes()];
03126     float *axis = new float[3*sym.numaxes()];
03127     for (j=0; j<sym.numaxes(); j++) {
03128       axisorder[j] = sym.get_axisorder(j);
03129       vec_copy(&axis[3*j], sym.axis(j));
03130     }
03131     int *rrorder = new int[sym.numrotreflect()];
03132     float *rraxis = new float[3*sym.numrotreflect()];
03133     for (j=0; j<sym.numrotreflect(); j++) {
03134       rrorder[j] = sym.get_rotreflectorder(j);
03135       vec_copy(&rraxis[3*j], sym.rotreflect(j));
03136     }
03137     // XXX must check if sel is subset of idealsel
03138     int k=0, m=0;
03139     for (j=0; j<sel->num_atoms; j++) {
03140       if (idealsel->on[j]) {
03141         if (sel->on[j]) {
03142           vec_copy(isym->idealpos(k), sym.idealpos(m));
03143           m++;
03144         }
03145         k++;
03146       }
03147     }
03148     isym->impose(sym.has_inversion(),
03149                  sym.numplanes(), plane,
03150                  sym.numaxes(),   axis, axisorder,
03151                  sym.numrotreflect(), rraxis, rrorder);
03152 
03153     ret_val = isym->guess(float(sigma));
03154     if (ret_val < 0) {
03155       Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03156       return TCL_ERROR;
03157     }
03158 
03159     natoms = idealsel->selected;
03160     s = isym;
03161   }
03162 
03163   int pgorder;
03164   char pointgroup[6];
03165   s->get_pointgroup(pointgroup, &pgorder);
03166 
03167   int i;
03168   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03169   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("pointgroup", -1));
03170   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(pointgroup, -1));
03171   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("order", -1));
03172   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(pgorder));
03173   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rmsd", -1));
03174   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(s->get_rmsd()));
03175   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("elements", -1));
03176   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_element_summary(), -1));
03177   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("missing", -1));
03178   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_missing_elements(), -1));
03179   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("additional", -1));
03180   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_additional_elements(), -1));
03181 
03182   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("com", -1));
03183   Tcl_Obj *invObj  = Tcl_NewListObj(0, NULL);
03184   float *com = s->center_of_mass();
03185   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[0]));
03186   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[1]));
03187   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[2]));
03188   Tcl_ListObjAppendElement(interp, tcl_result, invObj);
03189 
03190   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inertia", -1));
03191   Tcl_Obj *inertListListObj  = Tcl_NewListObj(0, NULL);
03192   float *inertia  = s->get_inertia_axes();
03193   float *eigenval = s->get_inertia_eigenvals();
03194   int   *unique   = s->get_inertia_unique();
03195   for (i=0; i<3; i++) {
03196     Tcl_Obj *inertObj  = Tcl_NewListObj(0, NULL);
03197     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3*i]));
03198     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3*i+1]));
03199     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3*i+2]));
03200     Tcl_Obj *inertListObj  = Tcl_NewListObj(0, NULL);
03201     Tcl_ListObjAppendElement(interp, inertListObj, inertObj);
03202     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewDoubleObj(eigenval[i]));
03203     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewIntObj(unique[i]));
03204     Tcl_ListObjAppendElement(interp, inertListListObj, inertListObj);
03205   }
03206   Tcl_ListObjAppendElement(interp, tcl_result, inertListListObj);
03207 
03208  
03209   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inversion", -1));
03210   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(s->has_inversion()));
03211 
03212 
03213   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("axes", -1));
03214   Tcl_Obj *axesListListObj  = Tcl_NewListObj(0, NULL);
03215   for (i=0; i<s->numaxes(); i++) {
03216     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03217     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03218     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[0]));
03219     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[1]));
03220     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[2]));
03221     Tcl_Obj *axesListObj  = Tcl_NewListObj(0, NULL);
03222     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03223     Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewIntObj(s->get_axisorder(i)));
03224     int axistype = s->get_axistype(i);
03225     if (axistype & PRINCIPAL_AXIS && 
03226         !(!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS))) {
03227       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("principal", -1));
03228     } else if (!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS)) {
03229       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("perpendicular", -1));
03230     } else {
03231       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewListObj(0, NULL));
03232     }
03233     Tcl_ListObjAppendElement(interp, axesListListObj, axesListObj);
03234   }
03235   Tcl_ListObjAppendElement(interp, tcl_result, axesListListObj);
03236 
03237 
03238   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rotreflect", -1));
03239   Tcl_Obj *rraxesListListObj  = Tcl_NewListObj(0, NULL);
03240   for (i=0; i<s->numrotreflect(); i++) {
03241     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03242     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03243     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[0]));
03244     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[1]));
03245     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[2]));
03246     Tcl_Obj *rraxesListObj  = Tcl_NewListObj(0, NULL);
03247     Tcl_ListObjAppendElement(interp, rraxesListObj, axesObj);
03248     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotreflectorder(i)));
03249     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotrefltype(i)));
03250     Tcl_ListObjAppendElement(interp, rraxesListListObj, rraxesListObj);
03251   }
03252   Tcl_ListObjAppendElement(interp, tcl_result, rraxesListListObj);
03253 
03254 
03255   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("planes", -1));
03256   Tcl_Obj *planesListListObj = Tcl_NewListObj(0, NULL);
03257   for (i=0; i<s->numplanes(); i++) {
03258     Tcl_Obj *planesObj = Tcl_NewListObj(0, NULL);
03259     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03260     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[0]));
03261     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[1]));
03262     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[2]));
03263     Tcl_Obj *planesListObj = Tcl_NewListObj(0, NULL);
03264     Tcl_ListObjAppendElement(interp, planesListObj, planesObj);
03265     switch (s->get_planetype(i)) {
03266     case 1:
03267       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("vertical", -1));
03268       break;
03269     case 3:
03270       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("dihedral", -1));
03271       break;
03272     case 4:
03273       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("horizontal", -1));
03274       break;
03275     default:
03276       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewListObj(0, NULL));
03277     }
03278     Tcl_ListObjAppendElement(interp, planesListListObj, planesListObj);
03279   }
03280   Tcl_ListObjAppendElement(interp, tcl_result, planesListListObj);
03281 
03282 
03283   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("ideal", -1));
03284   Tcl_Obj *idealcoorListObj = Tcl_NewListObj(0, NULL);
03285   for (i=0; i<natoms; i++) {
03286     Tcl_Obj *idealcoorObj = Tcl_NewListObj(0, NULL);
03287     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03288     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[0]));
03289     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[1]));
03290     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[2]));
03291     Tcl_ListObjAppendElement(interp, idealcoorListObj, idealcoorObj);
03292   }
03293   Tcl_ListObjAppendElement(interp, tcl_result, idealcoorListObj);
03294 
03295   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("unique", -1));
03296   Tcl_Obj *uniquecoorListObj = Tcl_NewListObj(0, NULL);
03297   for (i=0; i<natoms; i++) {
03298     if (!s->get_unique_atom(i)) continue; 
03299     Tcl_ListObjAppendElement(interp, uniquecoorListObj, Tcl_NewIntObj(i));
03300   }
03301   Tcl_ListObjAppendElement(interp, tcl_result, uniquecoorListObj);
03302 
03303   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("orient", -1));
03304   Matrix4 *orient = s->get_orientation();
03305   Tcl_Obj *matrixObj = Tcl_NewListObj(0, NULL);
03306   for (i=0; i<4; i++) {
03307     Tcl_Obj *rowObj = Tcl_NewListObj(0, NULL);
03308     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+0]));
03309     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+4]));
03310     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+8]));
03311     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+12]));
03312     Tcl_ListObjAppendElement(interp, matrixObj, rowObj);
03313   }
03314   Tcl_ListObjAppendElement(interp, tcl_result, matrixObj);
03315 
03316   Tcl_SetObjResult(interp, tcl_result);
03317   if (idealsel) {
03318     delete s;
03319   }
03320   return TCL_OK;
03321 
03322 }
03323 
03324 // Function: vmd_measure_transoverlap <selection>
03325 //  Returns: The structural overlap of a selection with a copy of itself
03326 //           that is transformed according to a given transformation matrix.
03327 //           The normalized sum over all gaussian function values of the pair
03328 //           distances between atoms in the original and the transformed
03329 //           selection.
03330 //    Input: the atom selection and the transformation matrix.
03331 //   Option: with -sigma you can specify the sigma value of the overlap 
03332 //           gaussian function. The default is 0.1 Angstrom.
03333 static int vmd_measure_trans_overlap(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03334   if (argc!=3 && argc!=5) {
03335     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> <matrix> [-sigma <value>]");
03336     return TCL_ERROR;
03337   }
03338   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03339   if (!sel) {
03340     Tcl_AppendResult(interp, "measure transoverlap: no atom selection", NULL);
03341     return TCL_ERROR;
03342   }
03343   if (!app->molecule_valid_id(sel->molid())) {
03344     Tcl_AppendResult(interp, "measure transoverlap: ",
03345                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03346     return TCL_ERROR;
03347   }
03348 
03349   // Get the transformation matrix
03350   Matrix4 trans;
03351   if (tcl_get_matrix("measure transoverlap: ",interp,objv[2], trans.mat) != TCL_OK) {
03352     return TCL_ERROR;
03353   }
03354 
03355   double sigma = 0.1;
03356   if (argc==5) {
03357     if (!strupncmp(Tcl_GetStringFromObj(objv[3],NULL), "-sigma", CMDLEN)) {
03358       if (Tcl_GetDoubleFromObj(interp, objv[4], &sigma) != TCL_OK) {
03359         Tcl_AppendResult(interp, " measure transoverlap: bad sigma value", NULL);
03360         return TCL_ERROR;
03361       } 
03362     } else {
03363       Tcl_AppendResult(interp, " measure transoverlap: unrecognized option\n", NULL);
03364       Tcl_AppendResult(interp, " Usage: measure transoverlap <sel> <matrix> [-sigma <value>]", NULL);
03365       return TCL_ERROR;
03366     }
03367   }
03368 
03369   int maxnatoms = 100;// XXX
03370   float overlap;
03371   int ret_val = measure_trans_overlap(sel, app->moleculeList, &trans,
03372                                       float(sigma), NOSKIP_IDENTICAL, 
03373                                       maxnatoms, overlap);
03374   if (ret_val < 0) {
03375     Tcl_AppendResult(interp, "measure transoverlap: ", measure_error(ret_val), NULL);
03376     return TCL_ERROR;
03377   }
03378 
03379   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03380   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03381   Tcl_SetObjResult(interp, tcl_result);
03382 
03383   return TCL_OK;
03384 }
03385 
03386 
03387 
03388 int obj_measure(ClientData cd, Tcl_Interp *interp, int argc,
03389                             Tcl_Obj * const objv[]) {
03390 
03391   if (argc < 2) {
03392     Tcl_SetResult(interp, 
03393       (char *) "usage: measure <command> [args...]\n"
03394       "\nMeasure Commands:\n"
03395       "  avpos <sel> [first <first>] [last <last>] [step <step>] -- average position\n"
03396       "  center <sel> [weight <weights>]          -- geometrical (or weighted) center\n"
03397       "  cluster <sel> [num <#clusters>] [distfunc <flag>] [cutoff <cutoff>]\n"
03398       "          [first <first>] [last <last>] [step <step>] [selupdate <bool>]\n"
03399       "          [weight <weights>]\n"
03400       "     -- perform a cluster analysis (cluster similar timesteps)\n"
03401       "  clustsize <sel> [cutoff <float>] [minsize <num>] [numshared <num>]\n"
03402       "            [usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]\n"
03403       "     -- perform a cluster size analysis (find clusters of atoms)\n"
03404       "  contacts <cutoff> <sel1> [<sel2>]        -- list contacts\n" 
03405       "  dipole <sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]\n"
03406       "     -- dipole moment\n"
03407       "  fit <sel1> <sel2> [weight <weights>] [order <index list>]\n"
03408       "     -- transformation matrix from selection 1 to 2\n"
03409       "  gofr <sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>]\n"
03410       "     [selupdate <bool>] [first <first>] [last <last>] [step <step>]\n"
03411       "     -- atomic pair distribution function g(r)\n"
03412       "  hbonds <cutoff> <angle> <sel1> [<sel2>]\n"
03413       "     -- list donors, acceptors, hydrogens involved in hydrogen bonds\n"
03414       "  inverse <matrix>                         -- inverse matrix\n"
03415       "  inertia <sel> [-moments] [-eigenvals]    -- COM and principle axes of inertia\n"
03416       "  minmax <sel> [-withradii]                -- bounding box\n"
03417       "  rgyr <sel> [weight <weights>]            -- radius of gyration\n"
03418       "  rmsd <sel1> <sel2> [weight <weights>]    -- RMS deviation\n"
03419       "  rmsf <sel> [first <first>] [last <last>] [step <step>] -- RMS fluctuation\n"
03420       "  sasa <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
03421       "     [-samples <numsamples>]               -- solvent-accessible surface area\n"
03422       "  sumweights <sel> weight <weights>        -- sum of selected weights\n"
03423       "  bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}}\n"
03424       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
03425       "     -- bond length between atoms 1 and 2\n"
03426       "  angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}}\n"
03427       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
03428       "     -- angle between atoms 1-3\n"
03429       "  dihed {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
03430       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
03431       "      -- dihedral angle between atoms 1-4\n"
03432       "  imprp {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
03433       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
03434       "     -- improper angle between atoms 1-4\n"
03435       // FIXME: Complete 'measure energy' usage info here?
03436       "  energy bond|angle|dihed|impr|vdw|elec     -- compute energy\n"
03437       "  surface <sel> <gridsize> <radius> <thickness> -- surface of selection\n"
03438       "  pbc2onc <center> [molid <default>] [frame <frame|last>]\n"
03439       "     --  transformation matrix to wrap a nonorthogonal PBC unit cell\n"
03440       "         into an orthonormal cell\n"
03441       "  pbcneighbors <center> <cutoff> [sel <sel>] [align <matrix>] [molid <default>]\n"
03442       "     [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]\n"
03443       "     -- all image atoms that are within cutoff Angstrom of the pbc unit cell\n"
03444       "  symmetry <sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]\n"
03445       "  transoverlap <sel> <matrix> [-sigma <value>]\n"
03446       "     -- overlap of a structure with a transformed copy of itself\n",
03447       TCL_STATIC);
03448     return TCL_ERROR;
03449   }
03450   VMDApp *app = (VMDApp *)cd;
03451   char *argv1 = Tcl_GetStringFromObj(objv[1],NULL);
03452   if (!strupncmp(argv1, "avpos", CMDLEN))
03453     return vmd_measure_avpos(app, argc-1, objv+1, interp);
03454   else if (!strupncmp(argv1, "center", CMDLEN))
03455     return vmd_measure_center(app, argc-1, objv+1, interp);
03456   else if (!strupncmp(argv1, "cluster", CMDLEN))
03457     return vmd_measure_cluster(app, argc-1, objv+1, interp);
03458   else if (!strupncmp(argv1, "clustsize", CMDLEN))
03459     return vmd_measure_clustsize(app, argc-1, objv+1, interp);
03460   else if (!strupncmp(argv1, "contacts", CMDLEN))
03461     return vmd_measure_contacts(app, argc-1, objv+1, interp);
03462   else if (!strupncmp(argv1, "dipole", CMDLEN))
03463     return vmd_measure_dipole(app, argc-1, objv+1, interp);
03464   else if (!strupncmp(argv1, "fit", CMDLEN)) 
03465     return vmd_measure_fit(app, argc-1, objv+1, interp);
03466   else if (!strupncmp(argv1, "minmax", CMDLEN))
03467     return vmd_measure_minmax(app, argc-1, objv+1, interp);
03468   else if (!strupncmp(argv1, "gofr", CMDLEN))
03469     return vmd_measure_gofr(app, argc-1, objv+1, interp);
03470   else if (!strupncmp(argv1, "rdf", CMDLEN))
03471     return vmd_measure_rdf(app, argc-1, objv+1, interp);
03472   else if (!strupncmp(argv1, "hbonds", CMDLEN))
03473     return vmd_measure_hbonds(app, argc-1, objv+1, interp);
03474   else if (!strupncmp(argv1, "inverse", CMDLEN)) 
03475     return vmd_measure_inverse(argc-1, objv+1, interp);
03476   else if (!strupncmp(argv1, "inertia", CMDLEN)) 
03477     return vmd_measure_inertia(app, argc-1, objv+1, interp);
03478   else if (!strupncmp(argv1, "rgyr", CMDLEN))
03479     return vmd_measure_rgyr(app, argc-1, objv+1, interp);
03480   else if (!strupncmp(argv1, "rmsd", CMDLEN))
03481     return vmd_measure_rmsd(app, argc-1, objv+1, interp);
03482   else if (!strupncmp(argv1, "rmsf", CMDLEN))
03483     return vmd_measure_rmsf(app, argc-1, objv+1, interp);
03484   else if (!strupncmp(argv1, "sasa", CMDLEN))
03485     return vmd_measure_sasa(app, argc-1, objv+1, interp);
03486   else if (!strupncmp(argv1, "sumweights", CMDLEN))
03487     return vmd_measure_sumweights(app, argc-1, objv+1, interp);
03488   else if (!strupncmp(argv1, "imprp", CMDLEN))
03489     return vmd_measure_dihed(app, argc-1, objv+1, interp);
03490   else if (!strupncmp(argv1, "dihed", CMDLEN))
03491     return vmd_measure_dihed(app, argc-1, objv+1, interp);
03492   else if (!strupncmp(argv1, "angle", CMDLEN))
03493     return vmd_measure_angle(app, argc-1, objv+1, interp);
03494   else if (!strupncmp(argv1, "bond", CMDLEN))
03495     return vmd_measure_bond(app, argc-1, objv+1, interp);
03496   else if (!strupncmp(argv1, "energy", CMDLEN))
03497     return vmd_measure_energy(app, argc-1, objv+1, interp);
03498   else if (!strupncmp(argv1, "pbc2onc", CMDLEN))
03499     return vmd_measure_pbc2onc_transform(app, argc-1, objv+1, interp);
03500   else if (!strupncmp(argv1, "pbcneighbors", CMDLEN))
03501     return vmd_measure_pbc_neighbors(app, argc-1, objv+1, interp);
03502   else if (!strupncmp(argv1, "surface", CMDLEN))
03503     return vmd_measure_surface(app, argc-1, objv+1, interp);
03504   else if (!strupncmp(argv1, "transoverlap", CMDLEN))
03505     return vmd_measure_trans_overlap(app, argc-1, objv+1, interp);
03506   else if (!strupncmp(argv1, "symmetry", CMDLEN))
03507     return vmd_measure_symmetry(app, argc-1, objv+1, interp);
03508 
03509   Tcl_SetResult(interp, (char *) "Type 'measure' for summary of usage\n", TCL_VOLATILE);
03510   return TCL_OK;
03511 }
03512 
03513 
03514 

Generated on Fri May 24 01:49:13 2013 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002