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: cmd_animate.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.52 $ $Date: 2019年01月17日 21:21:03 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * text commands for animation control 00019 ***************************************************************************/ 00020 00021 #include <tcl.h> 00022 #include <ctype.h> 00023 #include "config.h" 00024 #include "VMDApp.h" 00025 #include "MoleculeList.h" 00026 #include "TclCommands.h" 00027 #include "Animation.h" 00028 00029 static void cmd_animate_usage(Tcl_Interp *interp) { 00030 Tcl_AppendResult(interp, 00031 "usage: animate <command> [args...]" 00032 "animate styles\n" 00033 "animate style [once|rock|loop]\n" 00034 "animate dup [|frame <number>] <molecule id>\n" 00035 "animate goto [start|end|<num]\n" 00036 "animate [reverse|rev|forward|for|prev|next|pause]\n" 00037 "animate [speed|skip] [|<value>]\n" 00038 "animate delete all\n" 00039 "animate delete [|beg <num>] [|end <num>] [|skip <num>] <molecule id>\n" 00040 "animate [read|write] <file type> <filename>\n" 00041 " [|beg <num>] [|end <num>] [|skip <num>] [|waitfor <num/all>]\n" 00042 " [|sel <atom selection>] [|<molecule id>]", 00043 NULL); 00044 } 00045 00046 int text_cmd_animate(ClientData cd, Tcl_Interp *interp, int argc, 00047 const char *argv[]) { 00048 00049 VMDApp *app = (VMDApp *)cd; 00050 00051 // since I cannot easily add this to the rest of the code, I put 00052 // new code here: 00053 if (argc == 1) { 00054 cmd_animate_usage(interp); 00055 } 00056 00057 if (argc == 2) { 00058 if (!strupncmp(argv[1], "styles", CMDLEN)) { 00059 // enumerate the styles 00060 size_t loop = 0; 00061 while(loop < sizeof(animationStyleName) / sizeof(char*)) { 00062 Tcl_AppendElement(interp, animationStyleName[loop++]); 00063 } 00064 return TCL_OK; 00065 } 00066 if (!strupncmp(argv[1], "skip", CMDLEN)) { 00067 Tcl_SetObjResult(interp, Tcl_NewIntObj(app->anim->skip())); 00068 return TCL_OK; 00069 } 00070 if (!strupncmp(argv[1], "speed", CMDLEN)) { 00071 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(app->anim->speed())); 00072 return TCL_OK; 00073 } 00074 if (!strupncmp(argv[1], "style", CMDLEN)) { 00075 int style = app->anim->anim_style(); 00076 Tcl_AppendElement(interp, animationStyleName[style]); 00077 return TCL_OK; 00078 } 00079 // fall through and let the rest of the code catch the other 2 word cmds 00080 } 00081 00082 if ((argc == 3 || argc == 5) && !strupncmp(argv[1], "dup", CMDLEN)) { 00083 // option is: animate dup [frame <number>] <molecule id> 00084 // default frame is "now" 00085 // It adds a new animation frame to the molecule, which is a copy of 00086 // the given frame. If there is no frame, {0, 0, 0} is added. 00087 int frame = -1; 00088 int molid = -1; 00089 // get frame number 00090 if (argc == 3) { 00091 if (!strcmp(argv[2], "top")) { 00092 if (app->moleculeList -> top()) { 00093 molid = app->moleculeList -> top() -> id(); 00094 } else { 00095 molid = -1; 00096 } 00097 } else if (Tcl_GetInt(interp, argv[2], &molid) != TCL_OK) { 00098 Tcl_AppendResult(interp, " in animate dup", NULL); 00099 return TCL_ERROR; 00100 } 00101 00102 } else { 00103 if (strcmp(argv[2], "frame")) { 00104 // error 00105 Tcl_AppendResult(interp, 00106 "format is: animate dup [frame <number>] <molecule id>", NULL); 00107 return TCL_ERROR; 00108 } 00109 if (!strcmp(argv[3], "now")) { // check special cases 00110 frame = -1; 00111 } else if (!strcmp(argv[3], "null")) { 00112 frame = -2; 00113 } else { 00114 if (Tcl_GetInt(interp, argv[3], &frame) != TCL_OK) { 00115 Tcl_AppendResult(interp, " in animate dup frame", NULL); 00116 return TCL_ERROR; 00117 } 00118 } 00119 if (Tcl_GetInt(interp, argv[4], &molid) != TCL_OK) { 00120 Tcl_AppendResult(interp, " in animate dup", NULL); 00121 return TCL_ERROR; 00122 } 00123 } 00124 if (!app->molecule_dupframe(molid, frame)) return TCL_ERROR; 00125 return TCL_OK; 00126 } 00127 00128 00129 if (argc == 2) { 00130 Animation::AnimDir newDir; 00131 if(!strupncmp(argv[1], "reverse", CMDLEN) || 00132 !strupncmp(argv[1], "rev", CMDLEN)) 00133 newDir = Animation::ANIM_REVERSE; 00134 else if(!strupncmp(argv[1], "forward", CMDLEN) || 00135 !strupncmp(argv[1], "for", CMDLEN)) 00136 newDir = Animation::ANIM_FORWARD; 00137 else if(!strupncmp(argv[1], "prev", CMDLEN)) 00138 newDir = Animation::ANIM_REVERSE1; 00139 else if(!strupncmp(argv[1], "next", CMDLEN)) 00140 newDir = Animation::ANIM_FORWARD1; 00141 else if(!strupncmp(argv[1], "pause", CMDLEN)) 00142 newDir = Animation::ANIM_PAUSE; 00143 else { 00144 cmd_animate_usage(interp); 00145 return TCL_ERROR; // error 00146 } 00147 app->animation_set_dir(newDir); 00148 } else if(argc == 3) { 00149 if(!strupncmp(argv[1], "skip", CMDLEN)) { 00150 int tmp; 00151 if (Tcl_GetInt(interp, argv[2], &tmp) != TCL_OK) { 00152 Tcl_AppendResult(interp, " in animate skip", NULL); 00153 return TCL_ERROR; 00154 } 00155 app->animation_set_stride(tmp); 00156 } else if(!strupncmp(argv[1], "delete", CMDLEN)) { 00157 if(!strupncmp(argv[2], "all", CMDLEN)) { 00158 int molid = app->molecule_top(); 00159 int last = app->molecule_numframes(molid)-1; 00160 int rc = app->molecule_deleteframes(molid, 0, last, -1); 00161 return rc ? TCL_OK : TCL_ERROR; 00162 } else { 00163 cmd_animate_usage(interp); 00164 return TCL_ERROR; // error 00165 } 00166 } else if(!strupncmp(argv[1], "speed", CMDLEN)) 00167 app->animation_set_speed((float) atof(argv[2])); 00168 else if(!strupncmp(argv[1], "style", CMDLEN)) { 00169 int newStyle = Animation::ANIM_ONCE; 00170 Animation::AnimStyle enumVal; 00171 while(newStyle < Animation::ANIM_TOTAL_STYLES) { 00172 if(!strupncmp(argv[2], animationStyleName[newStyle], CMDLEN)) 00173 break; 00174 newStyle++; 00175 } 00176 if(newStyle == Animation::ANIM_ONCE) 00177 enumVal = Animation::ANIM_ONCE; 00178 else if(newStyle == Animation::ANIM_ROCK) 00179 enumVal = Animation::ANIM_ROCK; 00180 else if(newStyle == Animation::ANIM_LOOP) 00181 enumVal = Animation::ANIM_LOOP; 00182 else { 00183 Tcl_AppendResult(interp, 00184 "Unknown animate style '" ,argv[2] ,"'\n", NULL); 00185 Tcl_AppendResult(interp, "Valid styles are: ", NULL); 00186 newStyle = Animation::ANIM_ONCE; 00187 while(newStyle < Animation::ANIM_TOTAL_STYLES) { 00188 Tcl_AppendElement(interp, animationStyleName[newStyle]); 00189 newStyle ++; 00190 } 00191 return TCL_ERROR; // error, unknown style 00192 } 00193 app->animation_set_style(enumVal); 00194 } else if(!strupncmp(argv[1], "goto", CMDLEN)) { 00195 int newframe; 00196 if(!strupncmp(argv[2], "start", CMDLEN)) 00197 newframe = -1; 00198 else if(!strupncmp(argv[2], "end", CMDLEN)) 00199 newframe = -2; 00200 else if(isdigit(argv[2][0])) 00201 newframe = atoi(argv[2]); 00202 else { 00203 Tcl_AppendResult(interp, "Bad goto parameter '" ,argv[2] ,"'\n", NULL); 00204 Tcl_AppendResult(interp, 00205 "Valid values are a non-negative number, 'start', or 'end'.", NULL); 00206 return TCL_ERROR; // error, bad frame goto command 00207 } 00208 app->animation_set_frame(newframe); 00209 } else { 00210 cmd_animate_usage(interp); 00211 return TCL_ERROR; // 3 option parameter, didn't understand 3rd term 00212 } 00213 } else if(argc >= 4) { 00214 int bf = 0, ef = (-1), fs = 1, mid = (-1); 00215 const char *fileType = NULL; 00216 const char *fileName = NULL; 00217 int do_action = (-1); 00218 int currarg = 1; 00219 int waitfor = FileSpec::WAIT_BACK; 00220 00221 // find out what to do first 00222 if(!strupncmp(argv[currarg], "read", CMDLEN)) { 00223 do_action = 0; 00224 } else if(!strupncmp(argv[currarg], "write", CMDLEN)) { 00225 do_action = 1; 00226 waitfor = FileSpec::WAIT_ALL; // waitfor 'all' by default 00227 } else if(!strupncmp(argv[currarg], "delete", CMDLEN)) { 00228 do_action = 2; 00229 fs = -1; // for "delete", fs=1 means do not delete any frames. 00230 } else { 00231 cmd_animate_usage(interp); 00232 return TCL_ERROR; 00233 } 00234 currarg++; 00235 00236 // if reading or writing, get file type and name 00237 if(do_action == 0 || do_action == 1) { 00238 fileType = argv[currarg++]; 00239 fileName = argv[currarg++]; 00240 } 00241 00242 AtomSel *selection = NULL; 00243 ResizeArray<int> volsets; 00244 mid = app->molecule_top(); 00245 // find if any beg, end, or skip specifiers 00246 while(currarg < argc) { 00247 if(currarg < (argc - 1)) { 00248 if(!strupncmp(argv[currarg], "beg", CMDLEN)) { 00249 bf = atoi(argv[currarg+1]); 00250 currarg += 2; 00251 } else if(!strupncmp(argv[currarg], "end", CMDLEN)) { 00252 ef = atoi(argv[currarg+1]); 00253 currarg += 2; 00254 } else if(!strupncmp(argv[currarg], "skip", CMDLEN)) { 00255 fs = atoi(argv[currarg+1]); 00256 currarg += 2; 00257 } else if(do_action == 2 && argc == 4 && currarg == 2 && 00258 !strupncmp(argv[currarg], "all", CMDLEN)) { 00259 if (strcmp(argv[currarg+1], "top")) 00260 mid = atoi(argv[currarg+1]); 00261 else 00262 mid = app->molecule_top(); 00263 currarg += 2; 00264 } else if ((do_action == 0 || do_action == 1) && 00265 !strupncmp(argv[currarg], "waitfor", CMDLEN)) { 00266 const char *arg = argv[currarg+1]; 00267 if (!strupncmp(arg, "all", CMDLEN)) 00268 waitfor = FileSpec::WAIT_ALL; 00269 else 00270 waitfor = atoi(arg); 00271 currarg += 2; 00272 } else if (do_action == 1 && // writing 00273 !strupncmp(argv[currarg], "sel", CMDLEN)) { 00274 // interpret the next argument as an atom selection 00275 const char *selstr = argv[currarg+1]; 00276 if (!(selection = tcl_commands_get_sel(interp, selstr))) { 00277 Tcl_AppendResult(interp, "Invalid atom selection ", selstr, NULL); 00278 return TCL_ERROR; 00279 } 00280 currarg += 2; 00281 } else if (do_action == 1 && // writing 00282 !strupncmp(argv[currarg], "volsets", CMDLEN)) { 00283 // interpret the next argument as a list of volsets 00284 const char *volstr = argv[currarg+1]; 00285 int nsets; 00286 const char **setstrs; 00287 if (Tcl_SplitList(interp, volstr, &nsets, &setstrs) != TCL_OK) { 00288 Tcl_AppendResult(interp, "Invalid volset argument: ", volstr, NULL); 00289 return TCL_ERROR; 00290 } 00291 for (int i=0; i<nsets; i++) { 00292 int tmp; 00293 if (Tcl_GetInt(interp, setstrs[i], &tmp) != TCL_OK) { 00294 Tcl_Free((char *)setstrs); 00295 return TCL_ERROR; 00296 } 00297 volsets.append(tmp); 00298 } 00299 Tcl_Free((char *)setstrs); 00300 currarg += 2; 00301 } else 00302 return TCL_ERROR; 00303 } else { 00304 // only one item left; it must be the molecule id 00305 if (strcmp(argv[currarg], "top")) { 00306 mid = atoi(argv[currarg++]); 00307 } else { 00308 mid = app->molecule_top(); 00309 currarg++; 00310 } 00311 } 00312 } 00313 00314 // if a selection was given, make sure the molid of the selection matches 00315 // the molid for the write command. This ensures that the number of atoms 00316 // in the selection matches those in the molecule. 00317 if (selection) { 00318 if (mid != selection->molid()) { 00319 Tcl_SetResult(interp, (char *) "ERROR: animate: Molecule in selection must match animation molecule.", TCL_STATIC); 00320 return TCL_ERROR; 00321 } 00322 } 00323 00324 // do action now 00325 FileSpec spec; 00326 spec.first = bf; 00327 spec.last = ef; 00328 spec.stride = fs; 00329 spec.waitfor = waitfor; 00330 if (do_action == 0) { 00331 int rc = app->molecule_load(mid, fileName, fileType, &spec); 00332 if (rc < 0) return TCL_ERROR; 00333 00334 } else if (do_action == 1) { 00335 spec.selection = selection ? selection->on : NULL; 00336 if (volsets.num()) { 00337 // make a copy of the setids, since FileSpec frees its setids pointer 00338 spec.nvolsets = volsets.num(); 00339 spec.setids = new int[spec.nvolsets]; 00340 for (int i=0; i<spec.nvolsets; i++) spec.setids[i] = volsets[i]; 00341 } 00342 int numwritten = app->molecule_savetrajectory(mid, fileName, fileType, 00343 &spec); 00344 if (numwritten < 0) return TCL_ERROR; 00345 Tcl_SetObjResult(interp, Tcl_NewIntObj(numwritten)); 00346 00347 } else if (do_action == 2) 00348 app->molecule_deleteframes(mid, bf, ef, fs); 00349 00350 else 00351 return TCL_ERROR; 00352 00353 } else 00354 return TCL_ERROR; 00355 00356 // if here, everything worked out ok 00357 return TCL_OK; 00358 } 00359 00360 // read raw bytes into a typestep 00361 // Usage; rawtimestep <molid> <bytearray> -start index -frame whichframe 00362 // Optional start parameter specifies where in the byte array to start reading 00363 // The array must contain at least (12*numatoms) bytes beginning from start 00364 // or an error will be returned. 00365 // -frame parameter can be last, current, append, or a valid frame number. 00366 // If 'last' and there are no frames, a new frame will be created. 00367 // If 'current' and there are no frames, error. 00368 // If 'append', always append. 00369 00370 int cmd_rawtimestep(ClientData cd, Tcl_Interp *interp, int argc, 00371 Tcl_Obj * const objv[]) { 00372 VMDApp *app = (VMDApp *)cd; 00373 Molecule *mol; 00374 Timestep *ts; 00375 int molid=-1, start=0, frame=-1, length, neededLength; 00376 unsigned char *bytes; 00377 00378 if (argc != 3 && argc != 5 && argc != 7) { 00379 Tcl_WrongNumArgs(interp, 1,objv, 00380 "<molid> <bytearray> ?-start index? ?-frame whichframe?"); 00381 return TCL_ERROR; 00382 } 00383 00384 // get molid, either "top" or a number. 00385 if (!strcmp(Tcl_GetStringFromObj(objv[1], NULL), "top")) 00386 molid = app->molecule_top(); 00387 else if (Tcl_GetIntFromObj(interp, objv[1], &molid) != TCL_OK) 00388 return TCL_ERROR; 00389 if (!(mol = app->moleculeList->mol_from_id(molid))) { 00390 Tcl_SetResult(interp, (char *) "rawtimestep: invalid molid", TCL_STATIC); 00391 return TCL_ERROR; 00392 } 00393 00394 // Read raw bytes and get length 00395 if (!(bytes = Tcl_GetByteArrayFromObj(objv[2], &length))) { 00396 Tcl_SetResult(interp, (char *) "rawtimestep: could not read bytearray", TCL_STATIC); 00397 return TCL_ERROR; 00398 } 00399 00400 // Read optional frame and start otions 00401 for (int iarg=3; iarg<argc; iarg += 2) { 00402 const char *opt = Tcl_GetStringFromObj(objv[iarg], NULL); 00403 if (!strcmp(opt, "-start")) { 00404 if (Tcl_GetIntFromObj(interp, objv[iarg+1], &start) != TCL_OK) 00405 return TCL_ERROR; 00406 } else if (!strcmp(opt, "-frame")) { 00407 // check for "last", "current", "append", otherwise must be numeric and 00408 // in the correct range 00409 const char *strframe = Tcl_GetStringFromObj(objv[iarg+1], NULL); 00410 if (!strcmp(strframe, "last")) { 00411 // allow frame to be -1 if the number of frames is zero, which 00412 // corresponds to "append". 00413 frame = mol->numframes()-1; 00414 } else if (!strcmp(strframe, "current")) { 00415 frame = mol->frame(); 00416 } else if (!strcmp(strframe, "append")) { 00417 frame = -1; 00418 } else { 00419 int tmpframe = -1; 00420 if (Tcl_GetIntFromObj(interp, objv[iarg+1], &tmpframe) != TCL_OK) 00421 return TCL_ERROR; 00422 if (tmpframe < 0 || tmpframe >= mol->numframes()) { 00423 Tcl_SetResult(interp, (char *) "rawtimestep: invalid frame specified.", 00424 TCL_STATIC); 00425 return TCL_ERROR; 00426 } 00427 frame = tmpframe; 00428 } 00429 } else { 00430 Tcl_SetResult(interp, (char *) "rawtimestep: valid options are -frame and -start", 00431 TCL_STATIC); 00432 return TCL_ERROR; 00433 } 00434 } 00435 00436 // Check that the size of the byte array and the start option are valid 00437 neededLength = 12L*mol->nAtoms; 00438 if (length-start < neededLength) { 00439 Tcl_SetResult(interp, (char *) "rawtimestep: not enough bytes!", TCL_STATIC); 00440 return TCL_ERROR; 00441 } 00442 00443 // Get the timestep - either existing or new 00444 ts = (frame < 0) ? new Timestep(mol->nAtoms) 00445 : mol->get_frame(frame); 00446 if (!ts) { 00447 Tcl_SetResult(interp, (char *) "rawtimestep: Unable to find timestep!", TCL_STATIC); 00448 return TCL_ERROR; 00449 } 00450 memcpy(ts->pos, bytes+start, neededLength); 00451 if (frame < 0) { 00452 mol->append_frame(ts); 00453 } else { 00454 mol->force_recalc(DrawMolItem::MOL_REGEN); 00455 } 00456 return TCL_OK; 00457 } 00458 00459 00460 // return timestep as byte array. 00461 // arguments: molid, frame 00462 int cmd_gettimestep(ClientData cd, Tcl_Interp *interp, int argc, 00463 Tcl_Obj * const objv[]) { 00464 if (argc != 3) { 00465 Tcl_WrongNumArgs(interp, 1, objv, "molid frame"); 00466 return TCL_ERROR; 00467 } 00468 00469 VMDApp *app = (VMDApp *)cd; 00470 int molid = -1; 00471 const char *molidstr = Tcl_GetStringFromObj(objv[1], NULL); 00472 if (!strcmp(molidstr, "top")) { 00473 molid = app->molecule_top(); 00474 } else if (Tcl_GetIntFromObj(interp, objv[1], &molid) != TCL_OK) { 00475 return TCL_ERROR; 00476 } 00477 00478 Molecule *mol = app->moleculeList->mol_from_id(molid); 00479 if (!mol) { 00480 Tcl_AppendResult(interp, "Invalid molid: ", molidstr, NULL); 00481 return TCL_ERROR; 00482 } 00483 00484 int frame; 00485 if (Tcl_GetIntFromObj(interp, objv[2], &frame) != TCL_OK) 00486 return TCL_ERROR; 00487 00488 if (frame < 0 || frame >= mol->numframes()) { 00489 Tcl_AppendResult(interp, "Invalid frame for molecule ", molidstr, NULL); 00490 return TCL_ERROR; 00491 } 00492 00493 Timestep *ts = mol->get_frame(frame); 00494 Tcl_SetObjResult(interp, Tcl_NewByteArrayObj( 00495 (const unsigned char *)(ts->pos), // bytes 00496 3L*mol->nAtoms*sizeof(float))); // length 00497 00498 return TCL_OK; 00499 } 00500