1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 
18 #include <vector>
19 
20 #include"os_python.h"
21 #include "os_std.h"
22 #include "MemoryDebug.h"
23 #include "Err.h"
24 #include "PlugIOManager.h"
25 #include "Selector.h"
26 #include "CoordSet.h"
27 #include "Feedback.h"
28 #include "Scene.h"
29 #include "Executive.h"
30 #include "AtomInfo.h"
31 #include "Lex.h"
32 #include "CGO.h"
33 #include "ObjectCGO.h"
34 #include "Util.h"
35 
36 #ifndef _PYMOL_VMD_PLUGINS
PlugIOManagerInit(PyMOLGlobals * G)37 int PlugIOManagerInit(PyMOLGlobals * G)
38 {
39   return 1;
40 }
41 
PlugIOManagerFree(PyMOLGlobals * G)42 int PlugIOManagerFree(PyMOLGlobals * G)
43 {
44   return 1;
45 }
46 
47 int PlugIOManagerRegister(PyMOLGlobals * G, void *ptr);
PlugIOManagerRegister(PyMOLGlobals * G,void * ptr)48 int PlugIOManagerRegister(PyMOLGlobals * G, void *ptr)
49 {
50   return 1;
51 }
52 
PlugIOManagerLoadTraj(PyMOLGlobals * G,ObjectMolecule * obj,const char * fname,int frame,int interval,int average,int start,int stop,int max,const char * sele,int image,float * shift,int quiet,const char * plugin_type)53 int PlugIOManagerLoadTraj(PyMOLGlobals * G, ObjectMolecule * obj,
54                           const char *fname, int frame,
55                           int interval, int average, int start,
56                           int stop, int max, const char *sele, int image,
57                           float *shift, int quiet, const char *plugin_type)
58 {
59 
60   PRINTFB(G, FB_ObjectMolecule, FB_Errors)
61     " ObjectMolecule-Error: sorry, VMD Molfile Plugins not compiled into this build.\n"
62     ENDFB(G);
63   return 0;
64 }
65 
PlugIOManagerLoadVol(PyMOLGlobals * G,ObjectMap * obj,const char * fname,int state,int quiet,const char * plugin_type)66 ObjectMap *PlugIOManagerLoadVol(PyMOLGlobals * G, ObjectMap * obj,
67                                 const char *fname, int state, int quiet,
68                                 const char *plugin_type)
69 {
70   PRINTFB(G, FB_ObjectMolecule, FB_Errors)
71     " ObjectMap-Error: sorry, VMD Molfile Plugins not compiled into this build.\n"
72     ENDFB(G);
73   return NULL;
74 }
75 
PlugIOManagerLoadMol(PyMOLGlobals * G,ObjectMolecule * origObj,const char * fname,int state,int quiet,const char * plugin_type)76 ObjectMolecule *PlugIOManagerLoadMol(PyMOLGlobals * G, ObjectMolecule *origObj,
77     const char *fname, int state, int quiet, const char *plugin_type)
78 {
79   PRINTFB(G, FB_ObjectMolecule, FB_Errors)
80     " ObjectMolecule-Error: sorry, VMD Molfile Plugins not compiled into this build.\n"
81     ENDFB(G);
82   return 0;
83 }
84 
PlugIOManagerLoad(PyMOLGlobals * G,CObject ** obj_ptr,const char * fname,int state,int quiet,const char * plugin_type,int mask)85 CObject * PlugIOManagerLoad(PyMOLGlobals * G, CObject ** obj_ptr,
86     const char *fname, int state, int quiet, const char *plugin_type, int mask)
87 {
88   PRINTFB(G, FB_ObjectMolecule, FB_Errors)
89     " ObjectMolecule-Error: sorry, VMD Molfile Plugins not compiled into this build.\n"
90     ENDFB(G);
91   return 0;
92 }
93 
94 #else
95 
96 #include "molfile_plugin.h"
97 
98 #ifdef __cplusplus
99 extern "C" {
100 #endif
101 
102 struct _CPlugIOManager {
103   int NPlugin;
104   molfile_plugin_t **PluginVLA;
105 };
106 
107 int PlugIOManagerInitAll(PyMOLGlobals * G);     /* defined externally */
108 
PlugIOManagerInit(PyMOLGlobals * G)109 int PlugIOManagerInit(PyMOLGlobals * G)
110 {
111   CPlugIOManager *I = NULL;
112   if((I = (G->PlugIOManager = pymol::calloc<CPlugIOManager>(1)))) {
113     I->NPlugin = 0;
114     I->PluginVLA = VLAlloc(molfile_plugin_t *, 10);
115     return PlugIOManagerInitAll(G);
116   } else
117     return 0;
118 }
119 
120 int PlugIOManagerFreeAll(void); /* defined externally */
121 
PlugIOManagerFree(PyMOLGlobals * G)122 int PlugIOManagerFree(PyMOLGlobals * G)
123 {
124   CPlugIOManager *I = G->PlugIOManager;
125   PlugIOManagerFreeAll();
126   VLAFreeP(I->PluginVLA);
127   FreeP(G->PlugIOManager);
128   return 1;
129 }
130 
131 int PlugIOManagerRegister(PyMOLGlobals * G, vmdplugin_t * header);
132 
PlugIOManagerRegister(PyMOLGlobals * G,vmdplugin_t * header)133 int PlugIOManagerRegister(PyMOLGlobals * G, vmdplugin_t * header)
134 {
135   if(G && G->PlugIOManager) {
136     if(!strcmp(header->type, MOLFILE_PLUGIN_TYPE)) {
137       CPlugIOManager *I = G->PlugIOManager;
138       VLACheck(I->PluginVLA, molfile_plugin_t *, I->NPlugin);
139       I->PluginVLA[I->NPlugin] = (molfile_plugin_t *) header;
140       I->NPlugin++;
141       /*           printf("register %p %s\n",header,header->name); */
142     }
143     return VMDPLUGIN_SUCCESS;
144   } else
145     return VMDPLUGIN_ERROR;
146 }
147 
find_plugin(CPlugIOManager * I,const char * plugin_type)148 static molfile_plugin_t * find_plugin(CPlugIOManager * I, const char * plugin_type) {
149   for (int a = 0; a < I->NPlugin; a++)
150     if(!strcmp(plugin_type, I->PluginVLA[a]->name))
151       return I->PluginVLA[a];
152   return NULL;
153 }
154 
PlugIOManagerLoadTraj(PyMOLGlobals * G,ObjectMolecule * obj,const char * fname,int frame,int interval,int average,int start,int stop,int max,const char * sele,int image,float * shift,int quiet,const char * plugin_type)155 int PlugIOManagerLoadTraj(PyMOLGlobals * G, ObjectMolecule * obj,
156                           const char *fname, int frame,
157                           int interval, int average, int start,
158                           int stop, int max, const char *sele, int image,
159                           float *shift, int quiet, const char *plugin_type)
160 {
161   CPlugIOManager *I = G->PlugIOManager;
162   molfile_plugin_t *plugin = NULL;
163 
164   ok_assert(1, I && obj);
165   plugin = find_plugin(I, plugin_type);
166 
167   if(!plugin) {
168     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
169       " PlugIOManager: unable to locate plugin '%s'\n", plugin_type ENDFB(G);
170     return false;
171   }
172 
173   if(plugin->read_next_timestep == NULL) {
174     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
175       " PlugIOManager: not a trajectory plugin '%s'\n", plugin_type ENDFB(G);
176     return false;
177   }
178 
179   if (obj->DiscreteFlag) {
180     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
181       " %s: Discrete objects not supported\n", __func__ ENDFB(G);
182     return false;
183   }
184 
185   {
186       int natoms;
187       molfile_timestep_t timestep;
188       void *file_handle;
189       int zoom_flag = false;
190       int cnt = 0;
191       int icnt = interval;
192       int n_avg = 0;
193       int ncnt = 0;
194       CoordSet *cs = obj->NCSet > 0 ? obj->CSet[0] : obj->CSTmpl ? obj->CSTmpl : NULL;
195 
196       timestep.coords = NULL;
197       timestep.velocities = NULL;
198 
199       file_handle = plugin->open_file_read(fname, plugin_type, &natoms);
200 
201       if(!file_handle) {
202         PRINTFB(G, FB_ObjectMolecule, FB_Errors)
203           " ObjectMolecule: plugin '%s' cannot open '%s'.\n", plugin_type, fname ENDFB(G);
204         return false;
205       }
206 
207       if(natoms == -1) {
208         natoms = obj->NAtom;
209       } else if(natoms != obj->NAtom || (cs && cs->NIndex != natoms)) {
210 	PRINTFB(G, FB_ObjectMolecule, FB_Errors)
211           " ObjectMolecule: plugin '%s' cannot open file because the number "
212           "of atoms in the object (%d) did not equal the number of atoms in "
213           "the '%s' (%d) file.\n", plugin_type, obj->NAtom, plugin_type, natoms ENDFB(G);
214         return false;
215       }
216 
217       if(cs) {
218         ok_assert(1, cs = CoordSetCopy(cs));
219       } else {
220         ok_assert(1, cs = CoordSetNew(G));
221         ok_assert(1, cs->Coord = pymol::vla<float>(3 * natoms));
222 
223         cs->Obj = obj;
224         cs->NIndex = natoms;
225         cs->enumIndices();
226       }
227 
228       auto xref = LoadTrajSeleHelper(obj, cs, sele);
229 
230       auto coordbuf = std::vector<float>(natoms * 3);
231       timestep.coords = coordbuf.data();
232 
233       {
234 	  /* read_next_timestep fills in &timestep for each iteration; we need
235 	   * to copy that out to a new CoordSet, each time. */
236           while(!plugin->read_next_timestep(file_handle, natoms, &timestep)) {
237             cnt++;
238 	    /* start at the 'start'-th frame; skip 'start' frames,
239 	     * and skip every interval/icnt frames */
240             if(cnt >= start) {
241               icnt--;
242               if(icnt > 0) {
243                 PRINTFB(G, FB_ObjectMolecule, FB_Details)
244                   " ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
245               } else {
246                 icnt = interval;
247                 n_avg++;
248               }
249               if(icnt == interval) {
250                 if(n_avg < average) {
251                   PRINTFB(G, FB_ObjectMolecule, FB_Details)
252                     " ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
253                 } else {
254                   /* compute average */
255                   if(n_avg > 1) {
256                     // TODO this doesn't make any sense
257                     float* fp = timestep.coords;
258                     for (int i = 0; i < natoms; ++i) {
259                       *(fp++) /= n_avg;
260                       *(fp++) /= n_avg;
261                       *(fp++) /= n_avg;
262                     }
263                   }
264                   /* add new coord set */
265 
266                   for (int i = 0; i < natoms; ++i) {
267                     int idx = xref ? xref[i] : i;
268                     if (idx >= 0) {
269                       assert(idx < cs->NIndex);
270                       copy3(timestep.coords + 3 * i, cs->coordPtr(idx));
271                     }
272                   }
273 
274                   cs->invalidateRep(cRepAll, cRepInvRep);
275                   if(frame < 0) frame = obj->NCSet;
276                   if(!obj->NCSet) zoom_flag = true;
277 
278 		  /* make sure we have room for 'frame' CoordSet*'s in obj->CSet */
279 		  /* TODO: TEST this function */
280                   VLACheck(obj->CSet, CoordSet*, frame); /* was CoordSet* */
281 		  /* bump the object's state count */
282                   if(obj->NCSet <= frame) obj->NCSet = frame + 1;
283 		  /* if there's data in this state's coordset, emtpy it */
284                   if(obj->CSet[frame])
285                     obj->CSet[frame]->fFree();
286 		  /* set this state's coordset to cs */
287                   obj->CSet[frame] = cs;
288                   ncnt++;
289                   if(average < 2) {
290                     PRINTFB(G, FB_ObjectMolecule, FB_Details)
291                       " ObjectMolecule: read set %d into state %d...\n", cnt, frame + 1
292                       ENDFB(G);
293                   } else {
294                     PRINTFB(G, FB_ObjectMolecule, FB_Details)
295                       " ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
296                     PRINTFB(G, FB_ObjectMolecule, FB_Details)
297                       " ObjectMolecule: average loaded into state %d...\n", frame + 1
298                       ENDFB(G);
299                   }
300 
301                   if((stop > 0 && cnt >= stop) || (max > 0 && ncnt >= max)) {
302                     cs = NULL;
303                     break;
304                   }
305 
306                   frame++;
307                   /* make a new cs */
308                   cs = CoordSetCopy(cs);        /* otherwise, we need a place to put the next set */
309                   n_avg = 0;
310                 }
311               }
312             } else {
313               PRINTFB(G, FB_ObjectMolecule, FB_Details)
314                 " ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
315             }
316           } /* end while */
317         }
318         plugin->close_file_read(file_handle);
319         if(cs)
320           cs->fFree();
321         SceneChanged(G);
322         SceneCountFrames(G);
323         if(zoom_flag)
324           if(SettingGetGlobal_i(G, cSetting_auto_zoom)) {
325             ExecutiveWindowZoom(G, obj->Name, 0.0, -1, 0, 0, quiet);        /* auto zoom (all states) */
326           }
327   }
328   return true;
329 ok_except1:
330   return false;
331 }
332 
PlugIOManagerLoadVol(PyMOLGlobals * G,ObjectMap * obj,const char * fname,int state,int quiet,const char * plugin_type)333 ObjectMap *PlugIOManagerLoadVol(PyMOLGlobals * G, ObjectMap * obj,
334                                 const char *fname, int state, int quiet,
335                                 const char *plugin_type)
336 {
337   CPlugIOManager *I = G->PlugIOManager;
338   molfile_plugin_t *plugin = NULL;
339   molfile_volumetric_t *metadata;
340   int setsinfile = 0;
341   int natoms;
342   void *file_handle = NULL;
343   float *datablock = NULL;
344 
345   ok_assert(1, I);
346   plugin = find_plugin(I, plugin_type);
347 
348   if(!plugin) {
349     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
350       " PlugIOManager: unable to locate plugin '%s'\n", plugin_type ENDFB(G);
351     ok_raise(1);
352   }
353 
354   if(plugin->read_volumetric_data == NULL || plugin->read_volumetric_metadata == NULL) {
355     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
356       " PlugIOManager: not a map plugin '%s'\n", plugin_type ENDFB(G);
357     ok_raise(1);
358   }
359 
360   file_handle = plugin->open_file_read(fname, plugin_type, &natoms);
361 
362   if(!file_handle) {
363     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
364       " PlugIOManager: plugin '%s' cannot open '%s'.\n", plugin_type, fname ENDFB(G);
365     ok_raise(1);
366   }
367 
368   if(plugin->read_volumetric_metadata(file_handle, &setsinfile, &metadata) != MOLFILE_SUCCESS) {
369     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
370       " PlugIOManager: read_volumetric_metadata failed\n" ENDFB(G);
371     ok_raise(1);
372   }
373 
374   /* for now, just read in all sets of data in the file */
375 
376   for(int i = 0; i < setsinfile; i++) {
377 
378           const molfile_volumetric_t *v = metadata + i;
379           size_t size = v->xsize * v->ysize * v->zsize;
380 
381           if(!size) {
382             PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
383               " PlugIOManagerLoadVol-Waring: 0 values, skipping set %d\n", i ENDFB(G);
384             continue;
385           }
386 
387           ok_assert(1, datablock = pymol::malloc<float>(size));
388 
389           if(plugin->read_volumetric_data(file_handle, i, datablock, NULL) != MOLFILE_SUCCESS) {
390             PRINTFB(G, FB_ObjectMolecule, FB_Errors)
391               " PlugIOManager: read_volumetric_data failed\n" ENDFB(G);
392             ok_raise(1);
393           }
394 
395           {
396             ObjectMapState *ms = NULL;
397 
398             if(!obj)
399               ok_assert(1, obj = new ObjectMap(G));
400 
401             if(state < 0)
402               state = obj->State.size();
403             if(obj->State.size() <= state) {
404               VecCheckEmplace (obj->State, state, G);
405             }
406             ms = &obj->State[state];
407 
408             ms->FDim[0] = v->xsize;
409             ms->FDim[1] = v->ysize;
410             ms->FDim[2] = v->zsize;
411             ms->FDim[3] = 3;
412 
413             ms->Grid = std::vector<float>(3);
414             ms->Dim = std::vector<int>(3);
415             ms->Origin = std::vector<float>(3, 0.0f);
416             ms->Range = std::vector<float>(3);
417 
418             float axes33f[9];
419             float originf[3];
420             copy3(v->xaxis, axes33f + 0);
421             copy3(v->yaxis, axes33f + 3);
422             copy3(v->zaxis, axes33f + 6);
423             copy3(v->origin, originf);
424 
425             // check if inverted volume (TetsurfVolume would get normals wrong)
426             bool inverted = determinant33f(axes33f) < 0;
427             if (inverted) {
428               // flip the z-axis
429               add3f(axes33f + 6, originf, originf);
430               invert3f(axes33f + 6);
431             }
432 
433             // special case: orthogonal & cartesian-aligned
434             // -> don't use State.Matrix which causes trouble for e.g. CCP4 export
435             // (non-standard header) and ObjectSlice (incomplete implementation)
436             bool aligned_axes =
437               axes33f[0] > 0 && axes33f[4] > 0 && axes33f[8] > 0 &&
438               fabs(axes33f[1]) <= R_SMALL4 && fabs(axes33f[2]) <= R_SMALL4 &&
439               fabs(axes33f[3]) <= R_SMALL4 && fabs(axes33f[5]) <= R_SMALL4 &&
440               fabs(axes33f[6]) <= R_SMALL4 && fabs(axes33f[7]) <= R_SMALL4;
441 
442             // set corners to a unit cube, and manage world space with the state matrix
443             if (!aligned_axes) {
444               PRINTFB(G, FB_ObjectMap, FB_Warnings)
445                 " Warning: Axes not aligned, using map-state matrix\n" ENDFB(G);
446 
447               double m44d[16];
448 
449               if(ms->Matrix.empty())
450                 ms->Matrix = std::vector<double>(16);
451 
452               // state matrix transformation
453               copy33f44d(axes33f, m44d);
454               copy3(originf, m44d + 12);
455               transpose44d44d(m44d, ms->Matrix.data());
456             }
457 
458             // axis and corner stuff in a unit cube
459             {
460               // prime min+max
461               zero3f(ms->ExtentMin);
462               ones3f(ms->ExtentMax);
463               ones3f(ms->Range.data());
464 
465               for(int a = 0; a < 3; a++) {
466                 int dimL1 = ms->FDim[a] - 1;
467 
468                 // min+max indices
469                 ms->Min[a] = 0;
470                 ms->Max[a] = dimL1;
471 
472                 ms->Grid[a] = 1.f / dimL1;
473 
474                 // (redundant)
475                 ms->Dim[a] = ms->FDim[a];
476 
477                 // corner enumeration
478                 for(int b = 0; b < 8; b++)
479                   ms->Corner[3 * b + a] = (b >> a) & 0x1;
480               }
481             }
482 
483             if (aligned_axes) {
484               ms->Grid[0] = axes33f[0] / (ms->FDim[0] - 1);
485               ms->Grid[1] = axes33f[4] / (ms->FDim[1] - 1);
486               ms->Grid[2] = axes33f[8] / (ms->FDim[2] - 1);
487 
488               for(int a = 0; a < 3; a++) {
489                 ms->Origin[a] = originf[a];
490                 ms->Range[a] = ms->Grid[a] * (ms->Dim[a] - 1);
491                 ms->ExtentMin[a] = ms->Origin[a];
492                 ms->ExtentMax[a] = ms->Origin[a] + ms->Grid[a] * ms->Max[a];
493               }
494 
495               // corners
496               for(int c = 0, d = 0; c < ms->FDim[2]; c += ms->FDim[2] - 1) {
497                 float v[3];
498                 v[2] = ms->Origin[2] + ms->Grid[2] * c;
499                 for(int b = 0; b < ms->FDim[1]; b += ms->FDim[1] - 1) {
500                   v[1] = ms->Origin[1] + ms->Grid[1] * b;
501                   for(int a = 0; a < ms->FDim[0]; a += ms->FDim[0] - 1, ++d) {
502                     v[0] = ms->Origin[0] + ms->Grid[0] * a;
503                     copy3f(v, ms->Corner + 3 * d);
504                   }
505                 }
506               }
507             }
508 
509             // field
510             ms->Field.reset(new Isofield(G, ms->FDim));
511             ms->MapSource = cMapSourceVMDPlugin;
512             ms->Field->save_points = false;     /* save points in RAM only, not session file */
513             ms->Active = true;
514 
515             ObjectMapStateRegeneratePoints(ms);
516 
517             // copy data
518             {
519               int a, b, c;
520               float *data_ptr = datablock;
521 
522               /* VMD plugins appear to use fast-x med-y slow-z ordering: "&datablock[z*xysize + y*xsize + x]" */
523 
524               for(c = 0; c < ms->FDim[2]; c++) {
525                 int cc = inverted ? (ms->FDim[2] - c - 1) : c;
526                 for(b = 0; b < ms->FDim[1]; b++) {
527                   for(a = 0; a < ms->FDim[0]; a++) {
528                     F3(ms->Field->data, a, b, cc) = *(data_ptr++);
529                   }
530                 }
531               }
532 
533               PRINTFB(G, FB_ObjectMap, FB_Details)
534                 " ObjectMap: read %zu values\n", size ENDFB(G);
535             }
536 
537           }
538           FreeP(datablock);
539   }
540   if(obj) {
541     ObjectMapUpdateExtents(obj);
542     SceneChanged(G);
543     SceneCountFrames(G);
544   }
545 
546 ok_except1:
547   // close
548   if (plugin && file_handle)
549     plugin->close_file_read(file_handle);
550 
551   return obj;
552 }
553 
SymmetryNewFromTimestep(PyMOLGlobals * G,molfile_timestep_t * ts)554 static CSymmetry * SymmetryNewFromTimestep(PyMOLGlobals * G, molfile_timestep_t * ts)
555 {
556   CSymmetry * symm = NULL;
557   ok_assert(1,
558       ts->A > 0.f && ts->B > 0.f && ts->C > 0.f &&
559       ts->alpha > 0.f && ts->beta > 0.f && ts->gamma > 0.f);
560   ok_assert(1, symm = new CSymmetry(G));
561   symm->Crystal.Dim[0] = ts->A;
562   symm->Crystal.Dim[1] = ts->B;
563   symm->Crystal.Dim[2] = ts->C;
564   symm->Crystal.Angle[0] = ts->alpha;
565   symm->Crystal.Angle[1] = ts->beta;
566   symm->Crystal.Angle[2] = ts->gamma;
567   strcpy(symm->SpaceGroup, "P1");
568   SymmetryUpdate(symm);
569 ok_except1:
570   return symm;
571 }
572 
PlugIOManagerLoadMol(PyMOLGlobals * G,ObjectMolecule * origObj,const char * fname,int state,int quiet,const char * plugin_type)573 ObjectMolecule *PlugIOManagerLoadMol(PyMOLGlobals * G, ObjectMolecule *origObj,
574     const char *fname, int state, int quiet, const char *plugin_type)
575 {
576   CPlugIOManager *manager = G->PlugIOManager;
577   int natoms, nbonds = 0, *from, *to;
578   int optflags = 0;
579   float *order;
580   void *file_handle = NULL;
581   molfile_plugin_t * plugin = NULL;
582   molfile_timestep_t timestep;
583   molfile_atom_t * atoms = NULL;
584   ObjectMolecule *I = NULL;
585   CoordSet * cs = NULL;
586   int *bondtype, nbondtypes;
587   char **bondtypename;
588   int auto_show = RepGetAutoShowMask(G);
589   auto literal_names = SettingGet<bool>(G, cSetting_pdb_literal_names);
590 
591   memset(&timestep, 0, sizeof(molfile_timestep_t));
592 
593   ok_assert(1, manager);
594   plugin = find_plugin(manager, plugin_type);
595 
596   if (!plugin) {
597     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
598       " ObjectMolecule: unable to locate plugin '%s'\n", plugin_type ENDFB(G);
599     ok_raise(1);
600   }
601 
602   // open file
603   file_handle = plugin->open_file_read(fname, plugin_type, &natoms);
604 
605   if(!file_handle) {
606     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
607       " ObjectMolecule: plugin '%s' cannot open '%s'.\n", plugin_type, fname ENDFB(G);
608     ok_raise(1);
609   }
610 
611   // read atoms
612   atoms = pymol::calloc<molfile_atom_t>(natoms);
613   if (plugin->read_structure(file_handle, &optflags, atoms) != MOLFILE_SUCCESS) {
614     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
615       " ObjectMolecule: plugin '%s' failed to read atoms.\n", plugin_type ENDFB(G);
616     ok_raise(1);
617   }
618 
619   // Create ObjectMolecule
620   ok_assert(1, I = new ObjectMolecule(G, false));
621   I->Color = AtomInfoUpdateAutoColor(G);
622   VLASize(I->AtomInfo, AtomInfoType, natoms);
623   I->NAtom = natoms;
624 
625   // copy atom info
626   for (int i = 0; i < natoms; i++) {
627     AtomInfoType *ai = I->AtomInfo + i;
628     molfile_atom_t *a = atoms + i;
629 
630     if (!literal_names) {
631       UtilCleanStr(a->segid);
632       UtilCleanStr(a->chain);
633       UtilCleanStr(a->resname);
634       UtilCleanStr(a->name);
635     }
636 
637     ai->rank = i;
638     ai->id = i + 1;
639     ai->b = a->bfactor;
640     ai->q = a->occupancy;
641     ai->vdw = a->radius;
642     ai->partialCharge = a->charge;
643     ai->alt[0] = a->altloc[0];
644 
645     ai->segi = LexIdx(G, a->segid);
646     ai->resn = LexIdx(G, a->resname);
647     ai->name = LexIdx(G, a->name);
648     if (a->atomicnumber > 0)
649       atomicnumber2elem(ai->elem, a->atomicnumber);
650 
651     ai->chain = LexIdx(G, a->chain);
652     ai->textType = LexIdx(G, a->type);
653 
654     ai->hetatm = 0;
655 
656     ai->resv = a->resid;
657     ai->setInscode(a->insertion[0]);
658 
659     ai->visRep = auto_show;
660 
661     AtomInfoAssignParameters(G, ai);
662     AtomInfoAssignColors(G, ai);
663   }
664 
665   // read coordinates
666   while (/* true */ plugin->read_next_timestep != NULL) {
667     ok_assert(1, cs = CoordSetNew(G));
668     ok_assert(1, cs->Coord = pymol::vla<float>(3 * natoms));
669 
670     timestep.coords = cs->Coord.data();
671     timestep.velocities = NULL;
672 
673     if (plugin->read_next_timestep(file_handle, natoms, &timestep) != MOLFILE_SUCCESS) {
674       cs->fFree();
675       break;
676     }
677 
678     cs->Obj = I;
679     cs->NIndex = natoms;
680     cs->enumIndices();
681 
682     // append to object
683     VLACheck(I->CSet, CoordSet*, I->NCSet);
684     I->CSet[I->NCSet++] = cs;
685   }
686 
687   // topology-only (with template coord set)
688   if (!I->NCSet) {
689     ok_assert(1, cs = CoordSetNew(G));
690     ok_assert(1, cs->Coord = pymol::vla<float>(3 * natoms));
691 
692     cs->Obj = I;
693     cs->NIndex = natoms;
694     cs->enumIndices();
695 
696     I->CSTmpl = cs;
697   }
698 
699   // read bonds
700   if (plugin->read_bonds &&
701       plugin->read_bonds(file_handle, &nbonds, &from, &to, &order,
702         &bondtype, &nbondtypes, &bondtypename) != MOLFILE_SUCCESS) {
703     PRINTFB(G, FB_ObjectMolecule, FB_Errors)
704       " ObjectMolecule: plugin '%s' failed to read bonds.\n", plugin_type ENDFB(G);
705     ok_raise(1);
706   }
707 
708   // copy bonds
709   if (nbonds) {
710     I->NBond = nbonds;
711     I->Bond = pymol::vla<BondType>(nbonds);
712     for (int i = 0; i < nbonds; i++) {
713       BondTypeInit2(I->Bond + i, from[i] - 1, to[i] - 1,
714           order ? (int) order[i] : 1);
715     }
716   } else if (I->NCSet) {
717     ObjectMoleculeConnect(I, I->CSet[0]);
718   }
719 
720   // symmetry
721   I->Symmetry = SymmetryNewFromTimestep(G, &timestep);
722 
723   // finalize
724   I->invalidate(cRepAll, cRepInvAll, -1);
725   ObjectMoleculeUpdateIDNumbers(I);
726   ObjectMoleculeUpdateNonbonded(I);
727 
728   // merge
729   if(origObj) {
730     // TODO
731   }
732 
733   SceneCountFrames(G);
734 
735 ok_except1:
736   // close
737   if (plugin && file_handle)
738     plugin->close_file_read(file_handle);
739 
740   if(atoms)
741     mfree(atoms);
742 
743   return I;
744 }
745 
cgo_check_beginend(int type,int & currenttype,CGO * & cgo)746 static void cgo_check_beginend(int type, int & currenttype, CGO *& cgo) {
747   if (currenttype != type) {
748     if (currenttype)
749       CGOEnd(cgo);
750     if (type)
751       CGOBegin(cgo, type);
752     currenttype = type;
753   }
754 }
755 
756 static
PlugIOManagerLoadGraphics(PyMOLGlobals * G,ObjectCGO * origObj,const char * fname,int state,int quiet,const char * plugin_type)757 ObjectCGO *PlugIOManagerLoadGraphics(PyMOLGlobals * G, ObjectCGO *origObj,
758     const char *fname, int state, int quiet, const char *plugin_type)
759 {
760   CPlugIOManager *manager = G->PlugIOManager;
761   void *file_handle = NULL;
762   molfile_plugin_t * plugin = NULL;
763   const molfile_graphics_t * graphics = NULL;
764   int nelem = 0;
765   int beginend = 0;
766   CGO *cgo = NULL;
767   ObjectCGO *I = NULL;
768 
769   ok_assert(1, manager);
770   plugin = find_plugin(manager, plugin_type);
771 
772   if (!plugin) {
773     PRINTFB(G, FB_ObjectCGO, FB_Errors)
774       " ObjectCGO: unable to locate plugin '%s'\n", plugin_type ENDFB(G);
775     ok_raise(1);
776   }
777 
778   // open file
779   file_handle = plugin->open_file_read(fname, plugin_type, &nelem /* dummy */);
780 
781   if(!file_handle) {
782     PRINTFB(G, FB_ObjectCGO, FB_Errors)
783       " ObjectCGO: plugin '%s' cannot open '%s'.\n", plugin_type, fname ENDFB(G);
784     ok_raise(1);
785   }
786 
787   // read geometry
788   if (plugin->read_rawgraphics(file_handle, &nelem, &graphics) != MOLFILE_SUCCESS) {
789     PRINTFB(G, FB_ObjectCGO, FB_Errors)
790       " ObjectCGO: plugin '%s' failed to read graphics.\n", plugin_type ENDFB(G);
791     ok_raise(1);
792   }
793 
794   cgo = CGONew(G);
795 
796 #define CHECK_BEGINEND(type) cgo_check_beginend(type, beginend, cgo)
797 
798   // translate to CGO
799   for (auto g = graphics, g_end = graphics + nelem; g != g_end; ++g) {
800     auto g_current = g;
801     const float * tnormals = NULL;
802     const float * tcolors = NULL;
803 
804     switch (g->type) {
805       case MOLFILE_POINT:
806         break;
807       case MOLFILE_TRINORM:
808         // followed by normals
809       case MOLFILE_TRICOLOR:
810         // followed by normals and color
811         if (g + 1 != g_end && g[1].type == MOLFILE_NORMS) {
812           tnormals = (++g)->data;
813         }
814         if (g_current->type == MOLFILE_TRICOLOR &&
815             g + 1 != g_end && g[1].type == MOLFILE_COLOR) {
816           tcolors = (++g)->data;
817         }
818       case MOLFILE_TRIANGLE:
819         CHECK_BEGINEND(GL_TRIANGLES);
820         for (int i = 0; i < 9; i += 3) {
821           if (tnormals)
822             CGONormalv(cgo, tnormals + i);
823           if (tcolors)
824             CGOColorv(cgo, tcolors + i);
825           CGOVertexv(cgo, g_current->data + i);
826         }
827         break;
828       case MOLFILE_NORMS:
829         CGONormalv(cgo, g->data);
830         break;
831       case MOLFILE_LINE:
832         CHECK_BEGINEND(GL_LINES);
833         CGOVertexv(cgo, g->data + 0);
834         CGOVertexv(cgo, g->data + 3);
835         break;
836       case MOLFILE_CYLINDER:
837         CHECK_BEGINEND(0);
838         {
839           float axis[3];
840           subtract3f(g->data + 3, g->data, axis);
841           CGOShaderCylinder(cgo, g->data, axis, g->size, 0);
842         }
843         break;
844       case MOLFILE_CAPCYL:
845         break;
846       case MOLFILE_CONE:
847         break;
848       case MOLFILE_SPHERE:
849         CHECK_BEGINEND(0);
850         CGOSphere(cgo, g->data, g->size);
851         break;
852       case MOLFILE_TEXT:
853         break;
854       case MOLFILE_COLOR:
855         CGOColorv(cgo, g->data);
856         break;
857     }
858   }
859 
860   CHECK_BEGINEND(0);
861   CGOStop(cgo);
862 
863   // Create ObjectCGO
864   ok_assert(1, I = ObjectCGOFromCGO(G, NULL, cgo, state));
865 
866   // default is cgo_lighting=0 when loading CGOs without normals
867   SettingSet(cSetting_cgo_lighting, 1, (CObject *)I);
868 
869 ok_except1:
870   // close
871   if (plugin && file_handle)
872     plugin->close_file_read(file_handle);
873 
874   if (!I)
875     CGOFree(cgo);
876 
877   return I;
878 }
879 
880 /*
881  * Load any object type with the given plugin. If obj_ptr's object type
882  * doesn't match the plugin, the object will be deleted and a new one created
883  * (not for trajectories).
884  */
PlugIOManagerLoad(PyMOLGlobals * G,CObject ** obj_ptr,const char * fname,int state,int quiet,const char * plugin_type,int mask)885 CObject * PlugIOManagerLoad(PyMOLGlobals * G, CObject ** obj_ptr,
886     const char *fname, int state, int quiet, const char *plugin_type, int mask)
887 {
888   CObject *obj = obj_ptr ? *obj_ptr : NULL;
889   CPlugIOManager *manager = G->PlugIOManager;
890   molfile_plugin_t *plugin;
891 
892   ok_assert(1, manager);
893   plugin = find_plugin(manager, plugin_type);
894 
895   if (!plugin) {
896     PRINTFB(G, FB_ObjectMolecule, FB_Blather)
897       " PlugIOManagerLoad: no plugin '%s'\n", plugin_type ENDFB(G);
898     return NULL;
899   }
900 
901   if (!mask)
902     mask = cPlugIOManager_any;
903 
904   if ((mask & cPlugIOManager_vol) && plugin->read_volumetric_data) {
905     // maps
906 
907     if (obj && obj->type != cObjectMap) {
908       ExecutiveDelete(G, obj->Name);
909       obj = *obj_ptr = NULL;
910     }
911 
912     return (CObject *) PlugIOManagerLoadVol(G, (ObjectMap *) obj,
913         fname, state, quiet, plugin_type);
914 
915   } else if ((mask & cPlugIOManager_mol) && plugin->read_structure) {
916     // molecules
917 
918     if (obj
919 #if 0
920         && obj->type != cObjectMolecule
921 #else
922         // TODO no merge support yet, always delete existing object
923 #endif
924         ) {
925       ExecutiveDelete(G, obj->Name);
926       obj = *obj_ptr = NULL;
927     }
928 
929     return (CObject *) PlugIOManagerLoadMol(G, (ObjectMolecule *) obj,
930         fname, state, quiet, plugin_type);
931 
932   } else if ((mask & cPlugIOManager_traj) && plugin->read_next_timestep) {
933     // trajectories
934 
935     float shift[] = {0.f, 0.f, 0.f};
936 
937     if (obj && obj->type != cObjectMolecule) {
938       PRINTFB(G, FB_ObjectMolecule, FB_Errors)
939         " PlugIOManagerLoad: can't load trajectory into object '%s'\n", obj->Name ENDFB(G);
940       return NULL;
941     }
942 
943     PlugIOManagerLoadTraj(G, (ObjectMolecule *) obj,
944         fname, state, 1, 1, 1, -1, -1, "all", 1, shift, quiet, plugin_type);
945     return NULL;
946 
947   } else if ((mask & cPlugIOManager_graphics) && plugin->read_rawgraphics) {
948     // geometry (CGO)
949 
950     if (obj) {
951       // no merge support
952       ExecutiveDelete(G, obj->Name);
953       obj = *obj_ptr = NULL;
954     }
955 
956     return (CObject *) PlugIOManagerLoadGraphics(G, (ObjectCGO *) obj,
957         fname, state, quiet, plugin_type);
958   }
959 
960   PRINTFB(G, FB_ObjectMolecule, FB_Errors)
961     " PlugIOManagerLoad: '%s' doesn't provide any read function\n", plugin_type ENDFB(G);
962 
963 ok_except1:
964   return NULL;
965 }
966 
967 #ifdef __cplusplus
968 }
969 #endif
970 
971 #endif
972 
973 /*
974  * Find a plugin by filename extension
975  *
976  * ext: File extension
977  * mask: plugin needs to read any content (0), structure (1), trajectory (2) or map (4)
978  */
PlugIOManagerFindPluginByExt(PyMOLGlobals * G,const char * ext,int mask)979 const char * PlugIOManagerFindPluginByExt(PyMOLGlobals * G, const char * ext, int mask) {
980 #ifdef _PYMOL_VMD_PLUGINS
981   CPlugIOManager *I = G->PlugIOManager;
982 
983   if (!mask)
984     mask = cPlugIOManager_any;
985 
986   for (auto it = I->PluginVLA, it_end = it + I->NPlugin; it != it_end; ++it) {
987     const molfile_plugin_t * p = *it;
988 
989     if (WordMatchCommaExact(G, p->filename_extension, ext, true) >= 0)
990       continue;
991 
992     if (((mask & cPlugIOManager_mol) && p->read_structure) ||
993         ((mask & cPlugIOManager_traj) && p->read_next_timestep) ||
994         ((mask & cPlugIOManager_graphics) && p->read_rawgraphics) ||
995         ((mask & cPlugIOManager_vol) && p->read_volumetric_data))
996       return p->name;
997   }
998 #endif
999 
1000   return NULL;
1001 }
1002