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: PickList.C,v $ 00013 * $Author: johns $ $Locker: $ $State: Exp $ 00014 * $Revision: 1.49 $ $Date: 2019年01月17日 21:21:01 $ 00015 * 00016 *************************************************************************** 00017 * DESCRIPTION: 00018 * 00019 * The PickList object, which maintains a list of Pickable objects and 00020 * has the ability to find and deal with items picked by a pointer device. 00021 * 00022 * Only one picking operation may be in effect at a time. A picking operation 00023 * consists of these steps: 00024 * 1. A pick is started by a pointer, by queueing a CmdPickStart command. 00025 * 2. pick_start is called, which determines if something is selected. 00026 * If so, returns tag of item, and sets internal flags to 00027 * indicate what is being picked, and how. 00028 * The current PickMode and relevant picking parameters are 00029 * passed to the picked object. 00030 * 3. pick_move is called whenever the pointer moves, by queueing the 00031 * CmdPickMove command. This continues until the picking is 00032 * finished. 00033 * 4. pick_end is called by queueing CmdPickEnd; this behaves similarly 00034 * the pick_move. When finished, the internal flags are reset 00035 * to indicate that picking is finished, and a new one can begin. 00036 * NOTE: multiple concurrent picks could be implemented later by providing 00037 * an object which maintains separate sets of the internal flags and variables, 00038 * for each picking device. This would also require another argument to the 00039 * pick_* routines, to indicate which picking device is being used. Also, 00040 * PickMode and Pickable object involved in another picking operation would 00041 * have to be excluded from being informed of other picking operations. 00042 ***************************************************************************/ 00043 00044 #include "PickList.h" 00045 #include "DisplayDevice.h" 00046 #include "Pickable.h" 00047 #include "PickModeList.h" 00048 #include "PickMode.h" 00049 #include "ResizeArray.h" 00050 #include "NameList.h" 00051 #include "Inform.h" 00052 #include "VMDApp.h" 00053 #include "CommandQueue.h" 00054 #include "TextEvent.h" 00055 #include "Molecule.h" 00056 #include "MoleculeList.h" 00057 00058 // forward declaration 00059 static void print_atom_info(VMDApp *app, PickEvent *event); 00060 00062 PickList::PickList(VMDApp *vmdapp) : pickableObjs(32), app(vmdapp) { 00063 00064 // reset all current picking flags 00065 currPickDim = currPickTag = (-1); 00066 currPickable = NULL; 00067 app->pickModeList->set_pick_mode(2); 00068 total_callback_clients = 0; // no clients yet! 00069 } 00070 00072 00073 // adds a Pickable to the current list, if it is not already in the list 00074 void PickList::add_pickable(Pickable *p) { 00075 00076 // find if this is in the list already. 00077 int indx = pickableObjs.find(p); 00078 00079 // if it is not, append it 00080 if(indx < 0) { 00081 pickableObjs.append(p); 00082 } 00083 } 00084 00085 00086 // remove the given pickable from the list; return TRUE if it was in the list 00087 void PickList::remove_pickable(Pickable *p) { 00088 int ind = pickableObjs.find(p); 00089 00090 // check if we have a valid Pickable index 00091 if (ind >= 0) { 00092 // cancel any active picking state if we're deleting the 00093 // pickable that's currently active 00094 if (picking() && (currPickable == p)) { 00095 // reset the status variables to null out the active pick 00096 currPickDim = currPickTag = (-1); 00097 currPickable = NULL; 00098 } 00099 00100 pickableObjs.remove(ind); 00101 } 00102 } 00103 00105 // routines to handle starting a pick, moving during a pick, 00106 // ending of a pick 00108 void PickList::pick_callback_clear(char *callback_client){ 00109 app->commandQueue->runcommand(new PickAtomCallbackEvent(-1,-1,callback_client)); 00110 } 00111 00112 Pickable *PickList::pick_check(int dim, const float 00113 *pos, int &tag, int *cell, float window_size, 00114 char *callback_client){ 00115 00116 // printf("doing a pickcheck\n"); 00117 if(pos==NULL) { 00118 msgErr << "pick_check called with NULL pos" << sendmsg; 00119 return NULL; 00120 } 00121 00122 Pickable *currObj, *retobj = NULL; 00123 float eyedist = (-1.0); 00124 int currtag, rettag = (-1); 00125 int i, np = num_pickable(); 00126 00127 if(!np) 00128 return NULL; 00129 00130 // use left eye settings for picking; if not stereo, will just be normal 00131 app->display->left(); 00132 00133 // for all Pickable objects, check to see if they have a picked object 00134 for(i=0; i < np; i++) { 00135 currObj = pickable(i); 00136 if(currObj->pickable_on()) { 00137 currtag = app->display->pick(dim, pos, currObj->pick_cmd_list(), eyedist, 00138 cell, window_size); 00139 if(currtag != -1) { 00140 // a new object closer to the eye position was found. Save it. 00141 retobj = currObj; 00142 rettag = currtag; 00143 } 00144 } 00145 } 00146 00147 // clean up after setting stereo mode, but do not do buffer swap 00148 app->display->update(FALSE); 00149 00150 // for now, only check left eye. Can later see if checking right eye helps 00151 // as well. 00152 00153 // finished; report results 00154 if(callback_client != NULL) { 00155 if(retobj) { 00156 int mol,atom; 00157 Molecule *m = app->moleculeList->check_pickable(retobj); 00158 if (m) { 00159 mol = m->id(); 00160 atom = rettag; 00161 app->commandQueue->runcommand(new PickAtomCallbackEvent(mol,atom,callback_client)); 00162 } 00163 } 00164 else { // we didn't find anyhing 00165 app->commandQueue->runcommand(new PickAtomCallbackEvent(-1,-1,callback_client)); 00166 } 00167 } 00168 00169 if(retobj) { 00170 tag = rettag; 00171 // send a normal pick event if this is not a check for callbacks 00172 if(callback_client==NULL) { 00173 PickEvent *event = new PickEvent(retobj, tag); 00174 print_atom_info(app, event); 00175 app->commandQueue->runcommand(event); 00176 } 00177 } 00178 return retobj; 00179 } 00180 00181 00182 // called when a pick is begun: display device, button, mode, dim, pos 00183 // returns 'tag' of closest object, or (-1) if nothing is picked. 00184 // When a pick is started, the internal flags for this object are set, 00185 // and no other item can be picked until pick_end is called. 00186 // For 2D version: x & y are 0 ... 1, represent 'relative, scaled' coords. 00187 // For 3D version: x,y,z are transformed position of pointer 00188 int PickList::pick_start(int b, int dim, 00189 const float *pos) { 00190 Pickable *closePickable; 00191 int tag = (-1); 00192 int cell[3]; 00193 float window_size = 0.01f; 00194 if (dim == 3) window_size *= 5; 00195 00196 // make sure we're not already picking something 00197 if(picking()) 00198 return (-1); 00199 00200 cell[0] = cell[1] = cell[2] = 0; 00201 00202 // check if something has been actually picked 00203 if((closePickable = pick_check(dim, pos, tag, cell, window_size)) != NULL) { 00204 // set all variables to show that we're picking something 00205 currPickDim = dim; 00206 currPickTag = tag; 00207 currPickable = closePickable; 00208 00209 // use left eye settings for picking; if not stereo, will just be normal 00210 app->display->left(); 00211 00212 // printf("pick start got tag %d\n", tag); 00213 PickMode *pm = app->pickModeList->current_pick_mode(); 00214 if (pm != NULL) 00215 closePickable->pick_start(pm, app->display, b, currPickTag, cell, dim, pos); 00216 00217 // clean up after setting stereo mode, but do not do buffer swap 00218 app->display->update(FALSE); 00219 } 00220 return tag; 00221 } 00222 00223 00224 // called when a pick moves: display device, button, mode, dim, pos 00225 // Returns TRUE if a pick is currently active, FALSE otherwise. 00226 // For 2D version: x & y are 0 ... 1, represent 'relative, scaled' coords. 00227 // For 3D version: x,y,z are transformed position of pointer 00228 int PickList::pick_move(const float *pos) { 00229 00230 // make sure we're already picking something 00231 if(!picking() ) 00232 return FALSE; 00233 00234 // use left eye settings for picking; if not stereo, will just be normal 00235 app->display->left(); 00236 00237 currPickable->pick_move(app->pickModeList->current_pick_mode(), app->display, currPickTag, 00238 currPickDim, pos); 00239 // clean up after setting stereo mode, but do not do buffer swap 00240 app->display->update(FALSE); 00241 00242 return TRUE; 00243 } 00244 00245 00246 // called when a pick ends: display device, button, mode, dim, pos 00247 // Returns TRUE if a pick is currently active, FALSE otherwise. 00248 // For 2D version: x & y are 0 ... 1, represent 'relative, scaled' coords. 00249 // For 3D version: x,y,z are transformed position of pointer 00250 int PickList::pick_end() { 00251 // make sure we're already picking something 00252 if(!picking() ) 00253 return FALSE; 00254 00255 // use left eye settings for picking; if not stereo, will just be normal 00256 app->display->left(); 00257 00258 currPickable->pick_end(app->pickModeList->current_pick_mode(), app->display); 00259 00260 // clean up after setting stereo mode, but do not do buffer swap 00261 app->display->update(FALSE); 00262 00263 // reset all current status variables, and return 00264 currPickDim = currPickTag = (-1); 00265 currPickable = NULL; 00266 00267 return TRUE; 00268 } 00269 00270 00271 // print atom info to the console 00272 static void print_atom_info(VMDApp *app, PickEvent *event) { 00273 Molecule *mol = app->moleculeList->check_pickable(event->pickable); 00274 if (!mol) return; 00275 int atomindex = event->tag; 00276 if (atomindex >= mol->nAtoms) return; 00277 00278 MolAtom *a = mol->atom(atomindex); 00279 00280 msgInfo << "picked atom: \n------------\n" 00281 << "molecule id: " << mol->id() 00282 << "\ntrajectory frame: " << app->molecule_frame(mol->id()) 00283 << "\nname: " << mol->atomNames.name(a->nameindex) 00284 << "\ntype: " << mol->atomTypes.name(a->typeindex) 00285 << "\nindex: " << atomindex 00286 << "\nresidue: " << a->uniq_resid 00287 << "\nresname: " << mol->resNames.name(a->resnameindex) 00288 << "\nresid: " << a->resid 00289 << "\nchain: " << mol->chainNames.name(a->chainindex) 00290 << "\nsegname: " << mol->segNames.name(a->segnameindex) 00291 << "\nx: " << mol->current()->pos[3L*atomindex] 00292 << "\ny: " << mol->current()->pos[3L*atomindex+1] 00293 << "\nz: " << mol->current()->pos[3L*atomindex+2] << "\n" 00294 << sendmsg; 00295 }