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: dgomes $       $Locker:  $             $State: Exp $
00014  *      $Revision: 1.186 $      $Date: 2024/05/16 14:56:56 $
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 static int vmd_measure_sasaperresidue(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02474 
02475   int i;
02476   // srad and one atom selection, plus additional options
02477   if (argc < 3) {
02478     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-restrict <restrictedsel>] [-samples <numsamples>]");
02479     return TCL_ERROR;
02480   }
02481   // parse options
02482   Tcl_Obj *ptsvar = NULL;
02483   AtomSel *restrictsel = NULL;
02484   int nsamples = -1, rescount;
02485   int *sampleptr = NULL;
02486   for (i=3; i<argc-1; i+=2) {
02487     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02488     if (!strcmp(opt, "-points")) {
02489       ptsvar = objv[i+1];
02490     } else if (!strcmp(opt, "-restrict")) {
02491       restrictsel = tcl_commands_get_sel(interp, 
02492           Tcl_GetStringFromObj(objv[i+1], NULL));
02493       if (!restrictsel) {
02494         Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02495         return TCL_ERROR;
02496       }
02497     } else if (!strcmp(opt, "-samples")) {
02498       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02499         return TCL_ERROR;
02500       sampleptr = &nsamples;
02501     } else {
02502       Tcl_AppendResult(interp, "measure sasa per residue: unknown option '", opt, "'", 
02503           NULL);
02504       return TCL_ERROR;
02505     }
02506   }
02507 
02508   double srad;
02509   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02510     return TCL_ERROR;
02511   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02512   if (!sel1) {
02513     Tcl_AppendResult(interp, "measure sasa per residue: invalid first atom selection", NULL);
02514     return TCL_ERROR;
02515   }
02516 
02517   const float *pos = sel1->coordinates(app->moleculeList);
02518   if (!pos) {
02519     Tcl_AppendResult(interp, "measure sasa per residue: error, molecule contains no coordinates", NULL);
02520     return TCL_ERROR;
02521   }
02522   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02523   const float *radius = mol->extraflt.data("radius");
02524 
02525   ResizeArray<float> sasapts;
02526   float *sasavals;
02527   sasavals = new float[sel1->selected];
02528   int rc = measure_sasa_perresidue(sel1, pos, radius, (float) srad, sasavals, &sasapts, 
02529                         restrictsel, sampleptr,&rescount,app->moleculeList);
02530   if (rc < 0) {
02531     Tcl_AppendResult(interp, "measure: sasa per residue : ", measure_error(rc), NULL);
02532     delete [] sasavals;
02533     return TCL_ERROR;
02534   }
02535   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02536   for (int i=0; i<rescount; i++) {
02537     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(sasavals[i]));
02538   }
02539   Tcl_SetObjResult(interp, tcl_result);
02540   if (ptsvar) {
02541     // construct list from sasapts
02542     Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02543     i=0;
02544     while (i<sasapts.num()) {
02545       Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02546       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02547       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02548       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02549       Tcl_ListObjAppendElement(interp, listobj, elem);
02550     }
02551     Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02552   }
02553   delete [] sasavals;
02554   return TCL_OK;
02555 }
02556 
02557   
02558 static int vmd_measure_sasa(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02559 
02560   int i;
02561   // srad and one atom selection, plus additional options
02562   if (argc < 3) {
02563     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel> [-points <varname>] [-restrict <restrictedsel>] [-samples <numsamples>]");
02564     return TCL_ERROR;
02565   }
02566   // parse options
02567   Tcl_Obj *ptsvar = NULL;
02568   AtomSel *restrictsel = NULL;
02569   int nsamples = -1;
02570   int *sampleptr = NULL;
02571   for (i=3; i<argc-1; i+=2) {
02572     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02573     if (!strcmp(opt, "-points")) {
02574       ptsvar = objv[i+1];
02575     } else if (!strcmp(opt, "-restrict")) {
02576       restrictsel = tcl_commands_get_sel(interp, 
02577           Tcl_GetStringFromObj(objv[i+1], NULL));
02578       if (!restrictsel) {
02579         Tcl_AppendResult(interp, "measure sasa: invalid restrict atom selection", NULL);
02580         return TCL_ERROR;
02581       }
02582     } else if (!strcmp(opt, "-samples")) {
02583       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02584         return TCL_ERROR;
02585       sampleptr = &nsamples;
02586     } else {
02587       Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", 
02588           NULL);
02589       return TCL_ERROR;
02590     }
02591   }
02592 
02593   double srad;
02594   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02595     return TCL_ERROR;
02596   AtomSel *sel1 = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[2],NULL));
02597   if (!sel1) {
02598     Tcl_AppendResult(interp, "measure sasa: invalid first atom selection", NULL);
02599     return TCL_ERROR;
02600   }
02601 
02602   const float *pos = sel1->coordinates(app->moleculeList);
02603   if (!pos) {
02604     Tcl_AppendResult(interp, "measure sasa: error, molecule contains no coordinates", NULL);
02605     return TCL_ERROR;
02606   }
02607   Molecule *mol = app->moleculeList->mol_from_id(sel1->molid());
02608   const float *radius = mol->extraflt.data("radius");
02609 
02610   ResizeArray<float> sasapts;
02611   float sasa = 0;
02612   int rc = measure_sasa(sel1, pos, radius, (float) srad, &sasa, &sasapts, 
02613                         restrictsel, sampleptr);
02614   if (rc < 0) {
02615     Tcl_AppendResult(interp, "measure: sasa: ", measure_error(rc), NULL);
02616     return TCL_ERROR;
02617   }
02618   Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sasa));
02619   if (ptsvar) {
02620     // construct list from sasapts
02621     Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02622     i=0;
02623     while (i<sasapts.num()) {
02624       Tcl_Obj *elem = Tcl_NewListObj(0, NULL);
02625       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02626       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02627       Tcl_ListObjAppendElement(interp, elem, Tcl_NewDoubleObj(sasapts[i++]));
02628       Tcl_ListObjAppendElement(interp, listobj, elem);
02629     }
02630     Tcl_ObjSetVar2(interp, ptsvar, NULL, listobj, 0);
02631   }
02632   return TCL_OK;
02633 }
02634 
02635 
02636 #if 1
02637 
02638 static int vmd_measure_sasalist(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02639 
02640   int i;
02641   // srad and one atom selection, plus additional options
02642   if (argc < 3) {
02643     Tcl_WrongNumArgs(interp, 2, objv-1, (char *)"<srad> <sel list> [-samples <numsamples>]");
02644     return TCL_ERROR;
02645   }
02646 
02647   // parse options
02648   int nsamples = -1;
02649   int *sampleptr = NULL;
02650   for (i=3; i<argc-1; i+=2) {
02651     const char *opt = Tcl_GetStringFromObj(objv[i], NULL);
02652 
02653     if (!strcmp(opt, "-samples")) {
02654       if (Tcl_GetIntFromObj(interp, objv[i+1], &nsamples) != TCL_OK)
02655         return TCL_ERROR;
02656       sampleptr = &nsamples;
02657     } else {
02658       Tcl_AppendResult(interp, "measure sasa: unknown option '", opt, "'", NULL);
02659       return TCL_ERROR;
02660     }
02661   }
02662 
02663   double srad;
02664   if (Tcl_GetDoubleFromObj(interp, objv[1], &srad) != TCL_OK) 
02665     return TCL_ERROR;
02666 
02667   int numsels;
02668   Tcl_Obj **sel_list;
02669   if (Tcl_ListObjGetElements(interp, objv[2], &numsels, &sel_list) != TCL_OK) {
02670     Tcl_AppendResult(interp, "measure sasalist: bad selection list", NULL);
02671     return TCL_ERROR;
02672   }
02673 
02674 #if 0
02675 printf("measure sasalist: numsels %d\n", numsels);
02676 #endif
02677 
02678   AtomSel **asels = (AtomSel **) calloc(1, numsels * sizeof(AtomSel *));
02679   float *sasalist = (float *) calloc(1, numsels * sizeof(float));
02680 
02681   int s;
02682   for (s=0; s<numsels; s++) {
02683     asels[s] = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(sel_list[s], NULL));
02684     if (!asels[s]) {
02685 printf("measure sasalist: invalid selection %d\n", s);
02686       Tcl_AppendResult(interp, "measure sasalist: invalid atom selection list element", NULL);
02687       return TCL_ERROR;
02688     }
02689   }
02690 
02691   int rc = measure_sasalist(app->moleculeList, (const AtomSel **) asels, 
02692                             numsels, (float) srad, sasalist, sampleptr);
02693   free(asels);
02694   if (rc < 0) {
02695     Tcl_AppendResult(interp, "measure: sasalist: ", measure_error(rc), NULL);
02696     return TCL_ERROR;
02697   }
02698 
02699   // construct list from sasa values
02700   Tcl_Obj *listobj = Tcl_NewListObj(0, NULL);
02701   for (i=0; i<numsels; i++) {
02702     Tcl_ListObjAppendElement(interp, listobj, Tcl_NewDoubleObj(sasalist[i]));
02703   }
02704   Tcl_SetObjResult(interp, listobj);
02705   free(sasalist);
02706 
02707   return TCL_OK;
02708 }
02709 
02710 #endif
02711 
02712 
02713 // Function: vmd_measure_energy 
02714 // Returns: the energy for the specified bond/angle/dihed/imprp/vdw/elec
02715 // FIXME -- usage doesn't match user guide
02716 static int vmd_measure_energy(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02717 
02718   if (argc<3) {
02719     Tcl_WrongNumArgs(interp, 2, objv-1,
02720                      (char *) "bond|angle|dihed|imprp|vdw|elec {{<atomid1> ?<molid1>?} {<atomid2> ?<molid2>?}} ?molid <default molid>? [?frame <frame|all>? | ?first <first>? ?last <last>?]");
02721     return TCL_ERROR;
02722   }
02723 
02724   int geomtype, reqatoms;
02725   char *geomname = Tcl_GetStringFromObj(objv[1],NULL);
02726   if        (!strncmp(geomname, "bond", 4)) {
02727     reqatoms = 2; geomtype = MEASURE_BOND;
02728   } else if (!strncmp(geomname, "angl", 4)) {
02729     reqatoms = 3; geomtype = MEASURE_ANGLE;
02730   } else if (!strncmp(geomname, "dihe", 4)) {
02731     reqatoms = 4; geomtype = MEASURE_DIHED;
02732   } else if (!strncmp(geomname, "impr", 4)) {
02733     reqatoms = 4; geomtype = MEASURE_IMPRP;
02734   } else if (!strncmp(geomname, "vdw",  3)) {
02735     reqatoms = 2; geomtype = MEASURE_VDW;
02736   } else if (!strncmp(geomname, "elec", 4)) {
02737     reqatoms = 2; geomtype = MEASURE_ELECT;
02738   } else {
02739     Tcl_AppendResult(interp, " measure energy: bad syntax (must specify bond|angle|dihed|imprp|vdw|elec)", NULL);
02740     return TCL_ERROR;
02741   }
02742 
02743   int molid[4];
02744   int atmid[4];
02745   int defmolid = -1;
02746   bool allframes = false;
02747   char errstring[200];
02748 
02749   // Read the atom list
02750   int numatms;
02751   Tcl_Obj **data;
02752   if (Tcl_ListObjGetElements(interp, objv[2], &numatms, &data) != TCL_OK) {
02753     Tcl_AppendResult(interp, " measure energy: bad syntax", NULL);
02754     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);
02755     return TCL_ERROR;
02756   }
02757 
02758   if (numatms!=reqatoms) {
02759     sprintf(errstring, " measure energy %s: must specify exactly %i atoms in list", geomname, reqatoms);
02760     Tcl_AppendResult(interp, errstring, NULL);
02761     return TCL_ERROR;
02762   }
02763     
02764   int first=-1, last=-1, frame=-1;
02765   double params[6];
02766   memset(params, 0, 6L*sizeof(double));
02767 
02768   if (argc>4) {
02769     int i;
02770     for (i=3; i<argc-1; i+=2) {
02771       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
02772       if (!strupncmp(argvcur, "molid", CMDLEN)) {
02773         if (Tcl_GetIntFromObj(interp, objv[i+1], &defmolid) != TCL_OK) {
02774           Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02775           return TCL_ERROR;
02776         }
02777       } else if (!strupncmp(argvcur, "k", CMDLEN)) {
02778         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02779           Tcl_AppendResult(interp, " measure energy: bad force constant value", NULL);
02780           return TCL_ERROR;
02781         }
02782       } else if (!strupncmp(argvcur, "x0", CMDLEN)) {
02783         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02784           Tcl_AppendResult(interp, " measure energy: bad equilibrium value", NULL);
02785           return TCL_ERROR;
02786         }
02787       } else if (!strupncmp(argvcur, "kub", CMDLEN)) {
02788         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02789           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley force constant value", NULL);
02790           return TCL_ERROR;
02791         }
02792       } else if (!strupncmp(argvcur, "s0", CMDLEN)) {
02793         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02794           Tcl_AppendResult(interp, " measure energy: bad Urey-Bradley equilibrium distance", NULL);
02795           return TCL_ERROR;
02796         }
02797       } else if (!strupncmp(argvcur, "n", CMDLEN)) {
02798         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02799           Tcl_AppendResult(interp, " measure energy: bad dihedral periodicity", NULL);
02800           return TCL_ERROR;
02801         }
02802       } else if (!strupncmp(argvcur, "delta", CMDLEN)) {
02803         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02804           Tcl_AppendResult(interp, " measure energy: bad dihedral phase shift", NULL);
02805           return TCL_ERROR;
02806         }
02807       } else if (!strupncmp(argvcur, "eps1", CMDLEN)) {
02808         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02809           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02810           return TCL_ERROR;
02811         }
02812       } else if (!strupncmp(argvcur, "rmin1", CMDLEN)) {
02813         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02814           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02815           return TCL_ERROR;
02816         }
02817       } else if (!strupncmp(argvcur, "eps2", CMDLEN)) {
02818         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+2) != TCL_OK) {
02819           Tcl_AppendResult(interp, " measure energy: bad vdw well depth", NULL);
02820           return TCL_ERROR;
02821         }
02822       } else if (!strupncmp(argvcur, "rmin2", CMDLEN)) {
02823         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+3) != TCL_OK) {
02824           Tcl_AppendResult(interp, " measure energy: bad vdw equilibrium distance", NULL);
02825           return TCL_ERROR;
02826         }
02827       } else if (!strupncmp(argvcur, "q1", CMDLEN)) {
02828         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params) != TCL_OK) {
02829           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02830           return TCL_ERROR;
02831         }
02832         params[2]=1.0;
02833       } else if (!strupncmp(argvcur, "q2", CMDLEN)) {
02834         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+1) != TCL_OK) {
02835           Tcl_AppendResult(interp, " measure energy: bad charge value", NULL);
02836           return TCL_ERROR;
02837         }
02838         params[3]=1.0;
02839       } else if (!strupncmp(argvcur, "cutoff", CMDLEN)) {
02840         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+4) != TCL_OK) {
02841           Tcl_AppendResult(interp, " measure energy: bad electrostatic cutoff value", NULL);
02842           return TCL_ERROR;
02843         }
02844       } else if (!strupncmp(argvcur, "switchdist", CMDLEN)) {
02845         if (Tcl_GetDoubleFromObj(interp, objv[i+1], params+5) != TCL_OK) {
02846           Tcl_AppendResult(interp, " measure energy: bad switching distance value", NULL);
02847           return TCL_ERROR;
02848         }
02849       } else if (!strupncmp(argvcur, "first", CMDLEN)) {
02850         if (Tcl_GetIntFromObj(interp, objv[i+1], &first) != TCL_OK) {
02851           Tcl_AppendResult(interp, " measure energy: bad first frame value", NULL);
02852           return TCL_ERROR;
02853         }
02854       } else if (!strupncmp(argvcur, "last", CMDLEN)) {
02855         if (Tcl_GetIntFromObj(interp, objv[i+1], &last) != TCL_OK) {
02856           Tcl_AppendResult(interp, " measure energy: bad last frame value", NULL);
02857           return TCL_ERROR;
02858         }
02859       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
02860         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "all")) {
02861           allframes = true;
02862         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
02863           Tcl_AppendResult(interp, " measure energy: bad frame value", NULL);
02864           return TCL_ERROR;
02865         }
02866       } else {
02867         Tcl_AppendResult(interp, " measure energy: invalid syntax, no such keyword: ", argvcur, NULL);
02868         return TCL_ERROR;
02869       }
02870     }
02871   }
02872 
02873   if ((allframes || frame>=0) && (first>=0 || last>=0)) {
02874     Tcl_AppendResult(interp, "measure energy: Ambiguous syntax: You cannot specify a frame AND a frame range (using first or last).", NULL);
02875     Tcl_AppendResult(interp, "\nUsage:\nmeasure bond <molid1>/<atomid1> <molid2>/<atomid2> [?frame <frame|all>? | ?first <first>? ?last <last>?]", NULL);
02876     return TCL_ERROR;    
02877   }
02878 
02879   if (allframes) first=0;
02880 
02881   // If no molecule was specified use top as default
02882   if (defmolid<0) defmolid = app->molecule_top();
02883 
02884   // Assign atom IDs and molecule IDs
02885   int i,numelem;
02886   Tcl_Obj **atmmol;
02887   for (i=0; i<numatms; i++) {
02888     if (Tcl_ListObjGetElements(interp, data[i], &numelem, &atmmol) != TCL_OK) {
02889       return TCL_ERROR;
02890     }
02891 
02892     if (Tcl_GetIntFromObj(interp, atmmol[0], atmid+i) != TCL_OK) {
02893       Tcl_AppendResult(interp, " measure energy: bad atom index", NULL);
02894       return TCL_ERROR;
02895     }
02896     
02897     if (numelem==2) {
02898       if (Tcl_GetIntFromObj(interp, atmmol[1], molid+i) != TCL_OK) {
02899         Tcl_AppendResult(interp, " measure energy: bad molid", NULL);
02900         return TCL_ERROR;
02901       }
02902     } else molid[i] = defmolid;
02903   }
02904   
02905 
02906   // Compute the value
02907   ResizeArray<float> gValues(1024);
02908   int ret_val;
02909   ret_val = measure_energy(app->moleculeList, molid, atmid, reqatoms, &gValues, frame, first, last, defmolid, params, geomtype);
02910   if (ret_val<0) {
02911     printf("ERROR\n %s\n", measure_error(ret_val));
02912     Tcl_AppendResult(interp, measure_error(ret_val), NULL);
02913     return TCL_ERROR;
02914   }
02915 
02916   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
02917   int numvalues = gValues.num();
02918   for (int count = 0; count < numvalues; count++) {
02919     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(gValues[count]));
02920   }
02921   Tcl_SetObjResult(interp, tcl_result);
02922 
02923   return TCL_OK;
02924 }
02925 
02926 
02927 //
02928 // Function: vmd_measure_surface <selection> <gridsize> <radius> <depth>
02929 //
02930 static int vmd_measure_surface(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02931   if (argc!=5) {
02932     Tcl_WrongNumArgs(interp, 2, objv-1,
02933                      (char *) "<sel> <gridsize> <radius> <depth>");
02934     return TCL_ERROR;
02935   }
02936    
02937   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
02938   if (!sel) {
02939     Tcl_AppendResult(interp, "measure surface: no atom selection", NULL);
02940     return TCL_ERROR;
02941   }
02942 
02943   const float *framepos = sel->coordinates(app->moleculeList);
02944   if (!framepos) return TCL_ERROR;
02945 
02946   double gridsz;
02947   if (Tcl_GetDoubleFromObj(interp, objv[2], &gridsz) != TCL_OK) 
02948      return TCL_ERROR;
02949 
02950   double radius;
02951   if (Tcl_GetDoubleFromObj(interp, objv[3], &radius) != TCL_OK) 
02952      return TCL_ERROR;
02953 
02954   double depth;
02955   if (Tcl_GetDoubleFromObj(interp, objv[4], &depth) != TCL_OK) 
02956      return TCL_ERROR;
02957   
02958   int *surface;
02959   int n_surf;
02960   
02961   int ret_val = measure_surface(sel, app->moleculeList, framepos,
02962                                gridsz, radius, depth, &surface, &n_surf);
02963   if (ret_val < 0) {
02964     Tcl_AppendResult(interp, "measure surface: ", measure_error(ret_val));
02965     return TCL_ERROR;
02966   }
02967 
02968   Tcl_Obj *surf_list = Tcl_NewListObj(0, NULL);
02969 
02970   int i;
02971   for(i=0; i < n_surf; i++) {
02972     Tcl_ListObjAppendElement(interp, surf_list, Tcl_NewIntObj(surface[i]));
02973   }
02974   Tcl_SetObjResult(interp, surf_list);
02975   delete [] surface;
02976   
02977   return TCL_OK;
02978 }
02979 
02980 
02981 // Function: vmd_measure_pbc2onc <center> ?molid <default>? ?frame <frame|last>?
02982 //  Returns: the transformation matrix to wrap a nonorthogonal pbc unicell
02983 //           into an orthonormal cell.
02984 static int vmd_measure_pbc2onc_transform(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
02985   if (argc < 2) {
02986     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> [molid <default>] [frame <frame|last>]");
02987     return TCL_ERROR;
02988   }
02989 
02990   // Read the center
02991   int ndim;
02992   Tcl_Obj **centerObj;
02993   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
02994     Tcl_AppendResult(interp, " measure pbc2onc: bad syntax", NULL);
02995     Tcl_AppendResult(interp, " Usage: measure pbc2onc <center> [molid <default>] [frame <frame|last>]", NULL);
02996     return TCL_ERROR;
02997   }
02998 
02999   if (ndim!=3) {
03000     Tcl_AppendResult(interp, " measure pbc2onc: need three numbers for a vector", NULL);
03001     return TCL_ERROR;
03002   }
03003     
03004   int i;
03005   double tmp;
03006   float center[3];
03007   for (i=0; i<3; i++) {
03008     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
03009       Tcl_AppendResult(interp, " measure pbc2onc: non-numeric in center", NULL);
03010       return TCL_ERROR;
03011     }
03012     center[i] = (float)tmp;
03013   }
03014 
03015   int molid = app->molecule_top();
03016   int frame = -2;
03017   if (argc>3) {
03018     for (i=2; i<argc; i+=2) {
03019       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03020       if (!strupncmp(argvcur, "molid", CMDLEN)) {
03021         // XXX lame test formulation should be corrected
03022         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
03023           // top is already default
03024         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
03025           Tcl_AppendResult(interp, " measure pbc2onc: bad molid", NULL);
03026           return TCL_ERROR;
03027         }
03028       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
03029         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
03030           frame=-1;
03031         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
03032           Tcl_AppendResult(interp, " measure pbc2onc: bad frame value", NULL);
03033           return TCL_ERROR;
03034         }
03035       } else {
03036         Tcl_AppendResult(interp, " measure pbc2onc: invalid syntax, no such keyword: ", argvcur, NULL);
03037         return TCL_ERROR;
03038       }
03039     }
03040   }
03041 
03042 
03043   Matrix4 transform;
03044   int ret_val = measure_pbc2onc(app->moleculeList, molid, frame, center, transform);
03045   if (ret_val < 0) {
03046     Tcl_AppendResult(interp, "measure pbc2onc: ", measure_error(ret_val), NULL);
03047     return TCL_ERROR;
03048   }
03049 
03050   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03051   for (i=0; i<4; i++) {
03052     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
03053     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+0]));
03054     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+4]));
03055     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+8]));
03056     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(transform.mat[i+12]));
03057     Tcl_ListObjAppendElement(interp, tcl_result, rowListObj);
03058   }
03059   Tcl_SetObjResult(interp, tcl_result);
03060 
03061   return TCL_OK;
03062 }
03063 
03064 // Function: vmd_measure_pbc_neighbors <center> <cutoff>
03065 //  Returns: All image atoms that are within cutoff Angstrom of the pbc unitcell.
03066 //           Two lists are returned. The first list holds the atom coordinates while
03067 //           the second one is an indexlist mapping the image atoms to the atoms in
03068 //           the unitcell.
03069 
03070 //   Input:  Since the pbc cell <center> is not stored in DCDs and cannot be set in
03071 //           VMD it must be provided by the user as the first argument.
03072 //           The second argument <cutoff> is the maximum distance (in Angstrom) from
03073 //           the PBC unit cell for atoms to be considered.
03074 
03075 // Options:  ?sel <selection>? :
03076 //           If an atomselection is provided after the keyword 'sel' then only those
03077 //           image atoms are returned that are within cutoff of the selected atoms
03078 //           of the main cell. In case cutoff is a vector the largest value will be
03079 //           used.
03080 
03081 //           ?align <matrix>? :
03082 //           In case the molecule was aligned you can supply the alignment matrix
03083 //           which is then used to correct for the rotation and shift of the pbc cell.
03084 
03085 //           ?boundingbox PBC | {<mincoord> <maxcoord>}?
03086 //           With this option the atoms are wrapped into a rectangular bounding box.
03087 //           If you provide "PBC" as an argument then the bounding box encloses the
03088 //           PBC box but then the cutoff is added to the bounding box. Negative 
03089 //           values for the cutoff dimensions are allowed and lead to a smaller box.
03090 //           Instead you can also provide a custom bounding box in form of the 
03091 //           minmax coordinates (list containing two coordinate vectors such as 
03092 //           returned by the measure minmax command). Here, again, the cutoff is
03093 //           added to the bounding box.
03094 static int vmd_measure_pbc_neighbors(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03095   if (argc < 3) {
03096     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<center> <cutoff> ?sel <selection>? ?align <matrix>? ?molid <default>? ?frame <frame|last>? ?boundingbox PBC|{<mincoord> <maxcoord>}?");
03097     return TCL_ERROR;
03098   }
03099 
03100   // Read the center
03101   int ndim;
03102   Tcl_Obj **centerObj;
03103   if (Tcl_ListObjGetElements(interp, objv[1], &ndim, &centerObj) != TCL_OK) {
03104     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03105     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03106     return TCL_ERROR;
03107   }
03108 
03109   if (ndim!=3) {
03110     Tcl_AppendResult(interp, " measure pbcneighbors: need three numbers for a vector", NULL);
03111     return TCL_ERROR;
03112   }
03113     
03114   int i;
03115   double tmp;
03116   float center[3];
03117   for (i=0; i<3; i++) {
03118     if (Tcl_GetDoubleFromObj(interp, centerObj[i], &tmp) != TCL_OK) {
03119       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in center", NULL);
03120       return TCL_ERROR;
03121     }
03122     center[i] = float(tmp);
03123   }
03124 
03125   // Read the cutoff
03126   Tcl_Obj **cutoffObj;
03127   if (Tcl_ListObjGetElements(interp, objv[2], &ndim, &cutoffObj) != TCL_OK) {
03128     Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax", NULL);
03129     Tcl_AppendResult(interp, " Usage: measure pbcneighbors <center> <cutoff> [<sel <sel>] [align <matrix>] [molid <default>] [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]", NULL);
03130     return TCL_ERROR;
03131   }
03132 
03133   if (ndim!=3 && ndim!=1) {
03134     Tcl_AppendResult(interp, " measure pbcneighbors: need either one or three numbers for cutoff", NULL);
03135     return TCL_ERROR;
03136   }
03137 
03138   float cutoff[3];
03139   for (i=0; i<ndim; i++) {
03140     if (Tcl_GetDoubleFromObj(interp, cutoffObj[i], &tmp) != TCL_OK) {
03141       Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in cutoff", NULL);
03142       return TCL_ERROR;
03143     }
03144     cutoff[i] = float(tmp);
03145   }
03146 
03147   if (ndim==1) { cutoff[2] = cutoff[1] = cutoff[0]; }
03148 
03149   bool molidprovided=0;
03150   float *boxminmax=NULL;
03151   int molid = app->molecule_top();
03152   int frame = -2;
03153   AtomSel *sel = NULL;
03154   Matrix4 alignment;
03155   if (argc>4) {
03156     for (i=3; i<argc; i+=2) {
03157       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03158       if (!strupncmp(argvcur, "sel", CMDLEN)) {
03159         // Read the atom selection
03160         sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03161         if (!sel) {
03162           Tcl_AppendResult(interp, "measure pbcneighbors: invalid atom selection", NULL);
03163           return TCL_ERROR;
03164         }
03165         if (!app->molecule_valid_id(sel->molid())) {
03166           Tcl_AppendResult(interp, "measure pbcneighbors: ",
03167                            measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03168           return TCL_ERROR;
03169         }
03170         if (!sel->selected) {
03171           Tcl_AppendResult(interp, "measure pbcneighbors: selection contains no atoms.", NULL);
03172           return TCL_ERROR;
03173         }
03174       } else if (!strupncmp(argvcur, "molid", CMDLEN)) {
03175         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "top")) {
03176           // top is already default
03177         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
03178           Tcl_AppendResult(interp, " measure pbcneighbors: bad molid", NULL);
03179           return TCL_ERROR;
03180         }
03181         molidprovided = 1;
03182       } else if (!strupncmp(argvcur, "frame", CMDLEN)) {
03183         if (!strupcmp(Tcl_GetStringFromObj(objv[i+1],NULL), "last")) {
03184           frame=-1;
03185         } else if (Tcl_GetIntFromObj(interp, objv[i+1], &frame) != TCL_OK) {
03186           Tcl_AppendResult(interp, " measure pbcneighbors: bad frame value", NULL);
03187           return TCL_ERROR;
03188         }
03189       } else if (!strupncmp(argvcur, "align", CMDLEN)) {
03190         // Get the alignment matrix (as returned by 'measure fit')
03191         if (tcl_get_matrix("measure pbcneighbors: ", interp, objv[i+1], alignment.mat) != TCL_OK) {
03192           return TCL_ERROR;
03193         }
03194       } else if (!strupncmp(argvcur, "boundingbox", CMDLEN)) {
03195         // Determine if the atoms shall be wrapped to the smallest rectangular
03196         // bounding box that still encloses the unitcell plus the given cutoff.
03197         char *argv2 = Tcl_GetStringFromObj(objv[i+1],NULL);
03198         if (!strupncmp(argv2, "on", CMDLEN) || !strupncmp(argv2, "pbc", CMDLEN)) {
03199           boxminmax = new float[6];
03200           compute_pbcminmax(app->moleculeList, molid, frame, center, &alignment,
03201                             boxminmax, boxminmax+3);
03202         } else {
03203           // Read the bounding box
03204           int j, k, ncoor;
03205           Tcl_Obj **boxListObj;
03206           if (Tcl_ListObjGetElements(interp, objv[i+1], &ncoor, &boxListObj) != TCL_OK) {
03207             Tcl_AppendResult(interp, " measure pbcneighbors: invalid bounding box parameter", NULL);
03208             return TCL_ERROR;
03209           }
03210           if (ncoor!=2) {
03211             Tcl_AppendResult(interp, " measure pbcneighbors: need 2 points for bounding box", NULL);
03212             return TCL_ERROR;
03213           }
03214           int ndim = 0;
03215           double tmp;
03216           Tcl_Obj **boxObj;
03217           boxminmax = new float[6];
03218           for (j=0; j<2; j++) {
03219             if (Tcl_ListObjGetElements(interp, boxListObj[j], &ndim, &boxObj) != TCL_OK) {
03220               Tcl_AppendResult(interp, " measure pbcneighbors: bad syntax in boundingbox", NULL);
03221               return TCL_ERROR;
03222             }
03223 
03224             for (k=0; k<3; k++) {
03225               if (Tcl_GetDoubleFromObj(interp, boxObj[k], &tmp) != TCL_OK) {
03226                 Tcl_AppendResult(interp, " measure pbcneighbors: non-numeric in boundingbox", NULL);
03227                 return TCL_ERROR;
03228               }
03229               boxminmax[3L*j+k] = (float)tmp;
03230             }
03231           }
03232         }
03233 
03234       } else {
03235         Tcl_AppendResult(interp, " measure pbcneighbors: invalid syntax, no such keyword: ", argvcur, NULL);
03236         return TCL_ERROR;
03237       }
03238     }
03239   }
03240 
03241   // If no molid was provided explicitely but a selection was given
03242   // then use that molid.
03243   if (sel && !molidprovided) {
03244     molid = sel->molid();
03245   }
03246 
03247   ResizeArray<float> extcoord_array;
03248   ResizeArray<int> indexmap_array;
03249 
03250   int ret_val = measure_pbc_neighbors(app->moleculeList, sel, molid, frame, &alignment, center, cutoff, boxminmax, &extcoord_array, &indexmap_array);
03251   delete [] boxminmax;
03252 
03253   if (ret_val < 0) {
03254     Tcl_AppendResult(interp, "measure pbcneighbors: ", measure_error(ret_val), NULL);
03255     return TCL_ERROR;
03256   }
03257   printf("measure pbcneighbors: %ld neighbor atoms found\n", 
03258          long(indexmap_array.num()));
03259 
03260   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03261   Tcl_Obj *coorListObj = Tcl_NewListObj(0, NULL);
03262   Tcl_Obj *indexListObj = Tcl_NewListObj(0, NULL);
03263   for (i=0; i<indexmap_array.num(); i++) {
03264     Tcl_Obj *rowListObj = Tcl_NewListObj(0, NULL);
03265     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i]));
03266     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+1]));
03267     Tcl_ListObjAppendElement(interp, rowListObj, Tcl_NewDoubleObj(extcoord_array[3L*i+2]));
03268     Tcl_ListObjAppendElement(interp, coorListObj, rowListObj);
03269     Tcl_ListObjAppendElement(interp, indexListObj, Tcl_NewIntObj(indexmap_array[i]));
03270   }
03271   Tcl_ListObjAppendElement(interp, tcl_result, coorListObj);
03272   Tcl_ListObjAppendElement(interp, tcl_result, indexListObj);
03273   Tcl_SetObjResult(interp, tcl_result);
03274 
03275   return TCL_OK;
03276 }
03277 
03278 
03279 
03280 // Function: vmd_measure_inertia <selection> [moments] [eigenvals]
03281 //  Returns: The center of mass and the principles axes of inertia for the 
03282 //           selected atoms. 
03283 //  Options: -moments -- also return the moments of inertia tensor
03284 //           -eigenvals -- also return the corresponding eigenvalues
03285 static int vmd_measure_inertia(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03286   bool moments = FALSE;
03287   bool eigenvals = FALSE;
03288   if (argc < 2 || argc > 4) {
03289     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<selection> [moments] [eigenvals]");
03290     return TCL_ERROR;
03291   }
03292   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03293   if (!sel) {
03294     Tcl_AppendResult(interp, "measure inertia: no atom selection", NULL);
03295     return TCL_ERROR;
03296   }
03297   
03298   if (!app->molecule_valid_id(sel->molid())) {
03299     Tcl_AppendResult(interp, "measure inertia: ",
03300                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03301     return TCL_ERROR;
03302   }
03303   
03304   if (argc>2) {
03305     int i;
03306     for (i=2; i<argc; i++) {
03307       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03308       // Allow syntax with and without leading dash
03309       if (argvcur[0]=='-') argvcur++;
03310 
03311       if (!strupncmp(argvcur, "moments", CMDLEN)) {
03312         moments = TRUE; 
03313       }
03314       else if (!strupncmp(argvcur, "eigenvals", CMDLEN)) {
03315         eigenvals = TRUE; 
03316       }
03317       else {
03318         Tcl_AppendResult(interp, " measure inertia: unrecognized option\n", NULL);
03319         Tcl_AppendResult(interp, " Usage: measure inertia <selection> [moments] [eigenvals]", NULL);
03320         return TCL_ERROR;
03321       }
03322     }
03323   }
03324 
03325   float priaxes[3][3];
03326   float itensor[4][4];
03327   float evalue[3], rcom[3];
03328   int ret_val = measure_inertia(sel, app->moleculeList, NULL, rcom, priaxes, itensor, evalue);
03329   if (ret_val < 0) {
03330     Tcl_AppendResult(interp, "measure inertia: ", measure_error(ret_val), NULL);
03331     return TCL_ERROR;
03332   }
03333 
03334   // return COM and list of 3 principal axes
03335   int i;
03336   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03337 
03338   Tcl_Obj *rcomObj = Tcl_NewListObj(0, NULL);
03339   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[0]));
03340   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[1]));
03341   Tcl_ListObjAppendElement(interp, rcomObj, Tcl_NewDoubleObj(rcom[2]));
03342   Tcl_ListObjAppendElement(interp, tcl_result, rcomObj);
03343 
03344   Tcl_Obj *axesListObj = Tcl_NewListObj(0, NULL);
03345   for (i=0; i<3; i++) {
03346     Tcl_Obj *axesObj = Tcl_NewListObj(0, NULL);
03347     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][0]));
03348     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][1]));
03349     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(priaxes[i][2]));
03350     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03351   }
03352   Tcl_ListObjAppendElement(interp, tcl_result, axesListObj);
03353 
03354   if (moments) {
03355     Tcl_Obj *momListObj = Tcl_NewListObj(0, NULL);
03356     for (i=0; i<3; i++) {
03357       Tcl_Obj *momObj = Tcl_NewListObj(0, NULL);
03358       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][0]));
03359       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][1]));
03360       Tcl_ListObjAppendElement(interp, momObj, Tcl_NewDoubleObj(itensor[i][2]));
03361       Tcl_ListObjAppendElement(interp, momListObj, momObj);
03362     }
03363     Tcl_ListObjAppendElement(interp, tcl_result, momListObj);
03364   }
03365 
03366   if (eigenvals) {
03367       Tcl_Obj *eigvListObj = Tcl_NewListObj(0, NULL);
03368       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[0]));
03369       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[1]));
03370       Tcl_ListObjAppendElement(interp, eigvListObj, Tcl_NewDoubleObj(evalue[2]));
03371       Tcl_ListObjAppendElement(interp, tcl_result, eigvListObj);
03372   }
03373   Tcl_SetObjResult(interp, tcl_result);
03374 
03375   return TCL_OK;
03376 }
03377 
03378 
03379 // measure symmetry <sel> [plane|I|C<n>|S<n> [<vector>]] [-tol <value>]
03380 //                  [-nobonds] [-verbose <level>]
03381 //
03382 // This function evaluates the molecular symmetry of an atom selection.
03383 // The underlying algorithm finds the symmetry elements such as 
03384 // inversion center, mirror planes, rotary axes and rotary reflections.
03385 // Based on the found elements it guesses the underlying point group.
03386 // The guess is fairly robust and can handle molecules whose
03387 // coordinatesthat deviate to a certain extent from the ideal symmetry.
03388 // The closest match with the highest symmetry will be returned.
03389 //
03390 // Options:
03391 // --------
03392 // -tol <value>
03393 //    Allows one to control tolerance of the algorithm when
03394 //    considering wether something is symmetric or not.
03395 //    A smaller value signifies a lower tolerance, the default
03396 //    is 0.1.
03397 // -nobonds 
03398 //    If this flag is set then the bond order and orientation
03399 //    are not considered when comparing structures.
03400 // -verbose <level>
03401 //    Controls the amount of console output.
03402 //    A level of 0 means no output, 1 gives some statistics at the
03403 //    end of the search (default). Level 2 gives additional info
03404 //    about each stage, level 3 more output for each iteration
03405 //    and 3, 4 yield yet more additional info.
03406 // -I
03407 //    Instead of guessing the symmetry pointgroup of the selection
03408 //    determine if the selection's center off mass represents an
03409 //    inversion center. The returned value is a score between 0
03410 //    and 1 where 1 denotes a perfect match.
03411 // -plane <vector>
03412 //    Instead of guessing the symmetry pointgroup of the selection
03413 //    determine if the plane with the defined by its normal
03414 //    <vector> is a mirror plane of the selection. The
03415 //    returned value is a score between 0 and 1 where 1 denotes
03416 //    a perfect match.
03417 // -Cn | -Sn  <vector>
03418 //    Instead of guessing the symmetry pointgroup of the selection
03419 //    determine if the rotation or rotary reflection axis Cn/Sn
03420 //    with order n defined by <vector> exists for the
03421 //    selection. E.g., if you want to query wether the Y-axis
03422 //    has a C3 rotational symmetry you specify "C3 {0 1 0}".
03423 //    The returned value is a score between 0 and 1 where 1
03424 //    denotes a perfect match.
03425 //
03426 // Result:
03427 // -------
03428 // The return value is a TCL list of pairs consisting of a label
03429 // string and a value or list. For each label the data following
03430 // it are described below:
03431 //
03432 // * [pointgroup] The guessed pointgroup:
03433 // * [order] Point group order (the n from above)
03434 // * [elements] Summary of found elements.
03435 //     For instance {(i) (C3) 3*(C2) (S6) 3*(sigma)} for D3d.
03436 // * [missing] Elements missing with respect to ideal number of
03437 //     elements (same format as above). If this is not an empty
03438 //     list then something has gone awfully wrong with the symmetry
03439 //     finding algorithm.
03440 // * [additional] Additional elements  that would not be expected
03441 //     for this point group (same format as above). If this is not
03442 //     an empty list then something has gone awfully wrong with the
03443 //     symmetry finding algorithm.
03444 // * [com] Center of mass of the selection based on the idealized
03445 //     coordinates.
03446 // * [inertia] List of the three axes of inertia, the eigenvalues
03447 //     of the moments of inertia tensor and a list of three 0/1 flags 
03448 //     specifying for each axis wether it is unique or not.
03449 // * [inversion] Flag 0/1 signifying if there is an inversion center.
03450 // * [axes] Normalized vectors defining rotary axes
03451 // * [rotreflect] Normalized vectors defining rotary reflections
03452 // * [planes] Normalized vectors defining mirror planes.
03453 // * [ideal]  Idealized symmetric coordinates for all atoms of
03454 //     the selection. The coordinates are listed in the order of 
03455 //     increasing atom indices (same order asa returned by the
03456 //     atomselect command ``get {x y z}''). Thus you can use the list
03457 //     to set the atoms of your selection to the ideal coordinates
03458 // * [unique] Index list defining a set of atoms with unique
03459 //     coordinates
03460 // * [orient] Matrix that aligns molecule with GAMESS standard
03461 //     orientation
03462 //
03463 // If a certain item is not present (e.g. no planes or no axes)
03464 // then the corresponding value is an empty list.
03465 // The pair format allows to use the result as a TCL array for
03466 // convenient access of the different return items.
03467 
03468 static int vmd_measure_symmetry(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03469   if (argc<2) {
03470     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]");
03471     return TCL_ERROR;
03472   }
03473   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03474   if (!sel) {
03475     Tcl_AppendResult(interp, "measure symmetry: no atom selection", NULL);
03476     return TCL_ERROR;
03477   }
03478   if (!app->molecule_valid_id(sel->molid())) {
03479     Tcl_AppendResult(interp, "measure symmetry: ",
03480                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03481     return TCL_ERROR;
03482   }
03483 
03484   double sigma = 0.1;
03485   float axis[3];
03486   int checkelement = 0;
03487   int checkbonds = 1;
03488   int order = 0;
03489   int verbose = 1;
03490   int impose = 0;
03491   int imposeinvers = 0;
03492   int numimposeplan = 0;
03493   int numimposeaxes = 0;
03494   int numimposerref = 0;
03495   float *imposeplan = NULL;
03496   float *imposeaxes = NULL;
03497   float *imposerref = NULL;
03498   int *imposeaxesord = NULL;
03499   int *imposerreford = NULL;
03500   AtomSel *idealsel = NULL;
03501 
03502   if (argc>2) {
03503     int bailout = 0;
03504     int i;
03505     for (i=2; (i<argc && !bailout); i++) {
03506       char *argvcur = Tcl_GetStringFromObj(objv[i],NULL);
03507       // Allow syntax with and without leading dash
03508       if (argvcur[0]=='-') argvcur++;
03509       
03510       if (!strupncmp(argvcur, "tol", CMDLEN)) {
03511         if (Tcl_GetDoubleFromObj(interp, objv[i+1], &sigma) != TCL_OK) {
03512           Tcl_AppendResult(interp, " measure symmetry: bad tolerance value", NULL);
03513           bailout = 1; continue;
03514         }
03515         i++;
03516       }
03517       else if (!strupncmp(argvcur, "verbose", CMDLEN)) {
03518         if (Tcl_GetIntFromObj(interp, objv[i+1], &verbose) != TCL_OK) {
03519           Tcl_AppendResult(interp, " measure symmetry: bad verbosity level value", NULL);
03520           bailout = 1; continue;
03521         }
03522         i++;
03523       }
03524       else if (!strupncmp(argvcur, "nobonds", CMDLEN)) {
03525         checkbonds = 0;
03526       }
03527       else if (!strupcmp(argvcur, "I")) {
03528         checkelement = 1;
03529       }
03530       else if (!strupncmp(argvcur, "plane", CMDLEN)) {
03531         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03532           bailout = 1; continue;
03533         }
03534         checkelement = 2;
03535         i++;
03536       }
03537       else if (!strupncmp(argvcur, "C", 1) || !strupncmp(argvcur, "S", 1)) {
03538         char *begptr = argvcur+1;
03539         char *endptr;
03540         order = strtol(begptr, &endptr, 10);
03541         if (endptr==begptr || *endptr!='\0') {
03542           Tcl_AppendResult(interp, " measure symmetry: bad symmetry element format (must be I, C*, S*, plane, where * is the order). ", NULL);
03543           bailout = 1; continue;
03544         }
03545 
03546         if (tcl_get_vector(Tcl_GetStringFromObj(objv[i+1],NULL), axis, interp)!=TCL_OK) {
03547           bailout = 1; continue;
03548         }
03549         
03550         if (!strupncmp(argvcur, "C", 1)) checkelement = 3;
03551         if (!strupncmp(argvcur, "S", 1)) checkelement = 4;
03552         i++;
03553       }
03554       else if (!strupncmp(argvcur, "idealsel", CMDLEN)) {
03555         idealsel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[i+1],NULL));
03556         if (!sel) {
03557           Tcl_AppendResult(interp, "measure symmetry: no atom selection for idealized coordinates", NULL);
03558           bailout = 1; continue;
03559         }
03560         if (idealsel->molid()!=sel->molid()) {
03561           Tcl_AppendResult(interp, "measure symmetry: selection and idealsel must be from the same molecule", NULL);
03562           bailout = 1; continue;
03563         }
03564         if (idealsel->selected<sel->selected) {
03565           Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03566           bailout = 1; continue;
03567         }
03568         // make sure sel is subset of idealsel
03569         int j;
03570         for (j=0; j<sel->num_atoms; j++) {
03571           if (sel->on[j] && !idealsel->on[j]) {
03572             Tcl_AppendResult(interp, "measure symmetry: selection must be a subset of idealsel", NULL);
03573             bailout = 1; continue;
03574           }
03575         }
03576 
03577         i++;
03578       }
03579       else if (!strupncmp(argvcur, "imposeinversion", CMDLEN)) {
03580         imposeinvers = 1;
03581         impose = 1;
03582       }
03583       else if (!strupncmp(argvcur, "imposeplanes", CMDLEN)) {
03584         // Format: list of normal vectors {{x y z} {x y z} ...}
03585         int nelem;
03586         Tcl_Obj **elemListObj;
03587         if (i+1>=argc ||
03588             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK) {
03589           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03590           bailout = 1; continue;
03591         }
03592         float *elem = new float[3L*nelem];
03593         int k;
03594         for (k=0; k<nelem; k++) {
03595           int nobj;
03596           Tcl_Obj **vecObj;
03597           if (Tcl_ListObjGetElements(interp, elemListObj[k], &nobj, &vecObj) != TCL_OK) {
03598             delete [] elem;
03599             Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeplanes option", NULL);
03600             bailout = 1; continue;
03601           }
03602           if (nobj!=3) {
03603             delete [] elem;
03604             Tcl_AppendResult(interp, " measure symmetry imposeplanes: vector must have 3 elements", NULL);
03605             bailout = 1; continue;
03606           }
03607           
03608           int m;
03609           for (m=0; m<3; m++) {
03610             double d;
03611             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03612               delete [] elem;
03613               bailout = 1; continue;
03614             }
03615             elem[3L*k+m] = (float)d;
03616           }
03617         }
03618         if (imposeplan) delete [] imposeplan;
03619         imposeplan = elem;
03620         numimposeplan = nelem;
03621         impose = 1;
03622         i++;
03623       }
03624       else if (!strupncmp(argvcur, "imposeaxes", CMDLEN) ||
03625                !strupncmp(argvcur, "imposerotref", CMDLEN)) {
03626         // Format: list of axes and orders {{x y z} 3 {x y z} 2 ...}
03627         int nelem;
03628         Tcl_Obj **elemListObj;
03629         if (i+1>=argc ||
03630             Tcl_ListObjGetElements(interp, objv[i+1], &nelem, &elemListObj) != TCL_OK ||
03631             nelem%2) {
03632           Tcl_AppendResult(interp, " measure symmetry: bad syntax for imposeaxes/imposerotref option", NULL);
03633           bailout = 1; continue;
03634         }
03635         nelem /= 2;
03636      
03637         if (nelem<=0) {
03638           i++;
03639           continue;
03640         }
03641         float *elem = new float[3L*nelem];
03642         int *axorder = new int[nelem];
03643         int k;
03644         for (k=0; k<nelem; k++) {
03645           int nobj;
03646           Tcl_Obj **vecObj;
03647           if (Tcl_ListObjGetElements(interp, elemListObj[2L*k], &nobj, &vecObj) != TCL_OK) {
03648             delete [] elem;
03649             delete [] axorder;
03650             Tcl_AppendResult(interp, " measure symmetry impose: bad syntax for axis vector", NULL);
03651             bailout = 1; continue;
03652           }
03653           if (Tcl_GetIntFromObj(interp, elemListObj[2L*k+1], &axorder[k]) != TCL_OK) {
03654             delete [] elem;
03655             delete [] axorder;
03656             bailout = 1; continue;
03657           }
03658           if (nobj!=3) {
03659             delete [] elem;
03660             delete [] axorder;
03661             Tcl_AppendResult(interp, " measure symmetry impose: axis vector must have 3 elements", NULL);
03662             bailout = 1; continue;
03663           }
03664           
03665           int m;
03666           for (m=0; m<3; m++) {
03667             double d;
03668             if (Tcl_GetDoubleFromObj(interp, vecObj[m], &d) != TCL_OK) {
03669               delete [] elem;
03670               delete [] axorder;
03671               bailout = 1; continue;
03672             }
03673             elem[3L*k+m] = (float)d;
03674           }
03675         }
03676         if (!strupncmp(argvcur, "imposeaxes", CMDLEN)) {
03677           if (imposeaxes)    delete [] imposeaxes;
03678           if (imposeaxesord) delete [] imposeaxesord;
03679           imposeaxes = elem;
03680           imposeaxesord = axorder;
03681           numimposeaxes = nelem;
03682         } else if (!strupncmp(argvcur, "imposerotref", CMDLEN)) {
03683           if (imposerref)    delete [] imposerref;
03684           if (imposerreford) delete [] imposerreford;
03685           imposerref = elem;
03686           imposerreford = axorder;
03687           numimposerref = nelem;
03688         }
03689       
03690         impose = 1;
03691         i++;
03692       }
03693       else {
03694         Tcl_AppendResult(interp, " measure symmetry: unrecognized option ", NULL);
03695         Tcl_AppendResult(interp, argvcur);
03696         Tcl_AppendResult(interp, ".\n Usage: measure symmetry <selection> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]", NULL);
03697         bailout = 1; continue;
03698       }
03699     }
03700 
03701     if (bailout) {
03702       if (imposeplan) delete [] imposeplan;
03703       if (imposeaxes) delete [] imposeaxes;
03704       if (imposerref) delete [] imposerref;
03705       if (imposeaxesord) delete [] imposeaxesord;
03706       if (imposerreford) delete [] imposerreford;
03707       return TCL_ERROR;
03708     }
03709   }
03710 
03711   // Initialize
03712   Symmetry sym = Symmetry(sel, app->moleculeList, verbose);
03713 
03714   // Set tolerance for atomic overlapping
03715   sym.set_overlaptol(float(sigma));
03716 
03717   // Wether to include bonds into the analysis
03718   sym.set_checkbonds(checkbonds);
03719 
03720   if (checkelement) {
03721     // We are interested only in the score for a specific
03722     // symmetry element.
03723     float overlap = 0.0;
03724     if (checkelement==1) {
03725       overlap = sym.score_inversion();
03726     }
03727     else if (checkelement==2) {
03728       overlap = sym.score_plane(axis);
03729     }
03730     else if (checkelement==3) {
03731       overlap = sym.score_axis(axis, order);
03732     }
03733     else if (checkelement==4) {
03734       overlap = sym.score_rotary_reflection(axis, order);
03735     }
03736 
03737     Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03738     Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
03739     Tcl_SetObjResult(interp, tcl_result);
03740     return TCL_OK;
03741   }
03742 
03743   if (impose) {
03744     sym.impose(imposeinvers, numimposeplan, imposeplan,
03745                numimposeaxes, imposeaxes, imposeaxesord,
03746                numimposerref, imposerref, imposerreford);
03747     if (imposeplan) delete [] imposeplan;
03748     if (imposeaxes) delete [] imposeaxes;
03749     if (imposerref) delete [] imposerref;
03750     if (imposeaxesord) delete [] imposeaxesord;
03751     if (imposerreford) delete [] imposerreford;
03752   }
03753 
03754   int ret_val = sym.guess(float(sigma));
03755   if (ret_val < 0) {
03756     Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03757     return TCL_ERROR;
03758   }
03759 
03760   int natoms = sel->selected;
03761   Symmetry *s = &sym;
03762 
03763   if (idealsel) {
03764     Symmetry *isym = new Symmetry(idealsel, app->moleculeList, verbose);
03765     isym->set_overlaptol(float(sigma));
03766     isym->set_checkbonds(checkbonds);
03767     int j;
03768     float *plane = new float[3L*sym.numplanes()];
03769     for (j=0; j<sym.numplanes(); j++) {
03770       vec_copy(&plane[3L*j], sym.plane(j));
03771     }
03772     int *axisorder = new int[sym.numaxes()];
03773     float *axis = new float[3L*sym.numaxes()];
03774     for (j=0; j<sym.numaxes(); j++) {
03775       axisorder[j] = sym.get_axisorder(j);
03776       vec_copy(&axis[3L*j], sym.axis(j));
03777     }
03778     int *rrorder = new int[sym.numrotreflect()];
03779     float *rraxis = new float[3L*sym.numrotreflect()];
03780     for (j=0; j<sym.numrotreflect(); j++) {
03781       rrorder[j] = sym.get_rotreflectorder(j);
03782       vec_copy(&rraxis[3L*j], sym.rotreflect(j));
03783     }
03784     // XXX must check if sel is subset of idealsel
03785     int k=0, m=0;
03786     for (j=0; j<sel->num_atoms; j++) {
03787       if (idealsel->on[j]) {
03788         if (sel->on[j]) {
03789           vec_copy(isym->idealpos(k), sym.idealpos(m));
03790           m++;
03791         }
03792         k++;
03793       }
03794     }
03795     isym->impose(sym.has_inversion(),
03796                  sym.numplanes(), plane,
03797                  sym.numaxes(),   axis, axisorder,
03798                  sym.numrotreflect(), rraxis, rrorder);
03799 
03800     ret_val = isym->guess(float(sigma));
03801     if (ret_val < 0) {
03802       Tcl_AppendResult(interp, "measure symmetry: ", measure_error(ret_val), NULL);
03803       return TCL_ERROR;
03804     }
03805 
03806     natoms = idealsel->selected;
03807     s = isym;
03808   }
03809 
03810   int pgorder;
03811   char pointgroup[6];
03812   s->get_pointgroup(pointgroup, &pgorder);
03813 
03814   int i;
03815   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
03816   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("pointgroup", -1));
03817   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(pointgroup, -1));
03818   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("order", -1));
03819   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(pgorder));
03820   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rmsd", -1));
03821   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(s->get_rmsd()));
03822   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("elements", -1));
03823   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_element_summary(), -1));
03824   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("missing", -1));
03825   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_missing_elements(), -1));
03826   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("additional", -1));
03827   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj(s->get_additional_elements(), -1));
03828 
03829   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("com", -1));
03830   Tcl_Obj *invObj  = Tcl_NewListObj(0, NULL);
03831   float *com = s->center_of_mass();
03832   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[0]));
03833   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[1]));
03834   Tcl_ListObjAppendElement(interp, invObj, Tcl_NewDoubleObj(com[2]));
03835   Tcl_ListObjAppendElement(interp, tcl_result, invObj);
03836 
03837   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inertia", -1));
03838   Tcl_Obj *inertListListObj  = Tcl_NewListObj(0, NULL);
03839   float *inertia  = s->get_inertia_axes();
03840   float *eigenval = s->get_inertia_eigenvals();
03841   int   *unique   = s->get_inertia_unique();
03842   for (i=0; i<3; i++) {
03843     Tcl_Obj *inertObj  = Tcl_NewListObj(0, NULL);
03844     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i]));
03845     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+1]));
03846     Tcl_ListObjAppendElement(interp, inertObj, Tcl_NewDoubleObj(inertia[3L*i+2]));
03847     Tcl_Obj *inertListObj  = Tcl_NewListObj(0, NULL);
03848     Tcl_ListObjAppendElement(interp, inertListObj, inertObj);
03849     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewDoubleObj(eigenval[i]));
03850     Tcl_ListObjAppendElement(interp, inertListObj, Tcl_NewIntObj(unique[i]));
03851     Tcl_ListObjAppendElement(interp, inertListListObj, inertListObj);
03852   }
03853   Tcl_ListObjAppendElement(interp, tcl_result, inertListListObj);
03854 
03855  
03856   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("inversion", -1));
03857   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewIntObj(s->has_inversion()));
03858 
03859 
03860   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("axes", -1));
03861   Tcl_Obj *axesListListObj  = Tcl_NewListObj(0, NULL);
03862   for (i=0; i<s->numaxes(); i++) {
03863     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03864     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03865     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[0]));
03866     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[1]));
03867     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->axis(i)[2]));
03868     Tcl_Obj *axesListObj  = Tcl_NewListObj(0, NULL);
03869     Tcl_ListObjAppendElement(interp, axesListObj, axesObj);
03870     Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewIntObj(s->get_axisorder(i)));
03871     int axistype = s->get_axistype(i);
03872     if (axistype & PRINCIPAL_AXIS && 
03873         !(!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS))) {
03874       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("principal", -1));
03875     } else if (!s->is_spherical_top() && (axistype & PERPENDICULAR_AXIS)) {
03876       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewStringObj("perpendicular", -1));
03877     } else {
03878       Tcl_ListObjAppendElement(interp, axesListObj, Tcl_NewListObj(0, NULL));
03879     }
03880     Tcl_ListObjAppendElement(interp, axesListListObj, axesListObj);
03881   }
03882   Tcl_ListObjAppendElement(interp, tcl_result, axesListListObj);
03883 
03884 
03885   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("rotreflect", -1));
03886   Tcl_Obj *rraxesListListObj  = Tcl_NewListObj(0, NULL);
03887   for (i=0; i<s->numrotreflect(); i++) {
03888     Tcl_Obj *axesObj  = Tcl_NewListObj(0, NULL);
03889     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->axis(i)[0], s->axis(i)[1], s->axis(i)[2]);
03890     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[0]));
03891     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[1]));
03892     Tcl_ListObjAppendElement(interp, axesObj, Tcl_NewDoubleObj(s->rotreflect(i)[2]));
03893     Tcl_Obj *rraxesListObj  = Tcl_NewListObj(0, NULL);
03894     Tcl_ListObjAppendElement(interp, rraxesListObj, axesObj);
03895     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotreflectorder(i)));
03896     Tcl_ListObjAppendElement(interp, rraxesListObj, Tcl_NewIntObj(s->get_rotrefltype(i)));
03897     Tcl_ListObjAppendElement(interp, rraxesListListObj, rraxesListObj);
03898   }
03899   Tcl_ListObjAppendElement(interp, tcl_result, rraxesListListObj);
03900 
03901 
03902   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("planes", -1));
03903   Tcl_Obj *planesListListObj = Tcl_NewListObj(0, NULL);
03904   for (i=0; i<s->numplanes(); i++) {
03905     Tcl_Obj *planesObj = Tcl_NewListObj(0, NULL);
03906     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03907     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[0]));
03908     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[1]));
03909     Tcl_ListObjAppendElement(interp, planesObj, Tcl_NewDoubleObj(s->plane(i)[2]));
03910     Tcl_Obj *planesListObj = Tcl_NewListObj(0, NULL);
03911     Tcl_ListObjAppendElement(interp, planesListObj, planesObj);
03912     switch (s->get_planetype(i)) {
03913     case 1:
03914       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("vertical", -1));
03915       break;
03916     case 3:
03917       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("dihedral", -1));
03918       break;
03919     case 4:
03920       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewStringObj("horizontal", -1));
03921       break;
03922     default:
03923       Tcl_ListObjAppendElement(interp, planesListObj, Tcl_NewListObj(0, NULL));
03924     }
03925     Tcl_ListObjAppendElement(interp, planesListListObj, planesListObj);
03926   }
03927   Tcl_ListObjAppendElement(interp, tcl_result, planesListListObj);
03928 
03929 
03930   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("ideal", -1));
03931   Tcl_Obj *idealcoorListObj = Tcl_NewListObj(0, NULL);
03932   for (i=0; i<natoms; i++) {
03933     Tcl_Obj *idealcoorObj = Tcl_NewListObj(0, NULL);
03934     //printf("Tcl %i: %.2f %.2f %.2f\n", i, s->plane(i)[0], s->plane(i)[1], s->plane(i)[2]);
03935     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[0]));
03936     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[1]));
03937     Tcl_ListObjAppendElement(interp, idealcoorObj, Tcl_NewDoubleObj(s->idealpos(i)[2]));
03938     Tcl_ListObjAppendElement(interp, idealcoorListObj, idealcoorObj);
03939   }
03940   Tcl_ListObjAppendElement(interp, tcl_result, idealcoorListObj);
03941 
03942   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("unique", -1));
03943   Tcl_Obj *uniquecoorListObj = Tcl_NewListObj(0, NULL);
03944   for (i=0; i<natoms; i++) {
03945     if (!s->get_unique_atom(i)) continue; 
03946     Tcl_ListObjAppendElement(interp, uniquecoorListObj, Tcl_NewIntObj(i));
03947   }
03948   Tcl_ListObjAppendElement(interp, tcl_result, uniquecoorListObj);
03949 
03950   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewStringObj("orient", -1));
03951   Matrix4 *orient = s->get_orientation();
03952   Tcl_Obj *matrixObj = Tcl_NewListObj(0, NULL);
03953   for (i=0; i<4; i++) {
03954     Tcl_Obj *rowObj = Tcl_NewListObj(0, NULL);
03955     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+0]));
03956     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+4]));
03957     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+8]));
03958     Tcl_ListObjAppendElement(interp, rowObj, Tcl_NewDoubleObj(orient->mat[i+12]));
03959     Tcl_ListObjAppendElement(interp, matrixObj, rowObj);
03960   }
03961   Tcl_ListObjAppendElement(interp, tcl_result, matrixObj);
03962 
03963   Tcl_SetObjResult(interp, tcl_result);
03964   if (idealsel) {
03965     delete s;
03966   }
03967   return TCL_OK;
03968 
03969 }
03970 
03971 // Function: vmd_measure_transoverlap <selection>
03972 //  Returns: The structural overlap of a selection with a copy of itself
03973 //           that is transformed according to a given transformation matrix.
03974 //           The normalized sum over all gaussian function values of the pair
03975 //           distances between atoms in the original and the transformed
03976 //           selection.
03977 //    Input: the atom selection and the transformation matrix.
03978 //   Option: with -sigma you can specify the sigma value of the overlap 
03979 //           gaussian function. The default is 0.1 Angstrom.
03980 static int vmd_measure_trans_overlap(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
03981   if (argc!=3 && argc!=5) {
03982     Tcl_WrongNumArgs(interp, 2, objv-1, (char *) "<sel> <matrix> [-sigma <value>]");
03983     return TCL_ERROR;
03984   }
03985   AtomSel *sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
03986   if (!sel) {
03987     Tcl_AppendResult(interp, "measure transoverlap: no atom selection", NULL);
03988     return TCL_ERROR;
03989   }
03990   if (!app->molecule_valid_id(sel->molid())) {
03991     Tcl_AppendResult(interp, "measure transoverlap: ",
03992                      measure_error(MEASURE_ERR_NOMOLECULE), NULL);
03993     return TCL_ERROR;
03994   }
03995 
03996   // Get the transformation matrix
03997   Matrix4 trans;
03998   if (tcl_get_matrix("measure transoverlap: ",interp,objv[2], trans.mat) != TCL_OK) {
03999     return TCL_ERROR;
04000   }
04001 
04002   double sigma = 0.1;
04003   if (argc==5) {
04004     if (!strupncmp(Tcl_GetStringFromObj(objv[3],NULL), "-sigma", CMDLEN)) {
04005       if (Tcl_GetDoubleFromObj(interp, objv[4], &sigma) != TCL_OK) {
04006         Tcl_AppendResult(interp, " measure transoverlap: bad sigma value", NULL);
04007         return TCL_ERROR;
04008       }
04009     } else {
04010       Tcl_AppendResult(interp, " measure transoverlap: unrecognized option\n", NULL);
04011       Tcl_AppendResult(interp, " Usage: measure transoverlap <sel> <matrix> [-sigma <value>]", NULL);
04012       return TCL_ERROR;
04013     }
04014   }
04015 
04016   int maxnatoms = 100;// XXX
04017   float overlap;
04018   int ret_val = measure_trans_overlap(sel, app->moleculeList, &trans,
04019                                       float(sigma), NOSKIP_IDENTICAL, 
04020                                       maxnatoms, overlap);
04021   if (ret_val < 0) {
04022     Tcl_AppendResult(interp, "measure transoverlap: ", measure_error(ret_val), NULL);
04023     return TCL_ERROR;
04024   }
04025 
04026   Tcl_Obj *tcl_result = Tcl_NewListObj(0, NULL);
04027   Tcl_ListObjAppendElement(interp, tcl_result, Tcl_NewDoubleObj(overlap));
04028   Tcl_SetObjResult(interp, tcl_result);
04029 
04030   return TCL_OK;
04031 }
04032 
04033 
04034 
04035 //
04036 // Raycasting brute-force approach by Juan R. Perilla <juan@perilla.me>
04037 //
04038 int vmd_measure_volinterior(VMDApp *app, int argc, Tcl_Obj * const objv[], Tcl_Interp *interp) {
04039   int verbose = 0;
04040   int recognized=0;
04041   if (argc < 3) {
04042      // "     options: --allframes (average over all frames)\n"
04043     Tcl_SetResult(interp, (char *) "usage: volinterior "
04044                   " <selection1> [options]\n"
04045                   "-res (default 10.0)\n"
04046                   "-spacing (default res/3)\n"
04047                   "-loadmap (load synth map)\n"
04048                   "-probmap (compute and load probability map)\n"
04049                   "   --> -count_pmap (return percentile-based voxel counts)\n"
04050                   "-discretize [float cutoff] (creates discrete map with cutoff probability)\n"
04051                   "-mol molid [default: top]\n"
04052                   "-vol volid [0]\n"
04053                   "-nrays number of rays to cast [6]\n"
04054                   "-isovalue [float: 1.0]\n"
04055                   "-verbose\n"
04056                   "-overwrite volid\n",
04057                   TCL_STATIC);
04058     return TCL_ERROR;
04059   }
04060 
04061   //atom selection
04062   AtomSel *sel = NULL;
04063   sel = tcl_commands_get_sel(interp, Tcl_GetStringFromObj(objv[1],NULL));
04064   if (!sel) {
04065     Tcl_AppendResult(interp, "volinterior: no atom selection.", NULL);
04066     return TCL_ERROR;
04067   }
04068   if (!sel->selected) {
04069     Tcl_AppendResult(interp, "volinterior: no atoms selected.", NULL);
04070     return TCL_ERROR;
04071   }
04072   if (!app->molecule_valid_id(sel->molid())) {
04073     Tcl_AppendResult(interp, "invalid mol id.", NULL);
04074     return TCL_ERROR;
04075   }
04076   int loadsynthmap = 0;
04077   int loadpmap=0; 
04078   int count_pmap=0;
04079   int print_raydirs=0; 
04080   int molid = -1;
04081   int volid = -1;
04082   int volidOverwrite = -1;
04083   int overwrite=0;
04084   int nrays=6;
04085   long Nout = 0;
04086   float isovalue=1.0;
04087   float proCutoff=0.90f;
04088   int process=0;
04089   float radscale;
04090   double resolution = 10.0;
04091   double gspacing;
04092   MoleculeList *mlist = app->moleculeList;
04093   Molecule *mymol = mlist->mol_from_id(sel->molid());
04094 
04095   for (int i=2; i < argc; i++) {
04096     recognized=0;
04097     char *opt = Tcl_GetStringFromObj(objv[i], NULL);
04098     if (!strcmp(opt, "volinterior")) {
04099       recognized=1;
04100     }
04101     if (!strcmp(opt, "-mol")) {
04102       if (i == argc-1) {
04103         Tcl_AppendResult(interp, "No molid specified",NULL);
04104         return TCL_ERROR;
04105       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04106         Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04107         return TCL_ERROR;
04108       }
04109       recognized=1;
04110     }
04111     if (!strcmp(opt, "-probmap")) {
04112       loadpmap=1;
04113       recognized=1;
04114     }
04115     if (!strcmp(opt, "-checkrays")) {
04116       print_raydirs=1;
04117       recognized=1;
04118     }
04119     if (!strcmp(opt, "-count_pmap")) {
04120       count_pmap=1;
04121       recognized=1;
04122     }
04123     if (!strcmp(opt, "-discretize")) {
04124       double tcutoff;
04125       if (i==argc-1) {
04126         Tcl_AppendResult(interp, "No cutoff probability given for -discretize flag",NULL);
04127         return TCL_ERROR;
04128       } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &tcutoff)!=TCL_OK) {
04129         Tcl_AppendResult(interp, "\n Cutoff probability for -discretize flag incorrectly specified.",NULL);
04130         return TCL_ERROR;
04131       }
04132       proCutoff=(float)tcutoff;
04133       recognized=1;
04134       process=1;
04135     }
04136     if (!strcmp(opt, "-vol")) {
04137       if (i == argc-1){
04138         Tcl_AppendResult(interp, "No volume id specified",NULL);
04139         return TCL_ERROR;
04140       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &volid) != TCL_OK) {
04141         Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04142         return TCL_ERROR;
04143       }
04144       recognized=1;
04145     }
04146 
04147     if (!strcmp(opt, "-overwrite")) {
04148       if (i == argc-1){
04149         Tcl_AppendResult(interp, "No volume id specified",NULL);
04150         return TCL_ERROR;
04151       } else if (Tcl_GetIntFromObj(interp, objv[i+1], &volidOverwrite) != TCL_OK) {
04152         Tcl_AppendResult(interp, "\n volume id incorrectly specified",NULL);
04153         return TCL_ERROR;
04154       }
04155       overwrite=1;
04156       recognized=1;
04157     }
04158 
04159     // Calculate synthmap
04160     if (!strcmp(opt, "-loadmap")) {
04161       loadsynthmap = 1;
04162       recognized=1;
04163     }
04164     if (!strcmp(opt, "-mol")) {
04165       if (i == argc-1) {
04166         Tcl_AppendResult(interp, "No molid specified",NULL);
04167         return TCL_ERROR;
04168       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &molid) != TCL_OK) {
04169         Tcl_AppendResult(interp, "\n molid incorrectly specified",NULL);
04170         return TCL_ERROR;
04171       }
04172       recognized=1;
04173     }
04174    if (!strcmp(opt, "-res")) {
04175       if (i == argc-1) {
04176         Tcl_AppendResult(interp, "No resolution specified",NULL);
04177         return TCL_ERROR;
04178       } else if (Tcl_GetDoubleFromObj(interp, objv[i+1], &resolution) != TCL_OK) { 
04179         Tcl_AppendResult(interp, "\nResolution incorrectly specified",NULL);
04180         return TCL_ERROR;
04181       }
04182       recognized=1;
04183     }
04184     if (!strcmp(opt, "-spacing")) {
04185       if (i == argc-1) {
04186         Tcl_AppendResult(interp, "No spacing specified",NULL);
04187         return TCL_ERROR;
04188       } else if ( Tcl_GetDoubleFromObj(interp, objv[i+1], &gspacing) != TCL_OK) {
04189         Tcl_AppendResult(interp, "\nspacing incorrectly specified",NULL);
04190         return TCL_ERROR;
04191       }
04192       recognized=1;
04193     }
04194 
04195     if (!strcmp(opt,"-nrays")) {
04196       if (i == argc-1) {
04197         Tcl_AppendResult(interp, "No number of rays specified",NULL);
04198         return TCL_ERROR;
04199       } else if ( Tcl_GetIntFromObj(interp, objv[i+1], &nrays) != TCL_OK) {
04200         Tcl_AppendResult(interp, "\n number of rays incorrectly specified",NULL);
04201         return TCL_ERROR;
04202       }
04203       recognized=1;
04204     }
04205 
04206     if (!strcmp(opt,"-isovalue")) {
04207       double tmp;
04208       if (i == argc-1) {
04209         Tcl_AppendResult(interp,"No isovalue specified",NULL);
04210         return TCL_ERROR;
04211       } else if (Tcl_GetDoubleFromObj(interp,objv[i+1],&tmp) != TCL_OK) {
04212         Tcl_AppendResult(interp,"\n Isovalue incorrectly specified", NULL);
04213         return TCL_ERROR;
04214       }
04215       isovalue=(float) tmp;
04216       recognized=1;
04217     }
04218 
04219 //    if (!strcmp(opt, "-o")) {
04220 //      if (i == argc-1) {
04221 //      //  Tcl_AppendResult(interp, "No output file specified",NULL);
04222 //      //  return TCL_ERROR;
04223 //      if (verbose) 
04224 //        printf("No output file specified.");
04225 //      } else {
04226 //        outputmap = Tcl_GetStringFromObj(objv[i+1], NULL);
04227 //      }
04228 //    }
04229 
04230     if (!strcmp(opt, "-verbose") || (getenv("VMDVOLINVERBOSE") != NULL)) {
04231       verbose = 1;
04232       recognized=1;
04233     }
04234 
04235     if (recognized==0) {
04236       // XXX what case is this supposed to handle? Dangling number parms?
04237       if (isdigit(opt[0]) || isfloat(opt)) { 
04238         continue;
04239       } else {
04240         Tcl_AppendResult(interp,"\n unrecognized option: '",opt,"'",NULL);
04241         return TCL_ERROR;
04242       }
04243     }
04244   }
04245 
04246   if ((process||count_pmap) && !loadpmap) {
04247     Tcl_AppendResult(interp,"\n Cannot give -process or -count_pmap flags given without -probmap!",NULL);
04248     return TCL_ERROR;
04249   }
04250 
04251   Molecule *volmolOverwrite = NULL;
04252   VolumetricData *volmapOverwrite = NULL;
04253   Molecule *pmolOverwrite=NULL;
04254   VolumetricData *pmapOverwrite=NULL;
04255   if (overwrite==1) {
04256     if (loadpmap!=1) {
04257       volmolOverwrite = mlist->mol_from_id(molid);
04258       volmapOverwrite = volmolOverwrite->modify_volume_data(volidOverwrite);
04259       if(volmapOverwrite==NULL) {
04260         Tcl_AppendResult(interp, "\n no overwrite volume correctly specified",NULL);
04261         return TCL_ERROR;
04262       }
04263     } else {
04264       pmolOverwrite=mlist->mol_from_id(molid);
04265       pmapOverwrite=pmolOverwrite->modify_volume_data(volidOverwrite);
04266       if (pmapOverwrite==NULL) {
04267         Tcl_AppendResult(interp,"\n no pmap overwrite volume correctly specified",NULL);
04268         return TCL_ERROR;
04269       }
04270     }
04271   }
04272 
04273   VolumetricData *pmapT = NULL;
04274   VolumetricData *procmap = NULL;
04275   VolumetricData *pmap = NULL;  
04276   VolumetricData *target = NULL;
04277   VolumetricData *volmapA = NULL;
04278   
04279 
04280   const float *framepos = sel->coordinates(app->moleculeList);
04281   const float *radii = mymol->radius();
04282   radscale= 0.2f * float(resolution);
04283   if (gspacing == 0) {
04284     gspacing=resolution*0.33;
04285   }
04286 
04287   int quality=0;
04288   QuickSurf *qs = new QuickSurf();
04289   if (resolution >= 9)
04290     quality = 0;
04291   else
04292     quality = 3;
04293 
04294   if (molid != -1 && volid != -1 ) {
04295     Molecule *volmol = mlist->mol_from_id(molid);
04296     volmapA = (VolumetricData *) volmol->get_volume_data(volid);
04297     if (volmapA==NULL) {
04298       Tcl_AppendResult(interp, "\n no target volume correctly specified", NULL);
04299       return TCL_ERROR;
04300     }
04301   } else {
04302     if (verbose) printf("\n Calculating grid ... \n");
04303     int cuda_err=-1;
04304 #if defined(VMDCUDA)
04305     cuda_err = vmd_cuda_calc_density(sel, app->moleculeList, quality,
04306                                      radscale, gspacing, &volmapA, 
04307                                      NULL, NULL, verbose);
04308 #endif
04309     if (cuda_err == -1 ) {
04310       if (verbose) printf("Using CPU version of the code ... \n");
04311       volmapA = qs->calc_density_map(sel, mymol, framepos, radii,
04312                                      quality, radscale, float(gspacing));
04313     }
04314     if (verbose) printf("Done.\n");
04315   }
04316 
04317   if (volmapA == NULL) {
04318     Tcl_AppendResult(interp, "\n no test volume or molid correctly specified",NULL);
04319     return TCL_ERROR;
04320   }
04321   target=CreateEmptyGrid(volmapA);
04322 
04323   if (loadpmap == 1) {
04324     pmapT = CreateProbGrid(volmapA);
04325   }
04326 
04327   // Normalize dir vector 
04328   // TODO: Let user define direction vectors
04329 
04330   //vec_normalize(rayDir);
04331   
04332   // Create Grid
04333 #if 0
04334   float rayDir[3] = {1,0,0};
04335   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04336   rayDir[0] = -1; rayDir[1]=0; rayDir[2]=0;
04337   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04338   rayDir[0] = 0; rayDir[1]=1; rayDir[2]=0;
04339   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04340   rayDir[0] = 0; rayDir[1]=-1; rayDir[2]=0;
04341   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04342   rayDir[0] = 0; rayDir[1]=0; rayDir[2]=1;
04343   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04344   rayDir[0] = 0; rayDir[1]=0; rayDir[2]=-1;
04345   Nout += RaycastGrid(volmapA,target,isovalue,rayDir);
04346 #endif
04347 #if 0
04348   if (verbose) printf("Marking down boundary.\n");
04349   long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04350   if (verbose) printf("Casting rays ...\n");
04351   float rayDir[3] = {0.1f,0.1f,1.0f};
04352   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04353   if (verbose) printf("Dir 1 %ld \n",Nout);
04354   rayDir[0] = -0.1f; rayDir[1]=-0.1f; rayDir[2]=-1.0f;
04355   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04356   if (verbose) printf("Dir 2 %ld \n",Nout);
04357   rayDir[0] = 0.1f; rayDir[1]=1.0f; rayDir[2]=0.1f;
04358   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04359   if (verbose) printf("Dir 3 %ld \n",Nout);
04360   rayDir[0] = -0.1f; rayDir[1]=-1.0f; rayDir[2]=-0.1f;
04361   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04362   if (verbose) printf("Dir 4 %ld \n",Nout);
04363   rayDir[0] = 1.0f; rayDir[1]=0.1f; rayDir[2]=0.1f;
04364   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04365   if (verbose) printf("Dir 5 %ld \n",Nout);
04366   rayDir[0] = -1.0f; rayDir[1]=-0.1f; rayDir[2]=-0.1f;
04367   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04368   if (verbose) printf("Dir 6 %ld \n",Nout);
04369   rayDir[0] = -0.5f; rayDir[1]=-0.5f; rayDir[2]=-0.5f;
04370   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04371   if (verbose) printf("Dir 7 %ld \n",Nout);
04372   rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04373   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04374   if (verbose) printf("Dir 9 %ld \n",Nout);
04375   rayDir[0] = -0.5f; rayDir[1]=0.5f; rayDir[2]=0.5f;
04376   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04377   if (verbose) printf("Dir 10 %ld \n",Nout);
04378   rayDir[0] = 0.5f; rayDir[1]=-0.5f; rayDir[2]=0.5f;
04379   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04380   if (verbose) printf("Dir 11 %ld \n",Nout);
04381   rayDir[0] = 0.5f; rayDir[1]=0.5f; rayDir[2]=-0.5f;
04382   Nout += volin_threaded(volmapA,target,isovalue,rayDir);
04383   if (verbose) printf("Dir 12 %ld \n",Nout);
04384 #endif
04385   if (verbose) printf("Marking down boundary.\n");
04386   long nVoxIso=markIsoGrid(volmapA, target,isovalue);
04387   float rayDir[3] = {0.1f,0.1f,1.0f};
04388   static const float RAND_MAX_INV = 1.0f/float(VMD_RAND_MAX);
04389 
04390   float *poisson_set = new float[(nrays+1)*2];
04391   int numcandidates=40;
04392   int converged=poisson_sample_on_sphere(poisson_set,nrays,numcandidates,verbose);
04393   if (converged==0) {
04394     if (verbose) {
04395       printf("Poisson disk sampler failed for measure volinterior!\n");
04396       printf("... continuing with standard ray direction generator.\n");
04397     }
04398   }
04399 
04400   if (print_raydirs && converged)
04401     print_xyz(poisson_set,nrays);
04402 
04403   if (verbose) printf("Casting rays...\n");
04404   // Seed the random number generator before each calculation.  
04405   //vmd_srandom(103467829);
04406   // Cast nrays uniformly over the sphere
04407 
04408   // XXX Use of sincosf() now in place. It would be much more work-efficient
04409   //     to build up batches of rays and pass them into the multithreaded
04410   //     work routines, as launching the worker threads has a non-trivial
04411   //     cost.  It would be better if they plow through many/all rays before
04412   //     returning, since the control loops here aren't checking for any
04413   //     conditions to be met other than that we've processed all of the rays.
04414 
04415   float sine1, cosine1, sine2, cosine2;
04416   for (int ray=0; ray < nrays; ray++) { 
04417     float u1 = (float) vmd_random();
04418     float u2 = (float) vmd_random();
04419     float z = 2.0f*u1*RAND_MAX_INV -1.0f;
04420     float phi = (float) (2.0f*VMD_PI*u2*RAND_MAX_INV);
04421     float R = sqrtf(1.0f-z*z);
04422     if (converged==1) {
04423       sincosf(poisson_set[ray*2+0],&sine1,&cosine1);
04424       sincosf(poisson_set[ray*2+1],&sine2,&cosine2);
04425       rayDir[0]=cosine1*sine2;
04426       rayDir[1]=sine1*sine2;
04427       rayDir[2]=cosine2;
04428     } else {
04429       sincosf(phi,&sine1,&cosine1);
04430       rayDir[0]=R*cosine1;
04431       rayDir[1]=R*sine1;
04432       rayDir[2]=z;
04433     }
04434 
04435     // XXX rather than launching threads per ray, we should be launching
04436     //     the worker threads and processing _all_ of the rays...
04437     //     That would also lead toward an implementation that could
04438     //     eventually be migrated to the GPU for higher performance.
04439     if (loadpmap == 1) {
04440       Nout += volin_threaded_prob(volmapA, target, pmapT, isovalue, rayDir);
04441     } else {
04442       Nout += volin_threaded(volmapA, target, isovalue, rayDir);
04443     }
04444 
04445     if (verbose) printf("Ray(%d)  (%4.3f %4.3f %4.3f). Voxels : %ld \n",ray+1,rayDir[0],rayDir[1],rayDir[2],Nout);    
04446   }
04447   if (verbose) printf("Done.\n");
04448   
04449   delete [] poisson_set;
04450 
04451   if (verbose) printf("Counting voxels above isovalue ...\n");
04452   
04453   Nout=countIsoGrids(target,EXTERIORVOXEL);
04454   long nIn=countIsoGrids(target,INTERIORVOXEL);
04455  
04456   if (loadsynthmap == 1 ) {
04457     app->molecule_add_volumetric(molid, volmapA->name, volmapA->origin, 
04458                                  volmapA->xaxis, volmapA->yaxis, volmapA->zaxis,
04459                                  volmapA->xsize, volmapA->ysize, volmapA->zsize,
04460                                  volmapA->data);
04461     volmapA->compute_volume_gradient();
04462     volmapA->data = NULL; // prevent destruction of density array;  
04463   }
04464 
04465   if (overwrite != 1 ) {
04466     if (loadpmap==1) {
04467       if (verbose) printf("Normalizing probability map ...\n");
04468       pmap = normalize_pmap(pmapT, nrays);
04469       app->molecule_add_volumetric(molid, pmap->name, pmap->origin,
04470                                    pmap->xaxis, pmap->yaxis, pmap->zaxis,
04471                                    pmap->xsize, pmap->ysize, pmap->zsize, 
04472                                    pmap->data);
04473       pmap->compute_volume_gradient();
04474     } else {
04475       app->molecule_add_volumetric(molid, target->name, target->origin, 
04476                                    target->xaxis, target->yaxis, target->zaxis,
04477                                    target->xsize, target->ysize, target->zsize,
04478                                    target->data);
04479       target->compute_volume_gradient();
04480       target->data = NULL; // prevent destruction of density array;  
04481     }
04482   } else {
04483     if (loadpmap==1) {
04484       if (verbose) printf("Normalizing probability map ...\n");
04485       pmap = normalize_pmap(pmapT, nrays);
04486       if (verbose) printf("Overwriting volume...\n");
04487       pmapOverwrite->name=pmap->name;
04488       pmapOverwrite->xsize=pmap->xsize;
04489       pmapOverwrite->ysize=pmap->ysize;
04490       pmapOverwrite->zsize=pmap->zsize;
04491       pmapOverwrite->data=pmap->data;
04492       pmap->data=NULL;
04493       pmapOverwrite->compute_volume_gradient();
04494       if (verbose) printf("Done!\n");
04495     } else {
04496       if (verbose) printf("Overwriting volume ... \n");
04497       volmapOverwrite->name = target -> name;
04498       volmapOverwrite->xsize = target->xsize;
04499       volmapOverwrite->ysize = target->ysize;
04500       volmapOverwrite->zsize = target->zsize;
04501       volmapOverwrite->data = target->data;
04502       target->data=NULL;
04503       volmapOverwrite->compute_volume_gradient();
04504       if (verbose) printf("Done.\n ");
04505     }  
04506   }
04507 
04508   if ((loadpmap&&process)&&pmap->data!=NULL) {
04509     //Tcl_AppendResult(interp,"Processing probability map with cutoff: ",proCutoff,"...",NULL);
04510     procmap=process_pmap(pmap, proCutoff);
04511     app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04512       procmap->xaxis, procmap->yaxis, procmap->zaxis,
04513       procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04514     procmap->compute_volume_gradient();
04515     procmap->data=NULL;
04516     if (verbose) printf("Loaded processed map\n");
04517   } else if ((loadpmap&&process)&&pmapOverwrite->data!=NULL) {
04518      procmap=process_pmap(pmapOverwrite, proCutoff);
04519      app->molecule_add_volumetric(molid, procmap->name, procmap->origin,
04520        procmap->xaxis, procmap->yaxis, procmap->zaxis,
04521        procmap->xsize, procmap->ysize, procmap->zsize, procmap->data);
04522      procmap->compute_volume_gradient();
04523      procmap->data=NULL;
04524     if (verbose) printf("Loaded processed map (they don't overwrite).\n");
04525   }
04526 
04527   if (count_pmap==1 && pmap->data!=NULL) {
04528     if (loadpmap!=1) {
04529       printf("-count_pmap flag specified without -probmap!\n");
04530       return TCL_ERROR;
04531     } else {
04532       long Pvol=0;
04533       float currProb=0.0f;
04534       Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04535       for (int c=0; c<10; c++) {
04536         currProb+=0.1f;
04537         if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04538         Pvol=vol_probability(pmap, currProb, float(gspacing));
04539         Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04540       }
04541       Tcl_SetObjResult(interp,analysis);
04542       if (molid != -1 && volid != -1 ) {
04543         target->data = NULL; // prevent destruction of density array;
04544         pmap->data = NULL;
04545       } else {
04546         delete pmapT;
04547         delete qs;
04548         delete volmapA;
04549       }   
04550 
04551       return TCL_OK;
04552     }
04553   } else if (count_pmap==1 && overwrite==1 && pmapOverwrite->data!=NULL) {
04554     long Pvol=0;
04555     float currProb=0.0f;
04556     Tcl_Obj *analysis=Tcl_NewListObj(0,NULL);
04557     for (int c=0; c<10; c++) {
04558       currProb+=0.1f;
04559       if (c==9) currProb=((float)nrays-((float)nrays*0.01f))/(float)nrays;
04560       Pvol=vol_probability(pmapOverwrite, currProb, float(gspacing));
04561       Tcl_ListObjAppendElement(interp, analysis, Tcl_NewLongObj(Pvol));
04562     }
04563     Tcl_SetObjResult(interp,analysis);
04564     if (molid != -1 && volid != -1 ) {
04565       target->data = NULL; // prevent destruction of density array;
04566       pmap->data = NULL;
04567     } else {
04568       delete pmapT;
04569       delete qs;
04570       delete volmapA;
04571     }
04572 
04573     return TCL_OK;
04574   }
04575 
04576   if (molid != -1 && volid != -1 ) {
04577     target->data = NULL; // prevent destruction of density array;
04578     procmap->data=NULL;
04579     pmap->data = NULL;
04580   } else {
04581     delete pmapT;
04582     delete qs;
04583     delete volmapA;
04584   }
04585 
04586   long Ntotal = target->xsize * target->ysize * target->zsize;
04587   //printf("VolIn: %ld external voxels.\n",Nout);
04588   
04589   Tcl_Obj *tcl_result = Tcl_NewListObj(0,NULL);
04590   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Ntotal));
04591   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(Nout));
04592   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nIn));
04593   Tcl_ListObjAppendElement(interp,tcl_result,Tcl_NewLongObj(nVoxIso));
04594   Tcl_SetObjResult(interp,tcl_result);
04595   return TCL_OK;
04596 }
04597 
04598 int obj_measure(ClientData cd, Tcl_Interp *interp, int argc,
04599                             Tcl_Obj * const objv[]) {
04600 
04601   if (argc < 2) {
04602     Tcl_SetResult(interp, 
04603       (char *) "usage: measure <command> [args...]\n"
04604       "\nMeasure Commands:\n"
04605       "  avpos <sel> [first <first>] [last <last>] [step <step>] -- average position\n"
04606       "  center <sel> [weight <weights>]          -- geometrical (or weighted) center\n"
04607       "  centerperresidue <sel> [weight <weights>]  -- geometrical center for every residue in sel\n"
04608       "  cluster <sel> [num <#clusters>] [distfunc <flag>] [cutoff <cutoff>]\n"
04609       "          [first <first>] [last <last>] [step <step>] [selupdate <bool>]\n"
04610       "          [weight <weights>]\n"
04611       "     -- perform a cluster analysis (cluster similar timesteps)\n"
04612       "  clustsize <sel> [cutoff <float>] [minsize <num>] [numshared <num>]\n"
04613       "            [usepbc <bool>] [storesize <fieldname>] [storenum <fieldname>]\n"
04614       "     -- perform a cluster size analysis (find clusters of atoms)\n"
04615       "  contacts <cutoff> <sel1> [<sel2>]        -- list contacts\n" 
04616       "  dipole <sel> [-elementary|-debye] [-geocenter|-masscenter|-origincenter]\n"
04617       "     -- dipole moment\n"
04618       "  fit <sel1> <sel2> [weight <weights>] [order <index list>]\n"
04619       "     -- transformation matrix from selection 1 to 2\n"
04620       "  gofr <sel1> <sel2> [delta <value>] [rmax <value>] [usepbc <bool>]\n"
04621       "     [selupdate <bool>] [first <first>] [last <last>] [step <step>]\n"
04622       "     -- atomic pair distribution function g(r)\n"
04623       "  hbonds <cutoff> <angle> <sel1> [<sel2>]\n"
04624       "     -- list donors, acceptors, hydrogens involved in hydrogen bonds\n"
04625       "  inverse <matrix>                         -- inverse matrix\n"
04626       "  inertia <sel> [-moments] [-eigenvals]    -- COM and principle axes of inertia\n"
04627       "  minmax <sel> [-withradii]                -- bounding box\n"
04628       "  rgyr <sel> [weight <weights>]            -- radius of gyration\n"
04629       "  rmsd <sel1> <sel2> [weight <weights>]    -- RMS deviation\n"
04630       "  rmsdperresidue <sel1> <sel2> [weight <weights>] -- deviation per residue\n"
04631       "  rmsf <sel> [first <first>] [last <last>] [step <step>] -- RMS fluctuation\n"
04632       "  rmsfperresidue <sel> [first <first>] [last <last>] [step <step>] -- fluctuation per residue\n"
04633       "  sasa <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
04634       "     [-samples <numsamples>]               -- solvent-accessible surface area\n"
04635       "  sasaperresidue <srad> <sel> [-points <varname>] [-restrict <restrictedsel>]\n"
04636       "     [-samples <numsamples>]               -- solvent-accessible surface area per residue\n"
04637       "  sumweights <sel> weight <weights>        -- sum of selected weights\n"
04638       "  bond {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]}}\n"
04639       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04640       "     -- bond length between atoms 1 and 2\n"
04641       "  angle {{<atomid1> [<molid1>]} {<atomid2> [<molid2>]} {<atomid3> [<molid3>]}}\n"
04642       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04643       "     -- angle between atoms 1-3\n"
04644       "  dihed {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04645       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04646       "      -- dihedral angle between atoms 1-4\n"
04647       "  imprp {{<atomid1> [<molid1>]} ... {<atomid4> [<molid4>]}}\n"
04648       "     [molid <default mol>] [frame <frame|all|last> | first <first> last <last>]\n"
04649       "     -- improper angle between atoms 1-4\n"
04650       // FIXME: Complete 'measure energy' usage info here?
04651       "  energy bond|angle|dihed|impr|vdw|elec     -- compute energy\n"
04652       "  surface <sel> <gridsize> <radius> <thickness> -- surface of selection\n"
04653       "  pbc2onc <center> [molid <default>] [frame <frame|last>]\n"
04654       "     --  transformation matrix to wrap a nonorthogonal PBC unit cell\n"
04655       "         into an orthonormal cell\n"
04656       "  pbcneighbors <center> <cutoff> [sel <sel>] [align <matrix>] [molid <default>]\n"
04657       "     [frame <frame|last>] [boundingbox <PBC|{<mincoord> <maxcoord>}>]\n"
04658       "     -- all image atoms that are within cutoff Angstrom of the pbc unit cell\n"
04659       "  symmetry <sel> [element [<vector>]] [-tol <value>] [-nobonds] [-verbose <level>]\n"
04660       "  transoverlap <sel> <matrix> [-sigma <value>]\n"
04661       "     -- overlap of a structure with a transformed copy of itself\n"
04662       "  volinterior <sel> [options]\n"
04663       "     -- type 'measure volinterior' for usage\n",
04664       TCL_STATIC);
04665     return TCL_ERROR;
04666   }
04667   VMDApp *app = (VMDApp *)cd;
04668   char *argv1 = Tcl_GetStringFromObj(objv[1],NULL);
04669   if (!strupncmp(argv1, "avpos", CMDLEN))
04670     return vmd_measure_avpos(app, argc-1, objv+1, interp);
04671   else if (!strupncmp(argv1, "center", CMDLEN))
04672     return vmd_measure_center(app, argc-1, objv+1, interp);
04673 #if 1
04674   // XXX in test
04675   else if (!strupncmp(argv1, "centerperresidue", CMDLEN))
04676     return vmd_measure_centerperresidue(app, argc-1, objv+1, interp);
04677 #endif
04678   else if (!strupncmp(argv1, "cluster", CMDLEN))
04679     return vmd_measure_cluster(app, argc-1, objv+1, interp);
04680   else if (!strupncmp(argv1, "clustsize", CMDLEN))
04681     return vmd_measure_clustsize(app, argc-1, objv+1, interp);
04682   else if (!strupncmp(argv1, "contacts", CMDLEN))
04683     return vmd_measure_contacts(app, argc-1, objv+1, interp);
04684   else if (!strupncmp(argv1, "dipole", CMDLEN))
04685     return vmd_measure_dipole(app, argc-1, objv+1, interp);
04686   else if (!strupncmp(argv1, "fit", CMDLEN)) 
04687     return vmd_measure_fit(app, argc-1, objv+1, interp);
04688   else if (!strupncmp(argv1, "minmax", CMDLEN))
04689     return vmd_measure_minmax(app, argc-1, objv+1, interp);
04690   else if (!strupncmp(argv1, "gofr", CMDLEN))
04691     return vmd_measure_gofr(app, argc-1, objv+1, interp);
04692   else if (!strupncmp(argv1, "rdf", CMDLEN))
04693     return vmd_measure_rdf(app, argc-1, objv+1, interp);
04694   else if (!strupncmp(argv1, "hbonds", CMDLEN))
04695     return vmd_measure_hbonds(app, argc-1, objv+1, interp);
04696   else if (!strupncmp(argv1, "inverse", CMDLEN)) 
04697     return vmd_measure_inverse(argc-1, objv+1, interp);
04698   else if (!strupncmp(argv1, "inertia", CMDLEN)) 
04699     return vmd_measure_inertia(app, argc-1, objv+1, interp);
04700   else if (!strupncmp(argv1, "rgyr", CMDLEN))
04701     return vmd_measure_rgyr(app, argc-1, objv+1, interp);
04702   else if (!strupncmp(argv1, "rmsd", CMDLEN))
04703     return vmd_measure_rmsd(app, argc-1, objv+1, interp);
04704 #if 1
04705   // XXX in test
04706   else if (!strupncmp(argv1, "rmsdperresidue", CMDLEN))
04707     return vmd_measure_rmsdperresidue(app, argc-1, objv+1, interp);
04708 #endif
04709 #if 1
04710   else if (!strupncmp(argv1, "rmsd_qcp", CMDLEN))
04711     return vmd_measure_rmsd_qcp(app, argc-1, objv+1, interp);
04712   else if (!strupncmp(argv1, "rmsdmat_qcp", CMDLEN))
04713     return vmd_measure_rmsdmat_qcp(app, argc-1, objv+1, interp);
04714   else if (!strupncmp(argv1, "rmsdmat_qcp_ooc", CMDLEN))
04715     return vmd_measure_rmsdmat_qcp_ooc(app, argc-1, objv+1, interp);
04716 #endif
04717   else if (!strupncmp(argv1, "rmsf", CMDLEN))
04718     return vmd_measure_rmsf(app, argc-1, objv+1, interp);
04719 #if 1
04720   // XXX in test
04721   else if (!strupncmp(argv1, "rmsfperresidue", CMDLEN))
04722     return vmd_measure_rmsfperresidue(app, argc-1, objv+1, interp);
04723 #endif
04724   else if (!strupncmp(argv1, "sasa", CMDLEN))
04725     return vmd_measure_sasa(app, argc-1, objv+1, interp);
04726   else if (!strupncmp(argv1, "sasaperresidue", CMDLEN))
04727     return vmd_measure_sasaperresidue(app, argc-1, objv+1, interp);
04728 #if 1
04729   else if (!strupncmp(argv1, "sasalist", CMDLEN))
04730     return vmd_measure_sasalist(app, argc-1, objv+1, interp);
04731 #endif
04732   else if (!strupncmp(argv1, "sumweights", CMDLEN))
04733     return vmd_measure_sumweights(app, argc-1, objv+1, interp);
04734   else if (!strupncmp(argv1, "imprp", CMDLEN))
04735     return vmd_measure_dihed(app, argc-1, objv+1, interp);
04736   else if (!strupncmp(argv1, "dihed", CMDLEN))
04737     return vmd_measure_dihed(app, argc-1, objv+1, interp);
04738   else if (!strupncmp(argv1, "angle", CMDLEN))
04739     return vmd_measure_angle(app, argc-1, objv+1, interp);
04740   else if (!strupncmp(argv1, "bond", CMDLEN))
04741     return vmd_measure_bond(app, argc-1, objv+1, interp);
04742   else if (!strupncmp(argv1, "energy", CMDLEN))
04743     return vmd_measure_energy(app, argc-1, objv+1, interp);
04744   else if (!strupncmp(argv1, "pbc2onc", CMDLEN))
04745     return vmd_measure_pbc2onc_transform(app, argc-1, objv+1, interp);
04746   else if (!strupncmp(argv1, "pbcneighbors", CMDLEN))
04747     return vmd_measure_pbc_neighbors(app, argc-1, objv+1, interp);
04748   else if (!strupncmp(argv1, "surface", CMDLEN))
04749     return vmd_measure_surface(app, argc-1, objv+1, interp);
04750   else if (!strupncmp(argv1, "transoverlap", CMDLEN))
04751     return vmd_measure_trans_overlap(app, argc-1, objv+1, interp);
04752   else if (!strupncmp(argv1, "symmetry", CMDLEN))
04753     return vmd_measure_symmetry(app, argc-1, objv+1, interp);
04754   else if (!strupncmp(argv1, "volinterior", CMDLEN))
04755     return vmd_measure_volinterior(app, argc-1, objv+1, interp);
04756 
04757   Tcl_SetResult(interp, (char *) "Type 'measure' for summary of usage\n", TCL_VOLATILE);
04758   return TCL_OK;
04759 }
04760 
04761 
04762 

Generated on Sat May 18 02:54:10 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002