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 ×tep 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, ×tep)) {
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(×tep, 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, ×tep) != 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, ×tep);
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