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: DrawMolecule.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.146 $ $Date: 2019年04月12日 04:41:53 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * 00019 * Displayable version of a DrawMolecule, derived from BaseMolecule and 00020 * Displayable. This contains all the info for rendering 00021 * the molecule. 00022 * 00023 ***************************************************************************/ 00024 00025 #include "DrawMolecule.h" 00026 #include "AtomColor.h" 00027 #include "AtomRep.h" 00028 #include "AtomSel.h" 00029 #include "utilities.h" 00030 #include "VMDApp.h" 00031 #include "MoleculeList.h" 00032 #include "CommandQueue.h" 00033 #include "CmdAnimate.h" 00034 #include "Stride.h" 00035 #include "PickList.h" 00036 #include "MaterialList.h" 00037 #include "Inform.h" 00038 #include "TextEvent.h" 00039 #include "DisplayDevice.h" 00040 #include "MoleculeGraphics.h" 00041 #include "BondSearch.h" 00042 #include "DrawForce.h" 00043 #include "VolumetricData.h" 00044 #include "CUDAAccel.h" 00045 00047 00048 DrawMolecule::DrawMolecule(VMDApp *vmdapp, Displayable *par) 00049 : BaseMolecule(vmdapp->next_molid()), 00050 Displayable(par), app(vmdapp), repList(8) { 00051 repcounter = 0; 00052 curframe = -1; 00053 active = TRUE; 00054 did_secondary_structure = 0; 00055 molgraphics = new MoleculeGraphics(this); 00056 vmdapp->pickList->add_pickable(molgraphics); 00057 drawForce = new DrawForce(this); 00058 00059 invalidate_cov_scale(); 00060 center[0] = center[1] = center[2] = 0.0f; 00061 00062 need_find_bonds = 0; 00063 } 00064 00065 // destructor ... free up any extra allocated space (the child Displayables 00066 // will be deleted by the Displayable destructor) 00067 DrawMolecule::~DrawMolecule() { 00068 int i; 00069 00070 // delete all molecule representations 00071 for(i=0; i < components(); i++) { 00072 app->pickList->remove_pickable(component(i)); 00073 delete component(i); 00074 } 00075 00076 app->pickList->remove_pickable(molgraphics); 00077 00078 // delete all timesteps 00079 for (i=timesteps.num()-1; i>=0; i--) { 00080 delete timesteps[i]; 00081 timesteps.remove(i); 00082 } 00083 00084 delete molgraphics; 00085 } 00086 00088 00089 // return Nth component ... change to proper return type 00090 DrawMolItem *DrawMolecule::component(int n) { 00091 if(n >= 0 && n < components()) 00092 return repList[n]; 00093 else 00094 return NULL; 00095 } 00096 00097 00098 // return the component corresponding to the pickable 00099 DrawMolItem *DrawMolecule::component_from_pickable(const Pickable *p) { 00100 for (int i=0; i<components(); i++) 00101 if (repList[i] == p) return repList[i]; 00102 return NULL; // no matching component 00103 } 00104 00105 // return the available CPU thread pool to the caller 00106 wkf_threadpool_t * DrawMolecule::cpu_threadpool(void) { 00107 return (wkf_threadpool_t *) app->thrpool; 00108 } 00109 00110 // return the available CUDA device pool to the caller 00111 wkf_threadpool_t * DrawMolecule::cuda_devpool(void) { 00112 return (app->cuda != NULL) ? app->cuda->get_cuda_devpool() : NULL; 00113 } 00114 00115 00116 // Return true if ANY representation is displaying atom n 00117 int DrawMolecule::atom_displayed(int n) { 00118 if (displayed() && n >= 0 && n < nAtoms) { 00119 for (int i=(components() - 1); i >= 0; i--) { 00120 if ((repList[i])->atom_displayed(n)) 00121 return TRUE; // atom is shown 00122 } 00123 } 00124 return FALSE; // atom is not shown 00125 } 00126 00127 00128 // delete the Nth representation ... return success 00129 int DrawMolecule::del_rep(int n) { 00130 DrawMolItem *rep = component(n); 00131 if (rep) { 00132 app->pickList->remove_pickable(rep); 00133 delete rep; // delete the object 00134 repList.remove(n); // and it's slot in the representation list 00135 invalidate_cov_scale(); 00136 } 00137 00138 return (rep != NULL); 00139 } 00140 00141 00142 void DrawMolecule::add_rep(AtomColor *ac, AtomRep *ar, AtomSel *as, 00143 const Material *am) { 00144 // Rep has unique name (unique within the molecule) 00145 char buf[50]; 00146 sprintf(buf, "rep%d", repcounter++); 00147 DrawMolItem *rep = new DrawMolItem(buf, this, ac, ar, as); 00148 app->pickList->add_pickable(rep); 00149 rep->change_material(am); 00150 repList.append(rep); 00151 invalidate_cov_scale(); 00152 } 00153 00154 int DrawMolecule::show_rep(int repid, int onoff) { 00155 DrawMolItem *rep = component(repid); 00156 if (rep) { 00157 if (onoff) rep->on(); 00158 else rep->off(); 00159 invalidate_cov_scale(); 00160 return TRUE; 00161 } 00162 return FALSE; 00163 } 00164 00165 // change the Nth representation ... return success. 00166 // if any object is NULL, that characteristic is not changed. 00167 int DrawMolecule::change_rep(int n, AtomColor *ac, AtomRep *ar, const char *sel) { 00168 DrawMolItem *rep = component(n); 00169 if (rep) { 00170 rep->change_color(ac); 00171 rep->change_rep(ar); 00172 rep->change_sel(sel); // returns TRUE if there was no problem, or if 00173 // sel was NULL meaning no action is to be taken 00174 invalidate_cov_scale(); 00175 return TRUE; 00176 } 00177 00178 return FALSE; 00179 } 00180 00181 00182 // redraw all the representations 00183 void DrawMolecule::force_recalc(int reason) { 00184 int numcomp = components(); 00185 for (int i=0; i<numcomp; i++) { 00186 component(i)->force_recalc(reason); 00187 } 00188 // The preceding loop updates all the DrawMolItem reps, but other children 00189 // of DrawMolecule (i.e. DrawForce) need to know about the update as well. 00190 // Calling need_matrix_recalc sets the _needUpdate flag for this purpose. 00191 need_matrix_recalc(); 00192 app->commandQueue->runcommand(new CmdAnimNewFrame); 00193 00194 // MOL_REGEN or SEL_REGEN implies our scale factor may have changed. 00195 if (reason & (DrawMolItem::MOL_REGEN | DrawMolItem::SEL_REGEN)) 00196 invalidate_cov_scale(); 00197 } 00198 00199 00200 // tell the rep to update its PBC transformation matrices next prepare cycle 00201 void DrawMolecule::change_pbc() { 00202 int numcomp = components(); 00203 for (int i=0; i<numcomp; i++) 00204 component(i)->change_pbc(); 00205 // labels can be made between periodic images, so warn them that the 00206 // distance between images has changed. Would be better to set a flag 00207 // so that notify can only get called once, inside of prepare(). 00208 notify(); 00209 } 00210 00211 00212 // tell the rep to update its timestep 00213 void DrawMolecule::change_ts() { 00214 int numcomp = components(); 00215 for (int i=0; i<numcomp; i++) 00216 component(i)->change_ts(); 00217 00218 molgraphics->prepare(); 00219 drawForce->prepare(); 00220 00221 notify(); 00222 00223 // now that internal state has been updated, notify scripts 00224 app->commandQueue->runcommand(new FrameEvent(id(), curframe)); 00225 } 00226 00227 00228 // query whether this molecule contains a highlighted rep 00229 int DrawMolecule::highlighted_rep() const { 00230 if (app->highlighted_molid != id()) 00231 return -1; 00232 return app->highlighted_rep; 00233 } 00234 00235 00236 // get the component by its string name 00237 int DrawMolecule::get_component_by_name(const char *nm) { 00238 // XXX linear search for the name is slow 00239 int numreps = repList.num(); 00240 for (int i=0; i<numreps; i++) { 00241 if (!strcmp(repList[i]->name, nm)) 00242 return i; // return component 00243 } 00244 return -1; // failed to find a component with that name 00245 } 00246 00247 00248 // get the name of the specified component 00249 const char *DrawMolecule::get_component_name(int ind) { 00250 DrawMolItem *rep = component(ind); 00251 if (!rep) 00252 return FALSE; 00253 return rep->name; 00254 } 00255 00256 void DrawMolecule::prepare() { 00257 if (needUpdate()) { 00258 notify(); // notify monitors 00259 } 00260 } 00261 00262 void DrawMolecule::override_current_frame(int n) { 00263 if (n == curframe) return; 00264 int num = timesteps.num(); 00265 if ( num==0 ) return; 00266 if ( n<0 ) curframe = 0; 00267 else if ( n>=num ) curframe = num-1; 00268 else curframe = n; 00269 invalidate_cov_scale(); 00270 } 00271 00272 // notify monitors of an update 00273 void DrawMolecule::notify() { 00274 int monnum = monitorlist.num(); 00275 int nid = id(); 00276 for (int i=0; i<monnum; i++) 00277 monitorlist[i]->notify(nid); 00278 } 00279 00280 00281 // add a new frame 00282 void DrawMolecule::append_frame(Timestep *ts) { 00283 timesteps.append(ts); // add the timestep to the animation 00284 00285 // To ensure compatibility with legacy behavior, always advance to the 00286 // newly added frame. 00287 override_current_frame(timesteps.num() - 1); 00288 00289 // Notify that curframe changed. This appears to entail no significant 00290 // overhead: reps update lazily, molgraphics only regenerates if it's been 00291 // modified since the last update, and DrawForce seems to be innocuous as 00292 // well. 00293 change_ts(); 00294 00295 // recenter the molecule when the first coordinate frame is loaded 00296 if (timesteps.num() == 1) { 00297 #if 0 00298 // XXX this is a nice hack to allow easy benchmarking of real VMD 00299 // trajectory I/O rates without having to first load some coords 00300 if (getenv("VMDNOCOVCALC") == NULL) 00301 #endif 00302 app->scene_resetview_newmoldata(); 00303 } 00304 00305 // update bonds if needed, when any subsequent frame is loaded 00306 if (timesteps.num() >= 1) { 00307 // find bonds if necessary 00308 if (need_find_bonds == 1) { 00309 need_find_bonds = 0; 00310 vmd_bond_search(this, ts, -1, 0); // just add bonds, no dup checking 00311 } else if (need_find_bonds == 2) { 00312 need_find_bonds = 0; 00313 vmd_bond_search(this, ts, -1, 1); // add bonds checking for dups 00314 } 00315 } 00316 00317 addremove_ts(); // tell all reps to update themselves 00318 app->commandQueue->runcommand(new CmdAnimNewNumFrames); // update frame count 00319 } 00320 00321 00322 // duplicate an existing frame 00323 void DrawMolecule::duplicate_frame(const Timestep *ts) { 00324 Timestep *newts; 00325 if (ts == NULL) { // append a 'null' frame 00326 newts = new Timestep(nAtoms); 00327 newts->zero_values(); 00328 } else { 00329 newts = new Timestep(*ts); 00330 } 00331 append_frame(newts); 00332 } 00333 00334 00335 // delete a frame 00336 void DrawMolecule::delete_frame(int n) { 00337 if (n<0 || n>=timesteps.num()) return; 00338 delete timesteps[n]; 00339 timesteps.remove(n); 00340 00341 // notifications 00342 addremove_ts(); 00343 app->commandQueue->runcommand(new CmdAnimNewNumFrames); 00344 00345 // adjust current frame if necessary 00346 if (curframe >= timesteps.num()) { 00347 curframe = timesteps.num()-1; 00348 change_ts(); 00349 } 00350 } 00351 00352 00353 // add or remove a timestep 00354 void DrawMolecule::addremove_ts() { 00355 int numcomp = components(); 00356 for (int i=0; i<numcomp; i++) 00357 component(i)->change_traj(); 00358 } 00359 00360 00361 // return the norm, double-precision arguments 00362 static float dnorm(const double *v) { 00363 return (float)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); 00364 } 00365 00366 void DrawMolecule::invalidate_cov_scale() { 00367 scalefactor = -1; 00368 } 00369 00370 // scaling factor required to make the molecule fit within (-1 ... 1) 00371 float DrawMolecule::scale_factor() { 00372 if (scalefactor < 0) update_cov_scale(); 00373 if (scalefactor > 0) { 00374 return scalefactor; 00375 00376 } else if (molgraphics->num_elements() > 0) { 00377 return molgraphics->scale_factor(); 00378 00379 } else if (volumeList.num() > 0) { 00380 // scale factor is 1.5/(maxrange), where maxrange is the largest range 00381 // of the data along any cardinal axis. That's how Timestep does it, 00382 // anyway. The volumetric axes aren't necessarily orthogonal so I'll 00383 // just go with the largest value. 00384 const VolumetricData *data = volumeList[0]; 00385 float x=dnorm(data->xaxis), y=dnorm(data->yaxis), z=dnorm(data->zaxis); 00386 float scale_factor = x > y ? x : y; 00387 scale_factor = scale_factor > z ? scale_factor : z; 00388 if (scale_factor > 0) return 1.5f/scale_factor; 00389 } 00390 return 1.0f; 00391 } 00392 00393 00394 // center of volume of this molecule 00395 int DrawMolecule::cov(float& x, float& y, float& z) { 00396 if (scalefactor < 0) update_cov_scale(); 00397 00398 if (scalefactor > 0) { 00399 // have valid coordinate data 00400 x = center[0]; y = center[1]; z = center[2]; 00401 00402 } else if (molgraphics->num_elements() > 0) { 00403 // use user-defined graphics to center 00404 molgraphics->cov(x, y, z); 00405 00406 } else if (volumeList.num() > 0) { 00407 // use first volumetric data set 00408 const VolumetricData *data = volumeList[0]; 00409 x = (float) (data->origin[0] + 00410 0.5*(data->xaxis[0] + data->yaxis[0] + data->zaxis[0])); 00411 y = (float) (data->origin[1] + 00412 0.5*(data->xaxis[1] + data->yaxis[1] + data->zaxis[1])); 00413 z = (float) (data->origin[2] + 00414 0.5*(data->xaxis[2] + data->yaxis[2] + data->zaxis[2])); 00415 } else { 00416 return FALSE; 00417 } 00418 return TRUE; 00419 } 00420 00421 00423 int DrawMolecule::recalc_bonds() { 00424 Timestep *ts = current(); 00425 00426 if (ts) { 00427 clear_bonds(); // clear the existing bond list 00428 vmd_bond_search(this, ts, -1, 0); // just add bonds, no dup checking 00429 msgInfo << "Bond count: " << count_bonds() << sendmsg; 00430 return 0; 00431 } 00432 00433 msgInfo << "No coordinates" << sendmsg; 00434 return -1; 00435 } 00436 00437 int DrawMolecule::need_secondary_structure(int calc_if_not_yet_done) { 00438 if (did_secondary_structure) return TRUE; 00439 00440 if (calc_if_not_yet_done) { 00441 if (!current()) return FALSE; // fails if there's no frame 00442 did_secondary_structure = TRUE; 00443 app->show_stride_message(); 00444 int usestride=1; 00445 if (getenv("VMDUSEDSSP") != NULL) { 00446 usestride=0; 00447 } 00448 00449 if (!usestride) { 00450 msgInfo << "User override of STRIDE, using DSSP for secondary structure" 00451 << sendmsg; 00452 if (ss_from_dssp(this)) { 00453 msgErr << "Call to DSSP program failed." << sendmsg; 00454 return FALSE; 00455 } 00456 return TRUE; 00457 } 00458 if (ss_from_stride(this)) { 00459 msgErr << "Call to Stride program failed." << sendmsg; 00460 return FALSE; 00461 } 00462 return TRUE; 00463 } 00464 // just indicate that we don't need to do the calculation anymore 00465 did_secondary_structure = TRUE; 00466 return TRUE; 00467 } 00468 00469 void DrawMolecule::invalidate_ss() { 00470 did_secondary_structure = 0; 00471 } 00472 00473 int DrawMolecule::recalc_ss() { 00474 did_secondary_structure = 0; 00475 int success = need_secondary_structure(1); 00476 did_secondary_structure = 1; 00477 00478 if (success) for (int i=0; i<components(); i++) component(i)->change_ss(); 00479 return success; 00480 } 00481 00482 void DrawMolecule::register_monitor(DrawMoleculeMonitor *mon) { 00483 monitorlist.append(mon); 00484 } 00485 void DrawMolecule::unregister_monitor(DrawMoleculeMonitor *mon) { 00486 monitorlist.remove(monitorlist.find(mon)); 00487 } 00488 00489 void DrawMolecule::update_cov_scale() { 00490 const Timestep *ts = current(); 00491 if (!ts) return; 00492 int i, n = ts->num; 00493 // only do this if there are atoms 00494 if (!n) return; 00495 00496 float covx, covy, covz; 00497 float minposx, minposy, minposz; 00498 float maxposx, maxposy, maxposz; 00499 00500 // flags for selected atoms in displayed reps 00501 ResizeArray<int> tmp_(n); // so I free automatically on return 00502 int *on = &tmp_[0]; 00503 for (i=0; i<n; i++) on[i] = 0; 00504 00505 int istart=n; // first on atom, init to beyond end of list 00506 00507 // find the first selected atom, and merge selection flags 00508 // from all of the active reps to get the complete set 00509 // of visible atoms when computing the cov 00510 for (int j=0; j<repList.num(); j++) { 00511 const DrawMolItem *rep = repList[j]; 00512 if (!rep->displayed()) 00513 continue; // don't process hidden/non-visible reps 00514 00515 if (rep->atomSel->selected > 0) { 00516 const int first = rep->atomSel->firstsel; 00517 const int last = rep->atomSel->lastsel; 00518 if (first < istart) 00519 istart=first; 00520 00521 const int *flgs = rep->atomSel->on; 00522 for (i=first; i<=last; i++) 00523 on[i] |= flgs[i]; 00524 } 00525 } 00526 00527 // if there are no selected atoms, use all atom coordinates 00528 if (istart < 0 || istart >= n) { 00529 istart = 0; 00530 for (i=0; i<n; i++) on[i] = 1; 00531 } 00532 00533 // initialize min/max positions with values from the first on atom 00534 const float *mpos = ts->pos + 3L*istart; 00535 minposx = maxposx = mpos[0]; 00536 minposy = maxposy = mpos[1]; 00537 minposz = maxposz = mpos[2]; 00538 covx = covy = covz = 0.0; 00539 00540 int icount = 0; 00541 for (i=istart; i<n; ++i, mpos += 3) { 00542 if (!on[i]) continue; 00543 ++icount; 00544 00545 const float xpos = mpos[0]; 00546 const float ypos = mpos[1]; 00547 const float zpos = mpos[2]; 00548 00549 covx += xpos; 00550 covy += ypos; 00551 covz += zpos; 00552 00553 if (xpos < minposx) minposx = xpos; 00554 if (xpos > maxposx) maxposx = xpos; 00555 00556 if (ypos < minposy) minposy = ypos; 00557 if (ypos > maxposy) maxposy = ypos; 00558 00559 if (zpos < minposz) minposz = zpos; 00560 if (zpos > maxposz) maxposz = zpos; 00561 } 00562 00563 // set the center of volume variable now 00564 center[0] = covx; 00565 center[1] = covy; 00566 center[2] = covz; 00567 vec_scale(center, 1.0f / icount, center); 00568 00569 // calculate center-of-volume and scale factor 00570 scalefactor = maxposx - minposx; 00571 00572 // prevent getting a zero-scaled scene when loading a single atom. 00573 if (scalefactor == 0.0) { 00574 scalefactor = 3.0; 00575 } 00576 00577 if ((maxposx - minposx) > scalefactor) 00578 scalefactor = maxposx - minposx; 00579 if ((maxposy - minposy) > scalefactor) 00580 scalefactor = maxposy - minposy; 00581 if ((maxposz - minposz) > scalefactor) 00582 scalefactor = maxposz - minposz; 00583 00584 scalefactor = 1.5f / scalefactor; 00585 } 00586