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

TclMeasure.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 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: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.185 $      $Date: 2021/09/23 15:25:55 $
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 <math.h>
00027 #include "TclCommands.h"
00028 #include "AtomSel.h"
00029 #include "Matrix4.h"
00030 #include "SymbolTable.h"
00031 #include "VMDApp.h"
00032 #include "MoleculeList.h"
00033 #include "utilities.h"
00034 #include "config.h"
00035 #include "Measure.h"
00036 #include "MeasureSymmetry.h"
00037 #include "SpatialSearch.h"
00038 #include "Atom.h"
00039 #include "Molecule.h"
00040 
00041 // needed by VolInterior commands
00042 #include "QuickSurf.h"
00043 #include "MDFF.h"
00044 #include "CUDAMDFF.h"
00045 #include "MeasureVolInterior.h"
00046 
00047 
00048 // Get weights from a Tcl obj.  Data must hold sel->selected items, or natoms.
00049 // If there is no obj, or if it's "none", return a list of ones.
00050 // If the obj matches an atom selection keyword/field, and the field returns 
00051 // floating point data, return that data, otherwise return error.
00052 // Otherwise, the obj must be a list of floats to use for the weights,
00053 // with either a size == sel->selected,  or size == natoms.  
00054 //
00055 // NOTE: this routine cannot be used on selections that change between frames.
00056 //
00057 int tcl_get_weights(Tcl_Interp *interp, VMDApp *app, AtomSel *sel, 
00058                     Tcl_Obj *weight_obj, float *data) {
00059   char *weight_string = NULL;
00060 
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 MEASURE_NOERR;
00071   }
00072 
00073   // if a selection string was given, check the symbol table
00074   SymbolTable *atomSelParser = app->atomSelParser; 
00075 
00076   // weights must return floating point values, so the symbol must not 
00077   // be a singleword, so macro is NULL.
00078   atomsel_ctxt context(atomSelParser, 
00079                        app->moleculeList->mol_from_id(sel->molid()), 
00080                        sel->which_frame, NULL);
00081 
00082   int fctn = atomSelParser->find_attribute(weight_string);
00083   if (fctn >= 0) {
00084     // the keyword exists, so get the data if it is type float, otherwise fail
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 
00091     double *tmp_data = new double[sel->num_atoms];
00092     atomSelParser->fctns.data(fctn)->keyword_double(&context, 
00093                               sel->num_atoms, tmp_data, sel->on);
00094 
00095     for (int i=sel->firstsel, j=0; i<=sel->lastsel; i++) {
00096       if (sel->on[i])
00097         data[j++] = (float)tmp_data[i];
00098     }
00099 
00100     delete [] tmp_data;
00101     return MEASURE_NOERR;
00102   }
00103 
00104   // Determine if weights are a Tcl list with the right number of atoms
00105   int list_num;
00106   Tcl_Obj **list_data;
00107   if (Tcl_ListObjGetElements(interp, weight_obj, &list_num, &list_data) 
00108       != TCL_OK) {
00109     return MEASURE_ERR_BADWEIGHTPARM;
00110   }
00111   if (list_num != sel->selected && list_num != sel->num_atoms) 
00112     return MEASURE_ERR_BADWEIGHTNUM;
00113   
00114   for (int i=0, j=0; i<list_num; i++) {
00115     double tmp_data;
00116 
00117     if (Tcl_GetDoubleFromObj(interp, list_data[i], &tmp_data) != TCL_OK) 
00118       return MEASURE_ERR_NONNUMBERPARM;
00119 
00120     if (list_num == sel->selected) {
00121       data[i] = (float)tmp_data; // one weight list item per selected atom
00122     } else {
00123       if (sel->on[i]) {
00124         data[j++] = (float)tmp_data; // one weight list item for each atom
00125       }
00126     }
00127   }
00128 
00129   return MEASURE_NOERR;
00130 }
00131 
00132 
00133 int atomsel_default_weights(AtomSel *sel, float *weights) {
00134   if (sel->firstsel > 0)
00135     memset(&weights[0], 0, sel->firstsel * sizeof(float)); 
00136 
00137   for (int i=sel->firstsel; i<=sel->lastsel; i++) {
00138     // Use the standard "on" check instead of typecasting the array elements
00139     weights[i] = sel->on[i] ? 1.0f : 0.0f;
00140   }
00141 
00142   if (sel->lastsel < (sel->num_atoms - 1))
00143     memset(&weights[sel->lastsel+1], 0, ((sel->num_atoms - 1) - sel->lastsel) * sizeof(float)); 
00144 
00145   return 0;
00146 }
00147 
00148 
00149 int get_weights_from_tcl_list(Tcl_Interp *interp, VMDApp *app, AtomSel *sel,
00150                               Tcl_Obj *weights_obj, float *weights) {
00151   int list_num = 0;
00152   Tcl_Obj **list_elems = NULL;
00153   if (Tcl_ListObjGetElements(interp, weights_obj, &list_num, &list_elems)
00154       != TCL_OK) {
00155     return MEASURE_ERR_BADWEIGHTPARM;
00156   }
00157   if (list_num != sel->num_atoms) {
00158     return MEASURE_ERR_BADWEIGHTNUM;
00159   }
00160   for (int i = 0; i < sel->num_atoms; i++) {
00161     double tmp_data = 0.0;
00162     if (Tcl_GetDoubleFromObj(interp, list_elems[i], &tmp_data) != TCL_OK) {
00163       return TCL_ERROR;
00164     }
00165     weights[i] = static_cast<float>(tmp_data);
00166   }
00167   return 0;
00168 }
00169 
00170 
00171 // This routine eliminates the need to include SymbolTable.h,
00172 // and allows the caller to determine if a per-atom attribute/field
00173 // exists or not.
00174 int get_attribute_index(VMDApp *app, char const *string) {
00175   SymbolTable *atomSelParser = app->atomSelParser;
00176   return atomSelParser->find_attribute(string);
00177 }
00178 
00179 
00180 int get_weights_from_attribute(VMDApp *app, AtomSel *sel,
00181                                char const *weights_string, float *weights) {
00182   SymbolTable *atomSelParser = app->atomSelParser;
00183   int fctn = atomSelParser->find_attribute(weights_string);
00184   // first, check to see that the function returns floats.
00185   // if it doesn't, it makes no sense to use it as a weight
00186   if (atomSelParser->fctns.data(fctn)->returns_a !=
00187       SymbolTableElement::IS_FLOAT) {
00188     return MEASURE_ERR_BADWEIGHTPARM;  // can't understand weight parameter
00189   }
00190   atomsel_ctxt context(atomSelParser,
00191                        app->moleculeList->mol_from_id(sel->molid()),
00192                        sel->which_frame, NULL);
00193   double *tmp_data = new double[sel->num_atoms];
00194   atomSelParser->fctns.data(fctn)->keyword_double(&context, sel->num_atoms,
00195                                                   tmp_data, sel->on);
00196 
00197   if (sel->firstsel > 0)
00198     memset(&weights[0], 0, sel->firstsel * sizeof(float)); 
00199 
00200   for (int i=sel->firstsel; i<=sel->lastsel; i++) {
00201     weights[i] = sel->on[i] ? static_cast<float>(tmp_data[i]) : 0.0f;
00202   }
00203 
00204   if (sel->lastsel < (sel->num_atoms - 1))
00205     memset(&weights[sel->lastsel+1], 0, ((sel->num_atoms - 1) - sel->lastsel) * sizeof(float)); 
00206 
00207   delete [] tmp_data;
00208   return 0;
00209 }
00210 
00211 
00212 // get the  atom index re-ordering list for use by measure_fit
00213 int tcl_get_orders(Tcl_Interp *interp, int selnum, 
00214                        Tcl_Obj *order_obj, int *data) {
00215   int list_num;
00216   Tcl_Obj **list_data;
00217 
00218   if (Tcl_ListObjGetElements(interp, order_obj, &list_num, &list_data)
00219       != TCL_OK) {
00220     return MEASURE_ERR_NOSEL;
00221   }
00222 
00223   // see if this is a Tcl list with the right number of atom indices
00224   if (list_num != selnum) return MEASURE_ERR_NOSEL;
00225 
00226   for (int i=0; i<list_num; i++) {
00227     if (Tcl_GetIntFromObj(interp, list_data[i], &data[i]) != TCL_OK)
00228       return MEASURE_ERR_NONNUMBERPARM;
00229 
00230     // order indices are 0-based
00231     if (data[i] < 0 || data[i] >= selnum)
00232       return MEASURE_ERR_BADORDERINDEX; // order index is out of range
00233   }
00234 
00235   return MEASURE_NOERR;
00236 }
00237 
00238 
00239 //
00240 // Function:  vmd_measure_center
00241 // Parameters:  <selection>               // computes with weight == 1
00242 // Parameters:  <selection> weight [none | atom field | list] 
00243 //   computes with the weights based on the following:
00244 //       none       => weights all 1
00245 //       atom field => value from atomSelParser (eg
00246 //                     mass  => use weight based on mass
00247 //                     index => use weight based on atom index (0 to n-1)
00248 //             list => use list to get weights for each atom.  
00249 //                     The list can have length == number of selected atoms,
00250 //                     or it can have length == the total number of atoms
00251 // 
00252 //  Examples: 
00253 //     vmd_measure_center atomselect12
00254 //     vmd_measure_center atomselect12 weight mass
00255 //     vmd_measure_center atomselect12 {12 13} [atomselect top "index 2 3"]
00256 //  If no weight is given, no weight term is used (computes center of number)
00257 //
00258 static int vmd_measure_center(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00259   if (argc != 2 && argc != 4 ) {
00260     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [weight <weights>]");
00261     return TCL_ERROR;
00262   }
00263   if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00264     Tcl_SetResult(interp, (char *) "measure center: parameter can only be 'weight'", TCL_STATIC);
00265     return TCL_ERROR;
00266   }
00267   
00268   // get the selection
00269   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
00270   if (!sel) {
00271     Tcl_SetResult(interp, (char *) "measure center: no atom selection", TCL_STATIC);
00272     return TCL_ERROR;
00273   }
00274 
00275   // get the weight
00276   float *weight = new float[sel->selected];
00277   int ret_val=0;
00278   if (argc == 2) {          // only from atom selection, so weight is 1
00279     ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00280   } else {
00281     ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00282   }
00283   if (ret_val < 0) {
00284     Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00285     delete [] weight;
00286     return TCL_ERROR;
00287   }
00288 
00289   // compute the center of "mass"
00290   float com[3];
00291   const float *framepos = sel->coordinates(app->moleculeList);
00292   ret_val = measure_center(sel, framepos, weight, com);
00293   delete [] weight;
00294   if (ret_val < 0) {
00295     Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00296     return TCL_ERROR;
00297   }
00298 
00299   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00300   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[0]));
00301   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[1]));
00302   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(com[2]));
00303   Tcl_SetObjResult(interp, tcl_result);
00304 
00305   return TCL_OK;
00306 }
00307 
00308 
00309 static int vmd_measure_centerperresidue(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00310   int ret_val = 0;
00311 
00312   // check argument counts for valid combinations
00313   if (argc != 2 && argc != 4 ) {
00314     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [weight <weights>]");
00315     return TCL_ERROR;
00316   }
00317 
00318   // the only valid optional parameter is "weight"
00319   if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2], NULL), "weight")) {
00320     Tcl_SetResult(interp, (char *) "measure centerperresidue: parameter can only be 'weight'", TCL_STATIC);
00321     return TCL_ERROR;
00322   }
00323   
00324   // get the selection
00325   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
00326   if (!sel) {
00327     Tcl_SetResult(interp, (char *) "measure centerperresidue: no atom selection", TCL_STATIC);
00328     return TCL_ERROR;
00329   }
00330 
00331   // get the weight
00332   float *weight = new float[sel->selected];
00333   if (argc == 2) {
00334     // only from atom selection, so weights are all set to 1
00335     ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
00336   } else {
00337     // from a per-atom field or a list
00338     ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00339   }
00340   if (ret_val < 0) {
00341     Tcl_AppendResult(interp, "measure centerperresidue: ", 
00342                      measure_error(ret_val), NULL);
00343     delete [] weight;
00344     return TCL_ERROR;
00345   }
00346 
00347   // compute the center of "mass"
00348   float *com = new float[3*sel->selected];
00349   const float *framepos = sel->coordinates(app->moleculeList);
00350   ret_val = measure_center_perresidue(app->moleculeList, sel, 
00351                                       framepos, weight, com);
00352   delete [] weight;
00353   if (ret_val < 0) {
00354     Tcl_AppendResult(interp, "measure centerperresidue: ", 
00355                      measure_error(ret_val), NULL);
00356     return TCL_ERROR;
00357   }
00358 
00359   // generate lists of CoMs from results
00360   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00361   for (int i=0; i<ret_val; i++) {
00362     Tcl_Obj *m = Tcl_NewListObj(0, NULL);
00363     for (int j=0; j<3; j++) {
00364       Tcl_ListObjAppendElement(interp, m, Tcl_NewDoubleObj(com[3*i+j]));
00365     }
00366     Tcl_ListObjAppendElement(interp, tcl_result, m);
00367   }
00368   Tcl_SetObjResult(interp, tcl_result);
00369   delete [] com;
00370 
00371   return TCL_OK;
00372 }
00373 
00374 
00375 // measure sum of weights for selected atoms
00376 static int vmd_measure_sumweights(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
00377   if (argc != 4) {
00378     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> weight <weights>");
00379     return TCL_ERROR;
00380   }
00381   if (strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
00382     Tcl_SetResult(interp, (char *) "measure sumweights: parameter can only be 'weight'", TCL_STATIC);
00383     return TCL_ERROR;
00384   }
00385  
00386   // get the selection
00387   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00388 );
00389   if (!sel) {
00390     Tcl_SetResult(interp, (char *) "measure sumweights: no atom selection", TCL_STATIC);
00391     return TCL_ERROR;
00392   }
00393 
00394   // get the weight
00395   float *weight = new float[sel->selected];
00396   int ret_val = 0;
00397   ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
00398   if (ret_val < 0) {
00399     Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00400     delete [] weight;
00401     return TCL_ERROR;
00402   }
00403 
00404   // compute the sum of the weights
00405   float weightsum=0;
00406   ret_val = measure_sumweights(sel, sel->selected, weight, &weightsum);
00407   delete [] weight;
00408   if (ret_val < 0) {
00409     Tcl_AppendResult(interp, "measure center: ", measure_error(ret_val), NULL);
00410     return TCL_ERROR;
00411   }
00412 
00413   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00414   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(weightsum));
00415   Tcl_SetObjResult(interp, tcl_result);
00416 
00417   return TCL_OK;
00418 }
00419 
00420 
00421 // Function: vmd_measure_avpos <selection> first <first> last <last> step <step>
00422 //  Returns: the average position of the selected atoms over the selected frames
00423 //  Example: measure avpos atomselect76 0 20 1
00424 static int vmd_measure_avpos(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00425   int first = 0;  // start with first frame by default
00426   int last = -1;  // finish with last frame by default
00427   int step = 1;   // use all frames by default
00428 
00429   if (argc < 2 || argc > 8) {
00430     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [first <first>] [last <last>] [step <step>]");
00431     return TCL_ERROR;
00432   }
00433   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00434 );
00435   if (!sel) {
00436     Tcl_AppendResult(interp, "measure avpos: no atom selection", NULL);
00437     return TCL_ERROR;
00438   }
00439 
00440   int i;
00441   for (i=2; i<argc; i+=2) {
00442     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00443     if (!strupncmp(argvcur, "first", CMDLEN)) {
00444       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00445         Tcl_AppendResult(interp, "measure avpos: bad first frame value", NULL);
00446         return TCL_ERROR;
00447       }
00448     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00449       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00450         Tcl_AppendResult(interp, "measure avpos: bad last frame value", NULL);
00451         return TCL_ERROR;
00452       }
00453     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00454       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00455         Tcl_AppendResult(interp, "measure avpos: bad frame step value", NULL);
00456         return TCL_ERROR;
00457       }
00458     } else {
00459       Tcl_AppendResult(interp, "measure rmsdmat_qcp: invalid syntax, no such keyword: ", argvcur, NULL);
00460       return TCL_ERROR;
00461     }
00462   }
00463 
00464   float *avpos = new float[3L*sel->selected];
00465   int ret_val = measure_avpos(sel, app->moleculeList, first, last, step, avpos);
00466   if (ret_val < 0) {
00467     Tcl_AppendResult(interp, "measure avpos: ", measure_error(ret_val), NULL);
00468     delete [] avpos;
00469     return TCL_ERROR;
00470   }
00471 
00472   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00473   for (i=0; i<sel->selected; i++) {
00474     Tcl_Obj *atom = Tcl_NewListObj(0, NULL);
00475     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L    ]));
00476     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L + 1]));
00477     Tcl_ListObjAppendElement(interp, atom, Tcl_NewDoubleObj(avpos[i*3L + 2]));
00478     Tcl_ListObjAppendElement(interp, tcl_result, atom);
00479   }
00480 
00481   Tcl_SetObjResult(interp, tcl_result);
00482   delete [] avpos;
00483 
00484   return TCL_OK;
00485 }
00486 
00487 
00488 // Function: vmd_measure_dipole <selection>
00489 //  Returns: the dipole moment for the selected atoms
00490 static int vmd_measure_dipole(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00491   const char *opt;
00492   int unitsdebye=0; // default units are elementary charges/Angstrom
00493   int usecenter=1;  // remove net charge at the center of mass (-1), geometrical center (1), don't (0)
00494 
00495   if ((argc < 2) || (argc > 4)) {
00496     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]");
00497     return TCL_ERROR;
00498   }
00499   AtomSel *sel;
00500   sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
00501   if (!sel) {
00502     Tcl_AppendResult(interp, "measure dipole: no atom selection", NULL);
00503     return TCL_ERROR;
00504   }
00505 
00506   int i;
00507   for (i=0; i < (argc-2); ++i) {
00508     opt = Tcl_GetStringFromObj(objv[2+i], NULL);
00509     if (!strcmp(opt, "-debye"))
00510       unitsdebye=1; 
00511     if (!strcmp(opt, "-elementary"))
00512       unitsdebye=0; 
00513 
00514     if (!strcmp(opt, "-geocenter"))
00515       usecenter=1; 
00516     if (!strcmp(opt, "-masscenter"))
00517       usecenter=-1; 
00518     if (!strcmp(opt, "-origincenter"))
00519       usecenter=0; 
00520   }
00521 
00522   float dipole[3];
00523   int ret_val = measure_dipole(sel, app->moleculeList, dipole, unitsdebye, usecenter);
00524   if (ret_val < 0) {
00525     Tcl_AppendResult(interp, "measure dipole: ", measure_error(ret_val), NULL);
00526     return TCL_ERROR;
00527   }
00528 
00529   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00530   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[0]));
00531   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[1]));
00532   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(dipole[2]));
00533   Tcl_SetObjResult(interp, tcl_result);
00534 
00535   return TCL_OK;
00536 }
00537 
00538 
00539 
00540 // Function: vmd_measure_dihed {4 atoms as {<atomid> ?<molid>?}} ?molid <default molid>?
00541 //                             ?frame [f|all|last]? | ?first <first>? ?last <last>?
00542 //  Returns: the dihedral angle for the specified atoms
00543 static int vmd_measure_dihed(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00544   int first=-1, last=-1, frame=-1;
00545   if (argc < 2) {
00546     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>]");
00547     return TCL_ERROR;
00548   }
00549 
00550   int molid[4], atmid[4], defmolid = -1;
00551   bool allframes = false;
00552 
00553   // Get the geometry type dihed/imprp
00554   char *geomname = Tcl_GetStringFromObj(objv[0],NULL);
00555 
00556   // Read the atom list
00557   int numatms;
00558   Tcl_Obj **data;
00559   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00560     Tcl_AppendResult(interp, " measure ", geomname, ": bad syntax", NULL);
00561     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);
00562     return TCL_ERROR;
00563   }
00564 
00565   if (numatms != 4) {
00566     Tcl_AppendResult(interp, " measure dihed: must specify exactly four atoms in a list", NULL);
00567     return TCL_ERROR;
00568   }
00569 
00570   if (argc > 3) {
00571     int i;
00572     for (i=2; i<argc; i+=2) {
00573       char *argvcur = Tcl_GetStringFromObj(objv[i], NULL);
00574       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00575         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00576           Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00577           return TCL_ERROR;
00578         }
00579       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00580         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00581           Tcl_AppendResult(interp, " measure ", geomname, ": bad first frame value", NULL);
00582           return TCL_ERROR;
00583         }
00584       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00585         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00586           Tcl_AppendResult(interp, " measure ", geomname, ": bad last frame value", NULL);
00587           return TCL_ERROR;
00588         }
00589       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00590         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00591           allframes = true;
00592         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00593           frame=-2;
00594         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00595           Tcl_AppendResult(interp, " measure ", geomname, ": bad frame value", NULL);
00596           return TCL_ERROR;
00597         }
00598       } else {
00599         Tcl_AppendResult(interp, "measure ", geomname, ": invalid syntax, no such keyword: ", argvcur, NULL);
00600         return TCL_ERROR;
00601       }
00602     }
00603   }
00604 
00605   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00606     Tcl_AppendResult(interp, "measure ", geomname, ": Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00607     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);
00608     return TCL_ERROR;    
00609   }
00610 
00611   if (allframes) first=0;
00612 
00613   // If no molecule was specified use top as default
00614   if (defmolid<0) defmolid = app->molecule_top();
00615 
00616   // Assign atom IDs and molecule IDs
00617   int i,numelem;
00618   Tcl_Obj **atmmol;
00619   for (i=0; i<numatms; i++) {
00620     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00621       return TCL_ERROR;
00622     }
00623 
00624     if (!numelem) {
00625       Tcl_AppendResult(interp, " measure ", geomname, ": empty atom index", NULL);
00626       return TCL_ERROR;
00627     }
00628 
00629     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00630       Tcl_AppendResult(interp, " measure ", geomname, ": bad atom index", NULL);
00631       return TCL_ERROR;
00632     }
00633     
00634     if (numelem==2) {
00635       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00636         Tcl_AppendResult(interp, " measure ", geomname, ": bad molid", NULL);
00637         return TCL_ERROR;
00638       }
00639     } else molid[i] = defmolid;
00640   }
00641 
00642   // Compute the value
00643   ResizeArray<float> gValues(1024);
00644   int ret_val;
00645   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00646                          frame, first, last, defmolid, MEASURE_DIHED);
00647   if (ret_val < 0) {
00648     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00649     return TCL_ERROR;
00650   }
00651 
00652   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00653   int numvalues = gValues.num();
00654   for (int count = 0; count < numvalues; count++) {
00655     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00656   }
00657   Tcl_SetObjResult(interp, tcl_result);
00658 
00659   return TCL_OK;
00660 }
00661 
00662 
00663 // Function: vmd_measure_angle {3 atoms as {<atomid> ?<molid>?}} ?molid <default molid>? 
00664 //                             ?frame [f|all|last]? | ?first <first>? ?last <last>?
00665 //  Returns: the bond angle for the specified atoms
00666 static int vmd_measure_angle(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00667   int first=-1, last=-1, frame=-1;
00668 
00669   if(argc<2) {
00670     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]");
00671     return TCL_ERROR;
00672   }
00673 
00674   int molid[3], atmid[3], defmolid = -1;
00675   bool allframes = false;
00676 
00677   // Read the atom list
00678   int numatms;
00679   Tcl_Obj **data;
00680   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00681     Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00682     Tcl_AppendResult(interp, " Usage: measure angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00683     return TCL_ERROR;
00684   }
00685 
00686   if (numatms != 3) {
00687     Tcl_AppendResult(interp, " measure angle: must specify exactly three atoms in a list", NULL);
00688     return TCL_ERROR;
00689   }
00690 
00691   if (argc > 3) {
00692     for (int i=2; i<argc; i+=2) {
00693       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00694       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00695         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00696           Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00697           return TCL_ERROR;
00698         }
00699       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00700         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00701           Tcl_AppendResult(interp, " measure angle: bad first frame value", NULL);
00702           return TCL_ERROR;
00703         }
00704       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00705         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00706           Tcl_AppendResult(interp, " measure angle: bad last frame value", NULL);
00707           return TCL_ERROR;
00708         }
00709       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00710         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00711           allframes = true;
00712         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00713           frame=-2;
00714         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00715           Tcl_AppendResult(interp, " measure angle: bad frame value", NULL);
00716           return TCL_ERROR;
00717         }
00718       } else {
00719         Tcl_AppendResult(interp, " measure angle: invalid syntax, no such keyword: ", argvcur, NULL);
00720         return TCL_ERROR;
00721       }
00722     }
00723   }
00724 
00725   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00726     Tcl_AppendResult(interp, "measure angle: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00727     Tcl_AppendResult(interp, "\nUsage:\nmeasure angle {<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00728     return TCL_ERROR;    
00729   }
00730 
00731   if (allframes) first=0;
00732 
00733   // If no molecule was specified use top as default
00734   if (defmolid<0) defmolid = app->molecule_top();
00735 
00736   // Assign atom IDs and molecule IDs
00737   int i,numelem;
00738   Tcl_Obj **atmmol;
00739   for (i=0; i<numatms; i++) {
00740     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00741       return TCL_ERROR;
00742     }
00743 
00744     if (!numelem) {
00745       Tcl_AppendResult(interp, " measure angle: empty atom index", NULL);
00746       return TCL_ERROR;
00747     }
00748 
00749     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00750       Tcl_AppendResult(interp, " measure angle: bad atom index", NULL);
00751       return TCL_ERROR;
00752     }
00753     
00754     if (numelem==2) {
00755       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00756         Tcl_AppendResult(interp, " measure angle: bad molid", NULL);
00757         return TCL_ERROR;
00758       }
00759     } else molid[i] = defmolid;
00760   }
00761 
00762   // Compute the value
00763   ResizeArray<float> gValues(1024);
00764   int ret_val;
00765   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00766                          frame, first, last, defmolid, MEASURE_ANGLE);
00767   if (ret_val<0) {
00768     printf("ERROR\n %s\n", measure_error(ret_val));
00769     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00770     return TCL_ERROR;
00771   }
00772 
00773   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00774   int numvalues = gValues.num();
00775   for (int count = 0; count < numvalues; count++) {
00776     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00777   }
00778   Tcl_SetObjResult(interp, tcl_result);
00779 
00780   return TCL_OK;
00781 }
00782 
00783 
00784 // vmd_measure_bond {2 atoms as {<atomid> ?<molid>?}} ?molid <molid>? ?frame [f|all|last]? | ?first <first>? ?last <last>?
00785 // Returns the bond length for the specified atoms
00786 static int vmd_measure_bond(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00787   int first=-1, last=-1, frame=-1;
00788 
00789   if (argc<2) {
00790     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "{{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default molid>] [frame <frame|all|last> | first <first> last <last>]]");
00791     return TCL_ERROR;
00792   }
00793   
00794   int molid[2], atmid[2], defmolid = -1;
00795   bool allframes = false;
00796 
00797   // Read the atom list
00798   int numatms=0;
00799   Tcl_Obj **data;
00800   if (Tcl_ListObjGetElements(interp, objv[1], &numatms, &data) != TCL_OK) {
00801     Tcl_AppendResult(interp, " measure bond: bad syntax", NULL);
00802     Tcl_AppendResult(interp, " Usage: measure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]]", NULL);
00803     return TCL_ERROR;
00804   }
00805 
00806   if (numatms != 2) {
00807     Tcl_AppendResult(interp, " measure bond: must specify exactly two atoms in a list", NULL);
00808     return TCL_ERROR;
00809   }
00810 
00811   if (argc > 3) {
00812     for (int i=2; i<argc; i+=2) {
00813       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00814       if (!strupncmp(argvcur, "molid", CMDLEN)) {
00815         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
00816           Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00817           return TCL_ERROR;
00818         }
00819       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
00820         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00821           Tcl_AppendResult(interp, " measure bond: bad first frame value", NULL);
00822           return TCL_ERROR;
00823         }
00824       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00825         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00826           Tcl_AppendResult(interp, " measure bond: bad last frame value", NULL);
00827           return TCL_ERROR;
00828         }
00829       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
00830         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
00831           allframes = true;
00832         } else if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
00833           frame=-2;
00834         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
00835           Tcl_AppendResult(interp, " measure bond: bad frame value", NULL);
00836           return TCL_ERROR;
00837         }
00838       } else {
00839         Tcl_AppendResult(interp, " measure bond: invalid syntax, no such keyword: ", argvcur, NULL);
00840         return TCL_ERROR;
00841       }
00842     }
00843   }
00844 
00845   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
00846     Tcl_AppendResult(interp, "measure bond: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
00847     Tcl_AppendResult(interp, "\nUsage:\nmeasure bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}} [molid <default>] [frame <frame|all|last> | first <first> last <last>]", NULL);
00848     return TCL_ERROR;    
00849   }
00850 
00851   if (allframes) first=0;
00852 
00853   // If no molecule was specified use top as default
00854   if (defmolid<0) defmolid = app->molecule_top();
00855 
00856   // Assign atom IDs and molecule IDs
00857   int i, numelem;
00858   Tcl_Obj **atmmol;
00859   for (i=0; i<numatms; i++) {
00860     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
00861       return TCL_ERROR;
00862     }
00863 
00864     if (!numelem) {
00865       Tcl_AppendResult(interp, " measure bond: empty atom index", NULL);
00866       return TCL_ERROR;
00867     }
00868 
00869     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
00870       Tcl_AppendResult(interp, " measure bond: bad atom index", NULL);
00871       return TCL_ERROR;
00872     }
00873     
00874     if (numelem==2) {
00875       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
00876         Tcl_AppendResult(interp, " measure bond: bad molid", NULL);
00877         return TCL_ERROR;
00878       }
00879     } else molid[i] = defmolid;
00880   }
00881 
00882   // Compute the value
00883   ResizeArray<float> gValues(1024);
00884   int ret_val;
00885   ret_val = measure_geom(app->moleculeList, molid, atmid, &gValues, 
00886                          frame, first, last, defmolid, MEASURE_BOND);
00887   if (ret_val < 0) {
00888     printf("ERROR\n %s\n", measure_error(ret_val));
00889     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
00890     return TCL_ERROR;
00891   }
00892 
00893   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00894   int numvalues = gValues.num();
00895   for (int count = 0; count < numvalues; count++) {
00896     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
00897   }
00898   Tcl_SetObjResult(interp, tcl_result);
00899 
00900   return TCL_OK;
00901 }
00902 
00903 
00904 // vmd_measure_rmsfperresidue <selection> first <first> last <last> step <step>
00905 // Returns: position variance of the selected atoms over the selected frames, 
00906 // returning one value per residue in the selection.
00907 // Example: measure rmsfperresidue atomselect76 0 20 1
00908 static int vmd_measure_rmsfperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00909   int first = 0;  // start with first frame by default
00910   int last = -1;  // finish with last frame by default
00911   int step = 1;   // use all frames by default
00912 
00913   if (argc < 2 || argc > 8) {
00914     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [first <first>] [last <last>] [step <step>]");
00915     return TCL_ERROR;
00916   }
00917   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00918 );
00919   if (!sel) {
00920     Tcl_AppendResult(interp, "measure rmsfperresidue: no atom selection", NULL);
00921     return TCL_ERROR;
00922   }
00923  
00924   int i;
00925   for (i=2; i<argc; i+=2) {
00926     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00927     if (!strupncmp(argvcur, "first", CMDLEN)) {
00928       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00929         Tcl_AppendResult(interp, "measure rmsfperresidue: bad first frame value", NULL);
00930         return TCL_ERROR;
00931       }
00932     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00933       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00934         Tcl_AppendResult(interp, "measure rmsfperresidue: bad last frame value", NULL);
00935         return TCL_ERROR;
00936       }
00937     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
00938       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
00939         Tcl_AppendResult(interp, "measure rmsfperresidue: bad frame step value", NULL);
00940         return TCL_ERROR;
00941       }
00942     } else {
00943       Tcl_AppendResult(interp, "measure rmsfperresidue: invalid syntax, no such keyword: ", argvcur, NULL);
00944       return TCL_ERROR;
00945     }
00946   }
00947 
00948   float *rmsf = new float[sel->selected];
00949   int ret_val = measure_rmsf_perresidue(sel, app->moleculeList, first, last, step, rmsf);
00950   if (ret_val < 0) {
00951     Tcl_AppendResult(interp, "measure rmsfperresidue: ", measure_error(ret_val), NULL);
00952     delete [] rmsf;
00953     return TCL_ERROR;
00954   }
00955 
00956   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
00957   for (i=0; i<ret_val; i++) {
00958     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsf[i]));
00959   }
00960   Tcl_SetObjResult(interp, tcl_result);
00961   delete [] rmsf;
00962 
00963   return TCL_OK;
00964 }
00965 
00966 
00967 // Function: vmd_measure_rmsf <selection> first <first> last <last> step <step>
00968 //  Returns: position variance of the selected atoms over the selected frames
00969 //  Example: measure rmsf atomselect76 0 20 1
00970 static int vmd_measure_rmsf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
00971   int first = 0;  // start with first frame by default
00972   int last = -1;  // finish with last frame by default
00973   int step = 1;   // use all frames by default
00974 
00975   if (argc < 2 || argc > 8) {
00976     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<sel> [first <first>] [last <last>] [step <step>]");
00977     return TCL_ERROR;
00978   }
00979   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
00980 );
00981   if (!sel) {
00982     Tcl_AppendResult(interp, "measure rmsf: no atom selection", NULL);
00983     return TCL_ERROR;
00984   }
00985  
00986   int i;
00987   for (i=2; i<argc; i+=2) {
00988     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
00989     if (!strupncmp(argvcur, "first", CMDLEN)) {
00990       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
00991         Tcl_AppendResult(interp, "measure rmsf: bad first frame value", NULL);
00992         return TCL_ERROR;
00993       }
00994     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
00995       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
00996         Tcl_AppendResult(interp, "measure rmsf: bad last frame value", NULL);
00997         return TCL_ERROR;
00998       }
00999     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01000       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01001         Tcl_AppendResult(interp, "measure rmsf: bad frame step value", NULL);
01002         return TCL_ERROR;
01003       }
01004     } else {
01005       Tcl_AppendResult(interp, "measure rmsf: invalid syntax, no such keyword: ", argvcur, NULL);
01006       return TCL_ERROR;
01007     }
01008   }
01009 
01010   float *rmsf = new float[sel->selected];
01011   int ret_val = measure_rmsf(sel, app->moleculeList, first, last, step, rmsf);
01012   if (ret_val < 0) {
01013     Tcl_AppendResult(interp, "measure rmsf: ", measure_error(ret_val), NULL);
01014     delete [] rmsf;
01015     return TCL_ERROR;
01016   }
01017 
01018   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01019   for (i=0; i<sel->selected; i++) {
01020     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsf[i]));
01021   }
01022   Tcl_SetObjResult(interp, tcl_result);
01023 
01024   delete [] rmsf;
01025   return TCL_OK;
01026 }
01027 
01028 
01029 // measure radius of gyration for selected atoms
01030 static int vmd_measure_rgyr(VMDApp *app, int argc, Tcl_Obj *const objv[], Tcl_Interp *interp) {
01031   if (argc != 2 && argc != 4) {
01032     Tcl_WrongNumArgs(interp, 2, objv-1,
01033                      (char *)"<selection> [weight <weights>]");
01034     return TCL_ERROR;
01035   }
01036   if (argc == 4 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "weight")) {
01037     Tcl_SetResult(interp, (char *) "measure rgyr: parameter can only be 'weight'", TCL_STATIC);
01038     return TCL_ERROR;
01039   }
01040 
01041   // get the selection
01042   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01043   if (!sel) {
01044     Tcl_SetResult(interp, (char *) "measure rgyr: no atom selection", TCL_STATIC);
01045     return TCL_ERROR;
01046   }
01047 
01048   // get the weight
01049   float *weight = new float[sel->selected];
01050   int ret_val = 0;
01051   if (argc == 2) {          // only from atom selection, so weight is 1
01052     ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01053   } else {
01054     ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01055   }
01056   if (ret_val < 0) {
01057     Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val), NULL);
01058     delete [] weight;
01059     return TCL_ERROR;
01060   }
01061 
01062   float rgyr;
01063   ret_val = measure_rgyr(sel, app->moleculeList, weight, &rgyr);
01064   delete [] weight;
01065   if (ret_val < 0) {
01066     Tcl_AppendResult(interp, "measure rgyr: ", measure_error(ret_val), NULL);
01067     return TCL_ERROR;
01068   }
01069   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rgyr));
01070   return TCL_OK;
01071 }
01072 
01073 
01074 // Function: vmd_measure_minmax <selection>
01075 //  Returns: the cartesian range of a selection (min/max){x,y,z}
01076 //  Example: vmd_measure_minmax atomselect76
01077 //     {-5 0 0} {15 10 11.2}
01078 static int vmd_measure_minmax(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01079   const float *radii = NULL;
01080   if (argc != 2 && argc != 3) {
01081     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<selection> [-withradii]");
01082     return TCL_ERROR;
01083   }
01084   if (argc == 3 && strcmp(Tcl_GetStringFromObj(objv[2],NULL), "-withradii")) {
01085     Tcl_SetResult(interp, (char *) "measure minmax: parameter can only be '-withradii'", TCL_STATIC);
01086     return TCL_ERROR;
01087   }
01088 
01089   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01090   if (!sel) {
01091     Tcl_AppendResult(interp, "measure minmax: no atom selection", NULL);
01092     return TCL_ERROR;
01093   }
01094 
01095   float min_coord[3], max_coord[3];
01096   const float *framepos = sel->coordinates(app->moleculeList);
01097   if (!framepos) return TCL_ERROR;
01098 
01099   // get atom radii if requested
01100   if (argc == 3) {
01101     Molecule *mol = app->moleculeList->mol_from_id(sel->molid());
01102     radii = mol->extraflt.data("radius");
01103   } 
01104 
01105   int ret_val = measure_minmax(sel->num_atoms, sel->on, framepos, radii, 
01106                                min_coord, max_coord);
01107   if (ret_val < 0) {
01108     Tcl_AppendResult(interp, "measure minmax: ", measure_error(ret_val), NULL);
01109     return TCL_ERROR;
01110   }
01111 
01112   Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
01113   Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
01114   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01115 
01116   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[0]));
01117   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[1]));
01118   Tcl_ListObjAppendElement(interp, list1, Tcl_NewDoubleObj(min_coord[2]));
01119 
01120   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[0]));
01121   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[1]));
01122   Tcl_ListObjAppendElement(interp, list2, Tcl_NewDoubleObj(max_coord[2]));
01123 
01124   Tcl_ListObjAppendElement(interp, tcl_result, list1);
01125   Tcl_ListObjAppendElement(interp, tcl_result, list2);
01126   Tcl_SetObjResult(interp, tcl_result);
01127 
01128   return TCL_OK;
01129 }
01130 
01131 
01132 static int vmd_measure_rmsdperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01133   if (argc !=3 && argc != 5) {
01134     Tcl_WrongNumArgs(interp, 2, objv-1, 
01135                      (char *)"<sel1> <sel2> [weight <weights>]");
01136     return TCL_ERROR;
01137   }
01138 
01139   // get the selections
01140   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01141   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01142   if (!sel1 || !sel2) {
01143     Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01144     return TCL_ERROR;
01145   }
01146 
01147   if (sel1->selected != sel2->selected) {
01148     Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01149     return TCL_ERROR;
01150   }
01151   if (!sel1->selected) {
01152     Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01153     return TCL_ERROR;
01154   }
01155   float *weight = new float[sel1->selected];
01156   int ret_val = 0;
01157   if (argc == 3) {
01158     ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01159   } else {
01160     ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01161   }
01162   if (ret_val < 0) {
01163     Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01164     delete [] weight;
01165     return TCL_ERROR;
01166   }
01167 
01168   // compute the rmsd
01169   float *rmsd = new float[sel1->selected];
01170   int rc = measure_rmsd_perresidue(sel1, sel2, app->moleculeList, 
01171                                    sel1->selected, weight, rmsd);
01172   if (rc < 0) {
01173     Tcl_AppendResult(interp, "measure rmsd: ", measure_error(rc), NULL);
01174     delete [] weight;
01175     delete [] rmsd;
01176     return TCL_ERROR;
01177   }
01178   delete [] weight;
01179   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01180   int i;
01181   for (i=0; i<rc; i++) {
01182     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(rmsd[i]));
01183   }
01184   delete [] rmsd;
01185   Tcl_SetObjResult(interp, tcl_result);
01186 
01187   return TCL_OK;
01188 }
01189 
01190 // Function: vmd_measure_rmsd <selection1> <selection2> 
01191 //                     {weight [none|atom value|string array]}
01192 //
01193 // Returns the RMSD between the two selection, taking the weight (if
01194 // any) into account.  If number of elements in the weight != num_atoms
01195 // in sel1 then (num in weight = num selected in sel1 = num selected in
01196 // sel2) else (num in weight = total num in sel1 = total num in sel2).
01197 // The weights are taken from the FIRST selection, if needed
01198 // 
01199 // Examples:
01200 //   set sel1 [atomselect 0 all]
01201 //   set sel2 [atomselect 1 all]
01202 //   measure rmsd $sel1 $sel2
01203 //   measure rmsd $sel1 $sel2 weight mass
01204 //   set sel3 [atomselect 0 "index 3 4 5"]
01205 //   set sel4 [atomselect 1 "index 8 5 9"]    # gets turned to 5 8 9
01206 //   measure rmsd $sel3 $sel4 weight occupancy
01207 //
01208 static int vmd_measure_rmsd(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
01209 {
01210   if (argc !=3 && argc != 5) {
01211     Tcl_WrongNumArgs(interp, 2, objv-1, 
01212       (char *)"<sel1> <sel2> [weight <weights>]");
01213     return TCL_ERROR;
01214   }
01215 
01216   // get the selections
01217   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01218   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01219   if (!sel1 || !sel2) {
01220     Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01221     return TCL_ERROR;
01222   }
01223 
01224   if (sel1->selected != sel2->selected) {
01225     Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01226     return TCL_ERROR;
01227   }
01228   if (!sel1->selected) {
01229     Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01230     return TCL_ERROR;
01231   }
01232 
01233   float *weight = new float[sel1->selected];
01234   int ret_val = 0;
01235   if (argc == 3) {
01236     ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01237   } else {
01238     ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01239   }
01240   if (ret_val < 0) {
01241     Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01242     delete [] weight;
01243     return TCL_ERROR;
01244   }
01245 
01246   // compute the rmsd
01247   float rmsd = 0;
01248   const float *x = sel1->coordinates(app->moleculeList);
01249   const float *y = sel2->coordinates(app->moleculeList);
01250   if (!x || !y) {
01251     delete [] weight;
01252     return TCL_ERROR;
01253   }
01254 
01255   ret_val = measure_rmsd(sel1, sel2, sel1->selected, x, y, weight, &rmsd);
01256   delete [] weight;
01257   if (ret_val < 0) {
01258     Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01259     return TCL_ERROR;
01260   }
01261   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rmsd));
01262 
01263   return TCL_OK;
01264 }
01265 
01266 
01268 // measure rmsd_qcp $sel1 $sel2 [weight <weights>]
01269 static int vmd_measure_rmsd_qcp(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01270   if (argc !=3 && argc != 5) {
01271     Tcl_WrongNumArgs(interp, 2, objv-1, 
01272       (char *)"<sel1> <sel2> [weight <weights>]");
01273     return TCL_ERROR;
01274   }
01275   // get the selections
01276   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01277   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01278   if (!sel1 || !sel2) {
01279     Tcl_AppendResult(interp, "measure rmsd: no atom selection", NULL);
01280     return TCL_ERROR;
01281   }
01282 
01283   if (sel1->selected != sel2->selected) {
01284     Tcl_AppendResult(interp, "measure rmsd: selections must have the same number of atoms", NULL);
01285     return TCL_ERROR;
01286   }
01287   if (!sel1->selected) {
01288     Tcl_AppendResult(interp, "measure rmsd: no atoms selected", NULL);
01289     return TCL_ERROR;
01290   }
01291   float *weight = new float[sel1->selected];
01292   {
01293     int ret_val;
01294     if (argc == 3) {
01295       ret_val = tcl_get_weights(interp, app, sel1, NULL, weight);
01296     } else {
01297       ret_val = tcl_get_weights(interp, app, sel1, objv[4], weight);
01298     }
01299     if (ret_val < 0) {
01300       Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01301       delete [] weight;
01302       return TCL_ERROR;
01303     }
01304   }
01305 
01306   // compute the rmsd
01307   {
01308     float rmsd = 0;
01309     const float *x = sel1->coordinates(app->moleculeList);
01310     const float *y = sel2->coordinates(app->moleculeList);
01311     if (!x || !y) {
01312       delete [] weight;
01313       return TCL_ERROR;
01314     }
01315     int ret_val = measure_rmsd_qcp(app, sel1, sel2, sel1->selected, x, y, weight, &rmsd);
01316     delete [] weight;
01317     if (ret_val < 0) {
01318       Tcl_AppendResult(interp, "measure rmsd: ", measure_error(ret_val), NULL);
01319       return TCL_ERROR;
01320     }
01321     Tcl_SetObjResult(interp, Tcl_NewDoubleObj(rmsd));
01322   }
01323   return TCL_OK;
01324 }
01325 
01326 
01328 // measure rmsdmat_qcp $sel1 [weight <weights>] start s  end e  step s
01329 static int vmd_measure_rmsdmat_qcp(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01330   int first = 0;  // start with first frame by default
01331   int last = -1;  // finish with last frame by default
01332   int step = 1;   // use all frames by default
01333 
01334   if (argc < 2 || argc > 9) {
01335     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [weight <weights>]  [first <first>] [last <last>] [step <step>]");
01336     return TCL_ERROR;
01337   }
01338   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
01339 );
01340   if (!sel) {
01341     Tcl_AppendResult(interp, "measure rmsdmat_qcp: no atom selection", NULL);
01342     return TCL_ERROR;
01343   }
01344 
01345   int i;
01346   for (i=2; i<argc; i+=2) {
01347     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
01348     if (!strupncmp(argvcur, "first", CMDLEN)) {
01349       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
01350         Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad first frame value", NULL);
01351         return TCL_ERROR;
01352       }
01353     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
01354       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
01355         Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad last frame value", NULL);
01356         return TCL_ERROR;
01357       }
01358     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01359       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01360         Tcl_AppendResult(interp, "measure rmsdmat_qcp: bad frame step value", NULL);
01361         return TCL_ERROR;
01362       }
01363     } else {
01364       Tcl_AppendResult(interp, "measure rmsdmat_qcp: invalid syntax, no such keyword: ", argvcur, NULL);
01365       return TCL_ERROR;
01366     }
01367   }
01368 
01369   float *weight = NULL;
01370   if (0)  {
01371     weight = new float[sel->selected];
01372     int ret_val;
01373     if (argc == 2) {
01374       ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01375     } else {
01376       ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01377     }
01378 
01379     if (ret_val < 0) {
01380       Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val),
01381                        NULL);
01382       delete [] weight;
01383       return TCL_ERROR;
01384     }
01385   }
01386 
01387   //If no last frame is given, set it to be the last frame so the framecount is correct.
01388   if (last < 0)
01389     last = app->molecule_numframes(sel->molid()) - 1;
01390   int framecount = (last - first + 1) / step;
01391   float *rmsdmat = (float *) calloc(1, framecount * framecount * sizeof(float));
01392 
01393   // compute the rmsd matrix
01394   int ret_val = measure_rmsdmat_qcp(app, sel, app->moleculeList, 
01395                                     sel->selected, weight, 
01396                                     first, last, step, rmsdmat);
01397   delete [] weight;
01398 
01399   if (ret_val < 0) {
01400     Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val), NULL);
01401     return TCL_ERROR;
01402   }
01403 
01404   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01405   long j;
01406   for (j=0; j<framecount; j++) {
01407     Tcl_Obj *rmsdlist = Tcl_NewListObj(0, NULL);
01408     for (i=0; i<framecount; i++) {
01409       Tcl_ListObjAppendElement(interp, rmsdlist, 
01410                                Tcl_NewDoubleObj(rmsdmat[j*framecount + i]));
01411     }
01412     Tcl_ListObjAppendElement(interp, tcl_result, rmsdlist);
01413   }
01414   Tcl_SetObjResult(interp, tcl_result);
01415 
01416   free(rmsdmat);
01417 
01418   return TCL_OK;
01419 }
01420 
01421 
01423 // Experimental out-of-core variant 
01424 //
01425 // measure rmsdmat_qcp $sel1 [weight <weights>] start s  end e  step s
01426 static int vmd_measure_rmsdmat_qcp_ooc(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01427   int first = 0;  // start with first frame by default
01428   int last = -1;  // finish with last frame by default
01429   int step = 1;   // use all frames by default
01430   int i;
01431   float *weight = NULL;
01432   int nfiles = 0;
01433   const char **trjfileset = NULL;
01434 
01435   if (argc < 2) {
01436     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [weight <weights>]  [first <first>] [last <last>] [step <step>] files [file1] [fileN]");
01437     return TCL_ERROR;
01438   }
01439   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL)
01440 );
01441   if (!sel) {
01442     Tcl_AppendResult(interp, "measure rmsdmat_qcp: no atom selection", NULL);
01443     return TCL_ERROR;
01444   }
01445 
01446   for (i=2; i<argc; i+=2) {
01447     char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
01448     if (!strupncmp(argvcur, "first", CMDLEN)) {
01449       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
01450         Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad first frame value", NULL);
01451         return TCL_ERROR;
01452       }
01453     } else if (!strupncmp(argvcur, "last", CMDLEN)) {
01454       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
01455         Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad last frame value", NULL);
01456         return TCL_ERROR;
01457       }
01458     } else if (!strupncmp(argvcur, "step", CMDLEN)) {
01459       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK) {
01460         Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad frame step value", NULL);
01461         return TCL_ERROR;
01462       }
01463     } else if (!strupncmp(argvcur, "files", CMDLEN)) {
01464       int list_num;
01465       Tcl_Obj **list_data;
01466       if (Tcl_ListObjGetElements(interp, objv[i+1], &list_num, &list_data) != TCL_OK) {
01467         Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: bad trajectory file list", NULL);
01468         return TCL_ERROR;
01469       }
01470 
01471       int f;
01472       nfiles = list_num;
01473       trjfileset = (const char **) calloc(1, nfiles * sizeof(const char *));
01474       for (f=0; f<nfiles; f++) {
01475         trjfileset[f] = Tcl_GetStringFromObj(list_data[f], NULL);
01476         printf("File[%d] '%s'\n", f, trjfileset[f]);
01477       }
01478     } else {
01479       Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: invalid syntax, no such keyword: ", argvcur, NULL);
01480       return TCL_ERROR;
01481     }
01482   }
01483 
01484 #if 0
01485   if (0)  {
01486     weight = new float[sel->selected];
01487     int ret_val;
01488     if (argc == 2) {
01489       ret_val = tcl_get_weights(interp, app, sel, NULL, weight);
01490     } else {
01491       ret_val = tcl_get_weights(interp, app, sel, objv[3], weight);
01492     }
01493 
01494     if (ret_val < 0) {
01495       Tcl_AppendResult(interp, "measure rmsdmat_qcp: ", measure_error(ret_val),
01496                        NULL);
01497       delete [] weight;
01498       return TCL_ERROR;
01499     }
01500   }
01501 #endif
01502 
01503   // compute the rmsd matrix
01504   float *rmsdmat = NULL;
01505   int framecount = 0; // returned after work is done
01506   int ret_val = measure_rmsdmat_qcp_ooc(app, sel, app->moleculeList, 
01507                                         nfiles, trjfileset,
01508                                         sel->selected, weight, 
01509                                         first, last, step, 
01510                                         framecount, rmsdmat);
01511   delete [] weight;
01512 
01513   if (ret_val < 0) {
01514     Tcl_AppendResult(interp, "measure rmsdmat_qcp_ooc: ", measure_error(ret_val), NULL);
01515     return TCL_ERROR;
01516   }
01517 
01518 #if 0
01519   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01520   long j;
01521   for (j=0; j<framecount; j++) {
01522     Tcl_Obj *rmsdlist = Tcl_NewListObj(0, NULL);
01523     for (i=0; i<framecount; i++) {
01524       Tcl_ListObjAppendElement(interp, rmsdlist, 
01525                                Tcl_NewDoubleObj(rmsdmat[j*framecount + i]));
01526     }
01527     Tcl_ListObjAppendElement(interp, tcl_result, rmsdlist);
01528   }
01529   Tcl_SetObjResult(interp, tcl_result);
01530 #endif
01531 
01532   if (trjfileset != NULL)
01533     free(trjfileset);
01534 
01535   if (rmsdmat != NULL)
01536     free(rmsdmat);
01537 
01538   return TCL_OK;
01539 }
01540 
01541 
01542 
01544 // measure fit $sel1 $sel2 [weight <weights>][
01545 static int vmd_measure_fit(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp)
01546 {
01547   AtomSel *sel1, *sel2;
01548   int *order = NULL;
01549   float *weight = NULL;
01550   int rc;
01551 
01552   if (argc != 3 && argc != 5 
01553       && argc != 7) {
01554     Tcl_WrongNumArgs(interp, 2, objv-1, 
01555        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01556     return TCL_ERROR;
01557   } else if (argc == 5
01558              && strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) 
01559              && strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01560     Tcl_WrongNumArgs(interp, 2, objv-1, 
01561        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01562     return TCL_ERROR;
01563   } else if (argc == 7 && 
01564              (strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL)) || 
01565               strcmp("order", Tcl_GetStringFromObj(objv[5], NULL)))) {
01566     Tcl_WrongNumArgs(interp, 2, objv-1, 
01567        (char *)"<sel1> <sel2> [weight <weights>] [order <index list>]");
01568     return TCL_ERROR;
01569   }
01570 
01571   // get the selections
01572   sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
01573   sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01574 
01575   if (!sel1 || !sel2) {
01576     Tcl_AppendResult(interp, "measure fit: no atom selection", NULL);
01577     return TCL_ERROR;
01578   }
01579 
01580   int num = sel1->selected;
01581   if (!num) {
01582     Tcl_AppendResult(interp, "measure fit: no atoms selected", NULL);
01583     return TCL_ERROR;
01584   }
01585   if (num != sel2->selected) {
01586     Tcl_AppendResult(interp, "measure fit: selections must have the same number of atoms", NULL);
01587     return TCL_ERROR;
01588   }
01589 
01590   // get the weights
01591   weight = new float[num]; 
01592   if (argc > 3 && !strcmp("weight", Tcl_GetStringFromObj(objv[3], NULL))) {
01593     // get user weight parameter
01594     rc = tcl_get_weights(interp, app, sel1, objv[4], weight);
01595   } else {
01596     // default to weights of 1.0
01597     rc = tcl_get_weights(interp, app, sel1, NULL, weight);
01598   }
01599   if (rc < 0) {
01600     Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01601     delete [] weight;
01602     return TCL_ERROR;
01603   }
01604 
01605 
01606   int orderparam = 0;
01607   if (argc == 5 && !strcmp("order", Tcl_GetStringFromObj(objv[3], NULL))) {
01608     orderparam = 4;
01609   } else if (argc == 7 && !strcmp("order", Tcl_GetStringFromObj(objv[5], NULL))) {
01610     orderparam = 6;
01611   }
01612 
01613   if (orderparam != 0) {
01614     // get the atom order
01615     order = new int[num];
01616     rc = tcl_get_orders(interp, num, objv[orderparam], order);
01617     if (rc < 0) {
01618       Tcl_AppendResult(interp, "measure fit: ", measure_error(rc), NULL);
01619       delete [] order;
01620       return TCL_ERROR;
01621     }
01622   }
01623 
01624   // compute the transformation matrix
01625   Matrix4 T;
01626   const float *x = sel1->coordinates(app->moleculeList);
01627   const float *y = sel2->coordinates(app->moleculeList);
01628 
01629   int ret_val = MEASURE_ERR_NOMOLECULE;
01630   if (x && y) 
01631     ret_val = measure_fit(sel1, sel2, x, y, weight, order, &T);
01632 
01633   delete [] weight;
01634 
01635   if (order != NULL)
01636     delete [] order;
01637 
01638   if (ret_val < 0) {
01639     Tcl_AppendResult(interp, "measure fit: ", measure_error(ret_val), NULL);
01640     return TCL_ERROR;
01641   }
01642 
01643   // and return the matrix
01644   tcl_append_matrix(interp, T.mat);
01645   return TCL_OK;
01646 }
01647 
01649 // measure inverse 4x4matrix
01650 static int vmd_measure_inverse(int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01651   // make there there is exactly one matrix
01652   if (argc != 2) {
01653     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<matrix>");
01654     return TCL_ERROR;
01655   }
01656   // Get the first matrix
01657   Matrix4 inv;
01658   if (tcl_get_matrix("measure inverse: ",interp,objv[1],inv.mat) != TCL_OK) {
01659     return TCL_ERROR;
01660   }
01661   if (inv.inverse()) {
01662     Tcl_AppendResult(interp, "Singular Matrix; inverse not computed", NULL);
01663     return TCL_ERROR;
01664   }
01665   tcl_append_matrix(interp, inv.mat);
01666   return TCL_OK;
01667 }
01668 
01669 // Find all atoms p in sel1 and q in sel2 within the cutoff.  
01670 static int vmd_measure_contacts(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01671   
01672   // Cutoff, and either one or two atom selections
01673   if (argc != 3 && argc != 4) {
01674     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <sel1> [<sel2>]");
01675     return TCL_ERROR;
01676   }
01677   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
01678   if (!sel1) {
01679     Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01680     return TCL_ERROR;
01681   }
01682   AtomSel *sel2 = NULL;
01683   if (argc == 4) {
01684     sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
01685     if (!sel2) {
01686       Tcl_AppendResult(interp, "measure contacts: no atom selection", NULL);
01687       return TCL_ERROR;
01688     }
01689   }
01690   if (!sel2) sel2 = sel1;
01691   
01692   double cutoff;
01693   if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK)
01694     return TCL_ERROR;
01695 
01696   const float *pos1 = sel1->coordinates(app->moleculeList);
01697   const float *pos2 = sel2->coordinates(app->moleculeList);
01698   if (!pos1 || !pos2) {
01699     Tcl_AppendResult(interp, "measure contacts: error, molecule contains no coordinates", NULL);
01700     return TCL_ERROR;
01701   }
01702   Molecule *mol1 = app->moleculeList->mol_from_id(sel1->molid());
01703   Molecule *mol2 = app->moleculeList->mol_from_id(sel2->molid());
01704 
01705   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) * 27L);
01706   GridSearchPair *p, *tmp;
01707   Tcl_Obj *list1 = Tcl_NewListObj(0, NULL);
01708   Tcl_Obj *list2 = Tcl_NewListObj(0, NULL);
01709   for (p=pairlist; p != NULL; p=tmp) {
01710     // throw out pairs that are already bonded
01711     MolAtom *a1 = mol1->atom(p->ind1);
01712     if (mol1 != mol2 || !a1->bonded(p->ind2)) {
01713       Tcl_ListObjAppendElement(interp, list1, Tcl_NewIntObj(p->ind1));
01714       Tcl_ListObjAppendElement(interp, list2, Tcl_NewIntObj(p->ind2));
01715     }
01716     tmp = p->next;
01717     free(p);
01718   }
01719   Tcl_Obj *result = Tcl_NewListObj(0, NULL);
01720   Tcl_ListObjAppendElement(interp, result, list1);
01721   Tcl_ListObjAppendElement(interp, result, list2);
01722   Tcl_SetObjResult(interp, result);
01723   return TCL_OK;
01724 }
01725 
01726 
01727 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01728 // frame parameters the code will compute the normalized histogram.
01729 static int vmd_measure_gofr(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01730   int i;
01731   // initialize optional arguments to default values
01732   double rmax=10.0;
01733   double delta=0.1;
01734   int usepbc=0;
01735   int selupdate=0;
01736   int first=-1, last=-1, step=1;
01737   int rc;
01738 
01739   // argument error message
01740   const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01741 
01742   // Two atom selections and optional keyword/value pairs.
01743   if ((argc < 3) || (argc > 17) || (argc % 2 == 0) )  {
01744     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01745     return TCL_ERROR;
01746   }
01747 
01748   // check atom selections
01749   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01750   if (!sel1) {
01751     Tcl_AppendResult(interp, "measure gofr: invalid first atom selection", NULL);
01752     return TCL_ERROR;
01753   }
01754 
01755   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01756   if (!sel2) {
01757     Tcl_AppendResult(interp, "measure gofr: invalid second atom selection", NULL);
01758     return TCL_ERROR;
01759   }
01760 
01761   // parse optional arguments
01762   for (i=3; i<argc; i+=2) {
01763     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01764     if (i==(argc-1)) {
01765       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01766       return TCL_ERROR;
01767     }
01768     if (!strcmp(opt, "delta")) {
01769       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01770         return TCL_ERROR;
01771       if (delta <= 0.0) {
01772         Tcl_AppendResult(interp, "measure gofr: invalid 'delta' value", NULL);
01773         return TCL_ERROR;
01774       }
01775     } else if (!strcmp(opt, "rmax")) {
01776       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01777         return TCL_ERROR;
01778       if (rmax <= 0.0) {
01779         Tcl_AppendResult(interp, "measure gofr: invalid 'rmax' value", NULL);
01780         return TCL_ERROR;
01781       }
01782     } else if (!strcmp(opt, "usepbc")) {
01783       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01784         return TCL_ERROR;
01785     } else if (!strcmp(opt, "selupdate")) {
01786       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01787         return TCL_ERROR;
01788     } else if (!strcmp(opt, "first")) {
01789       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01790         return TCL_ERROR;
01791     } else if (!strcmp(opt, "last")) {
01792       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01793         return TCL_ERROR;
01794     } else if (!strcmp(opt, "step")) {
01795       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01796         return TCL_ERROR;
01797     } else { // unknown keyword.
01798       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure gofr ", argerrmsg, NULL);
01799       return TCL_ERROR;
01800     }
01801   }
01802 
01803   // allocate and initialize histogram arrays
01804   int    count_h = (int)(rmax / delta + 1.0);
01805   double *gofr   = new double[count_h];
01806   double *numint = new double[count_h];
01807   double *histog = new double[count_h];
01808   int *framecntr = new int[3];
01809 
01810   // do the gofr calculation
01811   rc = measure_gofr(sel1, sel2, app->moleculeList,
01812                count_h, gofr, numint, histog,
01813                (float) delta,
01814                first, last, step, framecntr,
01815                usepbc, selupdate);
01816 
01817   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01818   if (rc != MEASURE_NOERR) { 
01819     Tcl_AppendResult(interp, "measure gofr: error during g(r) calculation.", NULL);
01820     return TCL_ERROR;
01821   }
01822 
01823   // convert the results of the lowlevel call to tcl lists
01824   // and build a list from them as return value.
01825   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01826   Tcl_Obj *tcl_rlist  = Tcl_NewListObj(0, NULL);
01827   Tcl_Obj *tcl_gofr   = Tcl_NewListObj(0, NULL);
01828   Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01829   Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01830   Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01831 
01832   // build lists with results ready for plotting
01833   for (i=0; i<count_h; i++) { 
01834     Tcl_ListObjAppendElement(interp, tcl_rlist,  Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01835     Tcl_ListObjAppendElement(interp, tcl_gofr,   Tcl_NewDoubleObj(gofr[i]));
01836     Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01837     Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01838   }
01839 
01840   // build list with number of frames: 
01841   // total, skipped and processed (one entry for each algorithm).
01842   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01843   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01844   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01845 
01846   // build final list-of-lists as return value
01847   Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01848   Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01849   Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01850   Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01851   Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01852   Tcl_SetObjResult(interp, tcl_result);
01853 
01854   delete [] gofr;
01855   delete [] numint;
01856   delete [] histog;
01857   delete [] framecntr;
01858   return TCL_OK;
01859 }
01860 
01861 
01862 // measure g(r) for two selections, with delta, rmax, usepbc, first/last/step 
01863 // frame parameters the code will compute the normalized histogram.
01864 static int vmd_measure_rdf(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01865   int i;
01866   // initialize optional arguments to default values
01867   double rmax=10.0;
01868   double delta=0.1;
01869   int usepbc=0;
01870   int selupdate=0;
01871   int first=-1, last=-1, step=1;
01872   int rc;
01873 
01874   // argument error message
01875   const char *argerrmsg = "<sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>] [selupdate <bool>] [first <first>] [last <last>] [step <step>]";
01876 
01877   // Two atom selections and optional keyword/value pairs.
01878   if ((argc < 3) || (argc > 17) || (argc % 2 == 0) )  {
01879     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01880     return TCL_ERROR;
01881   }
01882 
01883   // check atom selections
01884   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
01885   if (!sel1) {
01886     Tcl_AppendResult(interp, "measure rdf: invalid first atom selection", NULL);
01887     return TCL_ERROR;
01888   }
01889 
01890   AtomSel *sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2], NULL));
01891   if (!sel2) {
01892     Tcl_AppendResult(interp, "measure rdf: invalid second atom selection", NULL);
01893     return TCL_ERROR;
01894   }
01895 
01896   // parse optional arguments
01897   for (i=3; i<argc; i+=2) {
01898     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
01899     if (i==(argc-1)) {
01900       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
01901       return TCL_ERROR;
01902     }
01903     if (!strcmp(opt, "delta")) {
01904       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &delta) != TCL_OK)
01905         return TCL_ERROR;
01906       if (delta <= 0.0) {
01907         Tcl_AppendResult(interp, "measure rdf: invalid 'delta' value", NULL);
01908         return TCL_ERROR;
01909       }
01910     } else if (!strcmp(opt, "rmax")) {
01911       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &rmax) != TCL_OK)
01912         return TCL_ERROR;
01913       if (rmax <= 0.0) {
01914         Tcl_AppendResult(interp, "measure rdf: invalid 'rmax' value", NULL);
01915         return TCL_ERROR;
01916       }
01917     } else if (!strcmp(opt, "usepbc")) {
01918       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
01919         return TCL_ERROR;
01920     } else if (!strcmp(opt, "selupdate")) {
01921       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
01922         return TCL_ERROR;
01923     } else if (!strcmp(opt, "first")) {
01924       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
01925         return TCL_ERROR;
01926     } else if (!strcmp(opt, "last")) {
01927       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
01928         return TCL_ERROR;
01929     } else if (!strcmp(opt, "step")) {
01930       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
01931         return TCL_ERROR;
01932     } else { // unknown keyword.
01933       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure rdf ", argerrmsg, NULL);
01934       return TCL_ERROR;
01935     }
01936   }
01937 
01938   // allocate and initialize histogram arrays
01939   int    count_h = (int)(rmax / delta + 1.0);
01940   double *gofr   = new double[count_h];
01941   double *numint = new double[count_h];
01942   double *histog = new double[count_h];
01943   int *framecntr = new int[3];
01944 
01945   // do the gofr calculation
01946   rc = measure_rdf(app, sel1, sel2, app->moleculeList,
01947                    count_h, gofr, numint, histog,
01948                    (float) delta,
01949                    first, last, step, framecntr,
01950                    usepbc, selupdate);
01951 
01952   // XXX: this needs a 'case' structure to provide more meaninful error messages.
01953   if (rc != MEASURE_NOERR) { 
01954     Tcl_AppendResult(interp, "measure rdf: error during rdf calculation.", NULL);
01955     return TCL_ERROR;
01956   }
01957 
01958   // convert the results of the lowlevel call to tcl lists
01959   // and build a list from them as return value.
01960   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
01961   Tcl_Obj *tcl_rlist  = Tcl_NewListObj(0, NULL);
01962   Tcl_Obj *tcl_gofr   = Tcl_NewListObj(0, NULL);
01963   Tcl_Obj *tcl_numint = Tcl_NewListObj(0, NULL);
01964   Tcl_Obj *tcl_histog = Tcl_NewListObj(0, NULL);
01965   Tcl_Obj *tcl_frames = Tcl_NewListObj(0, NULL);
01966 
01967   // build lists with results ready for plotting
01968   for (i=0; i<count_h; i++) { 
01969     Tcl_ListObjAppendElement(interp, tcl_rlist,  Tcl_NewDoubleObj(delta * ((double)i + 0.5)));
01970     Tcl_ListObjAppendElement(interp, tcl_gofr,   Tcl_NewDoubleObj(gofr[i]));
01971     Tcl_ListObjAppendElement(interp, tcl_numint, Tcl_NewDoubleObj(numint[i]));
01972     Tcl_ListObjAppendElement(interp, tcl_histog, Tcl_NewDoubleObj(histog[i]));
01973   }
01974 
01975   // build list with number of frames: 
01976   // total, skipped and processed (one entry for each algorithm).
01977   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[0]));
01978   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[1]));
01979   Tcl_ListObjAppendElement(interp, tcl_frames, Tcl_NewIntObj(framecntr[2]));
01980 
01981   // build final list-of-lists as return value
01982   Tcl_ListObjAppendElement(interp, tcl_result, tcl_rlist);
01983   Tcl_ListObjAppendElement(interp, tcl_result, tcl_gofr);
01984   Tcl_ListObjAppendElement(interp, tcl_result, tcl_numint);
01985   Tcl_ListObjAppendElement(interp, tcl_result, tcl_histog);
01986   Tcl_ListObjAppendElement(interp, tcl_result, tcl_frames);
01987   Tcl_SetObjResult(interp, tcl_result);
01988 
01989   delete [] gofr;
01990   delete [] numint;
01991   delete [] histog;
01992   delete [] framecntr;
01993   return TCL_OK;
01994 }
01995 
01996 
01998 static int vmd_measure_cluster(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
01999   int i,j;
02000   // initialize optional arguments to default values
02001   int algorithm=0; // will be MEASURE_CLUSTER_QT when finished
02002   int likeness=MEASURE_DIST_FITRMSD;
02003   int numcluster=5;
02004   double cutoff=1.0;
02005   float *weights=NULL;
02006   int selupdate=0;
02007   int first=0, last=-1, step=1;
02008   int rc;
02009 
02010   // argument error message
02011   const char *argerrmsg = "<sel> [num <#clusters>] [distfunc <flag>] "
02012     "[cutoff <cutoff>] [first <first>] [last <last>] [step <step>] "
02013     "[selupdate <bool>] [weight <weights>]";
02014 
02015   // Two atom selections and optional keyword/value pairs.
02016   if ((argc < 2) || (argc > 19) || ((argc-1) % 2 == 0) )  {
02017     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02018     return TCL_ERROR;
02019   }
02020 
02021   // check atom selection
02022   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
02023   if (!sel) {
02024     Tcl_AppendResult(interp, "measure cluster: invalid atom selection", NULL);
02025     return TCL_ERROR;
02026   }
02027   if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
02028 
02029   // parse optional arguments
02030   for (i=2; i<argc; i+=2) {
02031     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02032     if (i==(argc-1)) {
02033       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02034       return TCL_ERROR;
02035     }
02036     if (!strcmp(opt, "num")) {
02037       if (Tcl_GetIntFromObj(interp, objv[i+1], &numcluster) != TCL_OK)
02038         return TCL_ERROR;
02039       if (numcluster < 1) {
02040         Tcl_AppendResult(interp, "measure cluster: invalid 'num' value (cannot be smaller than 1)", NULL);
02041         return TCL_ERROR;
02042       }
02043     } else if (!strcmp(opt, "cutoff")) {
02044       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
02045         return TCL_ERROR;
02046       if (cutoff <= 0.0) {
02047         Tcl_AppendResult(interp, "measure cluster: invalid 'cutoff' value (should be larger than 0.0)", NULL);
02048         return TCL_ERROR;
02049       }
02050     } else if (!strcmp(opt, "distfunc")) {
02051       char *argstr = Tcl_GetStringFromObj(objv[i+1], NULL);
02052       if (!strcmp(argstr,"rmsd")) {
02053         likeness = MEASURE_DIST_RMSD;
02054       } else if (!strcmp(argstr,"fitrmsd")) {
02055         likeness = MEASURE_DIST_FITRMSD;
02056       } else if (!strcmp(argstr,"rgyrd")) {
02057         likeness = MEASURE_DIST_RGYRD;
02058       } else {
02059         Tcl_AppendResult(interp, "measure cluster: unknown distance function (supported are 'rmsd', 'rgyrd' and 'fitrmsd')", NULL);
02060         return TCL_ERROR;
02061       }
02062     } else if (!strcmp(opt, "selupdate")) {
02063       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &selupdate) != TCL_OK)
02064         return TCL_ERROR;
02065     } else if (!strcmp(opt, "weight")) {
02066       // NOTE: we cannot use tcl_get_weights here, since we have to 
02067       // get a full (all atoms) list of weights as we may be updating
02068       // the selection in the process. Also we don't support explicit
02069       // lists of weights for now.
02070       const char *weight_string = Tcl_GetStringFromObj(objv[i+1], NULL);
02071       weights = new float[sel->num_atoms];
02072 
02073       if (!weight_string || !strcmp(weight_string, "none")) {
02074         for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
02075       } else {
02076         // if a selection string was given, check the symbol table
02077         SymbolTable *atomSelParser = app->atomSelParser; 
02078         // weights must return floating point values, so the symbol must not 
02079         // be a singleword, so macro is NULL.
02080         atomsel_ctxt context(atomSelParser,
02081                              app->moleculeList->mol_from_id(sel->molid()), 
02082                              sel->which_frame, NULL);
02083 
02084         int fctn = atomSelParser->find_attribute(weight_string);
02085         if (fctn >= 0) {
02086           // the keyword exists, so get the data
02087           // first, check to see that the function returns floats.
02088           // if it doesn't, it makes no sense to use it as a weight
02089           if (atomSelParser->fctns.data(fctn)->returns_a != SymbolTableElement::IS_FLOAT) {
02090             Tcl_AppendResult(interp, "weight attribute must have floating point values", NULL);
02091             delete [] weights;
02092             return MEASURE_ERR_BADWEIGHTPARM;  // can't understand weight parameter 
02093           }
02094 
02095           double *tmp_data = new double[sel->num_atoms];
02096           int *all_on = new int[sel->num_atoms];
02097           for (j=0; j<sel->num_atoms; j++) all_on[j]=1;
02098 
02099           atomSelParser->fctns.data(fctn)->keyword_double(
02100             &context, sel->num_atoms, tmp_data, all_on);
02101           
02102           for (j=0; j<sel->num_atoms; j++) weights[j] = (float)tmp_data[j];
02103           
02104           // clean up.
02105           delete [] tmp_data;
02106           delete [] all_on;
02107         }
02108       }
02109     } else if (!strcmp(opt, "first")) {
02110       if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK)
02111         return TCL_ERROR;
02112     } else if (!strcmp(opt, "last")) {
02113       if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK)
02114         return TCL_ERROR;
02115     } else if (!strcmp(opt, "step")) {
02116       if (Tcl_GetIntFromObj(interp, objv[i+1], &step) != TCL_OK)
02117         return TCL_ERROR;
02118     } else { // unknown keyword.
02119       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure cluster ", argerrmsg, NULL);
02120       return TCL_ERROR;
02121     }
02122   }
02123 
02124   // set default for weights if not already defined
02125   if (!weights) {
02126     weights = new float[sel->num_atoms];
02127     for (j=0; j<sel->num_atoms; j++) weights[j]=1.0f;
02128   }
02129   
02130   // Allocate temporary result storage. we add one more cluster 
02131   // slot for collecting unclustered frames in an additional "cluster".
02132   // NOTE: the individual cluster lists are going to
02133   //       allocated in the ancilliary code.
02134   int  *clustersize = new int  [numcluster+1];
02135   int **clusterlist = new int *[numcluster+1];
02136   
02137   // do the cluster analysis
02138   rc = measure_cluster(sel, app->moleculeList, numcluster, algorithm, likeness, cutoff, 
02139                        clustersize, clusterlist, first, last, step, selupdate, weights);
02140 
02141   if (weights) delete [] weights;
02142 
02143   // XXX: this needs a 'case' structure to provide more meaninful error messages.
02144   if (rc != MEASURE_NOERR) { 
02145     Tcl_AppendResult(interp, "measure cluster: error during cluster analysis calculation.", NULL);
02146     return TCL_ERROR;
02147   }
02148 
02149   // convert the results of the lowlevel call to tcl lists
02150   // and build a list from them as return value.
02151   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02152 
02153   for (i=0; i <= numcluster; ++i) { 
02154     int j;
02155     Tcl_Obj *tcl_clist  = Tcl_NewListObj(0, NULL);
02156     for (j=0; j < clustersize[i]; ++j) {
02157       Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clusterlist[i][j]));
02158     }
02159     Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
02160   }
02161   Tcl_SetObjResult(interp, tcl_result);
02162 
02163   // free temporary result storage
02164   for (i=0; i <= numcluster; ++i)
02165     delete[] clusterlist[i];
02166 
02167   delete[] clusterlist;
02168   delete[] clustersize;
02169 
02170   return TCL_OK;
02171 }
02172 
02174 static int vmd_measure_clustsize(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02175   int i;
02176   // initialize optional arguments to default values
02177   double cutoff=3.0; // arbitrary. took hbond cutoff.
02178   char *storesize=NULL;
02179   char *storenum=NULL;
02180   int usepbc=0;
02181   int minsize=2;
02182   int numshared=1;
02183   int rc;
02184 
02185   // argument error message
02186   const char *argerrmsg = "<sel> [cutoff <float>] [minsize <num>] [numshared <num>] "
02187     "[usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]";
02188 
02189   // Two atom selections and optional keyword/value pairs.
02190   if ((argc < 2) || (argc > 13) || ((argc-1) % 2 == 0) )  {
02191     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02192     return TCL_ERROR;
02193   }
02194 
02195   // check atom selection
02196   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1], NULL));
02197   if (!sel) {
02198     Tcl_AppendResult(interp, "measure clustsize: invalid atom selection", NULL);
02199     return TCL_ERROR;
02200   }
02201   if (!app->molecule_valid_id(sel->molid())) return MEASURE_ERR_NOMOLECULE;
02202 
02203   // parse optional arguments
02204   for (i=2; i<argc; i+=2) {
02205     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02206     if (i==(argc-1)) {
02207       Tcl_WrongNumArgs(interp, 2, objv-1, (char *)argerrmsg);
02208       return TCL_ERROR;
02209     } else if (!strcmp(opt, "cutoff")) {
02210       if (Tcl_GetDoubleFromObj(interp, objv[i+1], &cutoff) != TCL_OK)
02211         return TCL_ERROR;
02212       if (cutoff <= 0.0) {
02213         Tcl_AppendResult(interp, "measure clustsize: invalid 'cutoff' value", NULL);
02214         return TCL_ERROR;
02215       }
02216     } else if (!strcmp(opt, "minsize")) {
02217       if (Tcl_GetIntFromObj(interp, objv[i+1], &minsize) != TCL_OK)
02218         return TCL_ERROR;
02219       if (minsize < 2) {
02220         Tcl_AppendResult(interp, "measure clustsize: invalid 'minsize' value", NULL);
02221         return TCL_ERROR;
02222       }
02223     } else if (!strcmp(opt, "numshared")) {
02224       if (Tcl_GetIntFromObj(interp, objv[i+1], &numshared) != TCL_OK)
02225         return TCL_ERROR;
02226       if (numshared < 0) {
02227         Tcl_AppendResult(interp, "measure clustsize: invalid 'numshared' value", NULL);
02228         return TCL_ERROR;
02229       }
02230     } else if (!strcmp(opt, "usepbc")) {
02231       if (Tcl_GetBooleanFromObj(interp, objv[i+1], &usepbc) != TCL_OK)
02232         return TCL_ERROR;
02233     } else if (!strcmp(opt, "storenum")) {
02234       storenum = Tcl_GetStringFromObj(objv[i+1], NULL);
02235     } else if (!strcmp(opt, "storesize")) {
02236       storesize = Tcl_GetStringFromObj(objv[i+1], NULL);
02237     } else { // unknown keyword.
02238       Tcl_AppendResult(interp, "unknown keyword '", opt, "'. usage: measure clustsize ", argerrmsg, NULL);
02239       return TCL_ERROR;
02240     }
02241   }
02242 
02243   if (usepbc) { 
02244     Tcl_AppendResult(interp, "measure clustsize: does not support periodic boundaries yet.", NULL);
02245     return TCL_ERROR;
02246   }
02247 
02248   // allocate temporary result storage
02249   // NOTE: the individual cluster lists are going to
02250   //       allocated in the ancilliary code.
02251   int num_selected=sel->selected;
02252   int *clustersize = new int[num_selected];
02253   int *clusternum= new int [num_selected];
02254   int *clusteridx= new int [num_selected];
02255   for (i=0; i < num_selected; i++) {
02256     clustersize[i] = 0;
02257     clusternum[i]  = -1;
02258     clusteridx[i]  = -1;
02259   }
02260   
02261   // do the cluster analysis
02262   rc = measure_clustsize(sel, app->moleculeList, cutoff, 
02263                          clustersize, clusternum, clusteridx,
02264                          minsize, numshared, usepbc);
02265 
02266   // XXX: this needs a 'case' structure to provide more meaninful error messages.
02267   if (rc != MEASURE_NOERR) { 
02268     Tcl_AppendResult(interp, "measure clustsize: error during cluster size analysis calculation.", NULL);
02269     return TCL_ERROR;
02270   }
02271 
02272 
02273   if (storenum || storesize) {
02274     // field names were given to store the results. check the keywords and so on.
02275     SymbolTable *atomSelParser = app->atomSelParser; 
02276     atomsel_ctxt context(atomSelParser, 
02277                          app->moleculeList->mol_from_id(sel->molid()), 
02278                          sel->which_frame, NULL);
02279 
02280     // the keyword exists, set the data
02281     if (storenum) {
02282       int fctn = atomSelParser->find_attribute(storenum);
02283       if (fctn >= 0) {
02284         if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
02285           double *tmp_data = new double[sel->num_atoms];
02286           int j=0;
02287           for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02288             if (sel->on[i])
02289               tmp_data[i] = (double) clusternum[j++];
02290           }
02291           atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
02292                                                               sel->num_atoms,
02293                                                               tmp_data, sel->on);
02294           delete[] tmp_data;
02295           
02296         } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
02297           int *tmp_data = new int[sel->num_atoms];
02298           int j=0;
02299           for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02300             if (sel->on[i])
02301               tmp_data[i] = clusternum[j++];
02302           }
02303           atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
02304                                                            sel->num_atoms,
02305                                                            tmp_data, sel->on);
02306           delete[] tmp_data;
02307         } else {
02308           Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
02309           return TCL_ERROR;
02310         }
02311       } else {
02312         Tcl_AppendResult(interp, "measure clustsize: invalid field name for storenum", NULL);
02313         return TCL_ERROR;
02314       }
02315     }
02316 
02317     // the keyword exists, set the data
02318     if (storesize) {
02319       int fctn = atomSelParser->find_attribute(storesize);
02320       if (fctn >= 0) {
02321         if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_FLOAT) {
02322           double *tmp_data = new double[sel->num_atoms];
02323           int j=0;
02324           for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02325             if (sel->on[i])
02326               tmp_data[i] = (double) clustersize[j++];
02327           }
02328           atomSelParser->fctns.data(fctn)->set_keyword_double(&context, 
02329                                                               sel->num_atoms,
02330                                                               tmp_data, sel->on);
02331           delete[] tmp_data;
02332           
02333         } else if (atomSelParser->fctns.data(fctn)->returns_a == SymbolTableElement::IS_INT) {
02334           int *tmp_data = new int[sel->num_atoms];
02335           int j=0;
02336           for (int i=sel->firstsel; i<=sel->lastsel; i++) {
02337             if (sel->on[i])
02338               tmp_data[i] = clustersize[j++];
02339           }
02340           atomSelParser->fctns.data(fctn)->set_keyword_int(&context, 
02341                                                            sel->num_atoms,
02342                                                            tmp_data, sel->on);
02343           delete[] tmp_data;
02344         } else {
02345           Tcl_AppendResult(interp, "measure clustsize: storenum field must accept numbers", NULL);
02346           return TCL_ERROR;
02347         }
02348       } else {
02349         Tcl_AppendResult(interp, "measure clustsize: invalid field name for storesize", NULL);
02350         return TCL_ERROR;
02351       }
02352     }
02353   } else {
02354     // convert the results of the lowlevel call to tcl lists
02355     // and build a list from them as return value.
02356     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02357 
02358     Tcl_Obj *tcl_ilist  = Tcl_NewListObj(0, NULL);
02359     Tcl_Obj *tcl_clist  = Tcl_NewListObj(0, NULL);
02360     Tcl_Obj *tcl_nlist  = Tcl_NewListObj(0, NULL);
02361     for (i=0; i<num_selected; i++) { 
02362       Tcl_ListObjAppendElement(interp, tcl_ilist, Tcl_NewIntObj(clusteridx[i]));
02363       Tcl_ListObjAppendElement(interp, tcl_clist, Tcl_NewIntObj(clustersize[i]));
02364       Tcl_ListObjAppendElement(interp, tcl_nlist, Tcl_NewIntObj(clusternum[i]));
02365     }
02366     Tcl_ListObjAppendElement(interp, tcl_result, tcl_ilist);
02367     Tcl_ListObjAppendElement(interp, tcl_result, tcl_clist);
02368     Tcl_ListObjAppendElement(interp, tcl_result, tcl_nlist);
02369     Tcl_SetObjResult(interp, tcl_result);
02370   }
02371   
02372   delete[] clustersize;
02373   delete[] clusternum;
02374   delete[] clusteridx;
02375 
02376   return TCL_OK;
02377 }
02378 
02379 static int vmd_measure_hbonds(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02380   
02381   // Cutoff, angle, and either one or two atom selections
02382   if (argc != 4 && argc != 5) {
02383     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<cutoff> <angle> <selection1> [<selection2>]");
02384     return TCL_ERROR;
02385   }
02386   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[3],NULL));
02387   if (!sel1) {
02388     Tcl_AppendResult(interp, "measure hbonds: invalid first atom selection", NULL);
02389     return TCL_ERROR;
02390   }
02391 
02392   AtomSel *sel2 = NULL;
02393   if (argc == 5) {
02394     sel2 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[4],NULL));
02395     if (!sel2) {
02396       Tcl_AppendResult(interp, "measure hbonds: invalid second atom selection", NULL);
02397       return TCL_ERROR;
02398     }
02399   }
02400   if (sel2 && sel2->molid() != sel1->molid()) {
02401     Tcl_AppendResult(interp, "measure hbonds: error, atom selections must come from same molecule.", NULL);
02402     return TCL_ERROR;
02403   }
02404   double cutoff;
02405   if (Tcl_GetDoubleFromObj(interp, objv[1], &cutoff) != TCL_OK) 
02406     return TCL_ERROR;
02407 
02408   double maxangle;
02409   if (Tcl_GetDoubleFromObj(interp, objv[2], &maxangle) != TCL_OK) 
02410     return TCL_ERROR;
02411   
02412   const float *pos = sel1->coordinates(app->moleculeList);
02413   if (!pos) {
02414     Tcl_AppendResult(interp, "measure bondsearch: error, molecule contains no coordinates", NULL);
02415     return TCL_ERROR;
02416   }
02417 
02418   // XXX the actual code for measuring hbonds doesn't belong here, it should
02419   //     be moved into Measure.[Ch] where it really belongs.  This file
02420   //     only implements the Tcl interface, and should not be doing the
02421   //     hard core math, particularly if we want to expose the same
02422   //     feature via other scripting interfaces.  Also, having a single
02423   //     implementation avoids having different Tcl/Python bugs in the 
02424   //     long-term.  Too late to do anything about this now, but should be
02425   //     addressed for the next major version when time allows.
02426 
02427   // XXX This code is close, but not identical to the HBonds code in 
02428   //     DrawMolItem.  Is there any good reason they aren't identical?
02429   //     This version does a few extra tests that the other does not.
02430 
02431   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02432 
02433   int *donlist, *hydlist, *acclist;
02434   int maxsize = 2 * sel1->num_atoms; //This heuristic is based on ice, where there are < 2 hydrogen bonds per atom if hydrogens are in the selection, and exactly 2 if hydrogens are not considered.
02435   donlist = new int[maxsize];
02436   hydlist = new int[maxsize];
02437   acclist = new int[maxsize];
02438   int rc = measure_hbonds(mol, sel1, sel2, cutoff, maxangle, donlist, hydlist, acclist, maxsize);
02439   if (rc > maxsize) {
02440     delete [] donlist;
02441     delete [] hydlist;
02442     delete [] acclist;
02443     maxsize = rc;
02444     donlist = new int[maxsize];
02445     hydlist = new int[maxsize];
02446     acclist = new int[maxsize];
02447     rc = measure_hbonds(mol, sel1, sel2, cutoff, maxangle, donlist, hydlist, acclist, maxsize);
02448   }
02449   if (rc < 0) {
02450     Tcl_AppendResult(interp, "measure hbonds: internal error to measure_hbonds", NULL);
02451     return TCL_ERROR;
02452   }
02453   
02454   Tcl_Obj *newdonlist = Tcl_NewListObj(0, NULL);
02455   Tcl_Obj *newhydlist = Tcl_NewListObj(0, NULL);
02456   Tcl_Obj *newacclist = Tcl_NewListObj(0, NULL);
02457   for (int k = 0; k < rc; k++) {
02458     Tcl_ListObjAppendElement(interp, newdonlist, Tcl_NewIntObj(donlist[k]));
02459     Tcl_ListObjAppendElement(interp, newhydlist, Tcl_NewIntObj(hydlist[k]));
02460     Tcl_ListObjAppendElement(interp, newacclist, Tcl_NewIntObj(acclist[k]));
02461   }
02462   delete [] donlist;
02463   delete [] hydlist;
02464   delete [] acclist;
02465   Tcl_Obj *result = Tcl_NewListObj(0, NULL);
02466   Tcl_ListObjAppendElement(interp, result, newdonlist);
02467   Tcl_ListObjAppendElement(interp, result, newacclist);
02468   Tcl_ListObjAppendElement(interp, result, newhydlist);
02469   Tcl_SetObjResult(interp, result);
02470   return TCL_OK;
02471 }
02472 
02473   
02474 static int vmd_measure_sasa(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02475 
02476   int i;
02477   // srad and one atom selection, plus additional options
02478   if (argc < 3) {
02479     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-points <varname>] [-restrict <restrictedsel>] [-samples <numsamples>]");
02480     return TCL_ERROR;
02481   }
02482   // parse options
02483   Tcl_Obj *ptsvar = NULL;
02484   AtomSel *restrictsel = NULL;
02485   int nsamples = -1;
02486   int *sampleptr = NULL;
02487   for (i=3; i<argc-1; i+=2) {
02488     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02489     if (!strcmp(opt, "-points")) {
02490       ptsvar = objv[i+1];
02491     } else if (!strcmp(opt, "-restrict")) {
02492       restrictsel = tcl_commands_get_sel(interp, 
02493           Tcl_GetStringFromObj(objv[i+1], NULL));
02494       if (!restrictsel) {
02495         Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02496         return TCL_ERROR;
02497       }
02498     } else if (!strcmp(opt, "-samples")) {
02499       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02500         return TCL_ERROR;
02501       sampleptr = &nsamples;
02502     } else {
02503       Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", 
02504           NULL);
02505       return TCL_ERROR;
02506     }
02507   }
02508 
02509   double srad;
02510   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02511     return TCL_ERROR;
02512   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02513   if (!sel1) {
02514     Tcl_AppendResult(interp, "measure sasa: invalid first atom selection", NULL);
02515     return TCL_ERROR;
02516   }
02517 
02518   const float *pos = sel1->coordinates(app->moleculeList);
02519   if (!pos) {
02520     Tcl_AppendResult(interp, "measure sasa: error, molecule contains no coordinates", NULL);
02521     return TCL_ERROR;
02522   }
02523   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02524   const float *radius = mol->extraflt.data("radius");
02525 
02526   ResizeArray<float> sasapts;
02527   float sasa = 0;
02528   int rc = measure_sasa(sel1, pos, radius, (float) srad, &sasa, &sasapts, 
02529                         restrictsel, sampleptr);
02530   if (rc < 0) {
02531     Tcl_AppendResult(interp, "measure: sasa: ", measure_error(rc), NULL);
02532     return TCL_ERROR;
02533   }
02534   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sasa));
02535   if (ptsvar) {
02536     // construct list from sasapts
02537     Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02538     i=0;
02539     while (i<sasapts.num()) {
02540       Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02541       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02542       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02543       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02544       Tcl_ListObjAppendElement(interp, listobj, elem);
02545     }
02546     Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02547   }
02548   return TCL_OK;
02549 }
02550 
02551 
02552 #if 1
02553 
02554 static int vmd_measure_sasalist(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02555 
02556   int i;
02557   // srad and one atom selection, plus additional options
02558   if (argc < 3) {
02559     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel list> [-samples <numsamples>]");
02560     return TCL_ERROR;
02561   }
02562 
02563   // parse options
02564   int nsamples = -1;
02565   int *sampleptr = NULL;
02566   for (i=3; i<argc-1; i+=2) {
02567     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02568 
02569     if (!strcmp(opt, "-samples")) {
02570       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02571         return TCL_ERROR;
02572       sampleptr = &nsamples;
02573     } else {
02574       Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", NULL);
02575       return TCL_ERROR;
02576     }
02577   }
02578 
02579   double srad;
02580   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02581     return TCL_ERROR;
02582 
02583   int numsels;
02584   Tcl_Obj **sel_list;
02585   if (Tcl_ListObjGetElements(interp, objv[2], &numsels, &sel_list) != TCL_OK) {
02586     Tcl_AppendResult(interp, "measure sasalist: bad selection list", NULL);
02587     return TCL_ERROR;
02588   }
02589 
02590 #if 0
02591 printf("measure sasalist: numsels %d\n", numsels);
02592 #endif
02593 
02594   AtomSel **asels = (AtomSel **) calloc(1, numsels * sizeof(AtomSel *));
02595   float *sasalist = (float *) calloc(1, numsels * sizeof(float));
02596 
02597   int s;
02598   for (s=0; s<numsels; s++) {
02599     asels[s] = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(sel_list[s], NULL));
02600     if (!asels[s]) {
02601 printf("measure sasalist: invalid selection %d\n", s);
02602       Tcl_AppendResult(interp, "measure sasalist: invalid atom selection list element", NULL);
02603       return TCL_ERROR;
02604     }
02605   }
02606 
02607   int rc = measure_sasalist(app->moleculeList, (const AtomSel **) asels, 
02608                             numsels, (float) srad, sasalist, sampleptr);
02609   free(asels);
02610   if (rc < 0) {
02611     Tcl_AppendResult(interp, "measure: sasalist: ", measure_error(rc), NULL);
02612     return TCL_ERROR;
02613   }
02614 
02615   // construct list from sasa values
02616   Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02617   for (i=0; i<numsels; i++) {
02618     Tcl_ListObjAppendElement(interp, listobj, Tcl_NewDoubleObj(sasalist[i]));
02619   }
02620   Tcl_SetObjResult(interp, listobj);
02621   free(sasalist);
02622 
02623   return TCL_OK;
02624 }
02625 
02626 #endif
02627 
02628 
02629 // Function: vmd_measure_energy 
02630 // Returns: the energy for the specified bond/angle/dihed/imprp/vdw/elec
02631 // FIXME -- usage doesn't match user guide
02632 static int vmd_measure_energy(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02633 
02634   if (argc<3) {
02635     Tcl_WrongNumArgs(interp, 2, objv-1,
02636                      (char *) "bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?}} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]");
02637     return TCL_ERROR;
02638   }
02639 
02640   int geomtype, reqatoms;
02641   char *geomname = Tcl_GetStringFromObj(objv[1],NULL);
02642   if        (!strncmp(geomname, "bond", 4)) {
02643     reqatoms = 2; geomtype = MEASURE_BOND;
02644   } else if (!strncmp(geomname, "angl", 4)) {
02645     reqatoms = 3; geomtype = MEASURE_ANGLE;
02646   } else if (!strncmp(geomname, "dihe", 4)) {
02647     reqatoms = 4; geomtype = MEASURE_DIHED;
02648   } else if (!strncmp(geomname, "impr", 4)) {
02649     reqatoms = 4; geomtype = MEASURE_IMPRP;
02650   } else if (!strncmp(geomname, "vdw",  3)) {
02651     reqatoms = 2; geomtype = MEASURE_VDW;
02652   } else if (!strncmp(geomname, "elec", 4)) {
02653     reqatoms = 2; geomtype = MEASURE_ELECT;
02654   } else {
02655     Tcl_AppendResult(interp, " measure energy: bad syntax (must specify bond|angle|dihed|imprp|vdw|elec)", NULL);
02656     return TCL_ERROR;
02657   }
02658 
02659   int molid[4];
02660   int atmid[4];
02661   int defmolid = -1;
02662   bool allframes = false;
02663   char errstring[200];
02664 
02665   // Read the atom list
02666   int numatms;
02667   Tcl_Obj **data;
02668   if (Tcl_ListObjGetElements(interp, objv[2], &numatms, &data) != TCL_OK) {
02669     Tcl_AppendResult(interp, " measure energy: bad syntax", NULL);
02670     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);
02671     return TCL_ERROR;
02672   }
02673 
02674   if (numatms!=reqatoms) {
02675     sprintf(errstring, " measure energy %s: must specify exactly %i atoms in list", geomname, reqatoms);
02676     Tcl_AppendResult(interp, errstring, NULL);
02677     return TCL_ERROR;
02678   }
02679     
02680   int first=-1, last=-1, frame=-1;
02681   double params[6];
02682   memset(params, 0, 6L*sizeof(double));
02683 
02684   if (argc>4) {
02685     int i;
02686     for (i=3; i<argc-1; i+=2) {
02687       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02688       if (!strupncmp(argvcur, "molid", CMDLEN)) {
02689         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
02690           Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02691           return TCL_ERROR;
02692         }
02693       } else if (!strupncmp(argvcur, "k", CMDLEN)) {
02694         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02695           Tcl_AppendResult(interp, " measure energy: bad force constant value", NULL);
02696           return TCL_ERROR;
02697         }
02698       } else if (!strupncmp(argvcur, "x0", CMDLEN)) {
02699         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02700           Tcl_AppendResult(interp, " measure energy: bad equilibrium value", NULL);
02701           return TCL_ERROR;
02702         }
02703       } else if (!strupncmp(argvcur, "kub", CMDLEN)) {
02704         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02705           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley force constant value", NULL);
02706           return TCL_ERROR;
02707         }
02708       } else if (!strupncmp(argvcur, "s0", CMDLEN)) {
02709         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02710           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley equilibrium distance", NULL);
02711           return TCL_ERROR;
02712         }
02713       } else if (!strupncmp(argvcur, "n", CMDLEN)) {
02714         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02715           Tcl_AppendResult(interp, " measure energy: bad dihedral periodicity", NULL);
02716           return TCL_ERROR;
02717         }
02718       } else if (!strupncmp(argvcur, "delta", CMDLEN)) {
02719         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02720           Tcl_AppendResult(interp, " measure energy: bad dihedral phase shift", NULL);
02721           return TCL_ERROR;
02722         }
02723       } else if (!strupncmp(argvcur, "eps1", CMDLEN)) {
02724         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02725           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02726           return TCL_ERROR;
02727         }
02728       } else if (!strupncmp(argvcur, "rmin1", CMDLEN)) {
02729         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02730           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02731           return TCL_ERROR;
02732         }
02733       } else if (!strupncmp(argvcur, "eps2", CMDLEN)) {
02734         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02735           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02736           return TCL_ERROR;
02737         }
02738       } else if (!strupncmp(argvcur, "rmin2", CMDLEN)) {
02739         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02740           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02741           return TCL_ERROR;
02742         }
02743       } else if (!strupncmp(argvcur, "q1", CMDLEN)) {
02744         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02745           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02746           return TCL_ERROR;
02747         }
02748         params[2]=1.0;
02749       } else if (!strupncmp(argvcur, "q2", CMDLEN)) {
02750         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02751           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02752           return TCL_ERROR;
02753         }
02754         params[3]=1.0;
02755       } else if (!strupncmp(argvcur, "cutoff", CMDLEN)) {
02756         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+4) != TCL_OK) {
02757           Tcl_AppendResult(interp, " measure energy: bad electrostatic cutoff value", NULL);
02758           return TCL_ERROR;
02759         }
02760       } else if (!strupncmp(argvcur, "switchdist", CMDLEN)) {
02761         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+5) != TCL_OK) {
02762           Tcl_AppendResult(interp, " measure energy: bad switching distance value", NULL);
02763           return TCL_ERROR;
02764         }
02765       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
02766         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
02767           Tcl_AppendResult(interp, " measure energy: bad first frame value", NULL);
02768           return TCL_ERROR;
02769         }
02770       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
02771         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
02772           Tcl_AppendResult(interp, " measure energy: bad last frame value", NULL);
02773           return TCL_ERROR;
02774         }
02775       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02776         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
02777           allframes = true;
02778         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02779           Tcl_AppendResult(interp, " measure energy: bad frame value", NULL);
02780           return TCL_ERROR;
02781         }
02782       } else {
02783         Tcl_AppendResult(interp, " measure energy: invalid syntax, no such keyword: ", argvcur, NULL);
02784         return TCL_ERROR;
02785       }
02786     }
02787   }
02788 
02789   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
02790     Tcl_AppendResult(interp, "measure energy: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
02791     Tcl_AppendResult(interp, "\nUsage:\nmeasure bond <molid1>/<atomid1> <molid2>/<atomid2> [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02792     return TCL_ERROR;    
02793   }
02794 
02795   if (allframes) first=0;
02796 
02797   // If no molecule was specified use top as default
02798   if (defmolid<0) defmolid = app->molecule_top();
02799 
02800   // Assign atom IDs and molecule IDs
02801   int i,numelem;
02802   Tcl_Obj **atmmol;
02803   for (i=0; i<numatms; i++) {
02804     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
02805       return TCL_ERROR;
02806     }
02807 
02808     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
02809       Tcl_AppendResult(interp, " measure energy: bad atom index", NULL);
02810       return TCL_ERROR;
02811     }
02812     
02813     if (numelem==2) {
02814       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
02815         Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02816         return TCL_ERROR;
02817       }
02818     } else molid[i] = defmolid;
02819   }
02820   
02821 
02822   // Compute the value
02823   ResizeArray<float> gValues(1024);
02824   int ret_val;
02825   ret_val = measure_energy(app->moleculeList, molid, atmid, reqatoms, &gValues, frame, first, last, defmolid, params, geomtype);
02826   if (ret_val<0) {
02827     printf("ERROR\n %s\n", measure_error(ret_val));
02828     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
02829     return TCL_ERROR;
02830   }
02831 
02832   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02833   int numvalues = gValues.num();
02834   for (int count = 0; count < numvalues; count++) {
02835     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
02836   }
02837   Tcl_SetObjResult(interp, tcl_result);
02838 
02839   return TCL_OK;
02840 }
02841 
02842 
02843 //
02844 // Function: vmd_measure_surface <selection> <gridsize> <radius> <depth>
02845 //
02846 static int vmd_measure_surface(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02847   if (argc!=5) {
02848     Tcl_WrongNumArgs(interp, 2, objv-1,
02849                      (char *) "<sel> <gridsize> <radius> <depth>");
02850     return TCL_ERROR;
02851   }
02852    
02853   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02854   if (!sel) {
02855     Tcl_AppendResult(interp, "measure surface: no atom selection", NULL);
02856     return TCL_ERROR;
02857   }
02858 
02859   const float *framepos = sel->coordinates(app->moleculeList);
02860   if (!framepos) return TCL_ERROR;
02861 
02862   double gridsz;
02863   if (Tcl_GetDoubleFromObj(interp, objv[2], &gridsz) != TCL_OK) 
02864      return TCL_ERROR;
02865 
02866   double radius;
02867   if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) 
02868      return TCL_ERROR;
02869 
02870   double depth;
02871   if (Tcl_GetDoubleFromObj(interp, objv[4], &depth) != TCL_OK) 
02872      return TCL_ERROR;
02873   
02874   int *surface;
02875   int n_surf;
02876   
02877   int ret_val = measure_surface(sel, app->moleculeList, framepos,
02878                                gridsz, radius, depth, &surface, &n_surf);
02879   if (ret_val < 0) {
02880     Tcl_AppendResult(interp, "measure surface: ", measure_error(ret_val));
02881     return TCL_ERROR;
02882   }
02883 
02884   Tcl_Obj *surf_list = Tcl_NewListObj(0, NULL);
02885 
02886   int i;
02887   for(i=0; i < n_surf; i++) {
02888     Tcl_ListObjAppendElement(interp, surf_list, Tcl_NewIntObj(surface[i]));
02889   }
02890   Tcl_SetObjResult(interp, surf_list);
02891   delete [] surface;
02892   
02893   return TCL_OK;
02894 }
02895 
02896 
02897 // Function: vmd_measure_pbc2onc <center> ?molid <default>? ?frame <frame|last>?
02898 //  Returns: the transformation matrix to wrap a nonorthogonal pbc unicell
02899 //           into an orthonormal cell.
02900 static int vmd_measure_pbc2onc_transform(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02901   if (argc < 2) {
02902     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> [molid <default>] [frame <frame|last>]");
02903     return TCL_ERROR;
02904   }
02905 
02906   // Read the center
02907   int ndim;
02908   Tcl_Obj **centerObj;
02909   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
02910     Tcl_AppendResult(interp, " measure pbc2onc: bad syntax", NULL);
02911     Tcl_AppendResult(interp, " Usage: measure pbc2onc <center> [molid <default>] [frame <frame|last>]", NULL);
02912     return TCL_ERROR;
02913   }
02914 
02915   if (ndim!=3) {
02916     Tcl_AppendResult(interp, " measure pbc2onc: need three numbers for a vector", NULL);
02917     return TCL_ERROR;
02918   }
02919     
02920   int i;
02921   double tmp;
02922   float center[3];
02923   for (i=0; i<3; i++) {
02924     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
02925       Tcl_AppendResult(interp, " measure pbc2onc: non-numeric in center", NULL);
02926       return TCL_ERROR;
02927     }
02928     center[i] = (float)tmp;
02929   }
02930 
02931   int molid = app->molecule_top();
02932   int frame = -2;
02933   if (argc>3) {
02934     for (i=2; i<argc; i+=2) {
02935       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02936       if (!strupncmp(argvcur, "molid", CMDLEN)) {
02937         // XXX lame test formulation should be corrected
02938         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
02939           // top is already default
02940         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
02941           Tcl_AppendResult(interp, " measure pbc2onc: bad molid", NULL);
02942           return TCL_ERROR;
02943         }
02944       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02945         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
02946           frame=-1;
02947         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02948           Tcl_AppendResult(interp, " measure pbc2onc: bad frame value", NULL);
02949           return TCL_ERROR;
02950         }
02951       } else {
02952         Tcl_AppendResult(interp, " measure pbc2onc: invalid syntax, no such keyword: ", argvcur, NULL);
02953         return TCL_ERROR;
02954       }
02955     }
02956   }
02957 
02958 
02959   Matrix4 transform;
02960   int ret_val = measure_pbc2onc(app->moleculeList, molid, frame, center, transform);
02961   if (ret_val < 0) {
02962     Tcl_AppendResult(interp, "measure pbc2onc: ", measure_error(ret_val), NULL);
02963     return TCL_ERROR;
02964   }
02965 
02966   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02967   for (i=0; i<4; i++) {
02968     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
02969     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+0]));
02970     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+4]));
02971     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+8]));
02972     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+12]));
02973     Tcl_ListObjAppendElement(interp, tcl_result, rowListObj);
02974   }
02975   Tcl_SetObjResult(interp, tcl_result);
02976 
02977   return TCL_OK;
02978 }
02979 
02980 // Function: vmd_measure_pbc_neighbors <center> <cutoff>
02981 //  Returns: All image atoms that are within cutoff Angstrom of the pbc unitcell.
02982 //           Two lists are returned. The first list holds the atom coordinates while
02983 //           the second one is an indexlist mapping the image atoms to the atoms in
02984 //           the unitcell.
02985 
02986 //   Input:  Since the pbc cell <center> is not stored in DCDs and cannot be set in
02987 //           VMD it must be provided by the user as the first argument.
02988 //           The second argument <cutoff> is the maximum distance (in Angstrom) from
02989 //           the PBC unit cell for atoms to be considered.
02990 
02991 // Options:  ?sel <selection>? :
02992 //           If an atomselection is provided after the keyword 'sel' then only those
02993 //           image atoms are returned that are within cutoff of the selected atoms
02994 //           of the main cell. In case cutoff is a vector the largest value will be
02995 //           used.
02996 
02997 //           ?align <matrix>? :
02998 //           In case the molecule was aligned you can supply the alignment matrix
02999 //           which is then used to correct for the rotation and shift of the pbc cell.
03000 
03001 //           ?boundingbox PBC | {<mincoord> <maxcoord>}?
03002 //           With this option the atoms are wrapped into a rectangular bounding box.
03003 //           If you provide "PBC" as an argument then the bounding box encloses the
03004 //           PBC box but then the cutoff is added to the bounding box. Negative 
03005 //           values for the cutoff dimensions are allowed and lead to a smaller box.
03006 //           Instead you can also provide a custom bounding box in form of the 
03007 //           minmax coordinates (list containing two coordinate vectors such as 
03008 //           returned by the measure minmax command). Here, again, the cutoff is
03009 //           added to the bounding box.
03010 static int vmd_measure_pbc_neighbors(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03011   if (argc < 3) {
03012     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> <cutoff> ?sel <selection>? ?align <matrix>? ?molid <default>? ?frame <frame|last>? ?boundingbox PBC|{<mincoord> <maxcoord>}?");
03013     return TCL_ERROR;
03014   }
03015 
03016   // Read the center
03017   int ndim;
03018   Tcl_Obj **centerObj;
03019   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
03020     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03021     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03022     return TCL_ERROR;
03023   }
03024 
03025   if (ndim!=3) {
03026     Tcl_AppendResult(interp, " measure pbcneighbors: need three numbers for a vector", NULL);
03027     return TCL_ERROR;
03028   }
03029     
03030   int i;
03031   double tmp;
03032   float center[3];
03033   for (i=0; i<3; i++) {
03034     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
03035       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in center", NULL);
03036       return TCL_ERROR;
03037     }
03038     center[i] = float(tmp);
03039   }
03040 
03041   // Read the cutoff
03042   Tcl_Obj **cutoffObj;
03043   if (Tcl_ListObjGetElements(interp, objv[2], &ndim, &cutoffObj) != TCL_OK) {
03044     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03045     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03046     return TCL_ERROR;
03047   }
03048 
03049   if (ndim!=3 && ndim!=1) {
03050     Tcl_AppendResult(interp, " measure pbcneighbors: need either one or three numbers for cutoff", NULL);
03051     return TCL_ERROR;
03052   }
03053 
03054   float cutoff[3];
03055   for (i=0; i<ndim; i++) {
03056     if (Tcl_GetDoubleFromObj(interp, cutoffObj[i], &tmp) != TCL_OK) {
03057       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in cutoff", NULL);
03058       return TCL_ERROR;
03059     }
03060     cutoff[i] = float(tmp);
03061   }
03062 
03063   if (ndim==1) { cutoff[2] = cutoff[1] = cutoff[0]; }
03064 
03065   bool molidprovided=0;
03066   float *boxminmax=NULL;
03067   int molid = app->molecule_top();
03068   int frame = -2;
03069   AtomSel *sel = NULL;
03070   Matrix4 alignment;
03071   if (argc>4) {
03072     for (i=3; i<argc; i+=2) {
03073       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03074       if (!strupncmp(argvcur, "sel", CMDLEN)) {
03075         // Read the atom selection
03076         sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03077         if (!sel) {
03078           Tcl_AppendResult(interp, "measure pbcneighbors: invalid atom selection", NULL);
03079           return TCL_ERROR;
03080         }
03081         if (!app->molecule_valid_id(sel->molid())) {
03082           Tcl_AppendResult(interp, "measure pbcneighbors: ",
03083                            measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03084           return TCL_ERROR;
03085         }
03086         if (!sel->selected) {
03087           Tcl_AppendResult(interp, "measure pbcneighbors: selection contains no atoms.", NULL);
03088           return TCL_ERROR;
03089         }
03090       } else if (!strupncmp(argvcur, "molid", CMDLEN)) {
03091         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
03092           // top is already default
03093         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
03094           Tcl_AppendResult(interp, " measure pbcneighbors: bad molid", NULL);
03095           return TCL_ERROR;
03096         }
03097         molidprovided = 1;
03098       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
03099         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
03100           frame=-1;
03101         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
03102           Tcl_AppendResult(interp, " measure pbcneighbors: bad frame value", NULL);
03103           return TCL_ERROR;
03104         }
03105       } else if (!strupncmp(argvcur, "align", CMDLEN)) {
03106         // Get the alignment matrix (as returned by 'measure fit')
03107         if (tcl_get_matrix("measure pbcneighbors: ", interp, objv[i+1], alignment.mat) != TCL_OK) {
03108           return TCL_ERROR;
03109         }
03110       } else if (!strupncmp(argvcur, "boundingbox", CMDLEN)) {
03111         // Determine if the atoms shall be wrapped to the smallest rectangular
03112         // bounding box that still encloses the unitcell plus the given cutoff.
03113         char *argv2 = Tcl_GetStringFromObj(objv[i+1],NULL);
03114         if (!strupncmp(argv2, "on", CMDLEN) || !strupncmp(argv2, "pbc", CMDLEN)) {
03115           boxminmax = new float[6];
03116           compute_pbcminmax(app->moleculeList, molid, frame, center, &alignment,
03117                             boxminmax, boxminmax+3);
03118         } else {
03119           // Read the bounding box
03120           int j, k, ncoor;
03121           Tcl_Obj **boxListObj;
03122           if (Tcl_ListObjGetElements(interp, objv[i+1], &ncoor, &boxListObj) != TCL_OK) {
03123             Tcl_AppendResult(interp, " measure pbcneighbors: invalid bounding box parameter", NULL);
03124             return TCL_ERROR;
03125           }
03126           if (ncoor!=2) {
03127             Tcl_AppendResult(interp, " measure pbcneighbors: need 2 points for bounding box", NULL);
03128             return TCL_ERROR;
03129           }
03130           int ndim = 0;
03131           double tmp;
03132           Tcl_Obj **boxObj;
03133           boxminmax = new float[6];
03134           for (j=0; j<2; j++) {
03135             if (Tcl_ListObjGetElements(interp, boxListObj[j], &ndim, &boxObj) != TCL_OK) {
03136               Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax in boundingbox", NULL);
03137               return TCL_ERROR;
03138             }
03139 
03140             for (k=0; k<3; k++) {
03141               if (Tcl_GetDoubleFromObj(interp, boxObj[k], &tmp) != TCL_OK) {
03142                 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in boundingbox", NULL);
03143                 return TCL_ERROR;
03144               }
03145               boxminmax[3L*j+k] = (float)tmp;
03146             }
03147           }
03148         }
03149 
03150       } else {
03151         Tcl_AppendResult(interp, " measure pbcneighbors: invalid syntax, no such keyword: ", argvcur, NULL);
03152         return TCL_ERROR;
03153       }
03154     }
03155   }
03156 
03157   // If no molid was provided explicitely but a selection was given
03158   // then use that molid.
03159   if (sel && !molidprovided) {
03160     molid = sel->molid();
03161   }
03162 
03163   ResizeArray<float> extcoord_array;
03164   ResizeArray<int> indexmap_array;
03165 
03166   int ret_val = measure_pbc_neighbors(app->moleculeList, sel, molid, frame, &alignment, center, cutoff, boxminmax, &extcoord_array, &indexmap_array);
03167   delete [] boxminmax;
03168 
03169   if (ret_val < 0) {
03170     Tcl_AppendResult(interp, "measure pbcneighbors: ", measure_error(ret_val), NULL);
03171     return TCL_ERROR;
03172   }
03173   printf("measure pbcneighbors: %ld neighbor atoms found\n", 
03174          long(indexmap_array.num()));
03175 
03176   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03177   Tcl_Obj *coorListObj = Tcl_NewListObj(0, NULL);
03178   Tcl_Obj *indexListObj = Tcl_NewListObj(0, NULL);
03179   for (i=0; i<indexmap_array.num(); i++) {
03180     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
03181     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i]));
03182     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+1]));
03183     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+2]));
03184     Tcl_ListObjAppendElement(interp, coorListObj, rowListObj);
03185     Tcl_ListObjAppendElement(interp, indexListObj, Tcl_NewIntObj(indexmap_array[i]));
03186   }
03187   Tcl_ListObjAppendElement(interp, tcl_result, coorListObj);
03188   Tcl_ListObjAppendElement(interp, tcl_result, indexListObj);
03189   Tcl_SetObjResult(interp, tcl_result);
03190 
03191   return TCL_OK;
03192 }
03193 
03194 
03195 
03196 // Function: vmd_measure_inertia <selection> [moments] [eigenvals]
03197 //  Returns: The center of mass and the principles axes of inertia for the 
03198 //           selected atoms. 
03199 //  Options: -moments -- also return the moments of inertia tensor
03200 //           -eigenvals -- also return the corresponding eigenvalues
03201 static int vmd_measure_inertia(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03202   bool moments = FALSE;
03203   bool eigenvals = FALSE;
03204   if (argc < 2 || argc > 4) {
03205     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<selection> [moments] [eigenvals]");
03206     return TCL_ERROR;
03207   }
03208   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03209   if (!sel) {
03210     Tcl_AppendResult(interp, "measure inertia: no atom selection", NULL);
03211     return TCL_ERROR;
03212   }
03213   
03214   if (!app->molecule_valid_id(sel->molid())) {
03215     Tcl_AppendResult(interp, "measure inertia: ",
03216                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03217     return TCL_ERROR;
03218   }
03219   
03220   if (argc>2) {
03221     int i;
03222     for (i=2; i<argc; i++) {
03223       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03224       // Allow syntax with and without leading dash
03225       if (argvcur[0]=='-') argvcur++;
03226 
03227       if (!strupncmp(argvcur, "moments", CMDLEN)) {
03228         moments = TRUE; 
03229       }
03230       else if (!strupncmp(argvcur, "eigenvals", CMDLEN)) {
03231         eigenvals = TRUE; 
03232       }
03233       else {
03234         Tcl_AppendResult(interp, " measure inertia: unrecognized option\n", NULL);
03235         Tcl_AppendResult(interp, " Usage: measure inertia <selection> [moments] [eigenvals]", NULL);
03236         return TCL_ERROR;
03237       }
03238     }
03239   }
03240 
03241   float priaxes[3][3];
03242   float itensor[4][4];
03243   float evalue[3], rcom[3];
03244   int ret_val = measure_inertia(sel, app->moleculeList, NULL, rcom, priaxes, itensor, evalue);
03245   if (ret_val < 0) {
03246     Tcl_AppendResult(interp, "measure inertia: ", measure_error(ret_val), NULL);
03247     return TCL_ERROR;
03248   }
03249 
03250   // return COM and list of 3 principal axes
03251   int i;
03252   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03253 
03254   Tcl_Obj *rcomObj = Tcl_NewListObj(0, NULL);
03255   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[0]));
03256   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[1]));
03257   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[2]));
03258   Tcl_ListObjAppendElement(interp, tcl_result, rcomObj);
03259 
03260   Tcl_Obj *axesListObj = Tcl_NewListObj(0, NULL);
03261   for (i=0; i<3; i++) {
03262     Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
03263     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][0]));
03264     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][1]));
03265     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][2]));
03266     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03267   }
03268   Tcl_ListObjAppendElement(interp, tcl_result, axesListObj);
03269 
03270   if (moments) {
03271     Tcl_Obj *momListObj = Tcl_NewListObj(0, NULL);
03272     for (i=0; i<3; i++) {
03273       Tcl_Obj *momObj = Tcl_NewListObj(0, NULL);
03274       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][0]));
03275       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][1]));
03276       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][2]));
03277       Tcl_ListObjAppendElement(interp, momListObj, momObj);
03278     }
03279     Tcl_ListObjAppendElement(interp, tcl_result, momListObj);
03280   }
03281 
03282   if (eigenvals) {
03283       Tcl_Obj *eigvListObj = Tcl_NewListObj(0, NULL);
03284       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[0]));
03285       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[1]));
03286       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[2]));
03287       Tcl_ListObjAppendElement(interp, tcl_result, eigvListObj);
03288   }
03289   Tcl_SetObjResult(interp, tcl_result);
03290 
03291   return TCL_OK;
03292 }
03293 
03294 
03295 // measure symmetry <sel> [plane|I|C<n>|S<n> [<vector>]] [-tol <value>]
03296 //                  [-nobonds] [-verbose <level>]
03297 //
03298 // This function evaluates the molecular symmetry of an atom selection.
03299 // The underlying algorithm finds the symmetry elements such as 
03300 // inversion center, mirror planes, rotary axes and rotary reflections.
03301 // Based on the found elements it guesses the underlying point group.
03302 // The guess is fairly robust and can handle molecules whose
03303 // coordinatesthat deviate to a certain extent from the ideal symmetry.
03304 // The closest match with the highest symmetry will be returned.
03305 //
03306 // Options:
03307 // --------
03308 // -tol <value>
03309 //    Allows one to control tolerance of the algorithm when
03310 //    considering wether something is symmetric or not.
03311 //    A smaller value signifies a lower tolerance, the default
03312 //    is 0.1.
03313 // -nobonds 
03314 //    If this flag is set then the bond order and orientation
03315 //    are not considered when comparing structures.
03316 // -verbose <level>
03317 //    Controls the amount of console output.
03318 //    A level of 0 means no output, 1 gives some statistics at the
03319 //    end of the search (default). Level 2 gives additional info
03320 //    about each stage, level 3 more output for each iteration
03321 //    and 3, 4 yield yet more additional info.
03322 // -I
03323 //    Instead of guessing the symmetry pointgroup of the selection
03324 //    determine if the selection's center off mass represents an
03325 //    inversion center. The returned value is a score between 0
03326 //    and 1 where 1 denotes a perfect match.
03327 // -plane <vector>
03328 //    Instead of guessing the symmetry pointgroup of the selection
03329 //    determine if the plane with the defined by its normal
03330 //    <vector> is a mirror plane of the selection. The
03331 //    returned value is a score between 0 and 1 where 1 denotes
03332 //    a perfect match.
03333 // -Cn | -Sn  <vector>
03334 //    Instead of guessing the symmetry pointgroup of the selection
03335 //    determine if the rotation or rotary reflection axis Cn/Sn
03336 //    with order n defined by <vector> exists for the
03337 //    selection. E.g., if you want to query wether the Y-axis
03338 //    has a C3 rotational symmetry you specify "C3 {0 1 0}".
03339 //    The returned value is a score between 0 and 1 where 1
03340 //    denotes a perfect match.
03341 //
03342 // Result:
03343 // -------
03344 // The return value is a TCL list of pairs consisting of a label
03345 // string and a value or list. For each label the data following
03346 // it are described below:
03347 //
03348 // * [pointgroup] The guessed pointgroup:
03349 // * [order] Point group order (the n from above)
03350 // * [elements] Summary of found elements.
03351 //     For instance {(i) (C3) 3*(C2) (S6) 3*(sigma)} for D3d.
03352 // * [missing] Elements missing with respect to ideal number of
03353 //     elements (same format as above). If this is not an empty
03354 //     list then something has gone awfully wrong with the symmetry
03355 //     finding algorithm.
03356 // * [additional] Additional elements  that would not be expected
03357 //     for this point group (same format as above). If this is not
03358 //     an empty list then something has gone awfully wrong with the
03359 //     symmetry finding algorithm.
03360 // * [com] Center of mass of the selection based on the idealized
03361 //     coordinates.
03362 // * [inertia] List of the three axes of inertia, the eigenvalues
03363 //     of the moments of inertia tensor and a list of three 0/1 flags 
03364 //     specifying for each axis wether it is unique or not.
03365 // * [inversion] Flag 0/1 signifying if there is an inversion center.
03366 // * [axes] Normalized vectors defining rotary axes
03367 // * [rotreflect] Normalized vectors defining rotary reflections
03368 // * [planes] Normalized vectors defining mirror planes.
03369 // * [ideal]  Idealized symmetric coordinates for all atoms of
03370 //     the selection. The coordinates are listed in the order of 
03371 //     increasing atom indices (same order asa returned by the
03372 //     atomselect command ``get {x y z}''). Thus you can use the list
03373 //     to set the atoms of your selection to the ideal coordinates
03374 // * [unique] Index list defining a set of atoms with unique
03375 //     coordinates
03376 // * [orient] Matrix that aligns molecule with GAMESS standard
03377 //     orientation
03378 //
03379 // If a certain item is not present (e.g. no planes or no axes)
03380 // then the corresponding value is an empty list.
03381 // The pair format allows to use the result as a TCL array for
03382 // convenient access of the different return items.
03383 
03384 static int vmd_measure_symmetry(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03385   if (argc<2) {
03386     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]");
03387     return TCL_ERROR;
03388   }
03389   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03390   if (!sel) {
03391     Tcl_AppendResult(interp, "measure symmetry: no atom selection", NULL);
03392     return TCL_ERROR;
03393   }
03394   if (!app->molecule_valid_id(sel->molid())) {
03395     Tcl_AppendResult(interp, "measure symmetry: ",
03396                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03397     return TCL_ERROR;
03398   }
03399 
03400   double sigma = 0.1;
03401   float axis[3];
03402   int checkelement = 0;
03403   int checkbonds = 1;
03404   int order = 0;
03405   int verbose = 1;
03406   int impose = 0;
03407   int imposeinvers = 0;
03408   int numimposeplan = 0;
03409   int numimposeaxes = 0;
03410   int numimposerref = 0;
03411   float *imposeplan = NULL;
03412   float *imposeaxes = NULL;
03413   float *imposerref = NULL;
03414   int *imposeaxesord = NULL;
03415   int *imposerreford = NULL;
03416   AtomSel *idealsel = NULL;
03417 
03418   if (argc>2) {
03419     int bailout = 0;
03420     int i;
03421     for (i=2; (i<argc && !bailout); i++) {
03422       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03423       // Allow syntax with and without leading dash
03424       if (argvcur[0]=='-') argvcur++;
03425       
03426       if (!strupncmp(argvcur, "tol", CMDLEN)) {
03427         if (Tcl_GetDoubleFromObj(interp, objv[i+1], &sigma) != TCL_OK) {
03428           Tcl_AppendResult(interp, " measure symmetry: bad tolerance value", NULL);
03429           bailout = 1; continue;
03430         }
03431         i++;
03432       }
03433       else if (!strupncmp(argvcur, "verbose", CMDLEN)) {
03434         if (Tcl_GetIntFromObj(interp, objv[i+1], &verbose) != TCL_OK) {
03435           Tcl_AppendResult(interp, " measure symmetry: bad verbosity level value", NULL);
03436           bailout = 1; continue;
03437         }
03438         i++;
03439       }
03440       else if (!strupncmp(argvcur, "nobonds", CMDLEN)) {
03441         checkbonds = 0;
03442       }
03443       else if (!strupcmp(argvcur, "I")) {
03444         checkelement = 1;
03445       }
03446       else if (!strupncmp(argvcur, "plane", CMDLEN)) {
03447         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03448           bailout = 1; continue;
03449         }
03450         checkelement = 2;
03451         i++;
03452       }
03453       else if (!strupncmp(argvcur, "C", 1) || !strupncmp(argvcur, "S", 1)) {
03454         char *begptr = argvcur+1;
03455         char *endptr;
03456         order = strtol(begptr, &endptr, 10);
03457         if (endptr==begptr || *endptr!='\0') {
03458           Tcl_AppendResult(interp, " measure symmetry: bad symmetry element format (must be I, C*, S*, plane, where * is the order). ", NULL);
03459           bailout = 1; continue;
03460         }
03461 
03462         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03463           bailout = 1; continue;
03464         }
03465         
03466         if (!strupncmp(argvcur, "C", 1)) checkelement = 3;
03467         if (!strupncmp(argvcur, "S", 1)) checkelement = 4;
03468         i++;
03469       }
03470       else if (!strupncmp(argvcur, "idealsel", CMDLEN)) {
03471         idealsel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03472         if (!sel) {
03473           Tcl_AppendResult(interp, "measure symmetry: no atom selection for idealized coordinates", NULL);
03474           bailout = 1; continue;
03475         }
03476         if (idealsel->molid()!=sel->molid()) {
03477           Tcl_AppendResult(interp, "measure symmetry: selection and idealsel must be from the same molecule", NULL);
03478           bailout = 1; continue;
03479         }
03480         if (idealsel->selected<sel->selected) {
03481           Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03482           bailout = 1; continue;
03483         }
03484         // make sure sel is subset of idealsel
03485         int j;
03486         for (j=0; j<sel->num_atoms; j++) {
03487           if (sel->on[j] && !idealsel->on[j]) {
03488             Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03489             bailout = 1; continue;
03490           }
03491         }
03492 
03493         i++;
03494       }
03495       else if (!strupncmp(argvcur, "imposeinversion", CMDLEN)) {
03496         imposeinvers = 1;
03497         impose = 1;
03498       }
03499       else if (!strupncmp(argvcur, "imposeplanes", CMDLEN)) {
03500         // Format: list of normal vectors {{x y z} {x y z} ...}
03501         int nelem;
03502         Tcl_Obj **elemListObj;
03503         if (i+1>=argc ||
03504             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK) {
03505           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03506           bailout = 1; continue;
03507         }
03508         float *elem = new float[3L*nelem];
03509         int k;
03510         for (k=0; k<nelem; k++) {
03511           int nobj;
03512           Tcl_Obj **vecObj;
03513           if (Tcl_ListObjGetElements(interp, elemListObj[k], &nobj, &vecObj) != TCL_OK) {
03514             delete [] elem;
03515             Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03516             bailout = 1; continue;
03517           }
03518           if (nobj!=3) {
03519             delete [] elem;
03520             Tcl_AppendResult(interp, " measure symmetry imposeplanes: vector must have 3 elements", NULL);
03521             bailout = 1; continue;
03522           }
03523           
03524           int m;
03525           for (m=0; m<3; m++) {
03526             double d;
03527             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03528               delete [] elem;
03529               bailout = 1; continue;
03530             }
03531             elem[3L*k+m] = (float)d;
03532           }
03533         }
03534         if (imposeplan) delete [] imposeplan;
03535         imposeplan = elem;
03536         numimposeplan = nelem;
03537         impose = 1;
03538         i++;
03539       }
03540       else if (!strupncmp(argvcur, "imposeaxes", CMDLEN) ||
03541                !strupncmp(argvcur, "imposerotref", CMDLEN)) {
03542         // Format: list of axes and orders {{x y z} 3 {x y z} 2 ...}
03543         int nelem;
03544         Tcl_Obj **elemListObj;
03545         if (i+1>=argc ||
03546             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK ||
03547             nelem%2) {
03548           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeaxes/imposerotref option", NULL);
03549           bailout = 1; continue;
03550         }
03551         nelem /= 2;
03552      
03553         if (nelem<=0) {
03554           i++;
03555           continue;
03556         }
03557         float *elem = new float[3L*nelem];
03558         int *axorder = new int[nelem];
03559         int k;
03560         for (k=0; k<nelem; k++) {
03561           int nobj;
03562           Tcl_Obj **vecObj;
03563           if (Tcl_ListObjGetElements(interp, elemListObj[2L*k], &nobj, &vecObj) != TCL_OK) {
03564             delete [] elem;
03565             delete [] axorder;
03566             Tcl_AppendResult(interp, " measure symmetry impose: bad syntax for axis vector", NULL);
03567             bailout = 1; continue;
03568           }
03569           if (Tcl_GetIntFromObj(interp, elemListObj[2L*k+1], &axorder[k]) != TCL_OK) {
03570             delete [] elem;
03571             delete [] axorder;
03572             bailout = 1; continue;
03573           }
03574           if (nobj!=3) {
03575             delete [] elem;
03576             delete [] axorder;
03577             Tcl_AppendResult(interp, " measure symmetry impose: axis vector must have 3 elements", NULL);
03578             bailout = 1; continue;
03579           }
03580           
03581           int m;
03582           for (m=0; m<3; m++) {
03583             double d;
03584             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03585               delete [] elem;
03586               delete [] axorder;
03587               bailout = 1; continue;
03588             }
03589             elem[3L*k+m] = (float)d;
03590           }
03591         }
03592         if (!strupncmp(argvcur, "imposeaxes", CMDLEN)) {
03593           if (imposeaxes)    delete [] imposeaxes;
03594           if (imposeaxesord) delete [] imposeaxesord;
03595           imposeaxes = elem;
03596           imposeaxesord = axorder;
03597           numimposeaxes = nelem;
03598         } else if (!strupncmp(argvcur, "imposerotref", CMDLEN)) {
03599           if (imposerref)    delete [] imposerref;
03600           if (imposerreford) delete [] imposerreford;
03601           imposerref = elem;
03602           imposerreford = axorder;
03603           numimposerref = nelem;
03604         }
03605       
03606         impose = 1;
03607         i++;
03608       }
03609       else {
03610         Tcl_AppendResult(interp, " measure symmetry: unrecognized option ", NULL);
03611         Tcl_AppendResult(interp, argvcur);
03612         Tcl_AppendResult(interp, ".\n Usage: measure symmetry <selection> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]", NULL);
03613         bailout = 1; continue;
03614       }
03615     }
03616 
03617     if (bailout) {
03618       if (imposeplan) delete [] imposeplan;
03619       if (imposeaxes) delete [] imposeaxes;
03620       if (imposerref) delete [] imposerref;
03621       if (imposeaxesord) delete [] imposeaxesord;
03622       if (imposerreford) delete [] imposerreford;
03623       return TCL_ERROR;
03624     }
03625   }
03626 
03627   // Initialize
03628   Symmetry sym = Symmetry(sel, app->moleculeList, verbose);
03629 
03630   // Set tolerance for atomic overlapping
03631   sym.set_overlaptol(float(sigma));
03632 
03633   // Wether to include bonds into the analysis
03634   sym.set_checkbonds(checkbonds);
03635 
03636   if (checkelement) {
03637     // We are interested only in the score for a specific
03638     // symmetry element.
03639     float overlap = 0.0;
03640     if (checkelement==1) {
03641       overlap = sym.score_inversion();
03642     }
03643     else if (checkelement==2) {
03644       overlap = sym.score_plane(axis);
03645     }
03646     else if (checkelement==3) {
03647       overlap = sym.score_axis(axis, order);
03648     }
03649     else if (checkelement==4) {
03650       overlap = sym.score_rotary_reflection(axis, order);
03651     }
03652 
03653     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03654     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03655     Tcl_SetObjResult(interp, tcl_result);
03656     return TCL_OK;
03657   }
03658 
03659   if (impose) {
03660     sym.impose(imposeinvers, numimposeplan, imposeplan,
03661                numimposeaxes, imposeaxes, imposeaxesord,
03662                numimposerref, imposerref, imposerreford);
03663     if (imposeplan) delete [] imposeplan;
03664     if (imposeaxes) delete [] imposeaxes;
03665     if (imposerref) delete [] imposerref;
03666     if (imposeaxesord) delete [] imposeaxesord;
03667     if (imposerreford) delete [] imposerreford;
03668   }
03669 
03670   int ret_val = sym.guess(float(sigma));
03671   if (ret_val < 0) {
03672     Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03673     return TCL_ERROR;
03674   }
03675 
03676   int natoms = sel->selected;
03677   Symmetry *s = &sym;
03678 
03679   if (idealsel) {
03680     Symmetry *isym = new Symmetry(idealsel, app->moleculeList, verbose);
03681     isym->set_overlaptol(float(sigma));
03682     isym->set_checkbonds(checkbonds);
03683     int j;
03684     float *plane = new float[3L*sym.numplanes()];
03685     for (j=0; j<sym.numplanes(); j++) {
03686       vec_copy(&plane[3L*j], sym.plane(j));
03687     }
03688     int *axisorder = new int[sym.numaxes()];
03689     float *axis = new float[3L*sym.numaxes()];
03690     for (j=0; j<sym.numaxes(); j++) {
03691       axisorder[j] = sym.get_axisorder(j);
03692       vec_copy(&axis[3L*j], sym.axis(j));
03693     }
03694     int *rrorder = new int[sym.numrotreflect()];
03695     float *rraxis = new float[3L*sym.numrotreflect()];
03696     for (j=0; j<sym.numrotreflect(); j++) {
03697       rrorder[j] = sym.get_rotreflectorder(j);
03698       vec_copy(&rraxis[3L*j], sym.rotreflect(j));
03699     }
03700     // XXX must check if sel is subset of idealsel
03701     int k=0, m=0;
03702     for (j=0; j<sel->num_atoms; j++) {
03703       if (idealsel->on[j]) {
03704         if (sel->on[j]) {
03705           vec_copy(isym->idealpos(k), sym.idealpos(m));
03706           m++;
03707         }
03708         k++;
03709       }
03710     }
03711     isym->impose(sym.has_inversion(),
03712                  sym.numplanes(), plane,
03713                  sym.numaxes(),   axis, axisorder,
03714                  sym.numrotreflect(), rraxis, rrorder);
03715 
03716     ret_val = isym->guess(float(sigma));
03717     if (ret_val < 0) {
03718       Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03719       return TCL_ERROR;
03720     }
03721 
03722     natoms = idealsel->selected;
03723     s = isym;
03724   }
03725 
03726   int pgorder;
03727   char pointgroup[6];
03728   s->get_pointgroup(pointgroup, &pgorder);
03729 
03730   int i;
03731   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03732   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("pointgroup", -1));
03733   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(pointgroup, -1));
03734   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("order", -1));
03735   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(pgorder));
03736   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rmsd", -1));
03737   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(s->get_rmsd()));
03738   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("elements", -1));
03739   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_element_summary(), -1));
03740   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("missing", -1));
03741   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_missing_elements(), -1));
03742   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("additional", -1));
03743   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_additional_elements(), -1));
03744 
03745   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("com", -1));
03746   Tcl_Obj *invObj  = Tcl_NewListObj(0, NULL);
03747   float *com = s->center_of_mass();
03748   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[0]));
03749   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[1]));
03750   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[2]));
03751   Tcl_ListObjAppendElement(interp, tcl_result, invObj);
03752 
03753   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inertia", -1));
03754   Tcl_Obj *inertListListObj  = Tcl_NewListObj(0, NULL);
03755   float *inertia  = s->get_inertia_axes();
03756   float *eigenval = s->get_inertia_eigenvals();
03757   int   *unique   = s->get_inertia_unique();
03758   for (i=0; i<3; i++) {
03759     Tcl_Obj *inertObj  = Tcl_NewListObj(0, NULL);
03760     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i]));
03761     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+1]));
03762     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+2]));
03763     Tcl_Obj *inertListObj  = Tcl_NewListObj(0, NULL);
03764     Tcl_ListObjAppendElement(interp, inertListObj, inertObj);
03765     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewDoubleObj(eigenval[i]));
03766     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewIntObj(unique[i]));
03767     Tcl_ListObjAppendElement(interp, inertListListObj, inertListObj);
03768   }
03769   Tcl_ListObjAppendElement(interp, tcl_result, inertListListObj);
03770 
03771  
03772   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inversion", -1));
03773   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(s->has_inversion()));
03774 
03775 
03776   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("axes", -1));
03777   Tcl_Obj *axesListListObj  = Tcl_NewListObj(0, NULL);
03778   for (i=0; i<s->numaxes(); i++) {
03779     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03780     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03781     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[0]));
03782     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[1]));
03783     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[2]));
03784     Tcl_Obj *axesListObj  = Tcl_NewListObj(0, NULL);
03785     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03786     Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewIntObj(s->get_axisorder(i)));
03787     int axistype = s->get_axistype(i);
03788     if (axistype & PRINCIPAL_AXIS && 
03789         !(!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS))) {
03790       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("principal", -1));
03791     } else if (!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS)) {
03792       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("perpendicular", -1));
03793     } else {
03794       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewListObj(0, NULL));
03795     }
03796     Tcl_ListObjAppendElement(interp, axesListListObj, axesListObj);
03797   }
03798   Tcl_ListObjAppendElement(interp, tcl_result, axesListListObj);
03799 
03800 
03801   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rotreflect", -1));
03802   Tcl_Obj *rraxesListListObj  = Tcl_NewListObj(0, NULL);
03803   for (i=0; i<s->numrotreflect(); i++) {
03804     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03805     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03806     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[0]));
03807     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[1]));
03808     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[2]));
03809     Tcl_Obj *rraxesListObj  = Tcl_NewListObj(0, NULL);
03810     Tcl_ListObjAppendElement(interp, rraxesListObj, axesObj);
03811     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotreflectorder(i)));
03812     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotrefltype(i)));
03813     Tcl_ListObjAppendElement(interp, rraxesListListObj, rraxesListObj);
03814   }
03815   Tcl_ListObjAppendElement(interp, tcl_result, rraxesListListObj);
03816 
03817 
03818   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("planes", -1));
03819   Tcl_Obj *planesListListObj = Tcl_NewListObj(0, NULL);
03820   for (i=0; i<s->numplanes(); i++) {
03821     Tcl_Obj *planesObj = Tcl_NewListObj(0, NULL);
03822     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03823     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[0]));
03824     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[1]));
03825     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[2]));
03826     Tcl_Obj *planesListObj = Tcl_NewListObj(0, NULL);
03827     Tcl_ListObjAppendElement(interp, planesListObj, planesObj);
03828     switch (s->get_planetype(i)) {
03829     case 1:
03830       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("vertical", -1));
03831       break;
03832     case 3:
03833       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("dihedral", -1));
03834       break;
03835     case 4:
03836       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("horizontal", -1));
03837       break;
03838     default:
03839       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewListObj(0, NULL));
03840     }
03841     Tcl_ListObjAppendElement(interp, planesListListObj, planesListObj);
03842   }
03843   Tcl_ListObjAppendElement(interp, tcl_result, planesListListObj);
03844 
03845 
03846   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("ideal", -1));
03847   Tcl_Obj *idealcoorListObj = Tcl_NewListObj(0, NULL);
03848   for (i=0; i<natoms; i++) {
03849     Tcl_Obj *idealcoorObj = Tcl_NewListObj(0, NULL);
03850     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03851     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[0]));
03852     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[1]));
03853     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[2]));
03854     Tcl_ListObjAppendElement(interp, idealcoorListObj, idealcoorObj);
03855   }
03856   Tcl_ListObjAppendElement(interp, tcl_result, idealcoorListObj);
03857 
03858   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("unique", -1));
03859   Tcl_Obj *uniquecoorListObj = Tcl_NewListObj(0, NULL);
03860   for (i=0; i<natoms; i++) {
03861     if (!s->get_unique_atom(i)) continue; 
03862     Tcl_ListObjAppendElement(interp, uniquecoorListObj, Tcl_NewIntObj(i));
03863   }
03864   Tcl_ListObjAppendElement(interp, tcl_result, uniquecoorListObj);
03865 
03866   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("orient", -1));
03867   Matrix4 *orient = s->get_orientation();
03868   Tcl_Obj *matrixObj = Tcl_NewListObj(0, NULL);
03869   for (i=0; i<4; i++) {
03870     Tcl_Obj *rowObj = Tcl_NewListObj(0, NULL);
03871     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+0]));
03872     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+4]));
03873     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+8]));
03874     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+12]));
03875     Tcl_ListObjAppendElement(interp, matrixObj, rowObj);
03876   }
03877   Tcl_ListObjAppendElement(interp, tcl_result, matrixObj);
03878 
03879   Tcl_SetObjResult(interp, tcl_result);
03880   if (idealsel) {
03881     delete s;
03882   }
03883   return TCL_OK;
03884 
03885 }
03886 
03887 // Function: vmd_measure_transoverlap <selection>
03888 //  Returns: The structural overlap of a selection with a copy of itself
03889 //           that is transformed according to a given transformation matrix.
03890 //           The normalized sum over all gaussian function values of the pair
03891 //           distances between atoms in the original and the transformed
03892 //           selection.
03893 //    Input: the atom selection and the transformation matrix.
03894 //   Option: with -sigma you can specify the sigma value of the overlap 
03895 //           gaussian function. The default is 0.1 Angstrom.
03896 static int vmd_measure_trans_overlap(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03897   if (argc!=3 && argc!=5) {
03898     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> <matrix> [-sigma <value>]");
03899     return TCL_ERROR;
03900   }
03901   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03902   if (!sel) {
03903     Tcl_AppendResult(interp, "measure transoverlap: no atom selection", NULL);
03904     return TCL_ERROR;
03905   }
03906   if (!app->molecule_valid_id(sel->molid())) {
03907     Tcl_AppendResult(interp, "measure transoverlap: ",
03908                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03909     return TCL_ERROR;
03910   }
03911 
03912   // Get the transformation matrix
03913   Matrix4 trans;
03914   if (tcl_get_matrix("measure transoverlap: ",interp,objv[2], trans.mat) != TCL_OK) {
03915     return TCL_ERROR;
03916   }
03917 
03918   double sigma = 0.1;
03919   if (argc==5) {
03920     if (!strupncmp(Tcl_GetStringFromObj(objv[3],NULL), "-sigma", CMDLEN)) {
03921       if (Tcl_GetDoubleFromObj(interp, objv[4], &sigma) != TCL_OK) {
03922         Tcl_AppendResult(interp, " measure transoverlap: bad sigma value", NULL);
03923         return TCL_ERROR;
03924       }
03925     } else {
03926       Tcl_AppendResult(interp, " measure transoverlap: unrecognized option\n", NULL);
03927       Tcl_AppendResult(interp, " Usage: measure transoverlap <sel> <matrix> [-sigma <value>]", NULL);
03928       return TCL_ERROR;
03929     }
03930   }
03931 
03932   int maxnatoms = 100;// XXX
03933   float overlap;
03934   int ret_val = measure_trans_overlap(sel, app->moleculeList, &trans,
03935                                       float(sigma), NOSKIP_IDENTICAL, 
03936                                       maxnatoms, overlap);
03937   if (ret_val < 0) {
03938     Tcl_AppendResult(interp, "measure transoverlap: ", measure_error(ret_val), NULL);
03939     return TCL_ERROR;
03940   }
03941 
03942   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03943   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03944   Tcl_SetObjResult(interp, tcl_result);
03945 
03946   return TCL_OK;
03947 }
03948 
03949 
03950 
03951 //
03952 // Raycasting brute-force approach by Juan R. Perilla <juan@perilla.me>
03953 //
03954 int vmd_measure_volinterior(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03955   int verbose = 0;
03956   int recognized=0;
03957   if (argc < 3) {
03958      // "     options: --allframes (average over all frames)\n"
03959     Tcl_SetResult(interp, (char *) "usage: volinterior "
03960                   " <selection1> [options]\n"
03961                   "-res (default 10.0)\n"
03962                   "-spacing (default res/3)\n"
03963                   "-loadmap (load synth map)\n"
03964                   "-probmap (compute and load probability map)\n"
03965                   "   --> -count_pmap (return percentile-based voxel counts)\n"
03966                   "-discretize [float cutoff] (creates discrete map with cutoff probability)\n"
03967                   "-mol molid [default: top]\n"
03968                   "-vol volid [0]\n"
03969                   "-nrays number of rays to cast [6]\n"
03970                   "-isovalue [float: 1.0]\n"
03971                   "-verbose\n"
03972                   "-overwrite volid\n",
03973                   TCL_STATIC);
03974     return TCL_ERROR;
03975   }
03976 
03977   //atom selection
03978   AtomSel *sel = NULL;
03979   sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03980   if (!sel) {
03981     Tcl_AppendResult(interp, "volinterior: no atom selection.", NULL);
03982     return TCL_ERROR;
03983   }
03984   if (!sel->selected) {
03985     Tcl_AppendResult(interp, "volinterior: no atoms selected.", NULL);
03986     return TCL_ERROR;
03987   }
03988   if (!app->molecule_valid_id(sel->molid())) {
03989     Tcl_AppendResult(interp, "invalid mol id.", NULL);
03990     return TCL_ERROR;
03991   }
03992   int loadsynthmap = 0;
03993   int loadpmap=0; 
03994   int count_pmap=0;
03995   int print_raydirs=0; 
03996   int molid = -1;
03997   int volid = -1;
03998   int volidOverwrite = -1;
03999   int overwrite=0;
04000   int nrays=6;
04001   long Nout = 0;
04002   float isovalue=1.0;
04003   float proCutoff=0.90f;
04004   int process=0;
04005   float radscale;
04006   double resolution = 10.0;
04007   double gspacing;
04008   MoleculeList *mlist = app->moleculeList;
04009   Molecule *mymol = mlist->mol_from_id(sel->molid());
04010 
04011   for (int i=2; i < argc; i++) {
04012     recognized=0;
04013     char *opt = Tcl_GetStringFromObj(objv[i], NULL);
04014     if (!strcmp(opt, "volinterior")) {
04015       recognized=1;
04016     }
04017     if (!strcmp(opt, "-mol")) {
04018       if (i == argc-1) {
04019         Tcl_AppendResult(interp, "No molid specified",NULL);
04020         return TCL_ERROR;
04021       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04022         Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04023         return TCL_ERROR;
04024       }
04025       recognized=1;
04026     }
04027     if (!strcmp(opt, "-probmap")) {
04028       loadpmap=1;
04029       recognized=1;
04030     }
04031     if (!strcmp(opt, "-checkrays")) {
04032       print_raydirs=1;
04033       recognized=1;
04034     }
04035     if (!strcmp(opt, "-count_pmap")) {
04036       count_pmap=1;
04037       recognized=1;
04038     }
04039     if (!strcmp(opt, "-discretize")) {
04040       double tcutoff;
04041       if (i==argc-1) {
04042         Tcl_AppendResult(interp, "No cutoff probability given for -discretize flag",NULL);
04043         return TCL_ERROR;
04044       } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &tcutoff)!=TCL_OK) {
04045         Tcl_AppendResult(interp, "\n Cutoff probability for -discretize flag incorrectly specified.",NULL);
04046         return TCL_ERROR;
04047       }
04048       proCutoff=(float)tcutoff;
04049       recognized=1;
04050       process=1;
04051     }
04052     if (!strcmp(opt, "-vol")) {
04053       if (i == argc-1){
04054         Tcl_AppendResult(interp, "No volume id specified",NULL);
04055         return TCL_ERROR;
04056       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &volid) != TCL_OK) {
04057         Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04058         return TCL_ERROR;
04059       }
04060       recognized=1;
04061     }
04062 
04063     if (!strcmp(opt, "-overwrite")) {
04064       if (i == argc-1){
04065         Tcl_AppendResult(interp, "No volume id specified",NULL);
04066         return TCL_ERROR;
04067       } else if (Tcl_GetIntFromObj(interp, objv[i+1], &volidOverwrite) != TCL_OK) {
04068         Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04069         return TCL_ERROR;
04070       }
04071       overwrite=1;
04072       recognized=1;
04073     }
04074 
04075     // Calculate synthmap
04076     if (!strcmp(opt, "-loadmap")) {
04077       loadsynthmap = 1;
04078       recognized=1;
04079     }
04080     if (!strcmp(opt, "-mol")) {
04081       if (i == argc-1) {
04082         Tcl_AppendResult(interp, "No molid specified",NULL);
04083         return TCL_ERROR;
04084       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04085         Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04086         return TCL_ERROR;
04087       }
04088       recognized=1;
04089     }
04090    if (!strcmp(opt, "-res")) {
04091       if (i == argc-1) {
04092         Tcl_AppendResult(interp, "No resolution specified",NULL);
04093         return TCL_ERROR;
04094       } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &resolution) != TCL_OK) { 
04095         Tcl_AppendResult(interp, "\nResolution incorrectly specified",NULL);
04096         return TCL_ERROR;
04097       }
04098       recognized=1;
04099     }
04100     if (!strcmp(opt, "-spacing")) {
04101       if (i == argc-1) {
04102         Tcl_AppendResult(interp, "No spacing specified",NULL);
04103         return TCL_ERROR;
04104       } else if ( Tcl_GetDoubleFromObj(interp, objv[i+1], &gspacing) != TCL_OK) {
04105         Tcl_AppendResult(interp, "\nspacing incorrectly specified",NULL);
04106         return TCL_ERROR;
04107       }
04108       recognized=1;
04109     }
04110 
04111     if (!strcmp(opt,"-nrays")) {
04112       if (i == argc-1) {
04113         Tcl_AppendResult(interp, "No number of rays specified",NULL);
04114         return TCL_ERROR;
04115       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &nrays) != TCL_OK) {
04116         Tcl_AppendResult(interp, "\n number of rays incorrectly specified",NULL);
04117         return TCL_ERROR;
04118       }
04119       recognized=1;
04120     }
04121 
04122     if (!strcmp(opt,"-isovalue")) {
04123       double tmp;
04124       if (i == argc-1) {
04125         Tcl_AppendResult(interp,"No isovalue specified",NULL);
04126         return TCL_ERROR;
04127       } else if (Tcl_GetDoubleFromObj(interp,objv[i+1],&tmp) != TCL_OK) {
04128         Tcl_AppendResult(interp,"\n Isovalue incorrectly specified", NULL);
04129         return TCL_ERROR;
04130       }
04131       isovalue=(float) tmp;
04132       recognized=1;
04133     }
04134 
04135 //    if (!strcmp(opt, "-o")) {
04136 //      if (i == argc-1) {
04137 //      //  Tcl_AppendResult(interp, "No output file specified",NULL);
04138 //      //  return TCL_ERROR;
04139 //      if (verbose) 
04140 //        printf("No output file specified.");
04141 //      } else {
04142 //        outputmap = Tcl_GetStringFromObj(objv[i+1], NULL);
04143 //      }
04144 //    }
04145 
04146     if (!strcmp(opt, "-verbose") || (getenv("VMDVOLINVERBOSE") != NULL)) {
04147       verbose = 1;
04148       recognized=1;
04149     }
04150 
04151     if (recognized==0) {
04152       // XXX what case is this supposed to handle? Dangling number parms?
04153       if (isdigit(opt[0]) || isfloat(opt)) { 
04154         continue;
04155       } else {
04156         Tcl_AppendResult(interp,"\n unrecognized option: '",opt,"'",NULL);
04157         return TCL_ERROR;
04158       }
04159     }
04160   }
04161 
04162   if ((process||count_pmap) && !loadpmap) {
04163     Tcl_AppendResult(interp,"\n Cannot give -process or -count_pmap flags given without -probmap!",NULL);
04164     return TCL_ERROR;
04165   }
04166 
04167   Molecule *volmolOverwrite = NULL;
04168   VolumetricData *volmapOverwrite = NULL;
04169   Molecule *pmolOverwrite=NULL;
04170   VolumetricData *pmapOverwrite=NULL;
04171   if (overwrite==1) {
04172     if (loadpmap!=1) {
04173       volmolOverwrite = mlist->mol_from_id(molid);
04174       volmapOverwrite = volmolOverwrite->modify_volume_data(volidOverwrite);
04175       if(volmapOverwrite==NULL) {
04176         Tcl_AppendResult(interp, "\n no overwrite volume correctly specified",NULL);
04177         return TCL_ERROR;
04178       }
04179     } else {
04180       pmolOverwrite=mlist->mol_from_id(molid);
04181       pmapOverwrite=pmolOverwrite->modify_volume_data(volidOverwrite);
04182       if (pmapOverwrite==NULL) {
04183         Tcl_AppendResult(interp,"\n no pmap overwrite volume correctly specified",NULL);
04184         return TCL_ERROR;
04185       }
04186     }
04187   }
04188 
04189   VolumetricData *pmapT = NULL;
04190   VolumetricData *procmap = NULL;
04191   VolumetricData *pmap = NULL;  
04192   VolumetricData *target = NULL;
04193   VolumetricData *volmapA = NULL;
04194   
04195 
04196   const float *framepos = sel->coordinates(app->moleculeList);
04197   const float *radii = mymol->radius();
04198   radscale= 0.2f * float(resolution);
04199   if (gspacing == 0) {
04200     gspacing=resolution*0.33;
04201   }
04202 
04203   int quality=0;
04204   QuickSurf *qs = new QuickSurf();
04205   if (resolution >= 9)
04206     quality = 0;
04207   else
04208     quality = 3;
04209 
04210   if (molid != -1 && volid != -1 ) {
04211     Molecule *volmol = mlist->mol_from_id(molid);
04212     volmapA = (VolumetricData *) volmol->get_volume_data(volid);
04213     if (volmapA==NULL) {
04214       Tcl_AppendResult(interp, "\n no target volume correctly specified", NULL);
04215       return TCL_ERROR;
04216     }
04217   } else {
04218     if (verbose) printf("\n Calculating grid ... \n");
04219     int cuda_err=-1;
04220 #if defined(VMDCUDA)
04221     cuda_err = vmd_cuda_calc_density(sel, app->moleculeList, quality,
04222                                      radscale, gspacing, &volmapA, 
04223                                      NULL, NULL, verbose);
04224 #endif
04225     if (cuda_err == -1 ) {
04226       if (verbose) printf("Using CPU version of the code ... \n");
04227       volmapA = qs->calc_density_map(sel, mymol, framepos, radii,
04228                                      quality, radscale, float(gspacing));
04229     }
04230     if (verbose) printf("Done.\n");
04231   }
04232 
04233   if (volmapA == NULL) {
04234     Tcl_AppendResult(interp, "\n no test volume or molid correctly specified",NULL);
04235     return TCL_ERROR;
04236   }
04237   target=CreateEmptyGrid(volmapA);
04238 
04239   if (loadpmap == 1) {
04240     pmapT = CreateProbGrid(volmapA);
04241   }
04242 
04243   // Normalize dir vector 
04244   // TODO: Let user define direction vectors
04245 
04246   //vec_normalize(rayDir);
04247   
04248   // Create Grid
04249 #if 0
04250   float rayDir[3] = {1,0,0};
04251   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04252   rayDir[0] = -1; rayDir[1]=0; rayDir[2]=0;
04253   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04254   rayDir[0] = 0; rayDir[1]=1; rayDir[2]=0;
04255   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04256   rayDir[0] = 0; rayDir[1]=-1; rayDir[2]=0;
04257   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04258   rayDir[0] = 0; rayDir[1]=0; rayDir[2]=1;
04259   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04260   rayDir[0] = 0; rayDir[1]=0; rayDir[2]=-1;
04261   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04262 #endif
04263 #if 0
04264   if (verbose) printf("Marking down boundary.\n");
04265   long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04266   if (verbose) printf("Casting rays ...\n");
04267   float rayDir[3] = {0.1f,0.1f,1.0f};
04268   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04269   if (verbose) printf("Dir 1 %ld \n",Nout);
04270   rayDir[0] = -0.1f; rayDir[1]=-0.1f; rayDir[2]=-1.0f;
04271   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04272   if (verbose) printf("Dir 2 %ld \n",Nout);
04273   rayDir[0] = 0.1f; rayDir[1]=1.0f; rayDir[2]=0.1f;
04274   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04275   if (verbose) printf("Dir 3 %ld \n",Nout);
04276   rayDir[0] = -0.1f; rayDir[1]=-1.0f; rayDir[2]=-0.1f;
04277   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04278   if (verbose) printf("Dir 4 %ld \n",Nout);
04279   rayDir[0] = 1.0f; rayDir[1]=0.1f; rayDir[2]=0.1f;
04280   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04281   if (verbose) printf("Dir 5 %ld \n",Nout);
04282   rayDir[0] = -1.0f; rayDir[1]=-0.1f; rayDir[2]=-0.1f;
04283   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04284   if (verbose) printf("Dir 6 %ld \n",Nout);
04285   rayDir[0] = -0.5f; rayDir[1]=-0.5f; rayDir[2]=-0.5f;
04286   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04287   if (verbose) printf("Dir 7 %ld \n",Nout);
04288   rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04289   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04290   if (verbose) printf("Dir 9 %ld \n",Nout);
04291   rayDir[0] = -0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04292   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04293   if (verbose) printf("Dir 10 %ld \n",Nout);
04294   rayDir[0] = 0.5f; rayDir[1]=-0.5f; rayDir[2]=0.5f;
04295   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04296   if (verbose) printf("Dir 11 %ld \n",Nout);
04297   rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=-0.5f;
04298   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04299   if (verbose) printf("Dir 12 %ld \n",Nout);
04300 #endif
04301   if (verbose) printf("Marking down boundary.\n");
04302   long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04303   float rayDir[3] = {0.1f,0.1f,1.0f};
04304   static const float RAND_MAX_INV = 1.0f/float(VMD_RAND_MAX);
04305 
04306   float *poisson_set = new float[(nrays+1)*2];
04307   int numcandidates=40;
04308   int converged=poisson_sample_on_sphere(poisson_set,nrays,numcandidates,verbose);
04309   if (converged==0) {
04310     if (verbose) {
04311       printf("Poisson disk sampler failed for measure volinterior!\n");
04312       printf("... continuing with standard ray direction generator.\n");
04313     }
04314   }
04315 
04316   if (print_raydirs && converged)
04317     print_xyz(poisson_set,nrays);
04318 
04319   if (verbose) printf("Casting rays...\n");
04320   // Seed the random number generator before each calculation.  
04321   //vmd_srandom(103467829);
04322   // Cast nrays uniformly over the sphere
04323 
04324   // XXX Use of sincosf() now in place. It would be much more work-efficient
04325   //     to build up batches of rays and pass them into the multithreaded
04326   //     work routines, as launching the worker threads has a non-trivial
04327   //     cost.  It would be better if they plow through many/all rays before
04328   //     returning, since the control loops here aren't checking for any
04329   //     conditions to be met other than that we've processed all of the rays.
04330 
04331   float sine1, cosine1, sine2, cosine2;
04332   for (int ray=0; ray < nrays; ray++) { 
04333     float u1 = (float) vmd_random();
04334     float u2 = (float) vmd_random();
04335     float z = 2.0f*u1*RAND_MAX_INV -1.0f;
04336     float phi = (float) (2.0f*VMD_PI*u2*RAND_MAX_INV);
04337     float R = sqrtf(1.0f-z*z);
04338     if (converged==1) {
04339       sincosf(poisson_set[ray*2+0],&sine1,&cosine1);
04340       sincosf(poisson_set[ray*2+1],&sine2,&cosine2);
04341       rayDir[0]=cosine1*sine2;
04342       rayDir[1]=sine1*sine2;
04343       rayDir[2]=cosine2;
04344     } else {
04345       sincosf(phi,&sine1,&cosine1);
04346       rayDir[0]=R*cosine1;
04347       rayDir[1]=R*sine1;
04348       rayDir[2]=z;
04349     }
04350 
04351     // XXX rather than launching threads per ray, we should be launching
04352     //     the worker threads and processing _all_ of the rays...
04353     //     That would also lead toward an implementation that could
04354     //     eventually be migrated to the GPU for higher performance.
04355     if (loadpmap == 1) {
04356       Nout += volin_threaded_prob(volmapA, target, pmapT, isovalue, rayDir);
04357     } else {
04358       Nout += volin_threaded(volmapA, target, isovalue, rayDir);
04359     }
04360 
04361     if (verbose) printf("Ray(%d)  (%4.3f %4.3f %4.3f). Voxels : %ld \n",ray+1,rayDir[0],rayDir[1],rayDir[2],Nout);    
04362   }
04363   if (verbose) printf("Done.\n");
04364   
04365   delete [] poisson_set;
04366 
04367   if (verbose) printf("Counting voxels above isovalue ...\n");
04368   
04369   Nout=countIsoGrids(target,EXTERIORVOXEL);
04370   long nIn=countIsoGrids(target,INTERIORVOXEL);
04371  
04372   if (loadsynthmap == 1 ) {
04373     app->molecule_add_volumetric(molid, volmapA->name, volmapA->origin, 
04374                                  volmapA->xaxis, volmapA->yaxis, volmapA->zaxis,
04375                                  volmapA->xsize, volmapA->ysize, volmapA->zsize,
04376                                  volmapA->data);
04377     volmapA->compute_volume_gradient();
04378     volmapA->data = NULL; // prevent destruction of density array;  
04379   }
04380 
04381   if (overwrite != 1 ) {
04382     if (loadpmap==1) {
04383       if (verbose) printf("Normalizing probability map ...\n");
04384       pmap = normalize_pmap(pmapT, nrays);
04385       app->molecule_add_volumetric(molid, pmap->name, pmap->origin,
04386                                    pmap->xaxis, pmap->yaxis, pmap->zaxis,
04387                                    pmap->xsize, pmap->ysize, pmap->zsize, 
04388                                    pmap->data);
04389       pmap->compute_volume_gradient();
04390     } else {
04391       app->molecule_add_volumetric(molid, target->name, target->origin, 
04392                                    target->xaxis, target->yaxis, target->zaxis,
04393                                    target->xsize, target->ysize, target->zsize,
04394                                    target->data);
04395       target->compute_volume_gradient();
04396       target->data = NULL; // prevent destruction of density array;  
04397     }
04398   } else {
04399     if (loadpmap==1) {
04400       if (verbose) printf("Normalizing probability map ...\n");
04401       pmap = normalize_pmap(pmapT, nrays);
04402       if (verbose) printf("Overwriting volume...\n");
04403       pmapOverwrite->name=pmap->name;
04404       pmapOverwrite->xsize=pmap->xsize;
04405       pmapOverwrite->ysize=pmap->ysize;
04406       pmapOverwrite->zsize=pmap->zsize;
04407       pmapOverwrite->data=pmap->data;
04408       pmap->data=NULL;
04409       pmapOverwrite->compute_volume_gradient();
04410       if (verbose) printf("Done!\n");
04411     } else {
04412       if (verbose) printf("Overwriting volume ... \n");
04413       volmapOverwrite->name = target -> name;
04414       volmapOverwrite->xsize = target->xsize;
04415       volmapOverwrite->ysize = target->ysize;
04416       volmapOverwrite->zsize = target->zsize;
04417       volmapOverwrite->data = target->data;
04418       target->data=NULL;
04419       volmapOverwrite->compute_volume_gradient();
04420       if (verbose) printf("Done.\n ");
04421     }  
04422   }
04423 
04424   if ((loadpmap&&process)&&pmap->data!=NULL) {
04425     //Tcl_AppendResult(interp,"Processing probability map with cutoff: ",proCutoff,"...",NULL);
04426     procmap=process_pmap(pmap, proCutoff);
04427     app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04428       procmap->xaxis, procmap->yaxis, procmap->zaxis,
04429       procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04430     procmap->compute_volume_gradient();
04431     procmap->data=NULL;
04432     if (verbose) printf("Loaded processed map\n");
04433   } else if ((loadpmap&&process)&&pmapOverwrite->data!=NULL) {
04434      procmap=process_pmap(pmapOverwrite, proCutoff);
04435      app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04436        procmap->xaxis, procmap->yaxis, procmap->zaxis,
04437        procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04438      procmap->compute_volume_gradient();
04439      procmap->data=NULL;
04440     if (verbose) printf("Loaded processed map (they don't overwrite).\n");
04441   }
04442 
04443   if (count_pmap==1 && pmap->data!=NULL) {
04444     if (loadpmap!=1) {
04445       printf("-count_pmap flag specified without -probmap!\n");
04446       return TCL_ERROR;
04447     } else {
04448       long Pvol=0;
04449       float currProb=0.0f;
04450       Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04451       for (int c=0; c<10; c++) {
04452         currProb+=0.1f;
04453         if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04454         Pvol=vol_probability(pmap, currProb, float(gspacing));
04455         Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04456       }
04457       Tcl_SetObjResult(interp,analysis);
04458       if (molid != -1 && volid != -1 ) {
04459         target->data = NULL; // prevent destruction of density array;
04460         pmap->data = NULL;
04461       } else {
04462         delete pmapT;
04463         delete qs;
04464         delete volmapA;
04465       }   
04466 
04467       return TCL_OK;
04468     }
04469   } else if (count_pmap==1 && overwrite==1 && pmapOverwrite->data!=NULL) {
04470     long Pvol=0;
04471     float currProb=0.0f;
04472     Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04473     for (int c=0; c<10; c++) {
04474       currProb+=0.1f;
04475       if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04476       Pvol=vol_probability(pmapOverwrite, currProb, float(gspacing));
04477       Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04478     }
04479     Tcl_SetObjResult(interp,analysis);
04480     if (molid != -1 && volid != -1 ) {
04481       target->data = NULL; // prevent destruction of density array;
04482       pmap->data = NULL;
04483     } else {
04484       delete pmapT;
04485       delete qs;
04486       delete volmapA;
04487     }
04488 
04489     return TCL_OK;
04490   }
04491 
04492   if (molid != -1 && volid != -1 ) {
04493     target->data = NULL; // prevent destruction of density array;
04494     procmap->data=NULL;
04495     pmap->data = NULL;
04496   } else {
04497     delete pmapT;
04498     delete qs;
04499     delete volmapA;
04500   }
04501 
04502   long Ntotal = target->xsize * target->ysize * target->zsize;
04503   //printf("VolIn: %ld external voxels.\n",Nout);
04504   
04505   Tcl_Obj *tcl_result = Tcl_NewListObj(0,NULL);
04506   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Ntotal));
04507   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Nout));
04508   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nIn));
04509   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nVoxIso));
04510   Tcl_SetObjResult(interp,tcl_result);
04511   return TCL_OK;
04512 }
04513 
04514 int obj_measure(ClientData cd, Tcl_Interp *interp, int argc,
04515                             Tcl_Obj * const objv[]) {
04516 
04517   if (argc < 2) {
04518     Tcl_SetResult(interp, 
04519       (char *) "usage: measure <command> [args...]\n"
04520       "\nMeasure Commands:\n"
04521       "  avpos <sel> [first <first>] [last <last>] [step <step>] -- average position\n"
04522       "  center <sel> [weight <weights>]          -- geometrical (or weighted) center\n"
04523       "  centerperresidue <sel> [weight <weights>]  -- geometrical center for every residue in sel\n"
04524       "  cluster <sel> [num <#clusters>] [distfunc <flag>] [cutoff <cutoff>]\n"
04525       "          [first <first>] [last <last>] [step <step>] [selupdate <bool>]\n"
04526       "          [weight <weights>]\n"
04527       "     -- perform a cluster analysis (cluster similar timesteps)\n"
04528       "  clustsize <sel> [cutoff <float>] [minsize <num>] [numshared <num>]\n"
04529       "            [usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]\n"
04530       "     -- perform a cluster size analysis (find clusters of atoms)\n"
04531       "  contacts <cutoff> <sel1> [<sel2>]        -- list contacts\n" 
04532       "  dipole <sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]\n"
04533       "     -- dipole moment\n"
04534       "  fit <sel1> <sel2> [weight <weights>] [order <index list>]\n"
04535       "     -- transformation matrix from selection 1 to 2\n"
04536       "  gofr <sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>]\n"
04537       "     [selupdate <bool>] [first <first>] [last <last>] [step <step>]\n"
04538       "     -- atomic pair distribution function g(r)\n"
04539       "  hbonds <cutoff> <angle> <sel1> [<sel2>]\n"
04540       "     -- list donors, acceptors, hydrogens involved in hydrogen bonds\n"
04541       "  inverse <matrix>                         -- inverse matrix\n"
04542       "  inertia <sel> [-moments] [-eigenvals]    -- COM and principle axes of inertia\n"
04543       "  minmax <sel> [-withradii]                -- bounding box\n"
04544       "  rgyr <sel> [weight <weights>]            -- radius of gyration\n"
04545       "  rmsd <sel1> <sel2> [weight <weights>]    -- RMS deviation\n"
04546       "  rmsdperresidue <sel1> <sel2> [weight <weights>] -- deviation per residue\n"
04547       "  rmsf <sel> [first <first>] [last <last>] [step <step>] -- RMS fluctuation\n"
04548       "  rmsfperresidue <sel> [first <first>] [last <last>] [step <step>] -- fluctuation per residue\n"
04549       "  sasa <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
04550       "     [-samples <numsamples>]               -- solvent-accessible surface area\n"
04551       "  sumweights <sel> weight <weights>        -- sum of selected weights\n"
04552       "  bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}}\n"
04553       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04554       "     -- bond length between atoms 1 and 2\n"
04555       "  angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}}\n"
04556       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04557       "     -- angle between atoms 1-3\n"
04558       "  dihed {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04559       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04560       "      -- dihedral angle between atoms 1-4\n"
04561       "  imprp {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04562       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04563       "     -- improper angle between atoms 1-4\n"
04564       // FIXME: Complete 'measure energy' usage info here?
04565       "  energy bond|angle|dihed|impr|vdw|elec     -- compute energy\n"
04566       "  surface <sel> <gridsize> <radius> <thickness> -- surface of selection\n"
04567       "  pbc2onc <center> [molid <default>] [frame <frame|last>]\n"
04568       "     --  transformation matrix to wrap a nonorthogonal PBC unit cell\n"
04569       "         into an orthonormal cell\n"
04570       "  pbcneighbors <center> <cutoff> [sel <sel>] [align <matrix>] [molid <default>]\n"
04571       "     [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]\n"
04572       "     -- all image atoms that are within cutoff Angstrom of the pbc unit cell\n"
04573       "  symmetry <sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]\n"
04574       "  transoverlap <sel> <matrix> [-sigma <value>]\n"
04575       "     -- overlap of a structure with a transformed copy of itself\n"
04576       "  volinterior <sel> [options]\n"
04577       "     -- type 'measure volinterior' for usage\n",
04578       TCL_STATIC);
04579     return TCL_ERROR;
04580   }
04581   VMDApp *app = (VMDApp *)cd;
04582   char *argv1 = Tcl_GetStringFromObj(objv[1],NULL);
04583   if (!strupncmp(argv1, "avpos", CMDLEN))
04584     return vmd_measure_avpos(app, argc-1, objv+1, interp);
04585   else if (!strupncmp(argv1, "center", CMDLEN))
04586     return vmd_measure_center(app, argc-1, objv+1, interp);
04587 #if 1
04588   // XXX in test
04589   else if (!strupncmp(argv1, "centerperresidue", CMDLEN))
04590     return vmd_measure_centerperresidue(app, argc-1, objv+1, interp);
04591 #endif
04592   else if (!strupncmp(argv1, "cluster", CMDLEN))
04593     return vmd_measure_cluster(app, argc-1, objv+1, interp);
04594   else if (!strupncmp(argv1, "clustsize", CMDLEN))
04595     return vmd_measure_clustsize(app, argc-1, objv+1, interp);
04596   else if (!strupncmp(argv1, "contacts", CMDLEN))
04597     return vmd_measure_contacts(app, argc-1, objv+1, interp);
04598   else if (!strupncmp(argv1, "dipole", CMDLEN))
04599     return vmd_measure_dipole(app, argc-1, objv+1, interp);
04600   else if (!strupncmp(argv1, "fit", CMDLEN)) 
04601     return vmd_measure_fit(app, argc-1, objv+1, interp);
04602   else if (!strupncmp(argv1, "minmax", CMDLEN))
04603     return vmd_measure_minmax(app, argc-1, objv+1, interp);
04604   else if (!strupncmp(argv1, "gofr", CMDLEN))
04605     return vmd_measure_gofr(app, argc-1, objv+1, interp);
04606   else if (!strupncmp(argv1, "rdf", CMDLEN))
04607     return vmd_measure_rdf(app, argc-1, objv+1, interp);
04608   else if (!strupncmp(argv1, "hbonds", CMDLEN))
04609     return vmd_measure_hbonds(app, argc-1, objv+1, interp);
04610   else if (!strupncmp(argv1, "inverse", CMDLEN)) 
04611     return vmd_measure_inverse(argc-1, objv+1, interp);
04612   else if (!strupncmp(argv1, "inertia", CMDLEN)) 
04613     return vmd_measure_inertia(app, argc-1, objv+1, interp);
04614   else if (!strupncmp(argv1, "rgyr", CMDLEN))
04615     return vmd_measure_rgyr(app, argc-1, objv+1, interp);
04616   else if (!strupncmp(argv1, "rmsd", CMDLEN))
04617     return vmd_measure_rmsd(app, argc-1, objv+1, interp);
04618 #if 1
04619   // XXX in test
04620   else if (!strupncmp(argv1, "rmsdperresidue", CMDLEN))
04621     return vmd_measure_rmsdperresidue(app, argc-1, objv+1, interp);
04622 #endif
04623 #if 1
04624   else if (!strupncmp(argv1, "rmsd_qcp", CMDLEN))
04625     return vmd_measure_rmsd_qcp(app, argc-1, objv+1, interp);
04626   else if (!strupncmp(argv1, "rmsdmat_qcp", CMDLEN))
04627     return vmd_measure_rmsdmat_qcp(app, argc-1, objv+1, interp);
04628   else if (!strupncmp(argv1, "rmsdmat_qcp_ooc", CMDLEN))
04629     return vmd_measure_rmsdmat_qcp_ooc(app, argc-1, objv+1, interp);
04630 #endif
04631   else if (!strupncmp(argv1, "rmsf", CMDLEN))
04632     return vmd_measure_rmsf(app, argc-1, objv+1, interp);
04633 #if 1
04634   // XXX in test
04635   else if (!strupncmp(argv1, "rmsfperresidue", CMDLEN))
04636     return vmd_measure_rmsfperresidue(app, argc-1, objv+1, interp);
04637 #endif
04638   else if (!strupncmp(argv1, "sasa", CMDLEN))
04639     return vmd_measure_sasa(app, argc-1, objv+1, interp);
04640 #if 1
04641   else if (!strupncmp(argv1, "sasalist", CMDLEN))
04642     return vmd_measure_sasalist(app, argc-1, objv+1, interp);
04643 #endif
04644   else if (!strupncmp(argv1, "sumweights", CMDLEN))
04645     return vmd_measure_sumweights(app, argc-1, objv+1, interp);
04646   else if (!strupncmp(argv1, "imprp", CMDLEN))
04647     return vmd_measure_dihed(app, argc-1, objv+1, interp);
04648   else if (!strupncmp(argv1, "dihed", CMDLEN))
04649     return vmd_measure_dihed(app, argc-1, objv+1, interp);
04650   else if (!strupncmp(argv1, "angle", CMDLEN))
04651     return vmd_measure_angle(app, argc-1, objv+1, interp);
04652   else if (!strupncmp(argv1, "bond", CMDLEN))
04653     return vmd_measure_bond(app, argc-1, objv+1, interp);
04654   else if (!strupncmp(argv1, "energy", CMDLEN))
04655     return vmd_measure_energy(app, argc-1, objv+1, interp);
04656   else if (!strupncmp(argv1, "pbc2onc", CMDLEN))
04657     return vmd_measure_pbc2onc_transform(app, argc-1, objv+1, interp);
04658   else if (!strupncmp(argv1, "pbcneighbors", CMDLEN))
04659     return vmd_measure_pbc_neighbors(app, argc-1, objv+1, interp);
04660   else if (!strupncmp(argv1, "surface", CMDLEN))
04661     return vmd_measure_surface(app, argc-1, objv+1, interp);
04662   else if (!strupncmp(argv1, "transoverlap", CMDLEN))
04663     return vmd_measure_trans_overlap(app, argc-1, objv+1, interp);
04664   else if (!strupncmp(argv1, "symmetry", CMDLEN))
04665     return vmd_measure_symmetry(app, argc-1, objv+1, interp);
04666   else if (!strupncmp(argv1, "volinterior", CMDLEN))
04667     return vmd_measure_volinterior(app, argc-1, objv+1, interp);
04668 
04669   Tcl_SetResult(interp, (char *) "Type 'measure' for summary of usage\n", TCL_VOLATILE);
04670   return TCL_OK;
04671 }
04672 
04673 
04674 

Generated on Fri Apr 19 02:45:26 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002