1 /*
2 A* -------------------------------------------------------------------
3 B* This file contains source code for the PyMOL computer program
4 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
5 D* -------------------------------------------------------------------
6 E* It is unlawful to modify or remove this copyright notice.
7 F* -------------------------------------------------------------------
8 G* Please see the accompanying LICENSE file for further information.
9 H* -------------------------------------------------------------------
10 I* Additional authors of this source file include:
11 -*
12 -*
13 -*
14 Z* -------------------------------------------------------------------
15 */
16 #include"os_python.h"
17
18 #include"os_predef.h"
19 #include"os_std.h"
20 #include"os_gl.h"
21
22 #include <algorithm>
23 #include <cassert>
24 #include <set>
25
26 #include"Base.h"
27 #include"Parse.h"
28 #include"OOMac.h"
29 #include"Vector.h"
30 #include"MemoryDebug.h"
31 #include"Err.h"
32 #include"Map.h"
33 #include"Selector.h"
34 #include"ObjectMolecule.h"
35 #include"Ortho.h"
36 #include"Util.h"
37 #include"Matrix.h"
38 #include"Scene.h"
39 #include"P.h"
40 #include"PConv.h"
41 #include"Executive.h"
42 #include"Setting.h"
43 #include"Sphere.h"
44 #include"main.h"
45 #include"CGO.h"
46 #include"Editor.h"
47 #include"Sculpt.h"
48 #include"OVContext.h"
49 #include"OVOneToOne.h"
50 #include"OVLexicon.h"
51 #include"ListMacros.h"
52 #include"File.h"
53 #include "Lex.h"
54 #include "MolV3000.h"
55 #include "HydrogenAdder.h"
56
57 #ifdef _WEBGL
58 #endif
59
60 #define cMaxNegResi 100
61
62 #define ntrim ParseNTrim
63 #define nextline ParseNextLine
64 #define ncopy ParseNCopy
65 #define nskip ParseNSkip
66
67
68 #ifndef NO_MMLIBS
69 #include "mmpymolx.h"
70 #endif
71
72 static
73 CoordSet *ObjectMoleculeMMDStr2CoordSet(PyMOLGlobals * G, const char *buffer,
74 AtomInfoType ** atInfoPtr, const char **restart);
75
76 static
77 void ObjectMoleculeTransformTTTf(ObjectMolecule * I, float *ttt, int state);
78
79 static
80 int ObjectMoleculeGetAtomGeometry(ObjectMolecule * I, int state, int at);
81
82 static
83 void ObjectMoleculeInferHBondFromChem(ObjectMolecule * I);
84
85 /*
86 * Order function for sorting bonds by atom indices (ignores other properties
87 * like bond order).
88 *
89 * Pre-condition: index pairs are in order (index[0] < index[1])
90 */
91 static
BondTypeInOrder(PyMOLGlobals * G,const BondType * bonds,int i1,int i2)92 int BondTypeInOrder(PyMOLGlobals * G, const BondType * bonds, int i1, int i2) {
93 auto lhs = bonds[i1].index;
94 auto rhs = bonds[i2].index;
95 return lhs[0] < rhs[0] || (lhs[0] == rhs[0] && lhs[1] < rhs[1]);
96 }
97
98 /*
99 * Remove duplicated bonds. Disregards if two atoms would be bonded by two
100 * bonds of different bond order (only one will be kept).
101 */
102 static
ObjectMoleculeRemoveDuplicateBonds(PyMOLGlobals * G,ObjectMolecule * I)103 void ObjectMoleculeRemoveDuplicateBonds(PyMOLGlobals * G, ObjectMolecule * I) {
104 // make sure index pairs are in order
105 for (int i = 0; i < I->NBond; ++i) {
106 auto& bond = I->Bond[i];
107 if (bond.index[0] > bond.index[1])
108 std::swap(bond.index[0], bond.index[1]);
109 }
110
111 // get sorted indices
112 int * sorted = pymol::malloc<int>(I->NBond);
113 UtilSortIndexGlobals(G, I->NBond, I->Bond.data(), sorted,
114 (UtilOrderFnGlobals *) BondTypeInOrder);
115
116 // purge duplicated bonds and mark for removal
117 for (int i = 0, j = -1; i < I->NBond; ++i) {
118 int b = sorted[i];
119 auto ptr = I->Bond + sorted[i];
120
121 bool equal = false;
122 if (j != -1) {
123 const auto& other = I->Bond[j];
124 if (ptr->index[0] == other.index[0] &&
125 ptr->index[1] == other.index[1])
126 equal = true;
127 }
128
129 if (!equal) {
130 j = b;
131 } else {
132 AtomInfoPurgeBond(G, ptr);
133
134 // mark for removal
135 ptr->index[0] = ptr->index[1] = 0;
136 }
137 }
138
139 FreeP(sorted);
140
141 // remove purged bonds from array
142 int j = 0;
143 for (int i = 0; i < I->NBond; ++i) {
144 auto& bond = I->Bond[i];
145 if (bond.index[0] != bond.index[1]) {
146 if (j != i)
147 std::swap(I->Bond[j], bond);
148 ++j;
149 }
150 }
151 I->NBond = j;
152 VLASize(I->Bond, BondType, I->NBond);
153 }
154
155 /*
156 * Convert a discrete object to non-discrete. Will merge atoms from states
157 * by identifiers. Atom level properties like color, b, q, etc. are lost
158 * on the merged atoms.
159 */
160 static
ObjectMoleculeSetNotDiscrete(PyMOLGlobals * G,ObjectMolecule * I)161 bool ObjectMoleculeSetNotDiscrete(PyMOLGlobals * G, ObjectMolecule * I) {
162 if (!I->DiscreteFlag)
163 return true;
164
165 VLAFreeP(I->DiscreteAtmToIdx);
166 VLAFreeP(I->DiscreteCSet);
167 I->DiscreteFlag = false;
168
169 for (int atm = 0; atm < I->NAtom; ++atm) {
170 I->AtomInfo[atm].discrete_state = 0;
171 }
172
173 int * outdex;
174 int * aindex = AtomInfoGetSortedIndex(G, I, I->AtomInfo, I->NAtom, &outdex);
175
176 // merge duplicate atoms
177 for (int i = 0, j = -1; i < I->NAtom; ++i) {
178 int atm = aindex[i];
179 auto ai = I->AtomInfo + atm;
180
181 if (j == -1 || !AtomInfoMatch(G, ai, I->AtomInfo + j, false, false)) {
182 j = atm;
183 } else {
184 ai->deleteFlag = true;
185 }
186
187 // remapping of merged indices
188 outdex[atm] = j;
189 }
190
191 // point coordsets to merged atoms
192 for (int state = 0; state < I->NCSet; ++state) {
193 auto cs = I->CSet[state];
194 if (!cs)
195 continue;
196
197 for (int idx = 0; idx < cs->NIndex; ++idx) {
198 int atm = cs->IdxToAtm[idx];
199 cs->IdxToAtm[idx] = outdex[atm];
200 }
201 }
202
203 // point bonds to merged atoms
204 for (int i = 0; i < I->NBond; ++i) {
205 auto& bond = I->Bond[i];
206 bond.index[0] = outdex[bond.index[0]];
207 bond.index[1] = outdex[bond.index[1]];
208 }
209
210 AtomInfoFreeSortedIndexes(G, &aindex, &outdex);
211
212 ObjectMoleculeRemoveDuplicateBonds(G, I);
213
214 I->updateAtmToIdx();
215 ObjectMoleculePurge(I);
216
217 return true;
218 }
219
220 /*
221 * Make a non-discrete object discrete (the reverse is not supported).
222 */
ObjectMoleculeSetDiscrete(PyMOLGlobals * G,ObjectMolecule * I,int discrete)223 int ObjectMoleculeSetDiscrete(PyMOLGlobals * G, ObjectMolecule * I, int discrete)
224 {
225 int state, idx, ao, an, ao1, ao2, an1, an2;
226 int maxnatom, natom = I->NAtom, nbond = I->NBond;
227 int *aostate2an = NULL;
228 char *bondseen = NULL;
229 BondType *bond;
230 CoordSet *cs;
231
232 if (!discrete) {
233 if (!I->DiscreteFlag)
234 return true;
235
236 return ObjectMoleculeSetNotDiscrete(G, I);
237 }
238
239 if (I->DiscreteFlag)
240 return true;
241
242 // upper bound for number of discrete atoms
243 maxnatom = I->NAtom * I->NCSet;
244
245 // mapping (for bonds): atom_old -> atom_new
246 ok_assert(1, aostate2an = pymol::malloc<int>(I->NAtom));
247 ok_assert(1, bondseen = pymol::calloc<char >(I->NBond));
248
249 // discrete setup
250 I->DiscreteFlag = discrete;
251 ok_assert(1, I->DiscreteAtmToIdx = pymol::vla<int>(maxnatom));
252 ok_assert(1, I->DiscreteCSet = pymol::vla<CoordSet*>(maxnatom));
253
254 // for all coordinate sets
255 for (state = 0; state < I->NCSet; state++) {
256 cs = I->CSet[state];
257
258 if (!cs)
259 continue;
260
261 // init the atom_old -> atom_new array
262 for (ao = 0; ao < I->NAtom; ao++)
263 aostate2an[ao] = -1;
264
265 // for all atoms in coordinate set
266 for (idx = 0; idx < cs->NIndex; idx++) {
267 ao = an = cs->IdxToAtm[idx];
268
269 if (I->DiscreteCSet[ao]) {
270 // seen before, have to copy
271 an = natom++;
272
273 VLACheck(I->AtomInfo, AtomInfoType, an);
274 ok_assert(1, I->AtomInfo);
275
276 AtomInfoCopy(G,
277 I->AtomInfo + ao,
278 I->AtomInfo + an);
279 cs->IdxToAtm[idx] = an;
280 }
281
282 I->AtomInfo[an].discrete_state = state + 1; // 1-based :-(
283 I->DiscreteCSet[an] = cs;
284 I->DiscreteAtmToIdx[an] = cs->AtmToIdx[ao];
285
286 // mapping (for bonds): (atom_old, state) -> atom_new
287 aostate2an[ao] = an;
288 }
289
290 // unused in discrete coordsets
291 VLAFreeP(cs->AtmToIdx);
292
293 // for all old bonds
294 for (idx = 0; idx < I->NBond; idx++) {
295 bond = I->Bond + idx;
296
297 // old atom indices
298 ao1 = bond->index[0];
299 ao2 = bond->index[1];
300
301 // new atom indices
302 an1 = aostate2an[ao1];
303 an2 = aostate2an[ao2];
304
305 // do both atoms exist in this coord set?
306 if (an1 == -1 || an2 == -1)
307 continue;
308
309 if (bondseen[idx]) {
310 // seen before, have to copy
311 VLACheck(I->Bond, BondType, nbond);
312 ok_assert(1, I->Bond);
313
314 bond = I->Bond + (nbond++);
315 AtomInfoBondCopy(G, I->Bond + idx, bond);
316 } else {
317 bondseen[idx] = true;
318 }
319
320 bond->index[0] = an1;
321 bond->index[1] = an2;
322 }
323 }
324
325 // not needed anymore
326 mfree(aostate2an);
327 mfree(bondseen);
328
329 // update N* count fields
330 I->NAtom = natom;
331 I->NBond = nbond;
332 for (state = 0; state < I->NCSet; state++)
333 if ((cs = I->CSet[state]))
334 cs->NAtIndex = natom;
335
336 // trim VLAs memory to actual size
337 if (I->NBond)
338 VLASize(I->Bond, BondType, I->NBond);
339 if (I->NAtom)
340 VLASize(I->AtomInfo, AtomInfoType, I->NAtom);
341
342 I->setNDiscrete(I->NAtom);
343
344 I->invalidate(cRepAll, cRepInvAll, -1);
345
346 return true;
347
348 ok_except1:
349 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
350 " %s: memory allocation failed\n", __func__ ENDFB(G);
351 return false;
352 }
353
ObjectMoleculeCheckFullStateSelection(ObjectMolecule * I,int sele,int state)354 int ObjectMoleculeCheckFullStateSelection(ObjectMolecule * I, int sele, int state)
355 {
356 PyMOLGlobals *G = I->G;
357 int result = false;
358 if((state >= 0) && (state < I->NCSet)) {
359 const AtomInfoType *ai = I->AtomInfo.data();
360 CoordSet *cs = I->CSet[state];
361 if(cs) {
362 int a;
363 int at;
364 result = true;
365 for(a = 0; a < cs->NIndex; a++) {
366 at = cs->IdxToAtm[a];
367 if(!SelectorIsMember(G, ai[at].selEntry, sele)) {
368 result = false;
369 break;
370 }
371 }
372 }
373 }
374 return result;
375 }
376
377 /* PARAMS
378 * ch -- ptr to empty str of length 'len'
379 * len -- str len
380 * RETURNS
381 * ch, with the caption in place
382 * NOTES
383 * User owns the buffer so must clean up after it
384 */
getCaption(char * ch,int len) const385 char *ObjectMolecule::getCaption(char * ch, int len) const
386 {
387 auto I = this;
388 int objState;
389 int n = 0;
390 int show_state = 0;
391 int show_as_fraction = 0;
392 const char *frozen_str = "";
393
394 int state = ObjectGetCurrentState((CObject *) I, false);
395 int counter_mode = SettingGet_i(I->G, I->Setting, NULL, cSetting_state_counter_mode);
396 int frozen = SettingGetIfDefined_i(I->G, I->Setting, cSetting_state, &objState);
397
398 /* if frozen print (blue) STATE / NSTATES
399 * if not frozen, print STATE/NSTATES
400 * if beyond NSTATES, print * /NSTATES.
401 */
402
403 if(frozen) { /* frozen color */
404 frozen_str = "\\789";
405 } else {
406 if (I->DiscreteFlag) { /* discrete states */
407 frozen_str = "\\993";
408 } else { /* normal case */
409 frozen_str = "";
410 }
411 }
412
413 switch(counter_mode) {
414 case 0: /* off */
415 show_state = show_as_fraction = 0;
416 break;
417 case 2: /* just state */
418 show_state = 1;
419 show_as_fraction = 0;
420 break;
421 case -1: /* fraction, full-on */
422 case 1:
423 default:
424 show_state = show_as_fraction = 1;
425 break;
426 }
427
428 /* bail on null string or no room */
429 if (!ch || len==0)
430 return NULL;
431
432 ch[0] = 0;
433
434 /* if the state is valid, setup the label */
435 if(state >= 0) {
436 if (state < I->NCSet) {
437 const CoordSet *cs = I->CSet[state];
438 if(cs) {
439 if(show_state) {
440 if (show_as_fraction) {
441 if (strlen(cs->Name)) { /* NAME */
442 n = snprintf(ch, len, "%s %s%d/%d", cs->Name, frozen_str, state+1, I->NCSet);
443 }
444 else { /* no name */
445 n = snprintf(ch, len, "%s%d/%d", frozen_str, state+1, I->NCSet);
446 }
447 } else { /* not fraction */
448 if (strlen(cs->Name)) {
449 n = snprintf(ch, len, "%s %s%d", cs->Name, frozen_str, state+1);
450 } else { /* no name */
451 n = snprintf(ch, len, "%s%d", frozen_str, state+1);
452 }
453 }
454 } else { /* no state */
455 n = snprintf(ch, len, "%s", cs->Name);
456 }
457 } else { /* no coord set, can be an N-state object missing a CSet */
458 }
459 } else { /* state > NCSet, out of range due to other object or global setting */
460 if(show_state) {
461 if(show_as_fraction) {
462 n = snprintf(ch, len, "%s--/%d", frozen_str, I->NCSet);
463 } else { /* no fraction */
464 n = snprintf(ch, len, "%s--", frozen_str);
465 }
466 }
467 }
468 } else if (state == -1) {
469 // all states
470 n = snprintf(ch, len, "%s*/%d", frozen_str, I->NCSet);
471 } else {
472 /* blank out the title if outside the valid # of states */
473 }
474
475 if (n > len)
476 return NULL;
477
478 return ch;
479 }
480
481
482 #define MAX_BOND_DIST 50
483
484 /* find sets of atoms with identical skeletons */
485
486 typedef struct {
487 AtomInfoType *ai_a;
488 AtomInfoType *ai_b;
489 BondType *bi_a;
490 BondType *bi_b;
491 int *nbr_a, *nbr_b;
492 int *matched;
493 } match_info;
494
495 #define recmat3(x,y,z) \
496 (recursive_match(u_a[0],u_b[x],x_a[0],x_b[x],mi) && \
497 recursive_match(u_a[1],u_b[y],x_a[1],x_b[y],mi) && \
498 recursive_match(u_a[2],u_b[z],x_a[2],x_b[z],mi))
499
500 #define recmat4(w,x,y,z) \
501 (recursive_match(u_a[0],u_b[w],x_a[0],x_b[w],mi) && \
502 recursive_match(u_a[1],u_b[x],x_a[1],x_b[x],mi) && \
503 recursive_match(u_a[2],u_b[y],x_a[2],x_b[y],mi) && \
504 recursive_match(u_a[3],u_b[z],x_a[3],x_b[z],mi))
505
undo_match(int * start,match_info * mi)506 static void undo_match(int *start, match_info * mi)
507 {
508 /* remove the matched atom set */
509 AtomInfoType *ai_a = mi->ai_a;
510 AtomInfoType *ai_b = mi->ai_b;
511 BondType *bi_a = mi->bi_a;
512 BondType *bi_b = mi->bi_b;
513 int *match = mi->matched;
514 while(match > start) {
515 int a = match[-4];
516 int b = match[-3];
517 int aa = match[-2];
518 int bb = match[-1];
519 ai_a[a].temp1 = false;
520 ai_b[b].temp1 = false;
521 bi_a[aa].temp1 = false;
522 bi_b[bb].temp1 = false;
523 match -= 4;
524 }
525 mi->matched = start;
526 }
527
recursive_match(int a,int b,int aa,int bb,match_info * mi)528 static int recursive_match(int a, int b, int aa, int bb, match_info * mi)
529 {
530 if(mi->ai_a[a].temp1 && mi->ai_b[b].temp1) {
531 if((aa >= 0) && (bb >= 0) && !(mi->bi_a[aa].temp1 || mi->bi_b[bb].temp1)) {
532 /* note bond */
533 mi->ai_a[a].temp1 = true;
534 mi->ai_b[b].temp1 = true;
535 mi->bi_a[aa].temp1 = true;
536 mi->bi_b[bb].temp1 = true;
537 mi->matched[0] = a; /* atoms */
538 mi->matched[1] = b;
539 mi->matched[2] = aa; /* bonds */
540 mi->matched[3] = bb;
541 mi->matched += 4;
542 }
543 return true;
544 } else if(mi->ai_a[a].temp1 != mi->ai_b[b].temp1)
545 return false;
546 else if(mi->ai_a[a].protons != mi->ai_b[b].protons)
547 return false;
548 else {
549 int ni_a = mi->nbr_a[a];
550 int ni_b = mi->nbr_b[b];
551 int num_a = mi->nbr_a[ni_a++];
552 int num_b = mi->nbr_b[ni_b++];
553
554 if((num_a != num_b) || (num_a > 4)) /* must have the same number of neighbors (four or less) */
555 return false;
556 else {
557 int match_flag = false;
558 int u_a[4], u_b[4], x_a[4], x_b[4];
559 int n_u_a = 0;
560 int n_u_b = 0;
561 int *before = mi->matched;
562 mi->ai_a[a].temp1 = true;
563 mi->ai_b[b].temp1 = true;
564 mi->bi_a[aa].temp1 = true;
565 mi->bi_b[bb].temp1 = true;
566 mi->matched[0] = a; /* atoms */
567 mi->matched[1] = b;
568 mi->matched[2] = aa; /* bonds */
569 mi->matched[3] = bb;
570 mi->matched += 4;
571
572 /* collect unvisited bonds */
573
574 while(num_a--) {
575 int i_a = mi->nbr_a[ni_a + 1];
576 if(!mi->bi_a[i_a].temp1) { /* bond not yet visited */
577 u_a[n_u_a] = mi->nbr_a[ni_a]; /* atom index */
578 x_a[n_u_a++] = i_a;
579 }
580 ni_a += 2;
581 }
582
583 while(num_b--) {
584 int i_b = mi->nbr_b[ni_b + 1];
585 if(!mi->bi_b[i_b].temp1) {
586 u_b[n_u_b] = mi->nbr_b[ni_b];
587 x_b[n_u_b++] = i_b;
588 }
589 ni_b += 2;
590 }
591 /* NOTE: implicitly relying upon C short-circuit evaluation */
592 if(n_u_a == n_u_b) {
593 if(!n_u_a)
594 match_flag = true;
595 else if(n_u_a == 1) {
596 match_flag = recursive_match(u_a[0], u_b[0], x_a[0], x_b[0], mi);
597 } else if(n_u_a == 2) {
598 match_flag =
599 (recursive_match(u_a[0], u_b[0], x_a[0], x_b[0], mi) &&
600 recursive_match(u_a[1], u_b[1], x_a[1], x_b[1], mi)) ||
601 (recursive_match(u_a[0], u_b[1], x_a[0], x_b[1], mi) &&
602 recursive_match(u_a[1], u_b[0], x_a[1], x_b[0], mi));
603 } else if(n_u_a == 3) {
604 match_flag =
605 recmat3(0, 1, 2) ||
606 recmat3(0, 2, 1) ||
607 recmat3(1, 0, 2) || recmat3(1, 2, 0) || recmat3(2, 0, 1) || recmat3(2, 1, 0);
608 } else if(n_u_a == 4) {
609 match_flag =
610 recmat4(0, 1, 2, 3) ||
611 recmat4(0, 2, 1, 3) ||
612 recmat4(1, 0, 2, 3) ||
613 recmat4(1, 2, 0, 3) || recmat4(2, 0, 1, 3) || recmat4(2, 1, 0, 3);
614 if(!match_flag) {
615 match_flag =
616 recmat4(0, 1, 3, 2) ||
617 recmat4(0, 2, 3, 1) ||
618 recmat4(1, 0, 3, 2) ||
619 recmat4(1, 2, 3, 0) || recmat4(2, 0, 3, 1) || recmat4(2, 1, 3, 0);
620 if(!match_flag) {
621 match_flag =
622 recmat4(0, 3, 1, 2) ||
623 recmat4(0, 3, 2, 1) ||
624 recmat4(1, 3, 0, 2) ||
625 recmat4(1, 3, 2, 0) || recmat4(2, 3, 0, 1) || recmat4(2, 3, 1, 0);
626 if(!match_flag) {
627 match_flag =
628 recmat4(3, 0, 1, 2) ||
629 recmat4(3, 0, 2, 1) ||
630 recmat4(3, 1, 0, 2) ||
631 recmat4(3, 1, 2, 0) || recmat4(3, 2, 0, 1) || recmat4(3, 2, 1, 0);
632 }
633 }
634 }
635 }
636 }
637 if(!match_flag) { /* clean the match tree */
638 undo_match(before, mi);
639 }
640 return match_flag;
641 }
642 }
643 }
644
ObjectMoleculeXferValences(ObjectMolecule * Ia,int sele1,int sele2,int target_state,ObjectMolecule * Ib,int sele3,int source_state,int quiet)645 int ObjectMoleculeXferValences(ObjectMolecule * Ia, int sele1, int sele2,
646 int target_state, ObjectMolecule * Ib, int sele3,
647 int source_state, int quiet)
648 {
649 int *matched = NULL;
650 int match_found = false;
651 PyMOLGlobals *G = Ia->G;
652 if(Ia == Ib)
653 return false;
654
655 ObjectMoleculeUpdateNeighbors(Ia);
656 ObjectMoleculeUpdateNeighbors(Ib);
657
658 {
659 int max_match = Ia->NAtom + Ia->NBond;
660 if(max_match < (Ib->NAtom + Ib->NBond))
661 max_match = (Ib->NAtom + Ib->NBond);
662 matched = pymol::calloc<int>(max_match * 4);
663 }
664
665 {
666 int a, b;
667 AtomInfoType *ai_a = Ia->AtomInfo.data();
668 AtomInfoType *ai_b = Ib->AtomInfo.data();
669 for(a = 0; a < Ia->NAtom; a++) {
670 (ai_a++)->temp1 = false;
671 }
672 for(b = 0; b < Ib->NAtom; b++) {
673 (ai_b++)->temp1 = false;
674 }
675 }
676
677 {
678 int a, b;
679 BondType *bi_a = Ia->Bond.data();
680 BondType *bi_b = Ib->Bond.data();
681 for(a = 0; a < Ia->NBond; a++) {
682 (bi_a++)->temp1 = false;
683 }
684 for(b = 0; b < Ib->NBond; b++) {
685 (bi_b++)->temp1 = false;
686 }
687 }
688
689 {
690 int a, b;
691 AtomInfoType *ai_a = Ia->AtomInfo.data();
692 AtomInfoType *ai_b = Ib->AtomInfo.data();
693 BondType *bi_a = Ia->Bond.data();
694 BondType *bi_b = Ib->Bond.data();
695
696 match_info mi;
697
698 mi.ai_a = ai_a;
699 mi.ai_b = ai_b;
700 mi.bi_a = bi_a;
701 mi.bi_b = bi_b;
702 mi.nbr_a = Ia->Neighbor;
703 mi.nbr_b = Ib->Neighbor;
704 mi.matched = matched;
705 for(a = 0; a < Ia->NAtom; a++) {
706 if(!ai_a[a].temp1) {
707 int a_entry = ai_a[a].selEntry;
708 if(SelectorIsMember(G, a_entry, sele1) || SelectorIsMember(G, a_entry, sele2)) {
709 for(b = 0; b < Ib->NAtom; b++) {
710 if(SelectorIsMember(G, ai_b[b].selEntry, sele3)) {
711 if(recursive_match(a, b, -1, -1, &mi)) {
712 int *match = mi.matched;
713 match_found = true;
714 /* now graft bond orders for bonds in matching atoms */
715
716 while(match > matched) {
717 int at_a = match[-4];
718 int at_b = match[-3];
719 int bd_a = match[-2];
720 int bd_b = match[-1];
721
722 if((bd_a >= 0) && (bd_a >= 0)) {
723 int a1 = bi_a[bd_a].index[0];
724 int a2 = bi_a[bd_a].index[1];
725
726 int a1_entry = ai_a[a1].selEntry;
727 int a2_entry = ai_a[a2].selEntry;
728
729 if((SelectorIsMember(G, a1_entry, sele1) &&
730 SelectorIsMember(G, a2_entry, sele2)) ||
731 (SelectorIsMember(G, a2_entry, sele1) &&
732 SelectorIsMember(G, a1_entry, sele2))) {
733 /* only update bonds which actually sit between the two selections */
734 bi_a[bd_a].order = bi_b[bd_b].order;
735 ai_a[at_a].chemFlag = false;
736 }
737 }
738 ai_b[at_b].temp1 = false; /* release source for future matching */
739 if(bd_b >= 0)
740 bi_b[bd_b].temp1 = false;
741 match -= 4;
742 }
743 break;
744 }
745 }
746 }
747 }
748 }
749 }
750 }
751 FreeP(matched);
752 return match_found;
753 }
754
ObjectMoleculeTransformState44f(ObjectMolecule * I,int state,const float * matrix,int log_trans,int homogenous,int transformed)755 void ObjectMoleculeTransformState44f(ObjectMolecule * I, int state, const float *matrix,
756 int log_trans, int homogenous, int transformed)
757 {
758 int a;
759 float tmp_matrix[16];
760 CoordSet *cs;
761 int use_matrices = SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
762 if(use_matrices<0) use_matrices = 0;
763 if(!use_matrices) {
764 ObjectMoleculeTransformSelection(I, state, -1, matrix, log_trans, I->Name,
765 homogenous, true);
766 } else {
767 double dbl_matrix[16];
768 if(state == -2)
769 state = ObjectGetCurrentState(I, false);
770 /* ensure homogenous matrix to preserve programmer sanity */
771 if(!homogenous) {
772 convertTTTfR44d(matrix, dbl_matrix);
773 copy44d44f(dbl_matrix, tmp_matrix);
774 matrix = tmp_matrix;
775 } else {
776 copy44f44d(matrix, dbl_matrix);
777 }
778
779 if(state < 0) { /* all states */
780 for(a = 0; a < I->NCSet; a++) {
781 cs = I->CSet[a];
782 if(cs)
783 ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
784 }
785 } else if(state < I->NCSet) { /* single state */
786 cs = I->CSet[state];
787 if(cs)
788 ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
789 } else if(I->NCSet == 1) { /* static singleton state */
790 cs = I->CSet[0];
791 if(cs && SettingGet_b(I->G, I->Setting, NULL, cSetting_static_singletons)) {
792 ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
793 }
794 }
795 }
796 }
797
798
799 /*========================================================================*/
ObjectMoleculeFixSeleHydrogens(ObjectMolecule * I,int sele,int state)800 static int ObjectMoleculeFixSeleHydrogens(ObjectMolecule * I, int sele, int state)
801 {
802 int a;
803 int seleFlag = false;
804 const AtomInfoType *ai0;
805 #ifndef _PYMOL_NO_UNDO
806 #endif
807 int ok = true;
808
809 ai0 = I->AtomInfo;
810 for(a = 0; a < I->NAtom; a++) {
811 if(SelectorIsMember(I->G, ai0->selEntry, sele)) {
812 seleFlag = true;
813 break;
814 }
815 ai0++;
816 }
817 if(seleFlag) {
818 seleFlag = false;
819 if(!ObjectMoleculeVerifyChemistry(I, state)) {
820 ErrMessage(I->G, " AddHydrogens", "missing chemical geometry information.");
821 } else {
822 if (ok)
823 ok &= ObjectMoleculeUpdateNeighbors(I);
824 ai0 = I->AtomInfo;
825 for(a = 0; a < I->NAtom; a++) {
826 if(!ai0->isHydrogen()) { /* only do heavies */
827 if(SelectorIsMember(I->G, ai0->selEntry, sele)) {
828 for(StateIterator iter(I->G, I->Setting, state, I->NCSet);
829 iter.next();) {
830 auto cs = I->CSet[iter.state];
831 if (!cs)
832 continue;
833
834 #ifndef _PYMOL_NO_UNDO
835 #endif
836
837 seleFlag |= ObjectMoleculeSetMissingNeighborCoords(I, cs, a, true);
838 }
839 }
840 }
841 ai0++;
842 }
843 }
844 if(seleFlag)
845 I->invalidate(cRepAll, cRepInvAll, -1);
846 }
847 return ok;
848 }
849
skip_fortran(int num,int per_line,const char * p)850 static const char *skip_fortran(int num, int per_line, const char *p)
851 {
852 int a, b;
853 b = 0;
854 for(a = 0; a < num; a++) {
855 if((++b) == per_line) {
856 b = 0;
857 p = nextline(p);
858 }
859 }
860 if(b || (!num))
861 p = nextline(p);
862 return (p);
863 }
864
ObjectMoleculeOpRecInit(ObjectMoleculeOpRec * op)865 void ObjectMoleculeOpRecInit(ObjectMoleculeOpRec * op)
866 {
867 UtilZeroMem((char *) op, sizeof(ObjectMoleculeOpRec));
868 }
869
ObjectMoleculeGetTopNeighbor(PyMOLGlobals * G,ObjectMolecule * I,int start,int excluded)870 int ObjectMoleculeGetTopNeighbor(PyMOLGlobals * G,
871 ObjectMolecule * I, int start, int excluded)
872 {
873 /* returns the most proton-rich element with the lowest priority value
874 (OG1 before OG2, CG, HB1) */
875
876 int n0, at;
877 AtomInfoType *ai;
878 int highest_at = -1, highest_prot = 0, lowest_pri = 9999;
879
880 ObjectMoleculeUpdateNeighbors(I);
881 n0 = I->Neighbor[start] + 1;
882 while(I->Neighbor[n0] >= 0) {
883 ai = I->AtomInfo + (at = I->Neighbor[n0]);
884 if((highest_at < 0) && (at != excluded)) {
885 highest_prot = ai->protons;
886 lowest_pri = ai->priority;
887 highest_at = at;
888 } else if(((ai->protons > highest_prot) ||
889 ((ai->protons == highest_prot) && (ai->priority < lowest_pri)))
890 && (at != excluded)) {
891 highest_prot = ai->protons;
892 highest_at = at;
893 lowest_pri = ai->priority;
894 }
895 n0 += 2;
896 }
897 return highest_at;
898 }
899
900 /**
901 * Selection mapping for `load_traj` to only load coordinates for a subset of
902 * atoms. Returns a mapping of old to new coordinate indices and modifies the
903 * index-related members of `cs`.
904 *
905 * @param[in] obj Object Molecule for evaluation of the selection
906 * @param[in,out] cs Candidate coordinate set (with valid index arrays)
907 * @param[in] selection Named selection
908 * @return index mapping, or NULL if `selection` is not valid
909 */
LoadTrajSeleHelper(const ObjectMolecule * obj,CoordSet * cs,const char * selection)910 std::unique_ptr<int[]> LoadTrajSeleHelper(
911 const ObjectMolecule* obj, CoordSet* cs, const char* selection)
912 {
913 auto G = obj->G;
914 int sele0 = SelectorIndexByName(G, selection);
915
916 // We can skip "all" (0) here since it's a trivial case
917 if (sele0 <= 0) {
918 return nullptr;
919 }
920
921 auto xref = std::unique_ptr<int[]>(new int[cs->NIndex]);
922
923 int idx_new = 0;
924
925 for (int idx = 0; idx < cs->NIndex; ++idx) {
926 auto atm = cs->IdxToAtm[idx];
927 if (SelectorIsMember(G, obj->AtomInfo[atm].selEntry, sele0)) {
928 cs->IdxToAtm[idx_new] = atm;
929 cs->AtmToIdx[atm] = idx_new;
930 xref[idx] = idx_new;
931 ++idx_new;
932 } else {
933 cs->AtmToIdx[atm] = -1;
934 xref[idx] = -1;
935 }
936 }
937
938 cs->NIndex = idx_new;
939 cs->IdxToAtm.resize(cs->NIndex);
940 cs->Coord.resize(cs->NIndex * 3);
941
942 return xref;
943 }
944
945 /*========================================================================*/
ObjectMoleculeLoadTRJFile(PyMOLGlobals * G,ObjectMolecule * I,const char * fname,int frame,int interval,int average,int start,int stop,int max,const char * sele,int image,float * shift,int quiet)946 ObjectMolecule *ObjectMoleculeLoadTRJFile(PyMOLGlobals * G, ObjectMolecule * I,
947 const char *fname, int frame, int interval,
948 int average, int start, int stop, int max,
949 const char *sele, int image, float *shift, int quiet)
950 {
951 FILE *f;
952 char *buffer;
953 const char *p;
954 char cc[MAXLINELEN];
955 int n_read;
956 int to_go;
957 int skip_first_line = true;
958 int periodic = false;
959 int angles = true;
960 float f0, f1, f2, *fp;
961 float box[3], angle[3];
962 float r_cent[3], r_trans[3];
963 int r_act, r_val, r_cnt;
964 float *r_fp_start = NULL, *r_fp_stop = NULL;
965 int a, b, c, i;
966 int zoom_flag = false;
967 int cnt = 0;
968 int n_avg = 0;
969 int icnt;
970 int ncnt = 0;
971 float zerovector[3] = { 0.0, 0.0, 0.0 };
972 CoordSet *cs = NULL;
973
974 if (I->DiscreteFlag) {
975 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
976 " %s: Discrete objects not supported\n", __func__ ENDFB(G);
977 return I;
978 }
979
980 if(!shift)
981 shift = zerovector;
982 if(interval < 1)
983 interval = 1;
984
985 icnt = interval;
986 #define BUFSIZE 4194304
987 #define GETTING_LOW 10000
988
989 f = pymol_fopen(fname, "rb");
990 if(!f) {
991 ErrMessage(G, __func__, "Unable to open file!");
992 } else {
993 if(I->CSTmpl) {
994 cs = CoordSetCopy(I->CSTmpl);
995 } else if (I->NCSet > 0) {
996 cs = CoordSetCopy(I->CSet[0]);
997 } else {
998 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
999 " ObjMolLoadTRJFile: Missing topology" ENDFB(G);
1000 return (I);
1001 }
1002
1003 auto xref = LoadTrajSeleHelper(I, cs, sele);
1004
1005 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1006 " ObjMolLoadTRJFile: Loading from \"%s\".\n", fname ENDFB(G);
1007 buffer = pymol::malloc<char>(BUFSIZE + 1); /* 1 MB read buffer */
1008 p = buffer;
1009 buffer[0] = 0;
1010 n_read = 0;
1011 to_go = 0;
1012 a = 0;
1013 b = 0;
1014 c = 0;
1015 f1 = 0.0;
1016 f2 = 0.0;
1017 while(1) {
1018 to_go = n_read - (p - buffer);
1019 if(to_go < GETTING_LOW)
1020 if(!feof(f)) {
1021 if(to_go)
1022 memcpy(buffer, p, to_go);
1023 n_read = fread(buffer + to_go, 1, BUFSIZE - to_go, f);
1024 n_read = to_go + n_read;
1025 buffer[n_read] = 0;
1026 p = buffer;
1027 if(skip_first_line) {
1028 p = nextline(p);
1029 skip_first_line = false;
1030 }
1031 to_go = n_read - (p - buffer);
1032 }
1033 if(!to_go)
1034 break;
1035 p = ncopy(cc, p, 8);
1036 if((++b) == 10) {
1037 b = 0;
1038 p = nextline(p);
1039 }
1040 f0 = f1;
1041 f1 = f2;
1042 if(sscanf(cc, "%f", &f2) == 1) {
1043 if((++c) == 3) {
1044 c = 0;
1045 if((cnt + 1) >= start) {
1046 if(icnt <= 1) {
1047 if(xref) {
1048 if(xref[a] >= 0)
1049 fp = cs->coordPtr(xref[a]);
1050 else
1051 fp = NULL;
1052 } else {
1053 fp = cs->coordPtr(a);
1054 }
1055 if(fp) {
1056 if(n_avg) {
1057 *(fp++) += f0;
1058 *(fp++) += f1;
1059 *(fp++) += f2;
1060 } else {
1061 *(fp++) = f0;
1062 *(fp++) = f1;
1063 *(fp++) = f2;
1064 }
1065 }
1066 }
1067 }
1068 if((++a) == I->NAtom) {
1069
1070 cnt++;
1071 a = 0;
1072 if(b)
1073 p = nextline(p);
1074 b = 0;
1075
1076 if(cs->PeriodicBoxType != CoordSet::NoPeriodicity) {
1077 /* read periodic box */
1078
1079 c = 0;
1080 periodic = true;
1081 angles = true;
1082
1083 p = ncopy(cc, p, 8);
1084 if(sscanf(cc, "%f", &box[0]) != 1)
1085 periodic = false;
1086 p = ncopy(cc, p, 8);
1087 if(sscanf(cc, "%f", &box[1]) != 1)
1088 periodic = false;
1089 p = ncopy(cc, p, 8);
1090 if(sscanf(cc, "%f", &box[2]) != 1)
1091 periodic = false;
1092
1093 p = ncopy(cc, p, 8);
1094 if(sscanf(cc, "%f", &angle[0]) != 1)
1095 angles = false;
1096
1097 p = ncopy(cc, p, 8);
1098 if(sscanf(cc, "%f", &angle[1]) != 1)
1099 angles = false;
1100
1101 p = ncopy(cc, p, 8);
1102 if(sscanf(cc, "%f", &angle[2]) != 1)
1103 angles = false;
1104 if(periodic) {
1105 if(!cs->PeriodicBox)
1106 cs->PeriodicBox = pymol::make_unique<CCrystal>(G);
1107 cs->PeriodicBox->Dim[0] = box[0];
1108 cs->PeriodicBox->Dim[1] = box[1];
1109 cs->PeriodicBox->Dim[2] = box[2];
1110 if(angles) {
1111 cs->PeriodicBox->Angle[0] = angle[0];
1112 cs->PeriodicBox->Angle[1] = angle[1];
1113 cs->PeriodicBox->Angle[2] = angle[2];
1114 }
1115 CrystalUpdate(cs->PeriodicBox.get());
1116 /* CrystalDump(cs->PeriodicBox); */
1117 p = nextline(p);
1118 b = 0;
1119 }
1120
1121 if(cs->PeriodicBoxType == CoordSet::Octahedral)
1122 periodic = false; /* can't handle this yet... */
1123 }
1124
1125 if((stop > 0) && (cnt >= stop))
1126 break;
1127 if(cnt >= start) {
1128 icnt--;
1129 if(icnt > 0) {
1130 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1131 " ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
1132 } else {
1133 icnt = interval;
1134 n_avg++;
1135 }
1136
1137 if(icnt == interval) {
1138 if(n_avg < average) {
1139 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1140 " ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
1141 } else {
1142
1143 /* compute average */
1144 if(n_avg > 1) {
1145 fp = cs->Coord.data();
1146 for(i = 0; i < cs->NIndex; i++) {
1147 *(fp++) /= n_avg;
1148 *(fp++) /= n_avg;
1149 *(fp++) /= n_avg;
1150 }
1151 }
1152 if(periodic && image) {
1153 /* Perform residue-based period image transformation */
1154 i = 0;
1155 r_cnt = 0;
1156 r_act = 0; /* 0 unspec, 1=load, 2=image, 3=leave */
1157 r_val = -1;
1158 while(r_act != 3) {
1159 if(i >= cs->NIndex) {
1160 if(r_cnt)
1161 r_act = 2;
1162 else
1163 r_act = 3;
1164 }
1165 if(r_act == 0) {
1166 /* start new residue */
1167 r_cnt = 0;
1168 r_act = 1; /* now load */
1169 }
1170 if(r_act == 1) {
1171 if(i < cs->NIndex) {
1172
1173 /* is there a coordinate for atom? */
1174 if(xref) {
1175 if(xref[i] >= 0)
1176 fp = cs->coordPtr(xref[i]);
1177 else
1178 fp = NULL;
1179 } else {
1180 fp = cs->coordPtr(i);
1181 }
1182 if(fp) { /* yes there is... */
1183 if(r_cnt) {
1184 if(r_val != I->AtomInfo[cs->IdxToAtm[i]].resv) {
1185 r_act = 2; /* end of residue-> time to image */
1186 } else {
1187 r_cnt++;
1188 r_cent[0] += *(fp++);
1189 r_cent[1] += *(fp++);
1190 r_cent[2] += *(fp++);
1191 r_fp_stop = fp; /* stop here */
1192 i++;
1193 }
1194 } else {
1195 r_val = I->AtomInfo[cs->IdxToAtm[i]].resv;
1196 r_cnt++;
1197 r_fp_start = fp; /* start here */
1198 r_cent[0] = *(fp++);
1199 r_cent[1] = *(fp++);
1200 r_cent[2] = *(fp++);
1201 r_fp_stop = fp; /* stop here */
1202 i++;
1203 }
1204 } else {
1205 i++;
1206 }
1207 } else {
1208 r_act = 2; /* image */
1209 }
1210 }
1211
1212 if(r_act == 2) { /* time to image */
1213 if(r_cnt) {
1214 r_cent[0] /= r_cnt;
1215 r_cent[1] /= r_cnt;
1216 r_cent[2] /= r_cnt;
1217 transform33f3f(cs->PeriodicBox->RealToFrac, r_cent, r_cent);
1218 r_trans[0] = fmodf(1000.0F + shift[0] + r_cent[0], 1.0F);
1219 r_trans[1] = fmodf(1000.0F + shift[1] + r_cent[1], 1.0F);
1220 r_trans[2] = fmodf(1000.0F + shift[2] + r_cent[2], 1.0F);
1221 r_trans[0] -= r_cent[0];
1222 r_trans[1] -= r_cent[1];
1223 r_trans[2] -= r_cent[2];
1224 transform33f3f(cs->PeriodicBox->FracToReal, r_trans, r_trans);
1225 fp = r_fp_start;
1226 while(fp < r_fp_stop) {
1227 *(fp++) += r_trans[0];
1228 *(fp++) += r_trans[1];
1229 *(fp++) += r_trans[2];
1230 }
1231 }
1232 r_act = 0; /* reset */
1233 r_cnt = 0;
1234 }
1235 }
1236 }
1237
1238 /* add new coord set */
1239 cs->invalidateRep(cRepAll, cRepInvRep);
1240 if(frame < 0)
1241 frame = I->NCSet;
1242 if(!I->NCSet) {
1243 zoom_flag = true;
1244 }
1245
1246 VLACheck(I->CSet, CoordSet *, frame);
1247 if(I->NCSet <= frame)
1248 I->NCSet = frame + 1;
1249 if(I->CSet[frame])
1250 I->CSet[frame]->fFree();
1251 I->CSet[frame] = cs;
1252 ncnt++;
1253
1254 if(average < 2) {
1255 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1256 " ObjectMolecule: read set %d into state %d...\n", cnt, frame + 1
1257 ENDFB(G);
1258 } else {
1259 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1260 " ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
1261 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1262 " ObjectMolecule: average loaded into state %d...\n", frame + 1
1263 ENDFB(G);
1264 }
1265 frame++;
1266 cs = CoordSetCopy(cs);
1267 n_avg = 0;
1268 if((stop > 0) && (cnt >= stop))
1269 break;
1270 if((max > 0) && (ncnt >= max))
1271 break;
1272 }
1273 }
1274 } else {
1275 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1276 " ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
1277 }
1278 }
1279 }
1280 } else {
1281 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
1282 " ObjMolLoadTRJFile-Error: Failed to read expected coordinate value.\n This traj. does not match the loaded parameter/topology file.\n Likely cause: either the atom count or the periodic box settings\n are inconsistent between the two files.\n"
1283 ENDFB(G);
1284 break;
1285 }
1286 }
1287 mfree(buffer);
1288 }
1289 if(cs)
1290 cs->fFree();
1291 SceneChanged(G);
1292 SceneCountFrames(G);
1293 if(zoom_flag)
1294 if(SettingGetGlobal_i(G, cSetting_auto_zoom)) {
1295 ExecutiveWindowZoom(G, I->Name, 0.0, -1, 0, 0, quiet); /* auto zoom (all states) */
1296 }
1297
1298 return (I);
1299 }
1300
ObjectMoleculeLoadRSTFile(PyMOLGlobals * G,ObjectMolecule * I,const char * fname,int frame,int quiet,char mode)1301 ObjectMolecule *ObjectMoleculeLoadRSTFile(PyMOLGlobals * G, ObjectMolecule * I,
1302 const char *fname, int frame, int quiet, char mode)
1303 {
1304 /*
1305 * mode = 0: AMBER coordinate/restart file (one frame only)
1306 * mode = 1: AMBER trajectory
1307 * mode = 2: AMBER trajectory with box
1308 * http://ambermd.org/formats.html
1309 */
1310 int ok = true;
1311 char *buffer, *p;
1312 char cc[MAXLINELEN];
1313 float f0, f1, f2, *fp;
1314 int a, b, c;
1315 int zoom_flag = false;
1316 CoordSet *cs = NULL;
1317 char ncolumn = 6; // number of coordinates per line
1318 char nbyte = 12; // width of one coordinate
1319
1320 if(mode > 0) {
1321 ncolumn = 10;
1322 nbyte = 8;
1323 }
1324
1325 #define BUFSIZE 4194304
1326 #define GETTING_LOW 10000
1327
1328 else {
1329 if(I->CSTmpl) {
1330 cs = CoordSetCopy(I->CSTmpl);
1331 } else if (I->NCSet > 0) {
1332 cs = CoordSetCopy(I->CSet[0]);
1333 } else {
1334 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
1335 " ObjMolLoadRSTFile: Missing topology" ENDFB(G);
1336 return (I);
1337 }
1338 CHECKOK(ok, cs);
1339 if (ok){
1340 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1341 " ObjMolLoadRSTFile: Loading from \"%s\".\n", fname ENDFB(G);
1342 p = buffer = FileGetContents(fname, NULL);
1343 if(!buffer)
1344 ok = ErrMessage(G, __func__, "Unable to open file!");
1345 }
1346 if (ok){
1347 p = nextline(p);
1348 if (mode == 0) // skip NATOM,TIME
1349 p = nextline(p);
1350 }
1351 a = 0;
1352 b = 0;
1353 c = 0;
1354 f1 = 0.0;
1355 f2 = 0.0;
1356 while(ok && *p) {
1357 p = ncopy(cc, p, nbyte);
1358 if((++b) == ncolumn) {
1359 b = 0;
1360 p = nextline(p);
1361 }
1362 f0 = f1;
1363 f1 = f2;
1364 if(sscanf(cc, "%f", &f2) == 1) {
1365 if((++c) == 3) {
1366 c = 0;
1367 fp = cs->coordPtr(a);
1368 *(fp++) = f0;
1369 *(fp++) = f1;
1370 *(fp++) = f2;
1371
1372 if((++a) == I->NAtom) {
1373 a = 0;
1374 if(b)
1375 p = nextline(p);
1376 if(mode == 2) // skip box
1377 p = nextline(p);
1378 b = 0;
1379 /* add new coord set */
1380 cs->invalidateRep(cRepAll, cRepInvRep);
1381 if(frame < 0)
1382 frame = I->NCSet;
1383 if(!I->NCSet) {
1384 zoom_flag = true;
1385 }
1386
1387 VLACheck(I->CSet, CoordSet *, frame);
1388 CHECKOK(ok, I->CSet);
1389 if (ok){
1390 if(I->NCSet <= frame)
1391 I->NCSet = frame + 1;
1392 if(I->CSet[frame])
1393 I->CSet[frame]->fFree();
1394 I->CSet[frame] = cs;
1395 }
1396 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1397 " ObjectMolecule: read coordinates into state %d...\n", frame + 1 ENDFB(G);
1398
1399 if (ok)
1400 cs = CoordSetCopy(cs);
1401 CHECKOK(ok, cs);
1402
1403 if (mode == 0) // restart file has only one frame
1404 break;
1405
1406 frame += 1;
1407 }
1408 }
1409 } else {
1410 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
1411 " ObjMolLoadRSTFile: atom/coordinate mismatch.\n" ENDFB(G);
1412 break;
1413 }
1414 }
1415 mfree(buffer);
1416 }
1417 if(cs)
1418 cs->fFree();
1419
1420 SceneChanged(G);
1421 SceneCountFrames(G);
1422 if(zoom_flag){
1423 if(SettingGetGlobal_i(G, cSetting_auto_zoom)) {
1424 ExecutiveWindowZoom(G, I->Name, 0.0, -1, 0, 0, quiet); /* auto zoom (all states) */
1425 }
1426 }
1427 return (I);
1428 }
1429
findflag(PyMOLGlobals * G,const char * p,const char * flag,const char * format)1430 static const char *findflag(PyMOLGlobals * G, const char *p, const char *flag, const char *format)
1431 {
1432
1433 char cc[MAXLINELEN];
1434 char pat[MAXLINELEN] = "%FLAG ";
1435 int l;
1436
1437 PRINTFD(G, FB_ObjectMolecule)
1438 " findflag: flag %s format %s\n", flag, format ENDFD;
1439
1440 strcat(pat, flag);
1441 l = strlen(pat);
1442 while(*p) {
1443 p = ncopy(cc, p, l);
1444 if(WordMatch(G, cc, pat, true) < 0) {
1445 p = nextline(p);
1446 break;
1447 }
1448 p = nextline(p);
1449 if(!*p) {
1450 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
1451 " ObjectMolecule-Error: Unrecognized file format (can't find \"%s\").\n", pat
1452 ENDFB(G);
1453 }
1454 }
1455
1456 strcpy(pat, "%FORMAT(");
1457 strcat(pat, format);
1458 strcat(pat, ")");
1459 l = strlen(pat);
1460 while(*p) {
1461 p = ncopy(cc, p, l);
1462 if(WordMatch(G, cc, pat, true) < 0) {
1463 p = nextline(p);
1464 break;
1465 }
1466 p = nextline(p);
1467 if(!*p) {
1468 PRINTFB(G, FB_ObjectMolecule, FB_Errors)
1469 " ObjectMolecule-Error: Unrecognized file format (can't find \"%s\").\n", pat
1470 ENDFB(G);
1471 }
1472
1473 }
1474 return (p);
1475 }
1476
1477
1478 /*========================================================================*/
ObjectMoleculeTOPStr2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr)1479 static CoordSet *ObjectMoleculeTOPStr2CoordSet(PyMOLGlobals * G, const char *buffer,
1480 AtomInfoType ** atInfoPtr)
1481 {
1482 const char *p;
1483 int nAtom;
1484 int a, b, c, bi, last_i, at_i, aa, rc;
1485 float *coord = NULL;
1486 float *f;
1487 CoordSet *cset = NULL;
1488 AtomInfoType *atInfo = NULL, *ai;
1489 BondType *bond = NULL, *bd;
1490 int nBond = 0;
1491 int auto_show = RepGetAutoShowMask(G);
1492 int amber7 = false;
1493
1494 WordType title;
1495 ResName *resn;
1496
1497 char cc[MAXLINELEN];
1498 int ok = true;
1499 int i0, i1, i2;
1500
1501 /* trajectory parameters */
1502
1503 int NTYPES, NBONH, MBONA, NTHETH, MTHETA;
1504 int NPHIH, MPHIA, NHPARM, NPARM, NNB, NRES;
1505 int NBONA, NTHETA, NPHIA, NUMBND, NUMANG, NPTRA;
1506 int NATYP, NPHB, IFPERT, NBPER, NGPER, NDPER;
1507 int MBPER, MGPER, MDPER, IFBOX = 0, NMXRS, IFCAP;
1508 int NEXTRA, IPOL = 0;
1509 int wid, col;
1510 float BETA;
1511 float BOX1, BOX2, BOX3;
1512
1513 cset = CoordSetNew(G);
1514
1515 p = buffer;
1516 nAtom = 0;
1517 if(atInfoPtr)
1518 atInfo = *atInfoPtr;
1519 if(!atInfo)
1520 ErrFatal(G, "TOPStr2CoordSet", "need atom information record!");
1521 /* failsafe for old version.. */
1522
1523 ncopy(cc, p, 8);
1524 if(strcmp(cc, "%VERSION") == 0) {
1525 amber7 = true;
1526 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1527 " ObjectMolecule: Attempting to read Amber7 topology file.\n" ENDFB(G);
1528 } else {
1529 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1530 " ObjectMolecule: Assuming this is an Amber6 topology file.\n" ENDFB(G);
1531 }
1532
1533 /* read title */
1534 if(amber7) {
1535 p = findflag(G, buffer, "TITLE", "20a4");
1536 }
1537
1538 p = ncopy(cc, p, 20);
1539 title[0] = 0;
1540 sscanf(cc, "%s", title);
1541 p = nextline(p);
1542
1543 if(amber7) {
1544
1545 p = findflag(G, buffer, "POINTERS", "10I8");
1546
1547 p = ncopy(cc, p, 8);
1548 ok = ok && (sscanf(cc, "%d", &nAtom) == 1);
1549 p = ncopy(cc, p, 8);
1550 ok = ok && (sscanf(cc, "%d", &NTYPES) == 1);
1551 p = ncopy(cc, p, 8);
1552 ok = ok && (sscanf(cc, "%d", &NBONH) == 1);
1553 p = ncopy(cc, p, 8);
1554 ok = ok && (sscanf(cc, "%d", &MBONA) == 1);
1555 p = ncopy(cc, p, 8);
1556 ok = ok && (sscanf(cc, "%d", &NTHETH) == 1);
1557
1558 p = ncopy(cc, p, 8);
1559 ok = ok && (sscanf(cc, "%d", &MTHETA) == 1);
1560 p = ncopy(cc, p, 8);
1561 ok = ok && (sscanf(cc, "%d", &NPHIH) == 1);
1562 p = ncopy(cc, p, 8);
1563 ok = ok && (sscanf(cc, "%d", &MPHIA) == 1);
1564 p = ncopy(cc, p, 8);
1565 ok = ok && (sscanf(cc, "%d", &NHPARM) == 1);
1566 p = ncopy(cc, p, 8);
1567 ok = ok && (sscanf(cc, "%d", &NPARM) == 1);
1568
1569 p = nextline(p);
1570
1571 p = ncopy(cc, p, 8);
1572 ok = ok && (sscanf(cc, "%d", &NNB) == 1);
1573 p = ncopy(cc, p, 8);
1574 ok = ok && (sscanf(cc, "%d", &NRES) == 1);
1575 p = ncopy(cc, p, 8);
1576 ok = ok && (sscanf(cc, "%d", &NBONA) == 1);
1577 p = ncopy(cc, p, 8);
1578 ok = ok && (sscanf(cc, "%d", &NTHETA) == 1);
1579 p = ncopy(cc, p, 8);
1580 ok = ok && (sscanf(cc, "%d", &NPHIA) == 1);
1581
1582 p = ncopy(cc, p, 8);
1583 ok = ok && (sscanf(cc, "%d", &NUMBND) == 1);
1584 p = ncopy(cc, p, 8);
1585 ok = ok && (sscanf(cc, "%d", &NUMANG) == 1);
1586 p = ncopy(cc, p, 8);
1587 ok = ok && (sscanf(cc, "%d", &NPTRA) == 1);
1588 p = ncopy(cc, p, 8);
1589 ok = ok && (sscanf(cc, "%d", &NATYP) == 1);
1590 p = ncopy(cc, p, 8);
1591 ok = ok && (sscanf(cc, "%d", &NPHB) == 1);
1592
1593 p = nextline(p);
1594
1595 p = ncopy(cc, p, 8);
1596 ok = ok && (sscanf(cc, "%d", &IFPERT) == 1);
1597 p = ncopy(cc, p, 8);
1598 ok = ok && (sscanf(cc, "%d", &NBPER) == 1);
1599 p = ncopy(cc, p, 8);
1600 ok = ok && (sscanf(cc, "%d", &NGPER) == 1);
1601 p = ncopy(cc, p, 8);
1602 ok = ok && (sscanf(cc, "%d", &NDPER) == 1);
1603 p = ncopy(cc, p, 8);
1604 ok = ok && (sscanf(cc, "%d", &MBPER) == 1);
1605
1606 p = ncopy(cc, p, 8);
1607 ok = ok && (sscanf(cc, "%d", &MGPER) == 1);
1608 p = ncopy(cc, p, 8);
1609 ok = ok && (sscanf(cc, "%d", &MDPER) == 1);
1610 p = ncopy(cc, p, 8);
1611 ok = ok && (sscanf(cc, "%d", &IFBOX) == 1);
1612 p = ncopy(cc, p, 8);
1613 ok = ok && (sscanf(cc, "%d", &NMXRS) == 1);
1614 p = ncopy(cc, p, 8);
1615 ok = ok && (sscanf(cc, "%d", &IFCAP) == 1);
1616
1617 p = nextline(p);
1618 p = ncopy(cc, p, 8);
1619 ok = ok && (sscanf(cc, "%d", &NEXTRA) == 1);
1620
1621 } else {
1622
1623 p = ncopy(cc, p, 6);
1624 ok = ok && (sscanf(cc, "%d", &nAtom) == 1);
1625 p = ncopy(cc, p, 6);
1626 ok = ok && (sscanf(cc, "%d", &NTYPES) == 1);
1627 p = ncopy(cc, p, 6);
1628 ok = ok && (sscanf(cc, "%d", &NBONH) == 1);
1629 p = ncopy(cc, p, 6);
1630 ok = ok && (sscanf(cc, "%d", &MBONA) == 1);
1631 p = ncopy(cc, p, 6);
1632 ok = ok && (sscanf(cc, "%d", &NTHETH) == 1);
1633 p = ncopy(cc, p, 6);
1634 ok = ok && (sscanf(cc, "%d", &MTHETA) == 1);
1635
1636 p = ncopy(cc, p, 6);
1637 ok = ok && (sscanf(cc, "%d", &NPHIH) == 1);
1638 p = ncopy(cc, p, 6);
1639 ok = ok && (sscanf(cc, "%d", &MPHIA) == 1);
1640 p = ncopy(cc, p, 6);
1641 ok = ok && (sscanf(cc, "%d", &NHPARM) == 1);
1642 p = ncopy(cc, p, 6);
1643 ok = ok && (sscanf(cc, "%d", &NPARM) == 1);
1644 p = ncopy(cc, p, 6);
1645 ok = ok && (sscanf(cc, "%d", &NNB) == 1);
1646 p = ncopy(cc, p, 6);
1647 ok = ok && (sscanf(cc, "%d", &NRES) == 1);
1648
1649 p = nextline(p);
1650
1651 p = ncopy(cc, p, 6);
1652 ok = ok && (sscanf(cc, "%d", &NBONA) == 1);
1653 p = ncopy(cc, p, 6);
1654 ok = ok && (sscanf(cc, "%d", &NTHETA) == 1);
1655 p = ncopy(cc, p, 6);
1656 ok = ok && (sscanf(cc, "%d", &NPHIA) == 1);
1657 p = ncopy(cc, p, 6);
1658 ok = ok && (sscanf(cc, "%d", &NUMBND) == 1);
1659 p = ncopy(cc, p, 6);
1660 ok = ok && (sscanf(cc, "%d", &NUMANG) == 1);
1661 p = ncopy(cc, p, 6);
1662 ok = ok && (sscanf(cc, "%d", &NPTRA) == 1);
1663
1664 p = ncopy(cc, p, 6);
1665 ok = ok && (sscanf(cc, "%d", &NATYP) == 1);
1666 p = ncopy(cc, p, 6);
1667 ok = ok && (sscanf(cc, "%d", &NPHB) == 1);
1668 p = ncopy(cc, p, 6);
1669 ok = ok && (sscanf(cc, "%d", &IFPERT) == 1);
1670 p = ncopy(cc, p, 6);
1671 ok = ok && (sscanf(cc, "%d", &NBPER) == 1);
1672 p = ncopy(cc, p, 6);
1673 ok = ok && (sscanf(cc, "%d", &NGPER) == 1);
1674 p = ncopy(cc, p, 6);
1675 ok = ok && (sscanf(cc, "%d", &NDPER) == 1);
1676
1677 p = nextline(p);
1678
1679 p = ncopy(cc, p, 6);
1680 ok = ok && (sscanf(cc, "%d", &MBPER) == 1);
1681 p = ncopy(cc, p, 6);
1682 ok = ok && (sscanf(cc, "%d", &MGPER) == 1);
1683 p = ncopy(cc, p, 6);
1684 ok = ok && (sscanf(cc, "%d", &MDPER) == 1);
1685 p = ncopy(cc, p, 6);
1686 ok = ok && (sscanf(cc, "%d", &IFBOX) == 1);
1687 p = ncopy(cc, p, 6);
1688 ok = ok && (sscanf(cc, "%d", &NMXRS) == 1);
1689 p = ncopy(cc, p, 6);
1690 ok = ok && (sscanf(cc, "%d", &IFCAP) == 1);
1691
1692 p = ncopy(cc, p, 6);
1693 if(sscanf(cc, "%d", &NEXTRA) != 1)
1694 NEXTRA = 0;
1695
1696 }
1697
1698 if(!ok) {
1699 ErrMessage(G, "TOPStrToCoordSet", "Error reading counts lines");
1700 ok_raise(1);
1701 } else {
1702 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1703 " TOPStr2CoordSet: read counts line nAtom %d NBONA %d NBONH %d\n",
1704 nAtom, NBONA, NBONH ENDFB(G);
1705 }
1706
1707 switch (IFBOX) {
1708 case 2:
1709 cset->PeriodicBoxType = CoordSet::Octahedral;
1710 PRINTFB(G, FB_ObjectMolecule, FB_Details)
1711 " TOPStrToCoordSet: Warning: can't currently image a truncated octahedron...\n"
1712 ENDFB(G);
1713 break;
1714 case 1:
1715 cset->PeriodicBoxType = CoordSet::Orthogonal;
1716 break;
1717 case 0:
1718 default:
1719 cset->PeriodicBoxType = CoordSet::NoPeriodicity;
1720 break;
1721 }
1722
1723 p = nextline(p);
1724
1725 if(ok) {
1726 VLACheck(atInfo, AtomInfoType, nAtom);
1727
1728 if(amber7) {
1729 p = findflag(G, buffer, "ATOM_NAME", "20a4");
1730 }
1731 /* read atoms */
1732
1733 b = 0;
1734 for(a = 0; a < nAtom; a++) {
1735 p = ntrim(cc, p, 4);
1736 atInfo[a].name = LexIdx(G, cc);
1737 if((++b) == 20) {
1738 b = 0;
1739 p = nextline(p);
1740 }
1741 }
1742
1743 if(b)
1744 p = nextline(p);
1745
1746 if(!ok) {
1747 ErrMessage(G, "TOPStrToCoordSet", "Error reading atom names");
1748 } else {
1749 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1750 " TOPStr2CoordSet: read atom names.\n" ENDFB(G);
1751 }
1752
1753 /* read charges */
1754
1755 if(amber7) {
1756 p = findflag(G, buffer, "CHARGE", "5E16.8");
1757 }
1758
1759 b = 0;
1760 for(a = 0; a < nAtom; a++) {
1761 p = ncopy(cc, p, 16);
1762 ai = atInfo + a;
1763 if(!sscanf(cc, "%f", &ai->partialCharge))
1764 ok = false;
1765 else {
1766 ai->partialCharge /= 18.2223F; /* convert to electron charge */
1767 }
1768 if((++b) == 5) {
1769 b = 0;
1770 p = nextline(p);
1771 }
1772 }
1773
1774 if(!ok) {
1775 ErrMessage(G, "TOPStrToCoordSet", "Error reading charges");
1776 } else {
1777 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1778 " TOPStr2CoordSet: read charges.\n" ENDFB(G);
1779 }
1780 if(b)
1781 p = nextline(p);
1782
1783 if(!amber7) {
1784 /* skip masses */
1785
1786 p = skip_fortran(nAtom, 5, p);
1787 }
1788
1789 /* read LJ atom types */
1790
1791 if(amber7) {
1792 p = findflag(G, buffer, "ATOM_TYPE_INDEX", "10I8");
1793 col = 10;
1794 wid = 8;
1795 } else {
1796 col = 12;
1797 wid = 6;
1798 }
1799
1800 b = 0;
1801 for(a = 0; a < nAtom; a++) {
1802 p = ncopy(cc, p, wid);
1803 ai = atInfo + a;
1804 if(!sscanf(cc, "%d", &ai->customType))
1805 ok = false;
1806 if((++b) == col) {
1807 b = 0;
1808 p = nextline(p);
1809 }
1810 }
1811 if(b)
1812 p = nextline(p);
1813
1814 if(!ok) {
1815 ErrMessage(G, "TOPStrToCoordSet", "Error LJ atom types");
1816 } else {
1817 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1818 " TOPStr2CoordSet: read LJ atom types.\n" ENDFB(G);
1819 }
1820
1821 if(!amber7) {
1822 /* skip excluded atom counts */
1823
1824 p = skip_fortran(nAtom, 12, p);
1825
1826 /* skip NB param arrays */
1827
1828 p = skip_fortran(NTYPES * NTYPES, 12, p);
1829 }
1830
1831 /* read residue labels */
1832
1833 if(amber7) {
1834 p = findflag(G, buffer, "RESIDUE_LABEL", "20a4");
1835 }
1836
1837 resn = pymol::malloc<ResName>(NRES);
1838
1839 b = 0;
1840 for(a = 0; a < NRES; a++) {
1841 p = ncopy(cc, p, 4);
1842 if(!sscanf(cc, "%s", resn[a]))
1843 resn[a][0] = 0;
1844 if((++b) == 20) {
1845 b = 0;
1846 p = nextline(p);
1847 }
1848 }
1849 if(b)
1850 p = nextline(p);
1851
1852 if(!ok) {
1853 ErrMessage(G, "TOPStrToCoordSet", "Error reading residue labels");
1854 } else {
1855 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1856 " TOPStr2CoordSet: read residue labels.\n" ENDFB(G);
1857 }
1858
1859 /* read residue assignments */
1860
1861 if(amber7) {
1862 p = findflag(G, buffer, "RESIDUE_POINTER", "10I8");
1863 col = 10;
1864 wid = 8;
1865 } else {
1866 col = 12;
1867 wid = 6;
1868 }
1869
1870 b = 0;
1871 last_i = 0;
1872 rc = 0;
1873 for(a = 0; a < NRES; a++) {
1874 p = ncopy(cc, p, wid);
1875 if(sscanf(cc, "%d", &at_i)) {
1876 if(last_i)
1877 for(aa = (last_i - 1); aa < (at_i - 1); aa++) {
1878 ai = atInfo + aa;
1879 ai->resn = LexIdx(G, resn[a - 1]);
1880 ai->resv = rc;
1881 }
1882 rc++;
1883 last_i = at_i;
1884 }
1885 if((++b) == col) {
1886 b = 0;
1887 p = nextline(p);
1888 }
1889 }
1890 if(b)
1891 p = nextline(p);
1892 if(last_i)
1893 for(aa = (last_i - 1); aa < nAtom; aa++) {
1894 ai = atInfo + aa;
1895 ai->resn = LexIdx(G, resn[NRES - 1]);
1896 ai->resv = rc;
1897 }
1898 rc++;
1899
1900 if(!ok) {
1901 ErrMessage(G, "TOPStrToCoordSet", "Error reading residues");
1902 } else {
1903 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
1904 " TOPStr2CoordSet: read residues.\n" ENDFB(G);
1905 }
1906
1907 FreeP(resn);
1908
1909 if(!amber7) {
1910 /* skip bond force constants */
1911
1912 p = skip_fortran(NUMBND, 5, p);
1913
1914 /* skip bond lengths */
1915
1916 p = skip_fortran(NUMBND, 5, p);
1917
1918 /* skip angle force constant */
1919
1920 p = skip_fortran(NUMANG, 5, p);
1921
1922 /* skip angle eq */
1923
1924 p = skip_fortran(NUMANG, 5, p);
1925
1926 /* skip dihedral force constant */
1927
1928 p = skip_fortran(NPTRA, 5, p);
1929
1930 /* skip dihedral periodicity */
1931
1932 p = skip_fortran(NPTRA, 5, p);
1933
1934 /* skip dihedral phases */
1935
1936 p = skip_fortran(NPTRA, 5, p);
1937
1938 /* skip SOLTYs */
1939
1940 p = skip_fortran(NATYP, 5, p);
1941
1942 /* skip LJ terms r12 */
1943
1944 p = skip_fortran((NTYPES * (NTYPES + 1)) / 2, 5, p);
1945
1946 /* skip LJ terms r6 */
1947
1948 p = skip_fortran((NTYPES * (NTYPES + 1)) / 2, 5, p);
1949
1950 }
1951
1952 /* read bonds */
1953
1954 if(amber7) {
1955 p = findflag(G, buffer, "BONDS_INC_HYDROGEN", "10I8");
1956 col = 10;
1957 wid = 8;
1958 } else {
1959 col = 12;
1960 wid = 6;
1961 }
1962
1963 nBond = NBONH + NBONA;
1964
1965 bond = VLACalloc(BondType, nBond);
1966
1967 bi = 0;
1968
1969 b = 0;
1970 c = 0;
1971 i0 = 0;
1972 i1 = 0;
1973 for(a = 0; a < 3 * NBONH; a++) {
1974 p = ncopy(cc, p, wid);
1975 i2 = i1;
1976 i1 = i0;
1977 if(!sscanf(cc, "%d", &i0))
1978 ok = false;
1979 if((++c) == 3) {
1980 c = 0;
1981 bd = bond + bi;
1982 bd->index[0] = (abs(i2) / 3);
1983 bd->index[1] = (abs(i1) / 3);
1984 bd->order = 1;
1985 bd->stereo = 0;
1986 bd->id = bi + 1;
1987 bi++;
1988 }
1989 if((++b) == col) {
1990 b = 0;
1991 p = nextline(p);
1992 }
1993 }
1994 if(b)
1995 p = nextline(p);
1996
1997 if(!ok) {
1998 ErrMessage(G, "TOPStrToCoordSet", "Error hydrogen containing bonds");
1999 } else {
2000 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
2001 " TOPStr2CoordSet: read %d hydrogen containing bonds.\n", NBONH ENDFB(G);
2002 }
2003
2004 if(amber7) {
2005 p = findflag(G, buffer, "BONDS_WITHOUT_HYDROGEN", "10I8");
2006 col = 10;
2007 wid = 8;
2008 } else {
2009 col = 12;
2010 wid = 6;
2011 }
2012
2013 b = 0;
2014 c = 0;
2015 for(a = 0; a < 3 * NBONA; a++) {
2016 p = ncopy(cc, p, wid);
2017 i2 = i1;
2018 i1 = i0;
2019 if(!sscanf(cc, "%d", &i0))
2020 ok = false;
2021 if((++c) == 3) {
2022 c = 0;
2023 bd = bond + bi;
2024 bd->index[0] = (abs(i2) / 3);
2025 bd->index[1] = (abs(i1) / 3);
2026 bd->order = 1; // PYMOL-2707
2027 bd->stereo = 0;
2028 bd->id = bi + 1;
2029 bi++;
2030 }
2031 if((++b) == col) {
2032 b = 0;
2033 p = nextline(p);
2034 }
2035 }
2036 if(b)
2037 p = nextline(p);
2038
2039 if(!ok) {
2040 ErrMessage(G, "TOPStrToCoordSet", "Error hydrogen free bonds");
2041 } else {
2042 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
2043 " TOPStr2CoordSet: read %d hydrogen free bonds.\n", NBONA ENDFB(G);
2044 }
2045
2046 if(!amber7) {
2047
2048 /* skip hydrogen angles */
2049
2050 p = skip_fortran(4 * NTHETH, 12, p);
2051
2052 /* skip non-hydrogen angles */
2053
2054 p = skip_fortran(4 * NTHETA, 12, p);
2055
2056 /* skip hydrogen dihedrals */
2057
2058 p = skip_fortran(5 * NPHIH, 12, p);
2059
2060 /* skip non hydrogen dihedrals */
2061
2062 p = skip_fortran(5 * NPHIA, 12, p);
2063
2064 /* skip nonbonded exclusions */
2065
2066 p = skip_fortran(NNB, 12, p);
2067
2068 /* skip hydrogen bonds ASOL */
2069
2070 p = skip_fortran(NPHB, 5, p);
2071
2072 /* skip hydrogen bonds BSOL */
2073
2074 p = skip_fortran(NPHB, 5, p);
2075
2076 /* skip HBCUT */
2077
2078 p = skip_fortran(NPHB, 5, p);
2079
2080 }
2081 /* read AMBER atom types */
2082
2083 if(amber7) {
2084 p = findflag(G, buffer, "AMBER_ATOM_TYPE", "20a4");
2085 }
2086
2087 b = 0;
2088 for(a = 0; a < nAtom; a++) {
2089 OrthoLineType temp;
2090 p = ncopy(cc, p, 4);
2091 ai = atInfo + a;
2092 if(sscanf(cc, "%s", temp) != 1)
2093 ok = false;
2094 else {
2095 ai->textType = LexIdx(G, temp);
2096 }
2097 if((++b) == 20) {
2098 b = 0;
2099 p = nextline(p);
2100 }
2101 }
2102 if(b)
2103 p = nextline(p);
2104
2105 if(!ok) {
2106 ErrMessage(G, "TOPStrToCoordSet", "Error reading atom types");
2107 } else {
2108 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
2109 " TOPStr2CoordSet: read atom types.\n" ENDFB(G);
2110 }
2111
2112 if(!amber7) {
2113 /* skip TREE classification */
2114
2115 p = skip_fortran(nAtom, 20, p);
2116
2117 /* skip tree joining information */
2118
2119 p = skip_fortran(nAtom, 12, p);
2120
2121 /* skip last atom rotated blah blah blah */
2122
2123 p = skip_fortran(nAtom, 12, p);
2124
2125 }
2126
2127 if(IFBOX > 0) {
2128
2129 int IPTRES, NSPM = 0, NSPSOL;
2130
2131 if(amber7) {
2132 p = findflag(G, buffer, "SOLVENT_POINTERS", "3I8");
2133 wid = 8;
2134 } else {
2135 wid = 6;
2136 }
2137 p = ncopy(cc, p, wid);
2138 ok = ok && (sscanf(cc, "%d", &IPTRES) == 1);
2139 p = ncopy(cc, p, wid);
2140 ok = ok && (sscanf(cc, "%d", &NSPM) == 1);
2141 p = ncopy(cc, p, wid);
2142 ok = ok && (sscanf(cc, "%d", &NSPSOL) == 1);
2143
2144 ok_assert(1, ok);
2145
2146 p = nextline(p);
2147
2148 if(amber7) {
2149 p = findflag(G, buffer, "ATOMS_PER_MOLECULE", "10I8");
2150 col = 10;
2151 } else {
2152 col = 12;
2153 }
2154
2155 /* skip num atoms per box */
2156
2157 p = skip_fortran(NSPM, col, p);
2158
2159 if(amber7) {
2160 p = findflag(G, buffer, "BOX_DIMENSIONS", "5E16.8");
2161 }
2162 wid = 16;
2163
2164 p = ncopy(cc, p, 16);
2165 ok = ok && (sscanf(cc, "%f", &BETA) == 1);
2166 p = ncopy(cc, p, 16);
2167 ok = ok && (sscanf(cc, "%f", &BOX1) == 1);
2168 p = ncopy(cc, p, 16);
2169 ok = ok && (sscanf(cc, "%f", &BOX2) == 1);
2170 p = ncopy(cc, p, 16);
2171 ok = ok && (sscanf(cc, "%f", &BOX3) == 1);
2172
2173 if(ok) {
2174 if(!cset->PeriodicBox)
2175 cset->PeriodicBox = pymol::make_unique<CCrystal>(G);
2176 cset->PeriodicBox->Dim[0] = BOX1;
2177 cset->PeriodicBox->Dim[1] = BOX2;
2178 cset->PeriodicBox->Dim[2] = BOX3;
2179 if((BETA > 109.47) && (BETA < 109.48)) {
2180 cset->PeriodicBoxType = CoordSet::Octahedral;
2181 cset->PeriodicBox->Angle[0] =
2182 (float) (2.0 * acos(1.0 / sqrt(3.0)) * 180.0 / PI);
2183 cset->PeriodicBox->Angle[1] =
2184 (float) (2.0 * acos(1.0 / sqrt(3.0)) * 180.0 / PI);
2185 cset->PeriodicBox->Angle[2] =
2186 (float) (2.0 * acos(1.0 / sqrt(3.0)) * 180.0 / PI);
2187 } else if(BETA == 60.0) {
2188 cset->PeriodicBox->Angle[0] = 60.0; /* rhombic dodecahedron (from ptraj.c) */
2189 cset->PeriodicBox->Angle[1] = 90.0;
2190 cset->PeriodicBox->Angle[2] = 60.0;
2191 } else {
2192 cset->PeriodicBox->Angle[0] = 90.0;
2193 cset->PeriodicBox->Angle[1] = BETA;
2194 cset->PeriodicBox->Angle[2] = 90.0;
2195 }
2196 CrystalUpdate(cset->PeriodicBox.get());
2197 /* CrystalDump(cset->PeriodicBox); */
2198 }
2199 /* skip periodic box */
2200
2201 p = nextline(p);
2202
2203 }
2204
2205 if(!amber7) {
2206
2207 if(IFCAP > 0) {
2208 p = nextline(p);
2209 p = nextline(p);
2210 p = nextline(p);
2211 }
2212
2213 if(IFPERT > 0) {
2214
2215 /* skip perturbed bond atoms */
2216
2217 p = skip_fortran(2 * NBPER, 12, p);
2218
2219 /* skip perturbed bond atom pointers */
2220
2221 p = skip_fortran(2 * NBPER, 12, p);
2222
2223 /* skip perturbed angles */
2224
2225 p = skip_fortran(3 * NGPER, 12, p);
2226
2227 /* skip perturbed angle pointers */
2228
2229 p = skip_fortran(2 * NGPER, 12, p);
2230
2231 /* skip perturbed dihedrals */
2232
2233 p = skip_fortran(4 * NDPER, 12, p);
2234
2235 /* skip perturbed dihedral pointers */
2236
2237 p = skip_fortran(2 * NDPER, 12, p);
2238
2239 /* skip residue names */
2240
2241 p = skip_fortran(NRES, 20, p);
2242
2243 /* skip atom names */
2244
2245 p = skip_fortran(nAtom, 20, p);
2246
2247 /* skip atom symbols */
2248
2249 p = skip_fortran(nAtom, 20, p);
2250
2251 /* skip unused field */
2252
2253 p = skip_fortran(nAtom, 5, p);
2254
2255 /* skip perturbed flags */
2256
2257 p = skip_fortran(nAtom, 12, p);
2258
2259 /* skip LJ atom flags */
2260
2261 p = skip_fortran(nAtom, 12, p);
2262
2263 /* skip perturbed charges */
2264
2265 p = skip_fortran(nAtom, 5, p);
2266
2267 }
2268
2269 if(IPOL > 0) {
2270
2271 /* skip atomic polarizabilities */
2272
2273 p = skip_fortran(nAtom, 5, p);
2274
2275 }
2276
2277 if((IPOL > 0) && (IFPERT > 0)) {
2278
2279 /* skip atomic polarizabilities */
2280
2281 p = skip_fortran(nAtom, 5, p);
2282
2283 }
2284 }
2285 /* for future reference
2286
2287 %FLAG LES_NTYP
2288 %FORMAT(10I8)
2289 %FLAG LES_TYPE
2290 %FORMAT(10I8)
2291 %FLAG LES_FAC
2292 %FORMAT(5E16.8)
2293 %FLAG LES_CNUM
2294 %FORMAT(10I8)
2295 %FLAG LES_ID
2296 %FORMAT(10I8)
2297
2298 Here is the additional information for LES topology formats:
2299 First, if NPARM ==1, LES entries are in topology (NPARM is the 10th
2300 entry in the initial list of control parameters); otherwise the standard
2301 format applies.
2302 So, with NPARM=1, you just need to read a few more things at the very
2303 end of topology file:
2304 LES_NTYP (format: I6) ... one number, number of LES types
2305 and four arrays:
2306 LES_TYPE (12I6) ... NATOM integer entries
2307 LES_FAC (E16.8) ... LES_NTYPxLES_NTYP float entries
2308 LES_CNUM (12I6) ... NATOM integer entries
2309 LES_ID (12I6) ... NATOM integer entries
2310
2311 and that's it. Your parser must have skipped this information because it
2312 was at the end of the file. Maybe that's good enough.
2313
2314 */
2315
2316 coord = VLAlloc(float, 3 * nAtom);
2317
2318 f = coord;
2319 for(a = 0; a < nAtom; a++) {
2320 *(f++) = 0.0;
2321 *(f++) = 0.0;
2322 *(f++) = 0.0;
2323 ai = atInfo + a;
2324 ai->id = a + 1; /* assign 1-based identifiers */
2325 ai->rank = a;
2326 AtomInfoAssignParameters(G, ai);
2327 AtomInfoAssignColors(G, ai);
2328 ai->visRep = auto_show;
2329 }
2330 }
2331 if(ok) {
2332 cset->NIndex = nAtom;
2333 cset->Coord = pymol::vla_take_ownership(coord);
2334 cset->TmpBond = pymol::vla_take_ownership(bond);
2335 cset->NTmpBond = nBond;
2336 } else {
2337 ok_except1:
2338 if(cset)
2339 cset->fFree();
2340 cset = NULL;
2341 ErrMessage(G, __func__, "failed");
2342 }
2343 if(atInfoPtr)
2344 *atInfoPtr = atInfo;
2345
2346 return (cset);
2347 }
2348
2349
2350 /*========================================================================*/
ObjectMoleculeReadTOPStr(PyMOLGlobals * G,ObjectMolecule * I,char * TOPStr,int frame,int discrete)2351 static ObjectMolecule *ObjectMoleculeReadTOPStr(PyMOLGlobals * G, ObjectMolecule * I,
2352 char *TOPStr, int frame, int discrete)
2353 {
2354 CoordSet *cset = NULL;
2355 pymol::vla<AtomInfoType> atInfo(1);
2356 int ok = true;
2357 int isNew = true;
2358 unsigned int nAtom = 0;
2359
2360 if(!I)
2361 isNew = true;
2362 else
2363 isNew = false;
2364
2365 if(ok) {
2366 if(isNew) {
2367 I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
2368 CHECKOK(ok, I);
2369 if (ok)
2370 std::swap(atInfo, I->AtomInfo);
2371 }
2372 if(ok && isNew) {
2373 I->Color = AtomInfoUpdateAutoColor(G);
2374 }
2375
2376 if (ok)
2377 cset = ObjectMoleculeTOPStr2CoordSet(G, TOPStr, &atInfo);
2378 CHECKOK(ok, cset);
2379 }
2380
2381 /* include coordinate set */
2382 if(ok) {
2383 nAtom = cset->NIndex;
2384
2385 if(I->DiscreteFlag && atInfo) {
2386 unsigned int a;
2387 int fp1 = frame + 1;
2388 AtomInfoType *ai = atInfo.data();
2389 for(a = 0; a < nAtom; a++) {
2390 (ai++)->discrete_state = fp1;
2391 }
2392 }
2393
2394 cset->Obj = I;
2395 cset->enumIndices();
2396 cset->invalidateRep(cRepAll, cRepInvRep);
2397 if(isNew) {
2398 std::swap(I->AtomInfo, atInfo);
2399 } else if (ok){
2400 ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_AllMask, true);
2401 }
2402 if(isNew)
2403 I->NAtom = nAtom;
2404 /*
2405 if(frame<0) frame=I->NCSet;
2406 VLACheck(I->CSet,CoordSet*,frame);
2407 if(I->NCSet<=frame) I->NCSet=frame+1;
2408 if(I->CSet[frame]) I->CSet[frame]->fFree(I->CSet[frame]);
2409 I->CSet[frame] = cset;
2410 */
2411
2412 if(ok && isNew)
2413 ok &= ObjectMoleculeConnect(I, cset, false);
2414 if(cset->Symmetry && (!I->Symmetry)) {
2415 I->Symmetry = new CSymmetry(*cset->Symmetry);
2416 CHECKOK(ok, I->Symmetry);
2417 if (ok)
2418 SymmetryUpdate(I->Symmetry);
2419 }
2420
2421 if(I->CSTmpl)
2422 I->CSTmpl->fFree();
2423 I->CSTmpl = cset; /* save template coordinate set */
2424
2425 SceneCountFrames(G);
2426 if (ok)
2427 ok &= ObjectMoleculeExtendIndices(I, -1);
2428 if (ok)
2429 ok &= ObjectMoleculeSort(I);
2430 if (ok){
2431 ObjectMoleculeUpdateIDNumbers(I);
2432 ObjectMoleculeUpdateNonbonded(I);
2433 }
2434 }
2435 if (!ok){
2436 DeleteP(I)
2437 }
2438 return (I);
2439 }
2440
ObjectMoleculeLoadTOPFile(PyMOLGlobals * G,ObjectMolecule * obj,const char * fname,int frame,int discrete)2441 ObjectMolecule *ObjectMoleculeLoadTOPFile(PyMOLGlobals * G, ObjectMolecule * obj,
2442 const char *fname, int frame, int discrete)
2443 {
2444 ObjectMolecule *I = NULL;
2445 char *buffer;
2446
2447 buffer = FileGetContents(fname, NULL);
2448
2449 if(!buffer)
2450 ErrMessage(G, __func__, "Unable to open file!");
2451 else {
2452 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
2453 " %s: Loading from %s.\n", __func__, fname ENDFB(G);
2454
2455 I = ObjectMoleculeReadTOPStr(G, obj, buffer, frame, discrete);
2456 mfree(buffer);
2457 }
2458
2459 return (I);
2460 }
2461
ObjectMoleculeSculptClear(ObjectMolecule * I)2462 void ObjectMoleculeSculptClear(ObjectMolecule * I)
2463 {
2464 PRINTFD(I->G, FB_ObjectMolecule)
2465 " %s: entered.\n", __func__ ENDFD;
2466 if(I->Sculpt)
2467 DeleteP(I->Sculpt);
2468 }
2469
ObjectMoleculeSculptImprint(ObjectMolecule * I,int state,int match_state,int match_by_segment)2470 void ObjectMoleculeSculptImprint(ObjectMolecule * I, int state, int match_state,
2471 int match_by_segment)
2472 {
2473 PRINTFD(I->G, FB_ObjectMolecule)
2474 " %s: entered.\n", __func__ ENDFD;
2475
2476 if(!I->Sculpt)
2477 I->Sculpt = new CSculpt(I->G);
2478 SculptMeasureObject(I->Sculpt, I, state, match_state, match_by_segment);
2479 }
2480
ObjectMoleculeSculptIterate(ObjectMolecule * I,int state,int n_cycle,float * center)2481 float ObjectMoleculeSculptIterate(ObjectMolecule * I, int state, int n_cycle,
2482 float *center)
2483 {
2484 PRINTFD(I->G, FB_ObjectMolecule)
2485 " %s: entered.\n", __func__ ENDFD;
2486 if(I->Sculpt) {
2487 return SculptIterateObject(I->Sculpt, I, state, n_cycle, center);
2488 } else
2489 return 0.0F;
2490 }
2491
2492 /*
2493 * - Assigns new id to all atoms with AtomInfoType.id == -1
2494 * - Assigns new id to all bonds with BondType.id == -1
2495 * - Assigns ObjectMolecule.AtomCounter if -1
2496 * - Assigns ObjectMolecule.BondCounter if -1
2497 *
2498 * Cost: O(NAtom + NBond)
2499 */
ObjectMoleculeUpdateIDNumbers(ObjectMolecule * I)2500 void ObjectMoleculeUpdateIDNumbers(ObjectMolecule * I)
2501 {
2502 int a;
2503 int max;
2504 AtomInfoType *ai;
2505 BondType *b;
2506
2507 if(I->AtomCounter < 0) {
2508 max = -1;
2509 ai = I->AtomInfo.data();
2510 for(a = 0; a < I->NAtom; a++) {
2511 if(ai->id > max)
2512 max = ai->id;
2513 ai++;
2514 }
2515 I->AtomCounter = max + 1;
2516 }
2517 ai = I->AtomInfo.data();
2518 for(a = 0; a < I->NAtom; a++) {
2519 if(ai->id < 0)
2520 ai->id = I->AtomCounter++;
2521 ai++;
2522 }
2523
2524 if(I->BondCounter < 0) {
2525 max = -1;
2526 b = I->Bond.data();
2527 for(a = 0; a < I->NBond; a++) {
2528 if(b->id > max)
2529 max = b->id;
2530 b++;
2531 }
2532 I->BondCounter = max + 1;
2533 }
2534 b = I->Bond.data();
2535 for(a = 0; a < I->NBond; a++) {
2536 if(!b->id)
2537 b->id = I->BondCounter++;
2538 b++;
2539 }
2540 }
2541
2542 /*========================================================================*/
ObjectMoleculeGetPhiPsi(ObjectMolecule * I,int ca,float * phi,float * psi,int state)2543 int ObjectMoleculeGetPhiPsi(ObjectMolecule * I, int ca, float *phi, float *psi, int state)
2544 {
2545 int np = -1;
2546 int cm = -1;
2547 int c = -1;
2548 int n = -1;
2549 int result = false;
2550 const AtomInfoType *ai;
2551 int n0, at;
2552 float v_ca[3];
2553 float v_n[3];
2554 float v_c[3];
2555 float v_cm[3];
2556 float v_np[3];
2557 auto G = I->G;
2558
2559 ai = I->AtomInfo;
2560
2561 if(ai[ca].name == G->lex_const.CA) {
2562 ObjectMoleculeUpdateNeighbors(I);
2563
2564 /* find C */
2565 n0 = I->Neighbor[ca] + 1;
2566 while(I->Neighbor[n0] >= 0) {
2567 at = I->Neighbor[n0];
2568 if(ai[at].name == G->lex_const.C) {
2569 c = at;
2570 break;
2571 }
2572 n0 += 2;
2573 }
2574
2575 /* find N */
2576 n0 = I->Neighbor[ca] + 1;
2577 while(I->Neighbor[n0] >= 0) {
2578 at = I->Neighbor[n0];
2579 if(ai[at].name == G->lex_const.N) {
2580 n = at;
2581 break;
2582 }
2583 n0 += 2;
2584 }
2585
2586 /* find NP */
2587 if(c >= 0) {
2588 n0 = I->Neighbor[c] + 1;
2589 while(I->Neighbor[n0] >= 0) {
2590 at = I->Neighbor[n0];
2591 if(ai[at].name == G->lex_const.N) {
2592 np = at;
2593 break;
2594 }
2595 n0 += 2;
2596 }
2597 }
2598
2599 /* find CM */
2600 if(n >= 0) {
2601 n0 = I->Neighbor[n] + 1;
2602 while(I->Neighbor[n0] >= 0) {
2603 at = I->Neighbor[n0];
2604 if(ai[at].name == G->lex_const.C) {
2605 cm = at;
2606 break;
2607 }
2608 n0 += 2;
2609 }
2610 }
2611 if((ca >= 0) && (np >= 0) && (c >= 0) && (n >= 0) && (cm >= 0)) {
2612 if(ObjectMoleculeGetAtomVertex(I, state, ca, v_ca) &&
2613 ObjectMoleculeGetAtomVertex(I, state, n, v_n) &&
2614 ObjectMoleculeGetAtomVertex(I, state, c, v_c) &&
2615 ObjectMoleculeGetAtomVertex(I, state, cm, v_cm) &&
2616 ObjectMoleculeGetAtomVertex(I, state, np, v_np)) {
2617
2618 (*phi) = rad_to_deg(get_dihedral3f(v_c, v_ca, v_n, v_cm));
2619 (*psi) = rad_to_deg(get_dihedral3f(v_np, v_c, v_ca, v_n));
2620 result = true;
2621 }
2622 }
2623 }
2624 return (result);
2625 }
2626
2627
2628 /*========================================================================*/
ObjectMoleculeCheckBondSep(ObjectMolecule * I,int a0,int a1,int dist)2629 int ObjectMoleculeCheckBondSep(ObjectMolecule * I, int a0, int a1, int dist)
2630 {
2631 int result = false;
2632 int n0;
2633 int stack[MAX_BOND_DIST + 1];
2634 int history[MAX_BOND_DIST + 1];
2635 int depth = 0;
2636 int distinct;
2637 int a;
2638 if(dist > MAX_BOND_DIST)
2639 return false;
2640
2641 /* NOTE: undealtwith crash log: fix this!
2642
2643 0 com.delsci.macpymol 0x00135590 ObjectMoleculeCheckBondSep + 304 (crt.c:355)
2644 1 <<00000000>> 0x00000002 0 + 2
2645 2 com.delsci.macpymol 0x001acb58 RepCartoonNew + 6936 (crt.c:355)
2646 3 com.delsci.macpymol 0x001009f4 CoordSetUpdate + 1108 (crt.c:355)
2647 4 com.delsci.macpymol 0x001f061c CmdCoordSetUpdateThread + 108 (crt.c:355)
2648
2649 presumably a race condition with UpdateNeighbors, which need to be mutexed somehow.
2650
2651 */
2652
2653 ObjectMoleculeUpdateNeighbors(I);
2654
2655 PRINTFD(I->G, FB_ObjectMolecule)
2656 " CBS-Debug: %s %d %d %d\n", I->Name, a0, a1, dist ENDFD;
2657 depth = 1;
2658 history[depth] = a0;
2659 stack[depth] = I->Neighbor[a0] + 1; /* go to first neighbor */
2660 while(depth) { /* keep going until we've traversed tree */
2661 while(I->Neighbor[stack[depth]] >= 0) { /* end of branches? go back up one bond */
2662 n0 = I->Neighbor[stack[depth]]; /* get current neighbor index */
2663 stack[depth] += 2; /* set up next neighbor */
2664 distinct = true; /* check to see if current candidate is distinct from ancestors */
2665 for(a = 1; a < depth; a++) {
2666 if(history[a] == n0)
2667 distinct = false;
2668 }
2669 if(distinct) {
2670 if(depth < dist) { /* are not yet at the proper distance? */
2671 if(distinct) {
2672 depth++;
2673 stack[depth] = I->Neighbor[n0] + 1; /* then keep moving outward */
2674 history[depth] = n0;
2675 }
2676 } else if(n0 == a1) /* otherwise, see if we have a match */
2677 result = true;
2678 }
2679 }
2680 depth--;
2681 }
2682 PRINTFD(I->G, FB_ObjectMolecule)
2683 " CBS-Debug: result %d\n", result ENDFD;
2684 return result;
2685 }
2686
2687
2688 /*========================================================================*/
ObjectGotoState(CObject * I,int state)2689 void ObjectGotoState(CObject* I, int state)
2690 {
2691 auto nstates = I->getNFrame();
2692 if (nstates > 1 || !SettingGet<bool>(I->G, cSetting_static_singletons)) {
2693 if(state > nstates)
2694 state = nstates - 1;
2695 if(state < 0)
2696 state = nstates - 1;
2697 SceneSetFrame(I->G, 0, state);
2698 }
2699 }
2700
2701
2702 /*========================================================================*/
_getObjectState(int state)2703 CObjectState* ObjectMolecule::_getObjectState(int state)
2704 {
2705 return CSet[state];
2706 }
2707
2708
2709 /*========================================================================*/
getSettingHandle(int state)2710 CSetting **ObjectMolecule::getSettingHandle(int state)
2711 {
2712 auto I = this;
2713
2714 if (state < -1) {
2715 state = I->getCurrentState();
2716 }
2717
2718 if(state < 0) {
2719 return (&I->Setting);
2720 } else if(state < I->NCSet) {
2721 if(I->CSet[state]) {
2722 return (&I->CSet[state]->Setting);
2723 } else {
2724 return (NULL);
2725 }
2726 } else {
2727 return (NULL);
2728 }
2729 }
2730
2731 /*========================================================================*/
setSymmetry(CSymmetry const & symmetry,int state)2732 bool ObjectMolecule::setSymmetry(CSymmetry const& symmetry, int state)
2733 {
2734 delete Symmetry;
2735 Symmetry = new CSymmetry(symmetry);
2736 CGOFree(UnitCellCGO);
2737 return true;
2738 }
2739
2740 /*========================================================================*/
ObjectMoleculeSetStateTitle(ObjectMolecule * I,int state,const char * text)2741 pymol::Result<> ObjectMoleculeSetStateTitle(ObjectMolecule * I, int state, const char *text)
2742 {
2743 auto cs = I->getCoordSet(state);
2744 if (!cs) {
2745 return pymol::make_error("Invalid state ", state + 1);
2746 }
2747 UtilNCopy(cs->Name, text, sizeof(WordType));
2748 return {};
2749 }
2750
2751
2752 /*========================================================================*/
ObjectMoleculeGetStateTitle(ObjectMolecule * I,int state)2753 const char *ObjectMoleculeGetStateTitle(ObjectMolecule * I, int state)
2754 {
2755 auto cs = I->getCoordSet(state);
2756 if (!cs) {
2757 PRINTFB(I->G, FB_ObjectMolecule, FB_Errors)
2758 "Error: invalid state %d\n", state + 1 ENDFB(I->G);
2759 return nullptr;
2760 }
2761 return cs->Name;
2762 }
2763
2764
2765 /*========================================================================*/
ObjectMoleculeRenderSele(ObjectMolecule * I,int curState,int sele,int vis_only SELINDICATORARG)2766 void ObjectMoleculeRenderSele(ObjectMolecule * I, int curState, int sele, int vis_only SELINDICATORARG)
2767 {
2768
2769 PyMOLGlobals *G = I->G;
2770 CoordSet *cs;
2771 int a, nIndex;
2772 const int* idx2atm;
2773 const float *coord, *v;
2774 int flag = true;
2775 int all_vis = !vis_only;
2776 int visRep;
2777 float tmp_matrix[16], v_tmp[3], *matrix = NULL;
2778 int use_matrices =
2779 SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
2780
2781 if(use_matrices<0) use_matrices = 0;
2782
2783 if (SettingGetIfDefined_i(G, I->Setting, cSetting_all_states, &a)) {
2784 curState = a ? -1 : SettingGet_i(G, I->Setting, NULL, cSetting_state);
2785 } else if (SettingGetIfDefined_i(G, I->Setting, cSetting_state, &a)) {
2786 curState = a - 1;
2787 }
2788
2789 if(G->HaveGUI && G->ValidContext) {
2790 const AtomInfoType *atInfo = I->AtomInfo.data();
2791
2792 for(StateIterator iter(G, I->Setting, curState, I->NCSet);
2793 iter.next();) {
2794 if((cs = I->CSet[iter.state])) {
2795 idx2atm = cs->IdxToAtm;
2796 nIndex = cs->NIndex;
2797 coord = cs->Coord;
2798 if(use_matrices && !cs->Matrix.empty()) {
2799 copy44d44f(cs->Matrix.data(), tmp_matrix);
2800 matrix = tmp_matrix;
2801 } else
2802 matrix = NULL;
2803
2804 if(I->TTTFlag) {
2805 if(!matrix) {
2806 convertTTTfR44f(I->TTT, tmp_matrix);
2807 } else {
2808 float ttt[16];
2809 convertTTTfR44f(I->TTT, ttt);
2810 left_multiply44f44f(ttt, tmp_matrix);
2811 }
2812 matrix = tmp_matrix;
2813 }
2814
2815 for(a = 0; a < nIndex; a++) {
2816 if(SelectorIsMember(G, atInfo[*(idx2atm++)].selEntry, sele)) {
2817 if(all_vis)
2818 flag = true;
2819 else {
2820 visRep = atInfo[idx2atm[-1]].visRep;
2821 flag = false;
2822 if(visRep & (cRepCylBit | cRepSphereBit | cRepSurfaceBit |
2823 cRepLabelBit | cRepNonbondedSphereBit | cRepCartoonBit |
2824 cRepRibbonBit | cRepLineBit | cRepMeshBit |
2825 cRepDotBit | cRepNonbondedBit)){
2826 flag = true;
2827 }
2828 }
2829 if(flag) {
2830 v = coord + a + a + a;
2831 if(matrix) {
2832 transform44f3f(matrix, v, v_tmp);
2833 if (SELINDICATORVAR)
2834 CGOVertexv(SELINDICATORVAR, v_tmp);
2835 else
2836 glVertex3fv(v_tmp);
2837 } else {
2838 if (SELINDICATORVAR)
2839 CGOVertexv(SELINDICATORVAR, v);
2840 else
2841 glVertex3fv(v);
2842 }
2843 }
2844 }
2845 }
2846 }
2847 }
2848 }
2849 }
2850
2851 /*========================================================================*/
ObjectMoleculeXYZStr2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr,const char ** restart)2852 static CoordSet *ObjectMoleculeXYZStr2CoordSet(PyMOLGlobals * G, const char *buffer,
2853 AtomInfoType ** atInfoPtr, const char **restart)
2854 {
2855 const char *p, *p_store;
2856 int nAtom;
2857 int a, c;
2858 float *coord = NULL;
2859 CoordSet *cset = NULL;
2860 AtomInfoType *atInfo = NULL, *ai;
2861 char cc[MAXLINELEN];
2862 int atomCount;
2863 BondType *bond = NULL;
2864 int nBond = 0;
2865 int b1, b2;
2866 WordType tmp_name;
2867 int auto_show = RepGetAutoShowMask(G);
2868 int tinker_xyz = true;
2869 int valid_atom;
2870 int have_n_atom = false;
2871 BondType *ii;
2872
2873 p = buffer;
2874 nAtom = 0;
2875 atInfo = *atInfoPtr;
2876
2877 p_store = p;
2878 p = ncopy(cc, p, MAXLINELEN - 1);
2879 if(sscanf(cc, "%d", &nAtom) != 1) {
2880 nAtom = 0;
2881 tinker_xyz = false;
2882 p = p_store;
2883 } else {
2884 have_n_atom = true;
2885 p = nskip(p, 2);
2886 p = ncopy(tmp_name, p, sizeof(WordType) - 1);
2887 p = nextline(p);
2888 }
2889
2890 if(tinker_xyz && nAtom) { /* test Tinker XYZ formatting assumption */
2891 const char *pp = p;
2892 int dummy_int;
2893 float dummy_float;
2894 AtomName dummy_name;
2895
2896 pp = ncopy(cc, pp, 6);
2897 if(!sscanf(cc, "%d", &dummy_int))
2898 tinker_xyz = false; /* id */
2899 pp = nskip(pp, 2);
2900 pp = ncopy(cc, pp, 3);
2901 if(sscanf(cc, "%s", dummy_name) != 1)
2902 tinker_xyz = false; /* name */
2903 pp = ncopy(cc, pp, 12);
2904 if(sscanf(cc, "%f", &dummy_float) != 1)
2905 tinker_xyz = false; /* x */
2906 pp = ncopy(cc, pp, 12);
2907 if(sscanf(cc, "%f", &dummy_float) != 1)
2908 tinker_xyz = false; /* y */
2909 pp = ncopy(cc, pp, 12);
2910 if(sscanf(cc, "%f", &dummy_float) != 1)
2911 tinker_xyz = false; /* z */
2912 pp = ncopy(cc, pp, 6);
2913 if(sscanf(cc, "%d", &dummy_int) != 1)
2914 tinker_xyz = false; /* numeric type */
2915 }
2916
2917 if(!tinker_xyz) {
2918 const char *pp = p;
2919 int have_atom_line = true;
2920 float dummy_float;
2921 AtomName dummy_name;
2922
2923 pp = ParseWordCopy(cc, pp, sizeof(AtomName) - 1);
2924 if(sscanf(cc, "%s", dummy_name) != 1)
2925 have_atom_line = false; /* name */
2926 pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
2927 if(sscanf(cc, "%f", &dummy_float) != 1)
2928 have_atom_line = false; /* x */
2929 pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
2930 if(sscanf(cc, "%f", &dummy_float) != 1)
2931 have_atom_line = false; /* y */
2932 pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
2933 if(sscanf(cc, "%f", &dummy_float) != 1)
2934 have_atom_line = false; /* z */
2935 if(!have_atom_line) { /* copy the comment line into the title field */
2936 p = ncopy(tmp_name, p, sizeof(WordType) - 1);
2937 p = nextline(p);
2938 }
2939 }
2940
2941 if(nAtom) {
2942 coord = VLAlloc(float, 3 * nAtom);
2943 if(atInfo)
2944 VLACheck(atInfo, AtomInfoType, nAtom);
2945 } else {
2946 coord = VLAlloc(float, 3);
2947 }
2948
2949 if(tinker_xyz) {
2950 nBond = 0;
2951 bond = VLACalloc(BondType, 6 * nAtom); /* is this a safe assumption? */
2952 ii = bond;
2953 }
2954
2955 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
2956 " ObjectMoleculeReadXYZ: Found %i atoms...\n", nAtom ENDFB(G);
2957
2958 a = 0;
2959 atomCount = 0;
2960 while(*p) {
2961 VLACheck(atInfo, AtomInfoType, atomCount);
2962 ai = atInfo + atomCount;
2963
2964 if(!tinker_xyz) {
2965 valid_atom = true;
2966
2967 p = ParseWordCopy(cc, p, MAXLINELEN - 1);
2968 UtilCleanStr(cc);
2969 if(!cc[0])
2970 valid_atom = false;
2971 if(valid_atom) {
2972 strncpy(ai->elem, cc, cElemNameLen);
2973 ai->name = LexIdx(G, cc);
2974
2975 ai->rank = atomCount;
2976 ai->id = atomCount + 1;
2977
2978 VLACheck(coord, float, a * 3 + 2);
2979 p = ParseWordCopy(cc, p, MAXLINELEN - 1);
2980 if(sscanf(cc, "%f", coord + a) != 1)
2981 valid_atom = false;
2982 p = ParseWordCopy(cc, p, MAXLINELEN - 1);
2983 if(sscanf(cc, "%f", coord + a + 1) != 1)
2984 valid_atom = false;
2985 p = ParseWordCopy(cc, p, MAXLINELEN - 1);
2986 if(sscanf(cc, "%f", coord + a + 2) != 1)
2987 valid_atom = false;
2988
2989 ai->resn = LexIdx(G, "UNK");
2990
2991 ai->alt[0] = 0;
2992 ai->chain = 0;
2993 ai->resv = atomCount + 1;
2994
2995 ai->q = 1.0;
2996 ai->b = 0.0;
2997
2998 ai->segi = 0;
2999
3000 ai->visRep = auto_show;
3001
3002 /* in the absense of external tinker information, assume hetatm */
3003
3004 ai->hetatm = 1;
3005
3006 AtomInfoAssignParameters(G, ai);
3007 AtomInfoAssignColors(G, ai);
3008 }
3009 } else { /* tinker XYZ */
3010
3011 valid_atom = true;
3012
3013 p = ncopy(cc, p, 6);
3014 if(!sscanf(cc, "%d", &ai->id))
3015 break;
3016 ai->rank = atomCount;
3017
3018 p = nskip(p, 2); /* to 12 */
3019 p = ntrim(cc, p, 3);
3020 ai->name = LexIdx(G, cc);
3021
3022 ai->resn = LexIdx(G, "UNK");
3023
3024 ai->alt[0] = 0;
3025 ai->chain = 0;
3026
3027 ai->resv = atomCount + 1;
3028
3029 valid_atom = true;
3030
3031 p = ncopy(cc, p, 12);
3032 sscanf(cc, "%f", coord + a);
3033 p = ncopy(cc, p, 12);
3034 sscanf(cc, "%f", coord + (a + 1));
3035 p = ncopy(cc, p, 12);
3036 sscanf(cc, "%f", coord + (a + 2));
3037
3038 ai->q = 1.0;
3039 ai->b = 0.0;
3040
3041 ai->segi = 0;
3042 ai->elem[0] = 0; /* let atom info guess/infer atom type */
3043
3044 ai->visRep = auto_show;
3045
3046 p = ncopy(cc, p, 6);
3047 sscanf(cc, "%d", &ai->customType);
3048
3049 /* in the absense of external tinker information, assume hetatm */
3050
3051 ai->hetatm = 1;
3052
3053 AtomInfoAssignParameters(G, ai);
3054 AtomInfoAssignColors(G, ai);
3055
3056 b1 = atomCount;
3057 for(c = 0; c < 6; c++) {
3058 p = ncopy(cc, p, 6);
3059 if(!cc[0])
3060 break;
3061 if(!sscanf(cc, "%d", &b2))
3062 break;
3063 if(b1 < (b2 - 1)) {
3064 VLACheck(bond, BondType, nBond);
3065 ii = bond + nBond;
3066 nBond++;
3067 ii->index[0] = b1;
3068 ii->index[1] = b2 - 1;
3069 ii->order = 1; /* missing bond order information */
3070 ii->stereo = 0;
3071 ii->id = -1; /* no serial number */
3072 ii++;
3073 }
3074 }
3075 }
3076
3077 if(valid_atom) {
3078 PRINTFD(G, FB_ObjectMolecule)
3079 " ObjectMolecule-DEBUG: %s %s %d %s %8.3f %8.3f %8.3f %6.2f %6.2f %s\n",
3080 LexStr(G, ai->name), LexStr(G, ai->resn), ai->resv, LexStr(G, ai->chain),
3081 *(coord + a), *(coord + a + 1), *(coord + a + 2), ai->b, ai->q, LexStr(G, ai->segi) ENDFD;
3082
3083 a += 3;
3084 atomCount++;
3085
3086 }
3087 p = nextline(p);
3088 if(have_n_atom && (atomCount >= nAtom)) {
3089 int dummy;
3090 ncopy(cc, p, MAXLINELEN - 1);
3091 if(sscanf(cc, "%d", &dummy) == 1)
3092 *restart = p;
3093 break;
3094 }
3095 }
3096
3097 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
3098 " XYZStr2CoordSet: Read %d bonds.\n", nBond ENDFB(G);
3099
3100 if(!tinker_xyz)
3101 nAtom = atomCount; /* use number of atoms actually read */
3102
3103 cset = CoordSetNew(G);
3104 cset->NIndex = nAtom;
3105 cset->Coord = pymol::vla_take_ownership(coord);
3106 cset->TmpBond = pymol::vla_take_ownership(bond);
3107 cset->NTmpBond = nBond;
3108 strcpy(cset->Name, tmp_name);
3109 if(atInfoPtr)
3110 *atInfoPtr = atInfo;
3111 return (cset);
3112 }
3113
3114
3115 /*========================================================================*/
ObjectMoleculeAreAtomsBonded(ObjectMolecule * I,int i0,int i1)3116 int ObjectMoleculeAreAtomsBonded(ObjectMolecule * I, int i0, int i1)
3117 {
3118 int result = false;
3119 int a;
3120 const BondType *b;
3121 b = I->Bond;
3122 for(a = 0; a < I->NBond; a++) {
3123 if(i0 == b->index[0]) {
3124 if(i1 == b->index[1]) {
3125 result = true;
3126 break;
3127 }
3128 }
3129 if(i1 == b->index[0]) {
3130 if(i0 == b->index[1]) {
3131 result = true;
3132 break;
3133 }
3134 }
3135 b++;
3136 }
3137 return (result);
3138 }
3139
3140
3141 /*========================================================================*/
ObjectMoleculeRenameAtoms(ObjectMolecule * I,int * flag,int force)3142 int ObjectMoleculeRenameAtoms(ObjectMolecule * I, int *flag, int force)
3143 {
3144 PyMOLGlobals * G = I->G;
3145 AtomInfoType *ai;
3146 int a;
3147 int result;
3148 if(force) {
3149 ai = I->AtomInfo.data();
3150 if(!flag) {
3151 for(a = 0; a < I->NAtom; a++) {
3152 LexAssign(G, ai->name, 0);
3153 ai++;
3154 }
3155 } else {
3156 for(a = 0; a < I->NAtom; a++) {
3157 if(flag[a])
3158 LexAssign(G, ai->name, 0);
3159 ai++;
3160 }
3161 }
3162 }
3163 result = AtomInfoUniquefyNames(I->G, NULL, 0, I->AtomInfo.data(), flag, I->NAtom);
3164 return result;
3165 }
3166
3167
3168 /*========================================================================*/
AddCoordinateIntoCoordSet(ObjectMolecule * I,int a,CoordSet * tcs,const int * AtmToIdx,CoordSet * cs,float * backup,int mode,int at0,int index0,int move_flag,float * va1,float * vh1,float * x1,float * y1,float * z1,float d,int ca0)3169 static int AddCoordinateIntoCoordSet(ObjectMolecule * I, int a, CoordSet *tcs,
3170 const int *AtmToIdx, CoordSet *cs, float *backup, int mode, int at0,
3171 int index0, int move_flag, float *va1, float *vh1,
3172 float *x1, float *y1, float *z1, float d, int ca0){
3173 float *f0, *f1;
3174 int b;
3175 int ch0;
3176 float vh0[3];
3177 float va0[3] = { 0.0F, 0.0F, 0.0F };
3178 float x0[3], y0[3], z0[3];
3179 float x[3], y[3], z[3];
3180 float t[3], t2[3];
3181 int ok = true;
3182
3183 if(tcs) {
3184 if(mode == 3) {
3185 f0 = backup;
3186 f1 = cs->Coord.data();
3187 for(b = 0; b < cs->NIndex; b++) { /* brute force transformation */
3188 copy3f(f0, f1);
3189 }
3190 f0 += 3;
3191 f1 += 3;
3192 } else {
3193 switch (mode) {
3194 case 0:
3195 ch0 = AtmToIdx[index0]; /* hydrogen */
3196 if((ca0 >= 0) && (ch0 >= 0)) {
3197 copy3f(tcs->coordPtr(ca0), va0);
3198 copy3f(tcs->coordPtr(ch0), vh0);
3199 subtract3f(vh0, va0, x0);
3200 get_system1f3f(x0, y0, z0);
3201 }
3202 break;
3203 case 1:
3204 if(ca0 >= 0) {
3205 ObjectMoleculeFindOpenValenceVector(I, a, at0, x0, NULL, -1);
3206 copy3f(tcs->coordPtr(ca0), va0);
3207 get_system1f3f(x0, y0, z0);
3208 }
3209 break;
3210 }
3211 scale3f(x0, d, t2);
3212 add3f(va0, t2, t2);
3213
3214 f0 = backup;
3215 f1 = cs->Coord.data();
3216 for(b = 0; b < cs->NIndex; b++) { /* brute force transformation */
3217 if(move_flag) {
3218 subtract3f(f0, va1, t);
3219 scale3f(x0, dot_product3f(t, x1), x);
3220 scale3f(y0, dot_product3f(t, y1), y);
3221 scale3f(z0, dot_product3f(t, z1), z);
3222 add3f(x, y, y);
3223 add3f(y, z, f1);
3224 add3f(t2, f1, f1);
3225 } else {
3226 copy3f(f0, f1);
3227 }
3228 f0 += 3;
3229 f1 += 3;
3230 }
3231 }
3232 if (ok)
3233 ok &= CoordSetMerge(I, tcs, cs);
3234 }
3235 return ok;
3236 }
3237
3238 /*========================================================================*/
ObjectMoleculeFuse(ObjectMolecule * I,int index0,ObjectMolecule * src,int index1,int mode,int move_flag)3239 int ObjectMoleculeFuse(ObjectMolecule * I, int index0, ObjectMolecule * src,
3240 int index1, int mode, int move_flag)
3241 {
3242 PyMOLGlobals *G = I->G;
3243 int a;
3244 const AtomInfoType *ai0, *ai1;
3245 int n, nn;
3246 int at0 = -1;
3247 int at1 = -1;
3248 int a0, a1;
3249 int hydr1 = -1;
3250 int anch1 = -1;
3251 float *backup = NULL;
3252 // float d;
3253 CoordSet *cs = NULL, *scs = NULL;
3254 int state1 = 0;
3255 CoordSet *tcs;
3256 int edit = 1;
3257 OrthoLineType sele1, sele2;
3258 float va1[3] = { 0.0F, 0.0F, 0.0F };
3259 float vh1[3];
3260 float x1[3], y1[3], z1[3];
3261 float d;
3262 int ca0;
3263 int ok = true;
3264
3265 ok &= ObjectMoleculeUpdateNeighbors(I);
3266 if (ok)
3267 ok &= ObjectMoleculeUpdateNeighbors(src);
3268
3269 /* make sure each link point has only one neighbor */
3270
3271 ai0 = I->AtomInfo;
3272 ai1 = src->AtomInfo;
3273
3274 // printf("ObjectMoleculeFuse: mode=%d src->DiscreteFlag=%d\n", mode, src->DiscreteFlag);
3275 switch (mode) {
3276 case 0: /* fusing by replacing hydrogens */
3277
3278 n = I->Neighbor[index0];
3279 nn = I->Neighbor[n++];
3280 if(nn == 1)
3281 at0 = I->Neighbor[n];
3282
3283 n = src->Neighbor[index1];
3284 nn = src->Neighbor[n++];
3285 if(nn == 1)
3286 at1 = src->Neighbor[n];
3287
3288 if(src->NCSet) {
3289 scs = src->CSet[state1];
3290 anch1 = scs->atmToIdx(at1);
3291 hydr1 = scs->atmToIdx(index1);
3292 }
3293 break;
3294 case 1: /* fuse merely by drawing a bond */
3295 case 3: /* don't actually fuse -- just combine into a single object */
3296 at0 = index0;
3297 at1 = index1;
3298
3299 if(src->NCSet) {
3300 scs = src->CSet[state1];
3301 anch1 = scs->atmToIdx(at1);
3302 }
3303
3304 break;
3305 }
3306
3307 if((at0 >= 0) && (at1 >= 0) && scs && (anch1 >= 0)) { /* have anchors and source coordinate set */
3308
3309 auto nai = pymol::vla<AtomInfoType>(src->NAtom);
3310 /* copy atoms and atom info into a 1:1 direct mapping */
3311 if (ok)
3312 cs = CoordSetNew(I->G);
3313 CHECKOK(ok, cs);
3314 if (ok)
3315 cs->Coord = pymol::vla<float>(scs->NIndex * 3);
3316 CHECKOK(ok, cs->Coord);
3317 if (ok)
3318 cs->NIndex = scs->NIndex;
3319 if (ok){
3320 for(a = 0; a < scs->NIndex; a++) {
3321 copy3f(scs->coordPtr(a), cs->coordPtr(a));
3322 a1 = scs->IdxToAtm[a];
3323
3324 AtomInfoCopy(G, ai1 + a1, nai + a);
3325 /* *(nai+a) = *(ai1+a1); ** unsafe */
3326
3327 if(a1 == at1) {
3328 (nai + a)->temp1 = 2; /* clear marks */
3329 } else {
3330 (nai + a)->temp1 = 0; /* clear marks */
3331 }
3332 }
3333 }
3334
3335 /* copy internal bond information */
3336 if (ok)
3337 cs->TmpBond = pymol::vla<BondType>(src->NBond);
3338 CHECKOK(ok, cs->TmpBond);
3339 if (ok){
3340 const BondType* b1 = src->Bond.data();
3341 BondType* b0 = cs->TmpBond.data();
3342 cs->NTmpBond = 0;
3343 for(a = 0; a < src->NBond; a++) {
3344 a0 = scs->atmToIdx(b1->index[0]);
3345 a1 = scs->atmToIdx(b1->index[1]);
3346 if((a0 >= 0) && (a1 >= 0)) {
3347 *b0 = *b1;
3348 b0->index[0] = a0;
3349 b0->index[1] = a1;
3350 b0++;
3351 cs->NTmpBond++;
3352 }
3353 b1++;
3354 }
3355 }
3356
3357 if (ok)
3358 backup = pymol::malloc<float>(cs->NIndex * 3); /* make untransformed copy of coordinate set */
3359 CHECKOK(ok, backup);
3360 if (ok){
3361 for(a = 0; a < cs->NIndex; a++) {
3362 copy3f(cs->coordPtr(a), backup + a * 3);
3363 }
3364 }
3365 if (ok){
3366 switch (mode) {
3367 case 0:
3368 nai[hydr1].deleteFlag = true;
3369 I->AtomInfo[index0].deleteFlag = true;
3370 copy3f(backup + 3 * anch1, va1);
3371 copy3f(backup + 3 * hydr1, vh1);
3372 subtract3f(va1, vh1, x1); /* note reverse dir from above */
3373 get_system1f3f(x1, y1, z1);
3374 break;
3375 case 1:
3376 copy3f(backup + 3 * anch1, va1);
3377 ObjectMoleculeFindOpenValenceVector(src, state1, at1, x1, NULL, -1);
3378 scale3f(x1, -1.0F, x1);
3379 get_system1f3f(x1, y1, z1);
3380 break;
3381 }
3382 }
3383
3384 if(ok && mode != 3) {
3385 /* set up the linking bond */
3386 cs->TmpLinkBond = pymol::vla<BondType>(1);
3387 CHECKOK(ok, cs->TmpLinkBond);
3388 if (ok){
3389 cs->NTmpLinkBond = 1;
3390 auto* bond = cs->TmpLinkBond.data();
3391 BondTypeInit2(bond, at0, anch1);
3392 }
3393 }
3394
3395 if(ok)
3396 cs->enumIndices();
3397
3398 if (ok){
3399 d = AtomInfoGetBondLength(I->G, ai0 + at0, ai1 + at1);
3400 AtomInfoUniquefyNames(I, nai.data(), cs->NIndex);
3401 }
3402
3403 /* set up tags which will enable use to continue editing bond */
3404
3405 if(ok && edit) {
3406 for(a = 0; a < I->NAtom; a++) {
3407 I->AtomInfo[a].temp1 = 0;
3408 }
3409 I->AtomInfo[at0].temp1 = 1;
3410 }
3411 if (ok)
3412 ok &= ObjectMoleculeMerge(I, std::move(nai), cs, false, cAIC_AllMask, true);
3413 /* will free nai, cs->TmpBond and cs->TmpLinkBond */
3414 if (ok){
3415 if (I->DiscreteFlag){
3416 for(a = 0; a < I->NCSet; a++) {
3417 CoordSet *qcs = I->CSet[a];
3418 if(qcs) {
3419 qcs->tmp_index = a;
3420 }
3421 }
3422 if (ok)
3423 ok &= ObjectMoleculeExtendIndices(I, I->DiscreteCSet[at0]->tmp_index);
3424 } else if (ok) {
3425 ok &= ObjectMoleculeExtendIndices(I, -1);
3426 }
3427 }
3428 if (ok)
3429 ok &= ObjectMoleculeUpdateNeighbors(I);
3430 if (ok){
3431 if (I->DiscreteFlag){
3432 tcs = I->DiscreteCSet[at0];
3433 ca0 = I->DiscreteAtmToIdx[at0]; /* anchor */
3434 if (ok)
3435 ok &= AddCoordinateIntoCoordSet(I, a, tcs, I->DiscreteAtmToIdx, cs, backup, mode, at0, index0, move_flag, va1, vh1, x1, y1, z1, d, ca0);
3436 } else {
3437 for(a = 0; a < I->NCSet; a++) { /* add coordinate into the coordinate set */
3438 tcs = I->CSet[a];
3439 ca0 = tcs->AtmToIdx[at0]; /* anchor */
3440 if (ok)
3441 ok &= AddCoordinateIntoCoordSet(I, a, tcs, tcs->AtmToIdx, cs, backup, mode, at0, index0, move_flag, va1, vh1, x1, y1, z1, d, ca0);
3442 }
3443 }
3444 }
3445 if (ok)
3446 ok &= ObjectMoleculeSort(I);
3447 if (ok)
3448 ObjectMoleculeUpdateIDNumbers(I);
3449 switch (mode) {
3450 case 0:
3451 ObjectMoleculePurge(I);
3452 break;
3453 }
3454 if(edit) { /* edit the resulting bond */
3455 at0 = -1;
3456 at1 = -1;
3457 for(a = 0; a < I->NAtom; a++) {
3458 if(I->AtomInfo[a].temp1 == 1)
3459 at0 = a;
3460 if(I->AtomInfo[a].temp1 == 2)
3461 at1 = a;
3462 }
3463 if((at0 >= 0) && (at1 >= 0)) {
3464 sprintf(sele1, "%s`%d", I->Name, at1 + 1); /* points outward... */
3465 sprintf(sele2, "%s`%d", I->Name, at0 + 1);
3466 EditorSelect(I->G, sele1, sele2, "", "", false, true, true);
3467 }
3468 }
3469 }
3470 if(cs)
3471 cs->fFree();
3472 FreeP(backup);
3473 return ok;
3474 }
3475
3476
3477 /*========================================================================*/
ObjectMoleculeVerifyChemistry(ObjectMolecule * I,int state)3478 int ObjectMoleculeVerifyChemistry(ObjectMolecule * I, int state)
3479 {
3480 int result = false;
3481 const AtomInfoType *ai;
3482 int a;
3483 int flag;
3484
3485 if(state < 0) {
3486 /* use the first defined state */
3487 for(a = 0; a < I->NCSet; a++) {
3488 if(I->CSet[a]) {
3489 state = a;
3490 break;
3491 }
3492 }
3493 }
3494 ai = I->AtomInfo;
3495 flag = true;
3496 for(a = 0; a < I->NAtom; a++) {
3497 if(!ai->chemFlag) {
3498 flag = false;
3499 }
3500 ai++;
3501 }
3502 if((!flag) && (state >= 0) && (state < I->NCSet)) {
3503 if(I->CSet[state]) {
3504 ObjectMoleculeInferChemFromBonds(I, state);
3505 ObjectMoleculeInferChemFromNeighGeom(I, state);
3506 ObjectMoleculeInferHBondFromChem(I);
3507 /* ObjectMoleculeInferChemForProtein(I,0); */
3508 }
3509 flag = true;
3510 ai = I->AtomInfo;
3511 for(a = 0; a < I->NAtom; a++) {
3512 if(!ai->chemFlag) {
3513 flag = false;
3514 break;
3515 }
3516 ai++;
3517 }
3518 }
3519 if(flag)
3520 result = true;
3521 return (result);
3522 }
3523
3524
3525 /*========================================================================*/
ObjectMoleculeAttach(ObjectMolecule * I,int index,pymol::vla<AtomInfoType> && nai)3526 int ObjectMoleculeAttach(ObjectMolecule * I, int index,
3527 pymol::vla<AtomInfoType>&& nai)
3528 {
3529 int a;
3530 AtomInfoType *ai;
3531 BondType* bond;
3532 float v[3], v0[3], d;
3533 CoordSet *cs = NULL;
3534 int ok = false;
3535
3536 ok_assert(1, ObjectMoleculeUpdateNeighbors(I));
3537
3538 ai = I->AtomInfo + index;
3539
3540 ok_assert(1, cs = CoordSetNew(I->G));
3541 ok_assert(1, cs->Coord = pymol::vla<float>(3));
3542
3543 cs->NIndex = 1;
3544 ok_assert(1, cs->TmpLinkBond = pymol::vla<BondType>(1));
3545
3546 cs->NTmpLinkBond = 1;
3547 bond = cs->TmpLinkBond.data();
3548 BondTypeInit2(bond, index, 0);
3549 cs->enumIndices();
3550
3551 ok_assert(1, ObjectMoleculePrepareAtom(I, index, nai.data()));
3552 d = AtomInfoGetBondLength(I->G, ai, nai);
3553
3554 ok_assert(1, ObjectMoleculeMerge(I, std::move(nai),
3555 cs, false, cAIC_AllMask, true)); // will free nai and cs->TmpLinkBond
3556 ok_assert(1, ObjectMoleculeExtendIndices(I, -1));
3557 ok_assert(1, ObjectMoleculeUpdateNeighbors(I));
3558
3559 for(a = 0; a < I->NCSet; a++) { /* add atom to each coordinate set */
3560 if(I->CSet[a]) {
3561 ObjectMoleculeGetAtomVertex(I, a, index, v0);
3562 ObjectMoleculeFindOpenValenceVector(I, a, index, v, NULL, -1);
3563 scale3f(v, d, v);
3564 add3f(v0, v, cs->Coord.data());
3565 ok_assert(1, CoordSetMerge(I, I->CSet[a], cs));
3566 }
3567 }
3568
3569 ok_assert(1, ObjectMoleculeSort(I));
3570 ObjectMoleculeUpdateIDNumbers(I);
3571
3572 ok = true;
3573 ok_except1:
3574 cs->fFree();
3575 return ok;
3576 }
3577
3578
3579 /*========================================================================*/
ObjectMoleculeFillOpenValences(ObjectMolecule * I,int index)3580 int ObjectMoleculeFillOpenValences(ObjectMolecule * I, int index)
3581 {
3582 int a;
3583 AtomInfoType *ai;
3584 int n, nn;
3585 int result = 0;
3586 int flag = true;
3587 float v[3], v0[3], d;
3588 CoordSet *cs = NULL;
3589 int ok = true;
3590
3591 if((index >= 0) && (index <= I->NAtom)) {
3592 while(ok) {
3593 if (ok)
3594 ok &= ObjectMoleculeUpdateNeighbors(I);
3595 ai = I->AtomInfo + index;
3596 n = I->Neighbor[index];
3597 nn = I->Neighbor[n++];
3598
3599 if((nn >= ai->valence) || (!flag))
3600 break;
3601 flag = false;
3602
3603 if (ok)
3604 cs = CoordSetNew(I->G);
3605 CHECKOK(ok, cs);
3606 if (ok){
3607 cs->Coord = pymol::vla<float>(3);
3608 CHECKOK(ok, cs->Coord);
3609
3610 cs->NIndex = 1;
3611 if (ok)
3612 cs->TmpLinkBond = pymol::vla<BondType>(1);
3613 CHECKOK(ok, cs->TmpLinkBond);
3614 if (ok){
3615 cs->NTmpLinkBond = 1;
3616 auto* bond = cs->TmpLinkBond.data();
3617 BondTypeInit2(bond, index, 0);
3618 }
3619 }
3620
3621 if(ok)
3622 cs->enumIndices();
3623 auto atInfo = pymol::vla<AtomInfoType>(1);
3624 AtomInfoType* nai = atInfo.data();
3625 if (ok){
3626 UtilNCopy(nai->elem, "H", 2);
3627 nai->geom = cAtomInfoSingle;
3628 nai->valence = 1;
3629 ok &= ObjectMoleculePrepareAtom(I, index, nai);
3630 d = AtomInfoGetBondLength(I->G, ai, nai);
3631 if (ok)
3632 ok &= ObjectMoleculeMerge(I, std::move(atInfo),
3633 cs, false, cAIC_AllMask, true); /* will free nai and cs->TmpLinkBond */
3634 }
3635 if (ok)
3636 ok &= ObjectMoleculeExtendIndices(I, -1);
3637 if (ok)
3638 ok &= ObjectMoleculeUpdateNeighbors(I);
3639 for(a = 0; ok && a < I->NCSet; a++) { /* add atom to each coordinate set */
3640 if(I->CSet[a]) {
3641 ObjectMoleculeGetAtomVertex(I, a, index, v0);
3642 ObjectMoleculeFindOpenValenceVector(I, a, index, v, NULL, -1);
3643 scale3f(v, d, v);
3644 add3f(v0, v, cs->Coord.data());
3645 if (ok)
3646 ok &= CoordSetMerge(I, I->CSet[a], cs);
3647 }
3648 }
3649 cs->fFree();
3650 result++;
3651 flag = true;
3652 }
3653 }
3654 ObjectMoleculeUpdateIDNumbers(I);
3655 return (result);
3656 }
3657
3658 #define MaxOcc 100
3659
3660
3661 /*========================================================================*/
get_planer_normal(ObjectMolecule * I,int state,int index,float * normal)3662 static int get_planer_normal(ObjectMolecule * I, int state, int index, float *normal)
3663 { /* NOTE assumes neighbors are defined */
3664 int found = false;
3665 int nOcc = 0;
3666 float occ[MaxOcc * 3];
3667 AtomInfoType *ai = I->AtomInfo + index;
3668 int n, a1;
3669 float v0[3], v1[3], v2[3], n0[3];
3670
3671 if(ObjectMoleculeGetAtomVertex(I, state, index, v0)) {
3672 n = I->Neighbor[index];
3673 n++; /* skip count */
3674 while(1) { /* look for an attached non-hydrogen as a base */
3675 a1 = I->Neighbor[n];
3676 n += 2;
3677 if(a1 < 0)
3678 break;
3679 if(ObjectMoleculeGetAtomVertex(I, state, a1, v1)) {
3680 subtract3f(v1, v0, n0);
3681 normalize3f(n0); /* n0's point away from center atom */
3682 copy3f(n0, occ + 3 * nOcc);
3683 nOcc++;
3684 if(nOcc == MaxOcc) /* safety valve */
3685 break;
3686 }
3687 }
3688 switch (ai->geom) {
3689 case cAtomInfoPlanar:
3690 if(nOcc > 1) {
3691 cross_product3f(occ, occ + 3, normal);
3692 if(nOcc > 2) {
3693 cross_product3f(occ, occ + 6, v2);
3694 if(dot_product3f(normal, v2) < 0) {
3695 subtract3f(normal, v2, normal);
3696 } else {
3697 add3f(normal, v2, normal);
3698 }
3699 cross_product3f(occ + 3, occ + 6, v2);
3700 if(dot_product3f(normal, v2) < 0) {
3701 subtract3f(normal, v2, normal);
3702 } else {
3703 add3f(normal, v2, normal);
3704 }
3705 }
3706 normalize3f(normal);
3707 found = true;
3708 }
3709 break;
3710 }
3711 }
3712 return found;
3713 }
3714
3715
3716 /*========================================================================*/
ObjectMoleculeFindOpenValenceVector(ObjectMolecule * I,int state,int index,float * v,float * seek,int ignore_index)3717 int ObjectMoleculeFindOpenValenceVector(ObjectMolecule * I, int state,
3718 int index, float *v, float *seek,
3719 int ignore_index)
3720 {
3721 CoordSet *cs;
3722 int nOcc = 0;
3723 float occ[MaxOcc * 3];
3724 int last_occ = -1;
3725 int n;
3726 int a1;
3727 float v0[3], v1[3], n0[3] = {0.0F,0.0F,0.0F}, t[3];
3728 int result = false;
3729 AtomInfoType *ai;
3730 float y[3], z[3];
3731
3732 /* default is +X */
3733 v[0] = 1.0;
3734 v[1] = 0.0;
3735 v[2] = 0.0;
3736
3737 if(state < 0)
3738 state = 0;
3739 if(I->NCSet == 1)
3740 state = 0;
3741 state = state % I->NCSet;
3742 cs = I->CSet[state];
3743 if(cs) {
3744 if((index >= 0) && (index <= I->NAtom)) {
3745 ai = I->AtomInfo + index;
3746 if(ObjectMoleculeGetAtomVertex(I, state, index, v0)) {
3747 ObjectMoleculeUpdateNeighbors(I);
3748 n = I->Neighbor[index];
3749 n++; /* skip count */
3750 while(1) { /* look for an attached non-hydrogen as a base */
3751 a1 = I->Neighbor[n];
3752 n += 2;
3753 if(a1 < 0)
3754 break;
3755 if(a1 != ignore_index) {
3756 if(ObjectMoleculeGetAtomVertex(I, state, a1, v1)) {
3757 last_occ = a1;
3758 subtract3f(v1, v0, n0);
3759 normalize3f(n0); /* n0's point away from center atom */
3760 copy3f(n0, occ + 3 * nOcc);
3761 nOcc++;
3762 if(nOcc == MaxOcc) /* safety valve */
3763 break;
3764 }
3765 }
3766 }
3767 if((!nOcc) || (nOcc > 4) || (ai->geom == cAtomInfoNone)) {
3768 if(!seek)
3769 get_random3f(v);
3770 else
3771 copy3f(seek, v);
3772 result = true;
3773 } else {
3774 switch (nOcc) {
3775 case 1: /* only one current occupied position */
3776 switch (ai->geom) {
3777 case cAtomInfoTetrahedral:
3778 if(!seek) {
3779 get_system1f3f(occ, y, z);
3780 scale3f(occ, -0.334F, v);
3781 scale3f(z, 0.943F, t);
3782 add3f(t, v, v);
3783 } else { /* point hydrogen towards sought vector */
3784 copy3f(seek, z);
3785 get_system2f3f(occ, z, y);
3786 scale3f(occ, -0.334F, v);
3787 scale3f(z, 0.943F, t);
3788 add3f(t, v, v);
3789 }
3790 result = true;
3791 break;
3792 case cAtomInfoPlanar:
3793 {
3794 if(!seek) {
3795 if((last_occ >= 0) && get_planer_normal(I, state, last_occ, n0)) {
3796 copy3f(n0, y);
3797 get_system2f3f(occ, y, z);
3798 } else {
3799 get_system1f3f(occ, y, z);
3800 }
3801 scale3f(occ, -0.500F, v);
3802 scale3f(z, 0.866F, t);
3803 add3f(t, v, v);
3804 } else {
3805 copy3f(seek, z);
3806 get_system2f3f(occ, z, y);
3807 scale3f(occ, -0.500F, v);
3808 scale3f(z, 0.866F, t);
3809 add3f(t, v, v);
3810 }
3811 result = true;
3812 }
3813 break;
3814 case cAtomInfoLinear:
3815 scale3f(occ, -1.0F, v);
3816 result = true;
3817 break;
3818 default:
3819 if(!seek)
3820 get_random3f(v);
3821 else
3822 copy3f(seek, v);
3823 result = true;
3824 break;
3825 }
3826 break;
3827 case 2: /* only two current occupied positions */
3828 switch (ai->geom) {
3829 case cAtomInfoTetrahedral:
3830 add3f(occ, occ + 3, t);
3831 get_system2f3f(t, occ, z);
3832 scale3f(t, -1.0F, v);
3833 if(seek) {
3834 if(dot_product3f(z, seek) < 0.0F) {
3835 invert3f(z);
3836 }
3837 }
3838 scale3f(z, 1.41F, t);
3839 add3f(t, v, v);
3840 result = true;
3841 break;
3842 case cAtomInfoPlanar:
3843 add3f(occ, occ + 3, t);
3844 scale3f(t, -1.0F, v);
3845 result = true;
3846 break;
3847 default:
3848 if(!seek) {
3849 add3f(occ, occ + 3, t);
3850 scale3f(t, -1.0F, v);
3851 if(length3f(t) < 0.1)
3852 get_random3f(v);
3853 } else
3854 copy3f(seek, v);
3855 /* hypervalent */
3856 result = true;
3857 break;
3858 }
3859 break;
3860 case 3: /* only three current occupied positions */
3861 switch (ai->geom) {
3862 case cAtomInfoTetrahedral:
3863 add3f(occ, occ + 3, t);
3864 add3f(occ + 6, t, t);
3865 scale3f(t, -1.0F, v);
3866 result = true;
3867 break;
3868 default:
3869 if(!seek) {
3870 add3f(occ, occ + 3, t);
3871 add3f(occ + 6, t, t);
3872 scale3f(t, -1.0F, v);
3873 if(length3f(t) < 0.1)
3874 get_random3f(v);
3875 } else
3876 copy3f(seek, v);
3877 /* hypervalent */
3878 result = true;
3879 break;
3880 }
3881 break;
3882 case 4:
3883 if(!seek)
3884 get_random3f(v);
3885 else
3886 copy3f(seek, v);
3887 /* hypervalent */
3888 result = true;
3889 break;
3890 }
3891 }
3892 }
3893 }
3894 }
3895 normalize3f(v);
3896 return (result);
3897 #undef MaxOcc
3898
3899 }
3900
3901
3902 /*========================================================================*/
ObjectMoleculeCreateSpheroid(ObjectMolecule * I,int average)3903 void ObjectMoleculeCreateSpheroid(ObjectMolecule * I, int average)
3904 {
3905 CoordSet *cs;
3906 int a, b, c, a0;
3907 SphereRec *sp;
3908 float *v, *v0, *s, *f, ang, min_dist, *max_sq;
3909 int *i;
3910 float *center = NULL;
3911 float d0[3], n0[3], d1[3], d2[3];
3912 float p0[3], p1[3], p2[3];
3913 int t0, t1, t2, bt0, bt1, bt2;
3914 float dp, l, *fsum = NULL;
3915 float spheroid_smooth;
3916 float spheroid_fill;
3917 float spheroid_ratio = 0.1F; /* minimum ratio of width over length */
3918 float spheroid_minimum = 0.02F; /* minimum size - to insure valid normals */
3919 int row, *count = NULL, base;
3920 int nRow;
3921 int first = 0;
3922 int last = 0;
3923 int current;
3924 int cscount;
3925 int n_state = 0;
3926 sp = GetSpheroidSphereRec(I->G);
3927
3928 nRow = I->NAtom * sp->nDot;
3929
3930 center = pymol::malloc<float>(I->NAtom * 3);
3931 count = pymol::malloc<int>(I->NAtom);
3932 fsum = pymol::malloc<float>(nRow);
3933 max_sq = pymol::malloc<float>(I->NAtom);
3934
3935 spheroid_smooth = SettingGetGlobal_f(I->G, cSetting_spheroid_smooth);
3936 spheroid_fill = SettingGetGlobal_f(I->G, cSetting_spheroid_fill);
3937 /* first compute average coordinate */
3938
3939 if(average < 1)
3940 average = I->NCSet;
3941 current = 0;
3942 cscount = 0;
3943 while(current < I->NCSet) {
3944 if(I->CSet[current]) {
3945 if(!cscount)
3946 first = current;
3947 cscount++;
3948 last = current + 1;
3949 }
3950
3951 if(cscount == average || current == I->NCSet - 1) {
3952 PRINTFB(I->G, FB_ObjectMolecule, FB_Details)
3953 " ObjectMolecule: computing spheroid from states %d to %d.\n",
3954 first + 1, last ENDFB(I->G);
3955
3956 auto spheroid = std::vector<float>(nRow);
3957
3958 v = center;
3959 i = count;
3960 for(a = 0; a < I->NAtom; a++) {
3961 *(v++) = 0.0;
3962 *(v++) = 0.0;
3963 *(v++) = 0.0;
3964 *(i++) = 0;
3965 }
3966
3967 for(b = first; b < last; b++) {
3968 cs = I->CSet[b];
3969 if(cs) {
3970 v = cs->Coord.data();
3971 for(a = 0; a < cs->NIndex; a++) {
3972 a0 = cs->IdxToAtm[a];
3973 v0 = center + 3 * a0;
3974 add3f(v, v0, v0);
3975 (*(count + a0))++;
3976 v += 3;
3977 }
3978 }
3979 }
3980
3981 i = count;
3982 v = center;
3983 for(a = 0; a < I->NAtom; a++)
3984 if(*i) {
3985 (*(v++)) /= (*i);
3986 (*(v++)) /= (*i);
3987 (*(v++)) /= (*i++);
3988 } else {
3989 v += 3;
3990 i++;
3991 }
3992
3993 /* now go through and compute radial distances */
3994
3995 f = fsum;
3996 s = spheroid.data();
3997 for(a = 0; a < nRow; a++) {
3998 *(f++) = 0.0;
3999 *(s++) = 0.0;
4000 }
4001
4002 v = max_sq;
4003 for(a = 0; a < I->NAtom; a++)
4004 *(v++) = 0.0;
4005
4006 for(b = first; b < last; b++) {
4007 cs = I->CSet[b];
4008 if(cs) {
4009 v = cs->Coord.data();
4010 for(a = 0; a < cs->NIndex; a++) {
4011 a0 = cs->IdxToAtm[a];
4012 base = (a0 * sp->nDot);
4013 v0 = center + (3 * a0);
4014 subtract3f(v, v0, d0); /* subtract from average */
4015 l = lengthsq3f(d0);
4016 if(l > max_sq[a0])
4017 max_sq[a0] = l;
4018 if(l > 0.0) {
4019 float isq = (float) (1.0 / sqrt1d(l));
4020 scale3f(d0, isq, n0);
4021 for(c = 0; c < sp->nDot; c++) { /* average over spokes */
4022 dp = dot_product3f(sp->dot[c], n0);
4023 row = base + c;
4024 if(dp >= 0.0) {
4025 ang = (float) ((acos(dp) / spheroid_smooth) * (cPI / 2.0));
4026 if(ang > spheroid_fill)
4027 ang = spheroid_fill;
4028 /* take envelop to zero over that angle */
4029 if(ang <= (cPI / 2.0)) {
4030 dp = (float) cos(ang);
4031 fsum[row] += dp * dp;
4032 spheroid[row] += l * dp * dp * dp;
4033 }
4034 }
4035 }
4036 }
4037 v += 3;
4038 }
4039 }
4040 }
4041
4042 f = fsum;
4043 s = spheroid.data();
4044 for(a = 0; a < I->NAtom; a++) {
4045 min_dist = (float) (spheroid_ratio * sqrt(max_sq[a]));
4046 if(min_dist < spheroid_minimum)
4047 min_dist = spheroid_minimum;
4048 for(b = 0; b < sp->nDot; b++) {
4049 if(*f > R_SMALL4) {
4050 (*s) = (float) (sqrt1d((*s) / (*(f++)))); /* we put the "rm" in "rms" */
4051 } else {
4052 f++;
4053 }
4054 if(*s < min_dist)
4055 *s = min_dist;
4056 s++;
4057 }
4058 }
4059
4060 /* set frame 0 coordinates to the average */
4061
4062 cs = I->CSet[first];
4063 if(cs) {
4064 v = cs->Coord.data();
4065 for(a = 0; a < cs->NIndex; a++) {
4066 a0 = cs->IdxToAtm[a];
4067 v0 = center + 3 * a0;
4068 copy3f(v0, v);
4069 v += 3;
4070 }
4071 }
4072
4073 /* now compute surface normals */
4074
4075 auto norm = std::vector<float>(nRow * 3);
4076 for(a = 0; a < nRow; a++) {
4077 zero3f(norm.data() + a * 3);
4078 }
4079 for(a = 0; a < I->NAtom; a++) {
4080 base = a * sp->nDot;
4081 for(b = 0; b < sp->NTri; b++) {
4082 t0 = sp->Tri[b * 3];
4083 t1 = sp->Tri[b * 3 + 1];
4084 t2 = sp->Tri[b * 3 + 2];
4085 bt0 = base + t0;
4086 bt1 = base + t1;
4087 bt2 = base + t2;
4088 copy3f(sp->dot[t0], p0);
4089 copy3f(sp->dot[t1], p1);
4090 copy3f(sp->dot[t2], p2);
4091 /* scale3f(sp->dot[t0].v,spheroid[bt0],p0);
4092 scale3f(sp->dot[t1].v,spheroid[bt1],p1);
4093 scale3f(sp->dot[t2].v,spheroid[bt2],p2); */
4094 subtract3f(p1, p0, d1);
4095 subtract3f(p2, p0, d2);
4096 cross_product3f(d1, d2, n0);
4097 normalize3f(n0);
4098 v = norm.data() + bt0 * 3;
4099 add3f(n0, v, v);
4100 v = norm.data() + bt1 * 3;
4101 add3f(n0, v, v);
4102 v = norm.data() + bt2 * 3;
4103 add3f(n0, v, v);
4104 }
4105 }
4106
4107 f = norm.data();
4108 for(a = 0; a < I->NAtom; a++) {
4109 base = a * sp->nDot;
4110 for(b = 0; b < sp->nDot; b++) {
4111 normalize3f(f);
4112 f += 3;
4113 }
4114 }
4115
4116 if(I->CSet[first]) {
4117 I->CSet[first]->Spheroid = std::move(spheroid);
4118 I->CSet[first]->SpheroidNormal = std::move(norm);
4119 }
4120
4121 for(b = first + 1; b < last; b++) {
4122 cs = I->CSet[b];
4123 if(cs) {
4124 cs->fFree();
4125 }
4126 I->CSet[b] = NULL;
4127 }
4128
4129 if(n_state != first) {
4130 I->CSet[n_state] = I->CSet[first];
4131 I->CSet[first] = NULL;
4132 }
4133 n_state++;
4134
4135 cscount = 0;
4136 }
4137 current++;
4138 }
4139 I->NCSet = n_state;
4140 FreeP(center);
4141 FreeP(count);
4142 FreeP(fsum);
4143 FreeP(max_sq);
4144
4145 I->invalidate(cRepSphere, cRepInvProp, -1);
4146 }
4147
4148
4149 /*========================================================================*/
ObjectMoleculeReplaceAtom(ObjectMolecule * I,int index,AtomInfoType && ai)4150 void ObjectMoleculeReplaceAtom(ObjectMolecule * I, int index, AtomInfoType&& ai)
4151 {
4152 if((index >= 0) && (index <= I->NAtom)) {
4153 I->AtomInfo[index] = std::move(ai);
4154 I->invalidate(cRepAll, cRepInvAtoms, -1);
4155 /* could we put in a refinement step here? */
4156 }
4157 }
4158
4159
4160 /*========================================================================*/
ObjectMoleculePrepareAtom(ObjectMolecule * I,int index,AtomInfoType * ai,bool uniquefy)4161 int ObjectMoleculePrepareAtom(ObjectMolecule * I, int index, AtomInfoType * ai,
4162 bool uniquefy)
4163 {
4164 /* match existing properties of the old atom */
4165 AtomInfoType *ai0;
4166 int ok = true;
4167
4168 if((index >= 0) && (index <= I->NAtom)) {
4169 ai0 = I->AtomInfo + index;
4170 ai->resv = ai0->resv;
4171 ai->hetatm = ai0->hetatm;
4172 ai->flags = ai0->flags;
4173
4174 if (!ai->geom)
4175 ai->geom = ai0->geom;
4176
4177 ai->discrete_state = ai0->discrete_state;
4178 ai->q = ai0->q;
4179 ai->b = ai0->b;
4180 strcpy(ai->alt, ai0->alt);
4181 ai->inscode = ai0->inscode;
4182 LexAssign(I->G, ai->segi, ai0->segi);
4183 LexAssign(I->G, ai->chain, ai0->chain);
4184 LexAssign(I->G, ai->resn, ai0->resn);
4185 ai->visRep = ai0->visRep;
4186 ai->id = -1;
4187 #ifdef _PYMOL_IP_EXTRAS
4188 ai->oldid = -1;
4189 #endif
4190 ai->rank = -1;
4191
4192 AtomInfoAssignParameters(I->G, ai);
4193
4194 if (uniquefy) {
4195 AtomInfoUniquefyNames(I->G, I->AtomInfo, I->NAtom, ai, NULL, 1);
4196 }
4197
4198 if((ai->elem[0] == ai0->elem[0]) && (ai->elem[1] == ai0->elem[1]))
4199 ai->color = ai0->color;
4200 else if((ai->elem[0] == 'C') && (ai->elem[1] == 0)) {
4201 int n, index2;
4202 int found = false;
4203 if (ok)
4204 ok &= ObjectMoleculeUpdateNeighbors(I);
4205 n = I->Neighbor[index] + 1;
4206 while(ok && (index2 = I->Neighbor[n]) >= 0) {
4207 AtomInfoType *ai1 = I->AtomInfo + index2;
4208 if(ai1->protons == cAN_C) {
4209 ai->color = ai1->color;
4210 found = true;
4211 break;
4212 }
4213 n += 2;
4214 }
4215 if(ok && !found) {
4216 /* if no carbon nearby, then color according to the object color */
4217 ai->color = I->Color;
4218 }
4219 } else {
4220 AtomInfoAssignColors(I->G, ai);
4221 }
4222 }
4223 return ok;
4224 }
4225
4226
4227 /*========================================================================*/
ObjectMoleculePreposReplAtom(ObjectMolecule * I,int index,AtomInfoType * ai)4228 int ObjectMoleculePreposReplAtom(ObjectMolecule * I, int index, AtomInfoType * ai)
4229 {
4230 int n;
4231 int a1;
4232 AtomInfoType *ai1;
4233 float v0[3], v1[3], v[3];
4234 float d0[3], d, n0[3];
4235 int cnt;
4236 float t[3], sum[3];
4237 int a;
4238 int ncycle;
4239 int ok = true;
4240 ok &= ObjectMoleculeUpdateNeighbors(I);
4241 if (ok){
4242 for(a = 0; a < I->NCSet; a++) {
4243 if(I->CSet[a]) {
4244 if(ObjectMoleculeGetAtomVertex(I, a, index, v0)) {
4245 copy3f(v0, v); /* default is direct superposition */
4246 ncycle = -1;
4247 while(ncycle) {
4248 cnt = 0;
4249 n = I->Neighbor[index];
4250 n++; /* skip count */
4251 zero3f(sum);
4252 while(1) { /* look for an attached non-hydrogen as a base */
4253 a1 = I->Neighbor[n];
4254 n += 2;
4255 if(a1 < 0)
4256 break;
4257 ai1 = I->AtomInfo + a1;
4258 if(ai1->protons != 1)
4259 if(ObjectMoleculeGetAtomVertex(I, a, a1, v1)) {
4260 d = AtomInfoGetBondLength(I->G, ai, ai1);
4261 subtract3f(v0, v1, n0);
4262 normalize3f(n0);
4263 scale3f(n0, d, d0);
4264 add3f(d0, v1, t);
4265 add3f(t, sum, sum);
4266 cnt++;
4267 }
4268 }
4269 if(cnt) {
4270 scale3f(sum, 1.0F / cnt, sum);
4271 copy3f(sum, v0);
4272 if((cnt > 1) && (ncycle < 0))
4273 ncycle = 5;
4274 }
4275 ncycle = abs(ncycle) - 1;
4276 }
4277 if(cnt)
4278 copy3f(sum, v);
4279 ObjectMoleculeSetAtomVertex(I, a, index, v);
4280 }
4281 }
4282 }
4283 }
4284 return ok;
4285 }
4286
4287
4288 /*========================================================================*/
ObjectMoleculeSaveUndo(ObjectMolecule * I,int state,int log)4289 void ObjectMoleculeSaveUndo(ObjectMolecule * I, int state, int log)
4290 {
4291 CoordSet *cs;
4292 PyMOLGlobals *G = I->G;
4293 FreeP(I->UndoCoord[I->UndoIter]);
4294 I->UndoState[I->UndoIter] = -1;
4295 if(state < 0)
4296 state = 0;
4297 if(I->NCSet == 1)
4298 state = 0;
4299 state = state % I->NCSet;
4300 cs = I->CSet[state];
4301 if(cs) {
4302 I->UndoCoord[I->UndoIter] = pymol::malloc<float>(cs->NIndex * 3);
4303 memcpy(I->UndoCoord[I->UndoIter], cs->Coord, sizeof(float) * cs->NIndex * 3);
4304 I->UndoState[I->UndoIter] = state;
4305 I->UndoNIndex[I->UndoIter] = cs->NIndex;
4306 }
4307 I->UndoIter = cUndoMask & (I->UndoIter + 1);
4308 ExecutiveSetLastObjectEdited(G, (CObject *) I);
4309 if(log) {
4310 OrthoLineType line;
4311 if(SettingGetGlobal_i(I->G, cSetting_logging)) {
4312 sprintf(line, "cmd.push_undo(\"%s\",%d)\n", I->Name, state + 1);
4313 PLog(G, line, cPLog_no_flush);
4314 }
4315 }
4316
4317 }
4318
4319
4320 /*========================================================================*/
ObjectMoleculeUndo(ObjectMolecule * I,int dir)4321 void ObjectMoleculeUndo(ObjectMolecule * I, int dir)
4322 {
4323 CoordSet *cs;
4324 int state;
4325
4326 FreeP(I->UndoCoord[I->UndoIter]);
4327 I->UndoState[I->UndoIter] = -1;
4328 state = SceneGetState(I->G);
4329 if(state < 0)
4330 state = 0;
4331 if(I->NCSet == 1)
4332 state = 0;
4333 state = state % I->NCSet;
4334 cs = I->CSet[state];
4335 if(cs) {
4336 I->UndoCoord[I->UndoIter] = pymol::malloc<float>(cs->NIndex * 3);
4337 memcpy(I->UndoCoord[I->UndoIter], cs->Coord, sizeof(float) * cs->NIndex * 3);
4338 I->UndoState[I->UndoIter] = state;
4339 I->UndoNIndex[I->UndoIter] = cs->NIndex;
4340 }
4341
4342 I->UndoIter = cUndoMask & (I->UndoIter + dir);
4343 if(!I->UndoCoord[I->UndoIter])
4344 I->UndoIter = cUndoMask & (I->UndoIter - dir);
4345
4346 if(I->UndoState[I->UndoIter] >= 0) {
4347 state = I->UndoState[I->UndoIter];
4348 if(state < 0)
4349 state = 0;
4350
4351 if(I->NCSet == 1)
4352 state = 0;
4353 state = state % I->NCSet;
4354 cs = I->CSet[state];
4355 if(cs) {
4356 if(cs->NIndex == I->UndoNIndex[I->UndoIter]) {
4357 memcpy(cs->Coord.data(), I->UndoCoord[I->UndoIter], sizeof(float) * cs->NIndex * 3);
4358 I->UndoState[I->UndoIter] = -1;
4359 FreeP(I->UndoCoord[I->UndoIter]);
4360 cs->invalidateRep(cRepAll, cRepInvCoord);
4361 SceneChanged(I->G);
4362 }
4363 }
4364 }
4365 }
4366
4367
4368 /*========================================================================*/
ObjectMoleculeAddBond(ObjectMolecule * I,int sele0,int sele1,int order)4369 int ObjectMoleculeAddBond(ObjectMolecule * I, int sele0, int sele1, int order)
4370 {
4371 int a1, a2;
4372 AtomInfoType *ai1, *ai2;
4373 int s1, s2;
4374 int c = 0;
4375
4376 /* TO DO: optimize for performance -- we shouldn't be doing full
4377 table scans */
4378
4379 ai1 = I->AtomInfo.data();
4380 for(a1 = 0; a1 < I->NAtom; a1++) {
4381 s1 = ai1->selEntry;
4382 if(SelectorIsMember(I->G, s1, sele0)) {
4383 ai2 = I->AtomInfo.data();
4384 for(a2 = 0; a2 < I->NAtom; a2++) {
4385 s2 = ai2->selEntry;
4386 if(SelectorIsMember(I->G, s2, sele1)) {
4387 if(!I->Bond){
4388 I->Bond = pymol::vla<BondType>(1);
4389 }
4390 if(I->Bond) {
4391 BondType* bnd = I->Bond.check(I->NBond);
4392 BondTypeInit2(bnd, a1, a2, order);
4393 I->NBond++;
4394 c++;
4395 I->AtomInfo[a1].chemFlag = false;
4396 I->AtomInfo[a2].chemFlag = false;
4397 I->AtomInfo[a1].bonded = true;
4398 I->AtomInfo[a2].bonded = true;
4399 }
4400 }
4401 ai2++;
4402 }
4403 }
4404 ai1++;
4405 }
4406 if(c) {
4407 I->invalidate(cRepAll, cRepInvBondsNoNonbonded, -1);
4408 }
4409 return (c);
4410 }
4411
4412 /*========================================================================*/
ObjectMoleculeAddBondByIndices(ObjectMolecule * I,unsigned atm1,unsigned atm2,int order)4413 pymol::Result<> ObjectMoleculeAddBondByIndices(
4414 ObjectMolecule* I, unsigned atm1, unsigned atm2, int order)
4415 {
4416 if (atm1 >= I->NAtom || atm2 >= I->NAtom) {
4417 return pymol::make_error("atom index out of bounds");
4418 }
4419
4420 if (!I->Bond) {
4421 I->Bond = pymol::vla<BondType>(1);
4422 } else {
4423 I->Bond.check(I->NBond);
4424 }
4425
4426 if (!I->Bond) {
4427 return pymol::Error::MEMORY;
4428 }
4429
4430 auto& bnd = I->Bond[I->NBond++];
4431 BondTypeInit2(&bnd, atm1, atm2, order);
4432 bnd.stereo = 0;
4433
4434 I->AtomInfo[atm1].chemFlag = false;
4435 I->AtomInfo[atm2].chemFlag = false;
4436 I->AtomInfo[atm1].bonded = true;
4437 I->AtomInfo[atm2].bonded = true;
4438
4439 I->invalidate(cRepAll, cRepInvBondsNoNonbonded, -1);
4440
4441 return {};
4442 }
4443
4444 /*========================================================================*/
ObjectMoleculeAdjustBonds(ObjectMolecule * I,int sele0,int sele1,int mode,int order)4445 int ObjectMoleculeAdjustBonds(ObjectMolecule * I, int sele0, int sele1, int mode,
4446 int order)
4447 {
4448 int a0, a1;
4449 int cnt = 0;
4450 BondType *b0;
4451 int both;
4452 int s;
4453 int a;
4454
4455 if(I->Bond) {
4456 b0 = I->Bond.data();
4457 for(a = 0; a < I->NBond; a++) {
4458 a0 = b0->index[0];
4459 a1 = b0->index[1];
4460
4461 both = 0;
4462 s = I->AtomInfo[a0].selEntry;
4463 if(SelectorIsMember(I->G, s, sele0))
4464 both++;
4465 s = I->AtomInfo[a1].selEntry;
4466 if(SelectorIsMember(I->G, s, sele1))
4467 both++;
4468 if(both < 2) { /* reverse combo */
4469 both = 0;
4470 s = I->AtomInfo[a1].selEntry;
4471 if(SelectorIsMember(I->G, s, sele0))
4472 both++;
4473 s = I->AtomInfo[a0].selEntry;
4474 if(SelectorIsMember(I->G, s, sele1))
4475 both++;
4476 }
4477
4478 if(both == 2) {
4479 cnt++;
4480 switch (mode) {
4481 case 0: /* cycle */
4482 switch(SettingGet_i(I->G, I->Setting, NULL, cSetting_editor_bond_cycle_mode)) {
4483 case 1: /* 1 arom 2 3 */
4484 switch(b0->order) {
4485 case 1:
4486 b0->order = 4;
4487 break;
4488 case 4:
4489 b0->order = 2;
4490 break;
4491 case 2:
4492 b0->order = 3;
4493 break;
4494 default:
4495 b0->order = 1;
4496 break;
4497 }
4498 break;
4499 case 2: /* 1 2 3 arom */
4500 b0->order++;
4501 if(b0->order > 4)
4502 b0->order = 1;
4503 break;
4504 default: /* old way -> 1 2 3 */
4505 b0->order++;
4506 if(b0->order > 3)
4507 b0->order = 1;
4508 break;
4509 }
4510 I->AtomInfo[a0].chemFlag = false;
4511 I->AtomInfo[a1].chemFlag = false;
4512 break;
4513 case 1: /* set */
4514 b0->order = order;
4515 I->AtomInfo[a0].chemFlag = false;
4516 I->AtomInfo[a1].chemFlag = false;
4517 break;
4518 }
4519 }
4520 b0++;
4521 }
4522 if(cnt) {
4523 I->invalidate(cRepLine, cRepInvBonds, -1);
4524 I->invalidate(cRepCyl, cRepInvBonds, -1);
4525 I->invalidate(cRepNonbonded, cRepInvBonds, -1);
4526 I->invalidate(cRepNonbondedSphere, cRepInvBonds, -1);
4527 I->invalidate(cRepRibbon, cRepInvBonds, -1);
4528 I->invalidate(cRepCartoon, cRepInvBonds, -1);
4529 }
4530 }
4531
4532 return (cnt);
4533 }
4534
4535
4536 /*========================================================================*/
ObjectMoleculeRemoveBonds(ObjectMolecule * I,int sele0,int sele1)4537 int ObjectMoleculeRemoveBonds(ObjectMolecule * I, int sele0, int sele1)
4538 {
4539 int a0, a1;
4540 int offset = 0;
4541 BondType *b0, *b1;
4542 int both;
4543 int s;
4544 int a;
4545
4546 if(I->Bond) {
4547 offset = 0;
4548 b0 = I->Bond.data();
4549 b1 = I->Bond.data();
4550 for(a = 0; a < I->NBond; a++) {
4551 a0 = b0->index[0];
4552 a1 = b0->index[1];
4553
4554 both = 0;
4555 s = I->AtomInfo[a0].selEntry;
4556 if(SelectorIsMember(I->G, s, sele0))
4557 both++;
4558 s = I->AtomInfo[a1].selEntry;
4559 if(SelectorIsMember(I->G, s, sele1))
4560 both++;
4561 if(both < 2) { /* reverse combo */
4562 both = 0;
4563 s = I->AtomInfo[a1].selEntry;
4564 if(SelectorIsMember(I->G, s, sele0))
4565 both++;
4566 s = I->AtomInfo[a0].selEntry;
4567 if(SelectorIsMember(I->G, s, sele1))
4568 both++;
4569 }
4570
4571 if(both == 2) {
4572 AtomInfoPurgeBond(I->G, b0);
4573 offset--;
4574 b0++;
4575 I->AtomInfo[a0].chemFlag = false;
4576 I->AtomInfo[a1].chemFlag = false;
4577 } else if(offset) {
4578 *(b1++) = *(b0++); /* copy bond info */
4579 } else {
4580 *(b1++) = *(b0++); /* copy bond info */
4581 }
4582 }
4583 if(offset) {
4584 I->NBond += offset;
4585 VLASize(I->Bond, BondType, I->NBond);
4586 I->invalidate(cRepLine, cRepInvBonds, -1);
4587 I->invalidate(cRepCyl, cRepInvBonds, -1);
4588 I->invalidate(cRepNonbonded, cRepInvBonds, -1);
4589 I->invalidate(cRepNonbondedSphere, cRepInvBonds, -1);
4590 I->invalidate(cRepRibbon, cRepInvBonds, -1);
4591 I->invalidate(cRepCartoon, cRepInvBonds, -1);
4592 }
4593 }
4594
4595 return (-offset);
4596 }
4597
4598
4599 /*========================================================================*/
ObjectMoleculePurge(ObjectMolecule * I)4600 void ObjectMoleculePurge(ObjectMolecule * I)
4601 {
4602 PyMOLGlobals *G = I->G;
4603 int a, a0, a1;
4604 int *oldToNew = NULL;
4605 int offset = 0;
4606 BondType *b0, *b1;
4607 AtomInfoType *ai0, *ai1;
4608
4609 PRINTFD(I->G, FB_ObjectMolecule)
4610 " ObjMolPurge-Debug: step 1, delete object selection\n" ENDFD;
4611
4612 SelectorDelete(G, I->Name); /* remove the object selection and free up any selection entries */
4613 /* note that we don't delete atom selection members -- those may be needed in the new object */
4614
4615 PRINTFD(G, FB_ObjectMolecule)
4616 " ObjMolPurge-Debug: step 2, purge coordinate sets\n" ENDFD;
4617
4618 for(a = 0; a < I->NCSet; a++)
4619 if(I->CSet[a])
4620 CoordSetPurge(I->CSet[a]);
4621 if(I->CSTmpl) {
4622 CoordSetPurge(I->CSTmpl);
4623 }
4624 PRINTFD(I->G, FB_ObjectMolecule)
4625 " ObjMolPurge-Debug: step 3, old-to-new mapping\n" ENDFD;
4626
4627 oldToNew = pymol::malloc<int>(I->NAtom);
4628 ai0 = I->AtomInfo.data();
4629 ai1 = I->AtomInfo.data();
4630 for(a = 0; a < I->NAtom; a++) {
4631 if(ai0->deleteFlag) {
4632 AtomInfoPurge(G, ai0);
4633 offset--;
4634 ai0++;
4635 oldToNew[a] = -1;
4636 } else {
4637 if(offset) {
4638 *(ai1) = *(ai0);
4639 }
4640 oldToNew[a] = a + offset;
4641 ai0++;
4642 ai1++;
4643 }
4644 }
4645 if(offset) {
4646 I->NAtom += offset;
4647 VLASize(I->AtomInfo, AtomInfoType, I->NAtom);
4648 if (I->DiscreteFlag){
4649 ObjectMoleculeAdjustDiscreteAtmIdx(I, oldToNew, I->NAtom-offset); /* need to include previous atoms */
4650 I->setNDiscrete(I->NAtom);
4651 }
4652 for(a = 0; a < I->NCSet; a++)
4653 if(I->CSet[a])
4654 CoordSetAdjustAtmIdx(I->CSet[a], oldToNew, I->NAtom);
4655 if (I->CSTmpl) {
4656 CoordSetAdjustAtmIdx(I->CSTmpl, oldToNew, I->NAtom);
4657 }
4658 }
4659
4660 PRINTFD(I->G, FB_ObjectMolecule)
4661 " ObjMolPurge-Debug: step 4, bonds\n" ENDFD;
4662
4663 offset = 0;
4664 b0 = I->Bond.data();
4665 b1 = I->Bond.data();
4666 for(a = 0; a < I->NBond; a++) {
4667 a0 = b0->index[0];
4668 a1 = b0->index[1];
4669 if(a0 < 0 || a1 < 0 || (oldToNew[a0] < 0) || (oldToNew[a1] < 0)) {
4670 /* deleting bond */
4671 AtomInfoPurgeBond(I->G, b0);
4672 offset--;
4673 b0++;
4674 } else {
4675 if(offset) {
4676 *b1 = *b0;
4677 }
4678 b1->index[0] = oldToNew[a0]; /* copy bond info */
4679 b1->index[1] = oldToNew[a1];
4680 b0++;
4681 b1++;
4682 }
4683 }
4684 if(offset) {
4685 I->NBond += offset;
4686 VLASize(I->Bond, BondType, I->NBond);
4687 }
4688 FreeP(oldToNew);
4689
4690 PRINTFD(I->G, FB_ObjectMolecule)
4691 " ObjMolPurge-Debug: step 5, invalidate...\n" ENDFD;
4692
4693 I->invalidate(cRepAll, cRepInvAtoms, -1);
4694
4695 PRINTFD(I->G, FB_ObjectMolecule)
4696 " ObjMolPurge-Debug: leaving...\n" ENDFD;
4697
4698 }
4699
4700
4701 /*========================================================================*/
ObjectMoleculeGetAtomGeometry(ObjectMolecule * I,int state,int at)4702 int ObjectMoleculeGetAtomGeometry(ObjectMolecule * I, int state, int at)
4703 {
4704 /* this determines hybridization from coordinates in those few cases
4705 * where it is unambiguous */
4706
4707 int result = -1;
4708 int n, nn;
4709 float v0[3], v1[3], v2[3], v3[3];
4710 float d1[3], d2[3], d3[3];
4711 float cp1[3], cp2[3], cp3[3];
4712 float avg;
4713 float dp;
4714 n = I->Neighbor[at];
4715 nn = I->Neighbor[n++]; /* get count */
4716 if(nn == 4)
4717 result = cAtomInfoTetrahedral;
4718 else if(nn == 3) {
4719 /* check cross products */
4720 ObjectMoleculeGetAtomVertex(I, state, at, v0);
4721 ObjectMoleculeGetAtomVertex(I, state, I->Neighbor[n], v1);
4722 ObjectMoleculeGetAtomVertex(I, state, I->Neighbor[n + 2], v2);
4723 ObjectMoleculeGetAtomVertex(I, state, I->Neighbor[n + 4], v3);
4724 subtract3f(v1, v0, d1);
4725 subtract3f(v2, v0, d2);
4726 subtract3f(v3, v0, d3);
4727 cross_product3f(d1, d2, cp1);
4728 cross_product3f(d2, d3, cp2);
4729 cross_product3f(d3, d1, cp3);
4730 normalize3f(cp1);
4731 normalize3f(cp2);
4732 normalize3f(cp3);
4733 avg = (dot_product3f(cp1, cp2) +
4734 dot_product3f(cp2, cp3) + dot_product3f(cp3, cp1)) / 3.0F;
4735 if(avg > 0.75)
4736 result = cAtomInfoPlanar;
4737 else
4738 result = cAtomInfoTetrahedral;
4739 } else if(nn == 2) {
4740 ObjectMoleculeGetAtomVertex(I, state, at, v0);
4741 ObjectMoleculeGetAtomVertex(I, state, I->Neighbor[n], v1);
4742 ObjectMoleculeGetAtomVertex(I, state, I->Neighbor[n + 2], v2);
4743 subtract3f(v1, v0, d1);
4744 subtract3f(v2, v0, d2);
4745 normalize3f(d1);
4746 normalize3f(d2);
4747 dp = dot_product3f(d1, d2);
4748 if(dp < -0.75)
4749 result = cAtomInfoLinear;
4750 }
4751 return (result);
4752 }
4753
4754
4755 /*========================================================================*/
compute_avg_center_dot_cross_fn(ObjectMolecule * I,CoordSet * cs,int n_atom,int * atix)4756 static float compute_avg_center_dot_cross_fn(ObjectMolecule * I, CoordSet * cs,
4757 int n_atom, int *atix)
4758 {
4759 float result = 0.0F;
4760 float *v[9];
4761 int missing_flag = false;
4762 {
4763 int a, i;
4764 for(i = 0; i < n_atom; i++) {
4765 int a1 = atix[i];
4766 a = cs->atmToIdx(a1);
4767 if(a < 0) {
4768 missing_flag = true;
4769 break;
4770 } else {
4771 v[i] = cs->coordPtr(a);
4772 }
4773 }
4774 }
4775 if(!missing_flag) {
4776 int i;
4777 float d10[3], d20[3], cp[8][3];
4778 float avg = 0.0;
4779
4780 v[n_atom] = v[1];
4781 for(i = 1; i < n_atom; i++) {
4782 subtract3f(v[i], v[0], d10);
4783 subtract3f(v[i + 1], v[0], d20);
4784 normalize3f(d10);
4785 normalize3f(d20);
4786 cross_product3f(d10, d20, cp[i]);
4787 normalize3f(cp[i]);
4788 if(i > 1) {
4789 if(dot_product3f(cp[i - 1], cp[i]) < 0.0)
4790 invert3f(cp[i]);
4791 }
4792 }
4793 copy3f(cp[1], cp[n_atom]);
4794 for(i = 1; i < n_atom; i++) {
4795 avg += dot_product3f(cp[i], cp[i + 1]);
4796 }
4797 result = avg / (n_atom - 1);
4798 }
4799 return result;
4800 }
4801
verify_planer_bonds(ObjectMolecule * I,CoordSet * cs,int n_atom,int * atix,int * neighbor,float * dir,float cutoff)4802 static int verify_planer_bonds(ObjectMolecule * I, CoordSet * cs,
4803 int n_atom, int *atix, int *neighbor,
4804 float *dir, float cutoff)
4805 {
4806 int a, i;
4807 for(i = 0; i < n_atom; i++) {
4808 int a1 = atix[i];
4809 a = cs->atmToIdx(a1);
4810 if(a >= 0) {
4811 const float* v0 = cs->coordPtr(a);
4812 int n = neighbor[a1] + 1;
4813 int a2;
4814 while((a2 = neighbor[n]) >= 0) {
4815 n += 2;
4816 a = cs->atmToIdx(a2);
4817 if(a >= 0) {
4818 const float* v1 = cs->coordPtr(a);
4819 float d10[] = { 0.f, 0.f, 0.f };
4820 subtract3f(v1, v0, d10);
4821 normalize3f(d10);
4822 {
4823 float dot = fabs(dot_product3f(d10, dir));
4824 if(dot > cutoff) {
4825 switch (I->AtomInfo[a1].protons) {
4826 case cAN_C:
4827 case cAN_N:
4828 case cAN_O:
4829 case cAN_S:
4830 switch (I->AtomInfo[a2].protons) {
4831 case cAN_C:
4832 case cAN_N:
4833 case cAN_O:
4834 case cAN_S:
4835 return 0;
4836 break;
4837 }
4838 break;
4839 }
4840 }
4841 }
4842 }
4843 }
4844 }
4845 }
4846 return 1;
4847 }
4848
compute_avg_ring_dot_cross_fn(ObjectMolecule * I,CoordSet * cs,int n_atom,int * atix,float * dir)4849 static float compute_avg_ring_dot_cross_fn(ObjectMolecule * I, CoordSet * cs,
4850 int n_atom, int *atix, float *dir)
4851 {
4852 float result = 0.0F;
4853 float *v[9];
4854 int missing_flag = false;
4855 {
4856 int a, i;
4857 for(i = 0; i < n_atom; i++) {
4858 int a1 = atix[i];
4859 a = cs->atmToIdx(a1);
4860 if(a < 0) {
4861 missing_flag = true;
4862 break;
4863 } else {
4864 v[i] = cs->coordPtr(a);
4865 }
4866 }
4867 }
4868 if(!missing_flag) {
4869 int i;
4870 float d10[3], d21[3], cp[8][3];
4871 float avg = 0.0;
4872
4873 v[n_atom] = v[0];
4874 v[n_atom + 1] = v[1];
4875 for(i = 0; i < n_atom; i++) {
4876 subtract3f(v[i + 0], v[i + 1], d10);
4877 subtract3f(v[i + 2], v[i + 1], d21);
4878 normalize3f(d10);
4879 normalize3f(d21);
4880 cross_product3f(d10, d21, cp[i]);
4881 normalize3f(cp[i]);
4882 if(i) {
4883 if(dot_product3f(cp[i - 1], cp[i]) < 0.0)
4884 invert3f(cp[i]);
4885 }
4886 add3f(cp[i], dir, dir);
4887 }
4888 copy3f(cp[0], cp[n_atom]);
4889 for(i = 0; i < n_atom; i++) {
4890 avg += dot_product3f(cp[i], cp[i + 1]);
4891 }
4892 result = avg / n_atom;
4893 }
4894 normalize3f(dir);
4895 return result;
4896 }
4897
4898 typedef struct {
4899 int cyclic, planer, aromatic;
4900 } ObservedInfo;
4901
ObjectMoleculeGuessValences(ObjectMolecule * I,int state,int * flag1,int * flag2,int reset)4902 void ObjectMoleculeGuessValences(ObjectMolecule * I, int state, int *flag1, int *flag2,
4903 int reset)
4904 {
4905 /* this a hacked 80% solution ...it will get things wrong, but it is
4906 better than nothing! */
4907
4908 const float planer_cutoff = 0.96F;
4909 CoordSet *cs = NULL;
4910 ObservedInfo *obs_atom = NULL;
4911 ObservedInfo *obs_bond = NULL;
4912 int *flag = NULL;
4913 int warning1 = 0, warning2 = 0;
4914
4915 /* WORKAROUND of a possible -funroll-loops inlining optimizer bug in gcc 3.3.3 */
4916
4917 float (*compute_avg_center_dot_cross) (ObjectMolecule *, CoordSet *, int, int *);
4918 float (*compute_avg_ring_dot_cross) (ObjectMolecule *, CoordSet *, int, int *, float *);
4919
4920 compute_avg_center_dot_cross = compute_avg_center_dot_cross_fn;
4921 compute_avg_ring_dot_cross = compute_avg_ring_dot_cross_fn;
4922
4923
4924 /* end WORKAROUND */
4925
4926 ObjectMoleculeUpdateNeighbors(I);
4927
4928 if((state >= 0) && (state < I->NCSet)) {
4929 cs = I->CSet[state];
4930 }
4931 if(cs) {
4932 obs_atom = pymol::calloc<ObservedInfo>(I->NAtom);
4933 obs_bond = pymol::calloc<ObservedInfo>(I->NBond);
4934 }
4935 flag = pymol::calloc<int>(I->NAtom);
4936 if(flag) {
4937 if(!flag1) {
4938 int a, *flag_a = flag;
4939 const AtomInfoType *ai = I->AtomInfo.data();
4940 /* default behavior: only reset hetatm valences */
4941 for(a = 0; a < I->NAtom; a++) {
4942 *(flag_a++) = (ai++)->hetatm;
4943 }
4944 } else if(flag1 && flag2) {
4945 int a, *flag_a = flag, *flag1_a = flag1, *flag2_a = flag2;
4946 for(a = 0; a < I->NAtom; a++) {
4947 *(flag_a++) = (*(flag1_a++) || *(flag2_a++));
4948 }
4949 } else if(flag1) {
4950 int a, *flag_a = flag, *flag1_a = flag1;
4951 for(a = 0; a < I->NAtom; a++) {
4952 *(flag_a++) = *(flag1_a++);
4953 }
4954 }
4955 }
4956 if(!flag1)
4957 flag1 = flag;
4958 if(!flag2)
4959 flag2 = flag;
4960 if(reset) {
4961 /* reset chemistry information and bond orders for selected atoms */
4962 {
4963 int a;
4964 AtomInfoType *ai = I->AtomInfo.data();
4965 for(a = 0; a < I->NAtom; a++) {
4966 if(flag[a])
4967 ai->chemFlag = 0;
4968 ai++;
4969 }
4970 }
4971 {
4972 int b;
4973 BondType *bi = I->Bond.data();
4974 for(b = 0; b < I->NBond; b++) {
4975 int at0 = bi->index[0];
4976 int at1 = bi->index[1];
4977 if((flag1[at0] && flag2[at1]) || (flag1[at1] && flag2[at0]))
4978 bi->order = 1;
4979 bi++;
4980 }
4981 }
4982 }
4983 if(cs && obs_bond && obs_atom && flag && flag1 && flag2) {
4984 int a;
4985 int *neighbor = I->Neighbor;
4986 AtomInfoType *atomInfo = I->AtomInfo.data();
4987 BondType *bondInfo = I->Bond.data();
4988
4989 for(a = 0; a < I->NAtom; a++) {
4990 AtomInfoType *ai = atomInfo + a;
4991 if((!ai->chemFlag) && (flag[a])) {
4992 /* determine whether or not atom participates in a planer system with 5 or 6 atoms */
4993
4994 {
4995 int mem[9];
4996 int nbr[7];
4997 const int* atmToIdx = nullptr;
4998 const int ESCAPE_MAX = 500;
4999
5000 int escape_count;
5001
5002 if(!I->DiscreteFlag)
5003 atmToIdx = cs->AtmToIdx;
5004
5005 escape_count = ESCAPE_MAX; /* don't get bogged down with structures
5006 that have unreasonable connectivity */
5007 mem[0] = a;
5008 nbr[0] = neighbor[mem[0]] + 1;
5009 while(((mem[1] = neighbor[nbr[0]]) >= 0) &&
5010 ((!atmToIdx) || (atmToIdx[mem[0]] >= 0))) {
5011 nbr[1] = neighbor[mem[1]] + 1;
5012 while(((mem[2] = neighbor[nbr[1]]) >= 0) &&
5013 ((!atmToIdx) || (atmToIdx[mem[1]] >= 0))) {
5014 if(mem[2] != mem[0]) {
5015 nbr[2] = neighbor[mem[2]] + 1;
5016 while(((mem[3] = neighbor[nbr[2]]) >= 0) &&
5017 ((!atmToIdx) || (atmToIdx[mem[2]] >= 0))) {
5018 if(mem[3] != mem[1]) {
5019 nbr[3] = neighbor[mem[3]] + 1;
5020 while(((mem[4] = neighbor[nbr[3]]) >= 0) &&
5021 ((!atmToIdx) || (atmToIdx[mem[3]] >= 0))) {
5022 if((mem[4] != mem[2]) && (mem[4] != mem[1]) && (mem[4] != mem[0])) {
5023 nbr[4] = neighbor[mem[4]] + 1;
5024 while(((mem[5] = neighbor[nbr[4]]) >= 0) &&
5025 ((!atmToIdx) || (atmToIdx[mem[4]] >= 0))) {
5026 if(!(escape_count--))
5027 goto escape;
5028 if((mem[5] != mem[3]) && (mem[5] != mem[2])
5029 && (mem[5] != mem[1])) {
5030 if(mem[5] == mem[0]) { /* five-cycle */
5031 int i;
5032 float dir[] = { 0.f, 0.f, 0.f } ;
5033 float avg_dot_cross =
5034 compute_avg_ring_dot_cross(I, cs, 5, mem, dir);
5035 for(i = 0; i < 5; i++) {
5036 obs_atom[mem[i]].cyclic = true;
5037 obs_bond[neighbor[nbr[0] + 1]].cyclic = true;
5038 }
5039 if(avg_dot_cross > planer_cutoff) {
5040 if(verify_planer_bonds
5041 (I, cs, 5, mem, neighbor, dir, 0.35F)) {
5042 for(i = 0; i < 5; i++) {
5043 obs_atom[mem[i]].planer = true;
5044 obs_bond[neighbor[nbr[i] + 1]].planer = true;
5045 }
5046 }
5047 }
5048 }
5049
5050 nbr[5] = neighbor[mem[5]] + 1;
5051 while(((mem[6] = neighbor[nbr[5]]) >= 0) &&
5052 ((!atmToIdx) || (atmToIdx[mem[5]] >= 0))) {
5053 if((mem[6] != mem[4]) && (mem[6] != mem[3])
5054 && (mem[6] != mem[2]) && (mem[6] != mem[1])) {
5055 if(mem[6] == mem[0]) { /* six-cycle */
5056 int i;
5057 float dir[3] = { 0.f, 0.f, 0.f } ;
5058 float avg_dot_cross =
5059 compute_avg_ring_dot_cross(I, cs, 6, mem, dir);
5060 for(i = 0; i < 6; i++) {
5061 obs_atom[mem[i]].cyclic = true;
5062 obs_bond[neighbor[nbr[i] + 1]].cyclic = true;
5063 }
5064 if(avg_dot_cross > planer_cutoff) {
5065 if(verify_planer_bonds
5066 (I, cs, 6, mem, neighbor, dir, 0.35F)) {
5067 for(i = 0; i < 6; i++) {
5068 obs_atom[mem[i]].planer = true;
5069 obs_bond[neighbor[nbr[i] + 1]].planer = true;
5070 }
5071 }
5072 }
5073 }
5074 }
5075 nbr[5] += 2;
5076 }
5077 }
5078 nbr[4] += 2;
5079 }
5080 }
5081 nbr[3] += 2;
5082 }
5083 }
5084 nbr[2] += 2;
5085 }
5086 }
5087 nbr[1] += 2;
5088 }
5089 nbr[0] += 2;
5090 }
5091
5092 escape:
5093 escape_count = ESCAPE_MAX; /* don't get bogged down with structures
5094 that have unreasonable connectivity */
5095 warning1 = 1;
5096 }
5097 }
5098 }
5099
5100 for(a = 0; a < I->NAtom; a++) {
5101 AtomInfoType *ai = atomInfo + a;
5102 if((!ai->chemFlag) && flag[a]) {
5103 ObservedInfo *ob_at = obs_atom + a;
5104
5105 {
5106 switch (ai->protons) {
5107 case cAN_P:
5108 case cAN_S:
5109 case cAN_N:
5110 case cAN_C:
5111 {
5112 int atm[5];
5113 int bnd[5];
5114 int n = neighbor[a];
5115 int nn = neighbor[n++];
5116 if(nn > 4)
5117 nn = 4;
5118 atm[0] = a;
5119 {
5120 int i;
5121 for(i = 1; i <= nn; i++) {
5122 atm[i] = neighbor[n];
5123 bnd[i] = neighbor[n + 1];
5124 n += 2;
5125 }
5126 }
5127 {
5128 int o1_at = -1, o2_at = -1, o3_at = -1, o4_at = -1;
5129 int o1_bd = 0, o2_bd = 0, o3_bd = 0, o4_bd = 0;
5130 float o1_len = 0.0F, o2_len = 0.0F;
5131 float n1_v[3] = { 0.0F, 0.0F, 0.0F };
5132 int n1_at = -1, n2_at = -1, n3_at = -1;
5133 int n1_bd = 0, n2_bd = 0, n3_bd = 0;
5134 float n1_len = 0.0F, n2_len = 0.0F, n3_len = 0.0F;
5135 int c1_at = -1, c2_at = -1;
5136 float c1_v[3] = { 0.0F, 0.0F, 0.0F };
5137 float *v0 = NULL;
5138
5139 {
5140 int idx0 = cs->atmToIdx(a);
5141 if(idx0 >= 0)
5142 v0 = cs->coordPtr(idx0);
5143 }
5144 {
5145 int i;
5146 for(i = 1; i <= nn; i++) {
5147 float *v1 = NULL;
5148
5149 {
5150 int idx1 = cs->atmToIdx(atm[i]);
5151 if(idx1 >= 0)
5152 v1 = cs->coordPtr(idx1);
5153 }
5154 if(v0 && v1) {
5155 float diff[3];
5156 subtract3f(v1, v0, diff);
5157 {
5158
5159 float bond_len = length3f(diff);
5160
5161 switch (atomInfo[atm[i]].protons) {
5162 case cAN_C:
5163 if(c1_at < 0) {
5164 c1_at = atm[i];
5165 copy3f(diff, c1_v);
5166 } else if(c2_at < 0) {
5167 c2_at = atm[i];
5168 }
5169 break;
5170 case cAN_O:
5171 if(o1_at < 0) {
5172 o1_at = atm[i];
5173 o1_len = bond_len;
5174 o1_bd = bnd[i];
5175 } else if(o2_at < 0) {
5176 o2_at = atm[i];
5177 o2_len = bond_len;
5178 o2_bd = bnd[i];
5179 } else if(o3_at < 0) {
5180 o3_at = atm[i];
5181 o3_bd = bnd[i];
5182 } else if(o4_at < 0) {
5183 o4_at = atm[i];
5184 o4_bd = bnd[i];
5185 }
5186 break;
5187 case cAN_N:
5188 if(n1_at < 0) {
5189 copy3f(diff, n1_v);
5190 n1_at = atm[i];
5191 n1_len = bond_len;
5192 n1_bd = bnd[i];
5193 } else if(n2_at < 0) {
5194 n2_at = atm[i];
5195 n2_len = bond_len;
5196 n2_bd = bnd[i];
5197 } else if(n3_at < 0) {
5198 n3_at = atm[i];
5199 n3_len = bond_len;
5200 n3_bd = bnd[i];
5201 }
5202 break;
5203 }
5204 }
5205 }
5206 }
5207 }
5208 {
5209 float avg_dot_cross = 0.0F;
5210
5211 switch (ai->protons) {
5212 case cAN_C: /* planer carbons */
5213 if(nn == 3) {
5214 avg_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm);
5215
5216 if(avg_dot_cross > planer_cutoff) {
5217
5218 if((n1_at >= 0) && (o1_at >= 0) && (o2_at < 0)) {
5219 /* simple amide? */
5220 if(o1_len < 1.38F) {
5221 if(neighbor[neighbor[o1_at]] == 1)
5222 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5223 bondInfo[o1_bd].order = 2;
5224 }
5225 } else if((n1_at >= 0) && (o1_at >= 0) && (o2_at >= 0)) {
5226 /* carbamyl */
5227 if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1) &&
5228 (o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1)) {
5229 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5230 bondInfo[o1_bd].order = 4;
5231 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5232 bondInfo[o2_bd].order = 4;
5233 } else if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1)) {
5234 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5235 bondInfo[o1_bd].order = 2;
5236 } else if((o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1))
5237 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5238 bondInfo[o2_bd].order = 2;
5239 } else if((n1_at < 0) && (o1_at >= 0) && (o2_at < 0)) {
5240 /* ketone */
5241 if((o1_len < 1.31F) && (neighbor[neighbor[o1_at]] == 1)) {
5242 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5243 bondInfo[o1_bd].order = 2;
5244 }
5245 } else if((o1_at >= 0) && (o2_at >= 0) && (n1_at < 0)) {
5246 /* simple carboxylate? */
5247 if((o1_len < 1.38F) && (o2_len < 1.38F) &&
5248 (neighbor[neighbor[o1_at]] == 1) &&
5249 (neighbor[neighbor[o2_at]] == 1)) {
5250 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5251 bondInfo[o1_bd].order = 4;
5252 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5253 bondInfo[o2_bd].order = 4;
5254 } else if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1)) { /* esters */
5255 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5256 bondInfo[o1_bd].order = 2;
5257 } else if((o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1)) {
5258 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5259 bondInfo[o2_bd].order = 2;
5260 }
5261 } else if((n1_at >= 0) && (n2_at >= 0) && (n3_at < 0)
5262 && (c1_at >= 0) && (n1_len < 1.43F) && (n2_len < 1.43F)
5263 && obs_atom[c1_at].planer && obs_atom[c1_at].cyclic
5264 && (!ob_at->cyclic) && (!obs_atom[n1_at].cyclic)
5265 && (!obs_atom[n2_at].cyclic)) {
5266 if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
5267 bondInfo[n1_bd].order = 4;
5268 atomInfo[n1_at].valence = 3;
5269 atomInfo[n1_at].geom = cAtomInfoPlanar;
5270 atomInfo[n1_at].chemFlag = 2;
5271 if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
5272 bondInfo[n2_bd].order = 4;
5273 atomInfo[n2_at].valence = 3;
5274 atomInfo[n2_at].geom = cAtomInfoPlanar;
5275 atomInfo[n2_at].chemFlag = 2;
5276 } else if((n1_at >= 0) && (n2_at >= 0) && (n3_at >= 0)) {
5277 /* guanido with no hydrogens */
5278 if((n1_len < 1.44F) && (n2_len < 1.44F) && (n3_len < 1.44F)) {
5279 if((neighbor[neighbor[n1_at]] == 1) &&
5280 (neighbor[neighbor[n2_at]] == 1) &&
5281 (neighbor[neighbor[n3_at]] >= 2)) {
5282 if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
5283 bondInfo[n1_bd].order = 4;
5284 atomInfo[n1_at].valence = 3;
5285 atomInfo[n1_at].geom = cAtomInfoPlanar;
5286 atomInfo[n1_at].chemFlag = 2;
5287 if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
5288 bondInfo[n2_bd].order = 4;
5289 atomInfo[n2_at].valence = 3;
5290 atomInfo[n2_at].geom = cAtomInfoPlanar;
5291 atomInfo[n2_at].chemFlag = 2;
5292 } else if((neighbor[neighbor[n1_at]] == 1) &&
5293 (neighbor[neighbor[n2_at]] >= 2) &&
5294 (neighbor[neighbor[n3_at]] == 1)) {
5295 if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
5296 bondInfo[n1_bd].order = 4;
5297 atomInfo[n1_at].valence = 3;
5298 atomInfo[n1_at].geom = cAtomInfoPlanar;
5299 atomInfo[n1_at].chemFlag = 2;
5300 if((flag1[a] && flag2[n3_at]) || (flag2[a] && flag1[n3_at]))
5301 bondInfo[n3_bd].order = 4;
5302 atomInfo[n3_at].valence = 3;
5303 atomInfo[n3_at].geom = cAtomInfoPlanar;
5304 atomInfo[n3_at].chemFlag = 2;
5305 } else if((neighbor[neighbor[n1_at]] >= 2) &&
5306 (neighbor[neighbor[n2_at]] == 1) &&
5307 (neighbor[neighbor[n3_at]] == 1)) {
5308 if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
5309 bondInfo[n2_bd].order = 4;
5310 atomInfo[n2_at].valence = 3;
5311 atomInfo[n2_at].geom = cAtomInfoPlanar;
5312 atomInfo[n2_at].chemFlag = 2;
5313 if((flag1[a] && flag2[n3_at]) || (flag2[a] && flag1[n3_at]))
5314 bondInfo[n3_bd].order = 4;
5315 atomInfo[n3_at].valence = 3;
5316 atomInfo[n3_at].geom = cAtomInfoPlanar;
5317 atomInfo[n3_at].chemFlag = 2;
5318 }
5319 }
5320 }
5321 }
5322 }
5323 /* any carbon */
5324
5325 /* handle imines and nitriles */
5326
5327 if((nn >= 2) && (nn <= 3) && (n1_at >= 0) && (o1_at < 0) &&
5328 (n2_at < 0) && (n1_len < 1.36F) &&
5329 (!ob_at->cyclic) && (!obs_bond[n1_bd].cyclic)
5330 && (!obs_atom[n1_at].planer) && ((nn == 2)
5331 || ((nn == 3)
5332 && (avg_dot_cross >
5333 planer_cutoff)))) {
5334
5335 float n1_dot_cross = 1.0F;
5336 int n2 = neighbor[n1_at];
5337 int nn2 = neighbor[n2++];
5338
5339 { /* check nitrogen planarity */
5340 int atm2[5];
5341 if(nn2 > 2) {
5342 nn2 = 3;
5343 atm2[0] = n1_at;
5344 {
5345 int i2;
5346 for(i2 = 1; i2 <= nn2; i2++) {
5347 atm2[i2] = neighbor[n2];
5348 n2 += 2;
5349 }
5350 }
5351 n1_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm2);
5352 }
5353 }
5354 if(n1_dot_cross > planer_cutoff) {
5355 if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
5356 bondInfo[n1_bd].order = 2;
5357 if((n1_len < 1.24F) && (c1_at >= 0) && (nn2 == 1)) {
5358 normalize3f(n1_v);
5359 normalize3f(c1_v);
5360 if(dot_product3f(n1_v, c1_v) < -0.9) {
5361 if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
5362 bondInfo[n1_bd].order = 3;
5363 }
5364 }
5365 }
5366 }
5367 break;
5368 case cAN_N:
5369 if(nn == 3) {
5370 avg_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm);
5371
5372 if((avg_dot_cross > planer_cutoff)) {
5373 if((o1_at >= 0) && (o2_at >= 0) && (o3_at < 0)) {
5374 /* nitro */
5375 if(neighbor[neighbor[o1_at]] == 1) {
5376 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5377 bondInfo[o1_bd].order = 4;
5378 }
5379 if(neighbor[neighbor[o2_at]] == 1) {
5380 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5381 bondInfo[o2_bd].order = 4;
5382 }
5383 }
5384 }
5385 }
5386 break;
5387 case cAN_S:
5388 case cAN_P:
5389 if((o1_at >= 0) && (o2_at >= 0) && (o3_at >= 0) && (o4_at >= 0)) {
5390 /* sulfate, phosphate */
5391 int o1 = -1, o2 = -1, o3 = -1;
5392 int a1 = 0;
5393 if(neighbor[neighbor[o1_at]] == 1) {
5394 o1 = o1_bd;
5395 a1 = o1_at;
5396 }
5397 if(neighbor[neighbor[o2_at]] == 1) {
5398 if(o1 < 0) {
5399 o1 = o2_bd;
5400 a1 = o2_at;
5401 } else if(o2 < 0) {
5402 o2 = o2_bd;
5403 }
5404 }
5405 if(neighbor[neighbor[o3_at]] == 1) {
5406 if(o1 < 0) {
5407 o1 = o3_bd;
5408 a1 = o3_at;
5409 } else if(o2 < 0)
5410 o2 = o3_bd;
5411 else if(o3 < 0)
5412 o3 = o3_bd;
5413 }
5414 if(neighbor[neighbor[o4_at]] == 1) {
5415 if(o1 < 0) {
5416 o1 = o4_bd;
5417 a1 = o4_at;
5418 } else if(o2 < 0)
5419 o2 = o4_bd;
5420 else if(o3 < 0)
5421 o3 = o4_bd;
5422 }
5423 if(o2 >= 0) {
5424 if((flag1[a] && flag2[a1]) || (flag2[a] && flag1[a1]))
5425 bondInfo[o1].order = 2;
5426 if(o2 == o2_bd) {
5427 atomInfo[o2_at].formalCharge = -1;
5428 } else if(o2 == o3_bd) {
5429 atomInfo[o3_at].formalCharge = -1;
5430 } else if(o2 == o4_bd) {
5431 atomInfo[o4_at].formalCharge = -1;
5432 }
5433 }
5434 } else if((o1_at >= 0) && (o2_at >= 0) && (o3_at >= 0) && (o4_at < 0)) {
5435 /* sulfonamide */
5436 int o1 = -1, o2 = -1;
5437 int a1 = 0, a2 = 0;
5438 if(neighbor[neighbor[o1_at]] == 1) {
5439 o1 = o1_bd;
5440 a1 = o1_at;
5441 }
5442 if(neighbor[neighbor[o2_at]] == 1) {
5443 if(o1 < 0) {
5444 o1 = o2_bd;
5445 a1 = o2_at;
5446 } else if(o2 < 0) {
5447 o2 = o2_bd;
5448 a2 = o2_at;
5449 }
5450 }
5451 if(neighbor[neighbor[o3_at]] == 1) {
5452 if(o1 < 0) {
5453 o1 = o3_bd;
5454 a1 = o3_at;
5455 } else if(o2 < 0) {
5456 o2 = o3_bd;
5457 a2 = o3_at;
5458 }
5459 }
5460 if(o1 >= 0) {
5461 if((flag1[a] && flag2[a1]) || (flag2[a] && flag1[a1]))
5462 bondInfo[o1].order = 2;
5463 }
5464 if(o2 >= 0) {
5465 if((flag1[a] && flag2[a2]) || (flag2[a] && flag1[a2]))
5466 bondInfo[o2].order = 2;
5467 }
5468 } else if((o1_at >= 0) && (o2_at >= 0) && (o3_at < 0)) {
5469 /* sulphone */
5470 if(neighbor[neighbor[o1_at]] == 1) {
5471 if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
5472 bondInfo[o1_bd].order = 2;
5473 }
5474 if(neighbor[neighbor[o2_at]] == 1) {
5475 if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
5476 bondInfo[o2_bd].order = 2;
5477 }
5478 }
5479 break;
5480 }
5481 }
5482 }
5483 }
5484 }
5485 /* this sets aromatic bonds for cyclic planer systems */
5486
5487 if(ob_at->cyclic && ob_at->planer) {
5488
5489 switch (ai->protons) {
5490 case cAN_C:
5491 case cAN_N:
5492 case cAN_O:
5493 case cAN_S:
5494 {
5495 int n, a0, b0;
5496 n = neighbor[a] + 1;
5497 while(1) {
5498 a0 = neighbor[n];
5499 if(a0 < 0)
5500 break;
5501 b0 = neighbor[n + 1];
5502 n += 2;
5503 if(obs_atom[a0].cyclic && obs_atom[a0].planer && obs_bond[b0].cyclic) {
5504 obs_atom[a0].aromatic = true;
5505 switch (I->AtomInfo[a0].protons) {
5506 case cAN_C:
5507 case cAN_N:
5508 case cAN_O:
5509 case cAN_S:
5510 if((flag1[a] && flag2[a0]) || (flag2[a] && flag1[a0]))
5511 I->Bond[b0].order = 4;
5512 break;
5513 }
5514 }
5515 }
5516 }
5517 break;
5518 }
5519 }
5520 }
5521 }
5522 }
5523
5524 /* now try to address some simple cases with aromatic nitrogens */
5525 for(a = 0; a < I->NAtom; a++) {
5526 AtomInfoType *ai = atomInfo + a;
5527 if((!ai->chemFlag) && (ai->protons == cAN_N) &&
5528 (ai->formalCharge == 0) && flag[a] &&
5529 obs_atom[a].cyclic && obs_atom[a].aromatic) {
5530
5531 int n = neighbor[a];
5532 int nn = neighbor[n++];
5533
5534 if(nn == 2) { /* only two explicit neighbors */
5535
5536 int mem[9];
5537 int nbr[7];
5538 const int* atmToIdx = nullptr;
5539 const int ESCAPE_MAX = 500;
5540
5541 int escape_count;
5542
5543 if(!I->DiscreteFlag)
5544 atmToIdx = cs->AtmToIdx;
5545
5546 escape_count = ESCAPE_MAX; /* don't get bogged down with structures
5547 that have unreasonable connectivity */
5548 mem[0] = a;
5549 nbr[0] = neighbor[mem[0]] + 1;
5550 while(((mem[1] = neighbor[nbr[0]]) >= 0) &&
5551 ((!atmToIdx) || (atmToIdx[mem[0]] >= 0))) {
5552 nbr[1] = neighbor[mem[1]] + 1;
5553 while(((mem[2] = neighbor[nbr[1]]) >= 0) &&
5554 ((!atmToIdx) || (atmToIdx[mem[1]] >= 0))) {
5555 if(mem[2] != mem[0]) {
5556 nbr[2] = neighbor[mem[2]] + 1;
5557 while(((mem[3] = neighbor[nbr[2]]) >= 0) &&
5558 ((!atmToIdx) || (atmToIdx[mem[2]] >= 0))) {
5559 if(mem[3] != mem[1]) {
5560 nbr[3] = neighbor[mem[3]] + 1;
5561 while(((mem[4] = neighbor[nbr[3]]) >= 0) &&
5562 ((!atmToIdx) || (atmToIdx[mem[3]] >= 0))) {
5563 if((mem[4] != mem[2]) && (mem[4] != mem[1]) && (mem[4] != mem[0])) {
5564 nbr[4] = neighbor[mem[4]] + 1;
5565 while(((mem[5] = neighbor[nbr[4]]) >= 0) &&
5566 ((!atmToIdx) || (atmToIdx[mem[4]] >= 0))) {
5567 if(!(escape_count--))
5568 goto escape2; /* BUG FIX: need a new escape2, instead of
5569 mistakenly going back to the first escape,
5570 which is in the loop above */
5571 if((mem[5] != mem[3]) && (mem[5] != mem[2])
5572 && (mem[5] != mem[1])) {
5573 if(mem[5] == mem[0] && (!ai->chemFlag)) {
5574
5575 /* unassigned aromatic nitrogen-containing five-cycle */
5576
5577 /* c1ccnc1 becomes c1ccn[H]c1 */
5578
5579 if((atomInfo[mem[1]].protons == cAN_C) &&
5580 (atomInfo[mem[2]].protons == cAN_C) &&
5581 (atomInfo[mem[3]].protons == cAN_C) &&
5582 (atomInfo[mem[4]].protons == cAN_C) &&
5583 obs_atom[mem[1]].aromatic &&
5584 obs_atom[mem[2]].aromatic &&
5585 obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
5586 ai->valence = 3;
5587 ai->chemFlag = 2;
5588 ai->geom = cAtomInfoPlanar;
5589 }
5590
5591 /* c1ncnc1 becomes c1n[H]cnc1 */
5592
5593 if((atomInfo[mem[1]].protons == cAN_C) &&
5594 (atomInfo[mem[2]].protons == cAN_N) &&
5595 (atomInfo[mem[2]].formalCharge == 0) &&
5596 (!atomInfo[mem[2]].chemFlag) &&
5597 (atomInfo[mem[3]].protons == cAN_C) &&
5598 (atomInfo[mem[4]].protons == cAN_C) &&
5599 obs_atom[mem[1]].aromatic &&
5600 obs_atom[mem[2]].aromatic &&
5601 obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
5602
5603 int n2 = neighbor[mem[2]];
5604 int nn2 = neighbor[n2++];
5605 if(nn2 == 2) { /* second nitrogen also ambiguous */
5606 ai->valence = 3;
5607 ai->chemFlag = 2;
5608 ai->geom = cAtomInfoPlanar;
5609 }
5610 }
5611
5612 /* c1cnnc1 becomes c1cn[H]nc1 */
5613
5614 if((atomInfo[mem[1]].protons == cAN_N) &&
5615 (atomInfo[mem[1]].formalCharge == 0) &&
5616 (!atomInfo[mem[1]].chemFlag) &&
5617 (atomInfo[mem[2]].protons == cAN_C) &&
5618 (atomInfo[mem[3]].protons == cAN_C) &&
5619 (atomInfo[mem[4]].protons == cAN_C) &&
5620 obs_atom[mem[1]].aromatic &&
5621 obs_atom[mem[2]].aromatic &&
5622 obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
5623
5624 int n2 = neighbor[mem[1]];
5625 int nn2 = neighbor[n2++];
5626 if(nn2 == 2) { /* second nitrogen also ambiguous */
5627 ai->valence = 3;
5628 ai->chemFlag = 2;
5629 ai->geom = cAtomInfoPlanar;
5630 }
5631 }
5632
5633 }
5634 }
5635 nbr[4] += 2;
5636 }
5637 }
5638 nbr[3] += 2;
5639 }
5640 }
5641 nbr[2] += 2;
5642 }
5643 }
5644 nbr[1] += 2;
5645 }
5646 nbr[0] += 2;
5647 }
5648 escape2: /* BUG FIX: Need separate escape for this loop */
5649 escape_count = ESCAPE_MAX; /* don't get bogged down with structures
5650 that have unreasonable connectivity */
5651 warning2 = 1;
5652 }
5653 }
5654 }
5655 }
5656 if (warning1 || warning2){
5657 PRINTFB(I->G, FB_ObjectMolecule, FB_Blather)
5658 " %s(%d,%d): Unreasonable connectivity in heteroatom,\n unsuccessful in guessing valences.\n", __func__, warning1, warning2
5659 ENDFB(I->G);
5660 }
5661 FreeP(obs_bond);
5662 FreeP(obs_atom);
5663 FreeP(flag);
5664 }
5665
5666
5667 /*========================================================================*/
5668
ObjectMoleculeInferChemForProtein(ObjectMolecule * I,int state)5669 void ObjectMoleculeInferChemForProtein(ObjectMolecule * I, int state)
5670 {
5671 /* Infers chemical relations for a molecules under protein assumptions.
5672 *
5673 * NOTE: this routine needs an all-atom model (with hydrogens!)
5674 * and it will make mistakes on non-protein atoms (if they haven't
5675 * already been assigned)
5676 */
5677
5678 int a, n, a0, a1, nn;
5679 int changedFlag = true;
5680
5681 AtomInfoType *ai, *ai0, *ai1 = NULL;
5682
5683 ObjectMoleculeUpdateNeighbors(I);
5684
5685 /* first, try to find all amids and acids */
5686 while(changedFlag) {
5687 changedFlag = false;
5688 for(a = 0; a < I->NAtom; a++) {
5689 ai = I->AtomInfo + a;
5690 if(ai->chemFlag) {
5691 if(ai->geom == cAtomInfoPlanar)
5692 if(ai->protons == cAN_C) {
5693 n = I->Neighbor[a];
5694 nn = I->Neighbor[n++];
5695 if(nn > 1) {
5696 a1 = -1;
5697 while(1) {
5698 a0 = I->Neighbor[n];
5699 n += 2;
5700 if(a0 < 0)
5701 break;
5702 ai0 = I->AtomInfo + a0;
5703 if((ai0->protons == cAN_O) && (!ai0->chemFlag)) {
5704 a1 = a0;
5705 ai1 = ai0; /* found candidate carbonyl */
5706 break;
5707 }
5708 }
5709 if(a1 > 0) {
5710 n = I->Neighbor[a] + 1;
5711 while(1) {
5712 a0 = I->Neighbor[n];
5713 if(a0 < 0)
5714 break;
5715 n += 2;
5716 if(a0 != a1) {
5717 ai0 = I->AtomInfo + a0;
5718 if(ai0->protons == cAN_O) {
5719 if(!ai0->chemFlag) {
5720 ai0->chemFlag = true; /* acid */
5721 ai0->geom = cAtomInfoPlanar;
5722 ai0->valence = 1;
5723 ai1->chemFlag = true;
5724 ai1->geom = cAtomInfoPlanar;
5725 ai1->valence = 1;
5726 changedFlag = true;
5727 break;
5728 }
5729 } else if(ai0->protons == cAN_N) {
5730 if(!ai0->chemFlag) {
5731 ai0->chemFlag = true; /* amide N */
5732 ai0->geom = cAtomInfoPlanar;
5733 ai0->valence = 3;
5734 ai1->chemFlag = true; /* amide =O */
5735 ai1->geom = cAtomInfoPlanar;
5736 ai1->valence = 1;
5737 changedFlag = true;
5738 break;
5739 } else if(ai0->geom == cAtomInfoPlanar) {
5740 ai1->chemFlag = true; /* amide =O */
5741 ai1->geom = cAtomInfoPlanar;
5742 ai1->valence = 1;
5743 changedFlag = true;
5744 break;
5745 }
5746 }
5747 }
5748 }
5749 }
5750 }
5751 }
5752 }
5753 }
5754 }
5755 /* then handle aldehydes and amines (partial amides - both missing a valence) */
5756
5757 changedFlag = true;
5758 while(changedFlag) {
5759 changedFlag = false;
5760 for(a = 0; a < I->NAtom; a++) {
5761 ai = I->AtomInfo + a;
5762 if(!ai->chemFlag) {
5763 if(ai->protons == cAN_C) {
5764 n = I->Neighbor[a];
5765 nn = I->Neighbor[n++];
5766 if(nn > 1) {
5767 a1 = -1;
5768 while(1) {
5769 a0 = I->Neighbor[n];
5770 n += 2;
5771 if(a0 < 0)
5772 break;
5773 ai0 = I->AtomInfo + a0;
5774 if((ai0->protons == cAN_O) && (!ai0->chemFlag)) { /* =O */
5775 ai->chemFlag = true;
5776 ai->geom = cAtomInfoPlanar;
5777 ai->valence = 1;
5778 ai0->chemFlag = true;
5779 ai0->geom = cAtomInfoPlanar;
5780 ai0->valence = 3;
5781 changedFlag = true;
5782 break;
5783 }
5784 }
5785 }
5786 } else if(ai->protons == cAN_N) {
5787 if((!ai->chemFlag) || ai->geom != cAtomInfoLinear) {
5788 if(ai->formalCharge == 0) {
5789 ai->chemFlag = true;
5790 ai->geom = cAtomInfoPlanar;
5791 ai->valence = 3;
5792 }
5793 }
5794 }
5795 }
5796 }
5797 }
5798
5799 }
5800
5801
5802 /*========================================================================*/
5803 /**
5804 * Assigns:
5805 * - geom
5806 * - valence
5807 * - chemFlag
5808 */
ObjectMoleculeInferChemFromNeighGeom(ObjectMolecule * I,int state)5809 void ObjectMoleculeInferChemFromNeighGeom(ObjectMolecule * I, int state)
5810 {
5811 /* infers chemical relations from neighbors and geometry
5812 * NOTE: very limited in scope */
5813
5814 int a, n, a0, nn;
5815 int changedFlag = true;
5816 int geom;
5817 int carbonVal[10];
5818
5819 AtomInfoType *ai, *ai2;
5820
5821 carbonVal[cAtomInfoTetrahedral] = 4;
5822 carbonVal[cAtomInfoPlanar] = 3;
5823 carbonVal[cAtomInfoLinear] = 2;
5824
5825 ObjectMoleculeUpdateNeighbors(I);
5826 while(changedFlag) {
5827 changedFlag = false;
5828 for(a = 0; a < I->NAtom; a++) {
5829 ai = I->AtomInfo + a;
5830 if(!ai->chemFlag) {
5831 geom = ObjectMoleculeGetAtomGeometry(I, state, a);
5832 switch (ai->protons) {
5833 case cAN_K:
5834 ai->chemFlag = 1;
5835 ai->geom = cAtomInfoNone;
5836 ai->valence = 0;
5837 break;
5838 case cAN_H:
5839 case cAN_F:
5840 case cAN_I:
5841 case cAN_Br:
5842 ai->chemFlag = 1;
5843 ai->geom = cAtomInfoSingle;
5844 ai->valence = 1;
5845 break;
5846 case cAN_O:
5847 n = I->Neighbor[a];
5848 nn = I->Neighbor[n++];
5849 if(nn != 1) { /* water, hydroxy, ether */
5850 ai->chemFlag = 1;
5851 ai->geom = cAtomInfoTetrahedral;
5852 ai->valence = 2;
5853 } else { /* hydroxy or carbonyl? check carbon geometry */
5854 a0 = I->Neighbor[n + 2];
5855 ai2 = I->AtomInfo + a0;
5856 if(ai2->chemFlag) {
5857 if((ai2->geom == cAtomInfoTetrahedral) || (ai2->geom == cAtomInfoLinear)) {
5858 ai->chemFlag = 1; /* hydroxy */
5859 ai->geom = cAtomInfoTetrahedral;
5860 ai->valence = 2;
5861 }
5862 }
5863 }
5864 break;
5865 case cAN_C:
5866 if(geom >= 0) {
5867 ai->geom = geom;
5868 ai->valence = carbonVal[geom];
5869 ai->chemFlag = true;
5870 } else {
5871 n = I->Neighbor[a];
5872 nn = I->Neighbor[n++];
5873 if(nn == 1) { /* only one neighbor */
5874 ai2 = I->AtomInfo + I->Neighbor[n];
5875 if(ai2->chemFlag && (ai2->geom == cAtomInfoTetrahedral)) {
5876 ai->chemFlag = true; /* singleton carbon bonded to tetC must be tetC */
5877 ai->geom = cAtomInfoTetrahedral;
5878 ai->valence = 4;
5879 }
5880 }
5881 }
5882 break;
5883 case cAN_N:
5884 if(geom == cAtomInfoPlanar) {
5885 ai->chemFlag = true;
5886 ai->geom = cAtomInfoPlanar;
5887 ai->valence = 3;
5888 } else if(geom == cAtomInfoTetrahedral) {
5889 ai->chemFlag = true;
5890 ai->geom = cAtomInfoTetrahedral;
5891 ai->valence = 4;
5892 }
5893 break;
5894 case cAN_S:
5895 n = I->Neighbor[a];
5896 nn = I->Neighbor[n++];
5897 if(nn == 4) { /* sulfone */
5898 ai->chemFlag = true;
5899 ai->geom = cAtomInfoTetrahedral;
5900 ai->valence = 4;
5901 } else if(nn == 3) { /* suloxide */
5902 ai->chemFlag = true;
5903 ai->geom = cAtomInfoTetrahedral;
5904 ai->valence = 3;
5905 } else if(nn == 2) { /* thioether */
5906 ai->chemFlag = true;
5907 ai->geom = cAtomInfoTetrahedral;
5908 ai->valence = 2;
5909 }
5910 break;
5911 case cAN_Cl:
5912 ai->chemFlag = 1;
5913 if(ai->formalCharge == 0) {
5914 ai->geom = cAtomInfoSingle;
5915 ai->valence = 1;
5916 } else {
5917 ai->geom = cAtomInfoNone;
5918 ai->valence = 0;
5919 }
5920 break;
5921 }
5922 if(ai->chemFlag)
5923 changedFlag = true;
5924 }
5925 }
5926 }
5927 }
5928
5929
5930 /*========================================================================*/
5931 /**
5932 * Assigns, based on valence, geom, formalCharge, and bonded atoms:
5933 * - hb_donor
5934 * - hb_acceptor
5935 */
ObjectMoleculeInferHBondFromChem(ObjectMolecule * I)5936 void ObjectMoleculeInferHBondFromChem(ObjectMolecule * I)
5937 {
5938 int a;
5939 AtomInfoType *ai;
5940 int a1;
5941 int n, nn;
5942 int has_hydro;
5943 /* initialize accumulators on uncategorized atoms */
5944
5945 const lexborrow_t lex_pseudo = LexBorrow(I->G, "pseudo");
5946
5947 ObjectMoleculeUpdateNeighbors(I);
5948 ai = I->AtomInfo.data();
5949 for(a = 0; a < I->NAtom; a++) {
5950 n = I->Neighbor[a];
5951 nn = I->Neighbor[n++];
5952 ai->hb_donor = false;
5953 ai->hb_acceptor = false;
5954
5955 has_hydro = (nn < ai->valence); /* implicit hydrogens? */
5956
5957 if(!has_hydro) {
5958 /* explicit hydrogens? */
5959 switch (ai->protons) {
5960 case cAN_N:
5961 case cAN_O:
5962 while((a1 = I->Neighbor[n]) >= 0) {
5963 n += 2;
5964 if(I->AtomInfo[a1].protons == 1) {
5965 has_hydro = true;
5966 break;
5967 }
5968 if (I->AtomInfo[a1].name == lex_pseudo && --nn < ai->valence) {
5969 has_hydro = true;
5970 break;
5971 }
5972 }
5973 break;
5974 }
5975 }
5976
5977 switch (ai->protons) {
5978 /* cat-ions, lewis acids etc. */
5979 case cAN_Fe:
5980 case cAN_Ca:
5981 case cAN_Cu:
5982 case cAN_K:
5983 case cAN_Na:
5984 case cAN_Mg:
5985 case cAN_Zn:
5986 case cAN_Hg:
5987 case cAN_Sr:
5988 case cAN_Ba:
5989 ai->hb_donor = true;
5990 break;
5991 case cAN_N:
5992 if(has_hydro)
5993 ai->hb_donor = true;
5994 else {
5995 int delocalized = false;
5996 int has_double_bond = false;
5997 int neighbor_has_double_bond = false;
5998 int mem[3];
5999 int nbr[3];
6000 int *neighbor = I->Neighbor;
6001
6002 mem[0] = a;
6003 nbr[0] = neighbor[mem[0]] + 1;
6004 while(((mem[1] = neighbor[nbr[0]]) >= 0)) {
6005 int b_order = I->Bond[neighbor[nbr[0] + 1]].order;
6006 if(b_order > 1) { /* any double/triple/aromatic bonds? */
6007 delocalized = true;
6008 }
6009 if(b_order == 2) {
6010 has_double_bond = true;
6011 }
6012 nbr[1] = neighbor[mem[1]] + 1;
6013 while(((mem[2] = neighbor[nbr[1]]) >= 0)) {
6014 if(mem[2] != mem[0]) {
6015 int b_order2 = I->Bond[neighbor[nbr[1] + 1]].order;
6016 if(b_order2 == 2) {
6017 neighbor_has_double_bond = true;
6018 }
6019 }
6020 nbr[1] += 2;
6021 }
6022 nbr[0] += 2;
6023 }
6024 if((ai->formalCharge <= 0) && delocalized && (nn < 3)) {
6025 /* delocalized nitrogen can likely serve as an acceptor */
6026 ai->hb_acceptor = true;
6027 }
6028 if(delocalized && (neighbor_has_double_bond) && (!has_double_bond) &&
6029 (ai->geom == cAtomInfoPlanar) && (nn == 2) && (ai->formalCharge >= 0)) {
6030 /* there's a fair chance of a resonance structure with this nitrogen as a donor */
6031 ai->hb_donor = true;
6032 }
6033 if((ai->geom != cAtomInfoPlanar) && (nn == 3) && (ai->formalCharge >= 0)
6034 && (!delocalized)) {
6035 /* tertiary amine case -- assume potential donor */
6036 ai->hb_donor = true;
6037 }
6038 }
6039 break;
6040 case cAN_O:
6041 if(ai->formalCharge <= 0)
6042 ai->hb_acceptor = true;
6043 if(has_hydro)
6044 ai->hb_donor = true;
6045 else {
6046 int has_double_bond = false;
6047 int neighbor_has_aromatic_bond = false;
6048 int mem[3];
6049 int nbr[3];
6050 int *neighbor = I->Neighbor;
6051
6052 mem[0] = a;
6053 nbr[0] = neighbor[mem[0]] + 1;
6054 while(((mem[1] = neighbor[nbr[0]]) >= 0)) {
6055 int b_order = I->Bond[neighbor[nbr[0] + 1]].order;
6056 if(b_order == 2) {
6057 has_double_bond = true;
6058 }
6059 nbr[1] = neighbor[mem[1]] + 1;
6060 while(((mem[2] = neighbor[nbr[1]]) >= 0)) {
6061 if(mem[2] != mem[0]) {
6062 int b_order2 = I->Bond[neighbor[nbr[1] + 1]].order;
6063 if(b_order2 == 4) {
6064 neighbor_has_aromatic_bond = true;
6065 }
6066 }
6067 nbr[1] += 2;
6068 }
6069 nbr[0] += 2;
6070 }
6071
6072 if(has_double_bond && neighbor_has_aromatic_bond && (ai->formalCharge >= 0)) {
6073 /* allow for phenolic resonance structures (and the like) */
6074 ai->hb_donor = true;
6075 }
6076 }
6077 break;
6078 }
6079 ai++;
6080 }
6081
6082 }
6083
6084
6085 /*========================================================================*/
6086 /**
6087 * Assigns:
6088 * - geom
6089 * - valence
6090 * - chemFlag
6091 */
ObjectMoleculeInferChemFromBonds(ObjectMolecule * I,int state)6092 void ObjectMoleculeInferChemFromBonds(ObjectMolecule * I, int state)
6093 {
6094
6095 int a, b;
6096 const BondType *b0;
6097 AtomInfoType *ai, *ai0, *ai1 = NULL;
6098 int a0, a1;
6099 int expect, order;
6100 int n, nn;
6101 int changedFlag;
6102 /* initialize accumulators on uncategorized atoms */
6103
6104 ObjectMoleculeUpdateNeighbors(I);
6105 ai = I->AtomInfo.data();
6106 for(a = 0; a < I->NAtom; a++) {
6107 if(!ai->chemFlag) {
6108 ai->geom = 0;
6109 ai->valence = 0;
6110 }
6111 ai++;
6112 }
6113
6114 // Ignore "pseudo" atoms (Desmond virtual sites)
6115 const lexborrow_t lex_pseudo = LexBorrow(I->G, "pseudo");
6116 std::vector<unsigned char> pseudo_neighbor_count(
6117 lex_pseudo == LEX_BORROW_NOTFOUND ? 0 : I->NAtom);
6118
6119 /* find maximum bond order for each atom */
6120
6121 b0 = I->Bond;
6122 for(b = 0; b < I->NBond; b++) {
6123 a0 = b0->index[0];
6124 a1 = b0->index[1];
6125 ai0 = I->AtomInfo + a0;
6126 ai1 = I->AtomInfo + a1;
6127 order = b0->order;
6128 b0++;
6129
6130 // count "pseudo" neighbors
6131 if (!pseudo_neighbor_count.empty()) {
6132 if (ai0->name == lex_pseudo) {
6133 pseudo_neighbor_count[a1] += 1;
6134 }
6135 if (ai1->name == lex_pseudo) {
6136 pseudo_neighbor_count[a0] += 1;
6137 }
6138 }
6139
6140 if(!ai0->chemFlag) {
6141 if(order > ai0->geom)
6142 ai0->geom = order;
6143 ai0->valence += order;
6144 }
6145 if(!ai1->chemFlag) {
6146 if(order > ai1->geom)
6147 ai1->geom = order;
6148 ai1->valence += order;
6149 }
6150 if(order == 3) {
6151 /* override existing chemistry * this is a temp fix to a pressing problem...
6152 we need to rethink the chemisty assignment ordering (should bond
6153 information come first? */
6154 ai0->geom = cAtomInfoLinear;
6155 ai1->geom = cAtomInfoLinear;
6156 if(ai0->chemFlag != 2) {
6157 switch (ai0->protons) {
6158 case cAN_C:
6159 ai0->valence = 2;
6160 break;
6161 default:
6162 ai0->valence = 1;
6163 }
6164 ai0->chemFlag = true;
6165 }
6166 if(ai1->chemFlag != 2) {
6167 switch (ai1->protons) {
6168 case cAN_C:
6169 ai1->valence = 2;
6170 break;
6171 default:
6172 ai1->valence = 1;
6173 }
6174 ai1->chemFlag = true;
6175 }
6176 } else if(order == 4) {
6177 ai0->geom = cAtomInfoPlanar;
6178 ai1->geom = cAtomInfoPlanar;
6179 if(ai0->chemFlag != 2) {
6180 switch (ai0->protons) {
6181 case cAN_O:
6182 ai0->valence = 1;
6183 break;
6184 case cAN_N:
6185 ai0->valence = (ai0->formalCharge == 1) ? 3 : 2; // TODO wrong for neutral -NH-
6186 break;
6187 case cAN_C:
6188 ai0->valence = 3;
6189 break;
6190 case cAN_S:
6191 ai0->valence = 2;
6192 break;
6193 default:
6194 ai0->valence = 4;
6195 }
6196 ai0->chemFlag = true;
6197 }
6198 if(ai1->chemFlag != 2) {
6199 switch (ai1->protons) {
6200 case cAN_O:
6201 ai1->valence = 1;
6202 break;
6203 case cAN_N:
6204 ai1->valence = (ai1->formalCharge == 1) ? 3 : 2; // TODO wrong for neutral -NH-
6205 break;
6206 case cAN_C:
6207 ai1->valence = 3;
6208 break;
6209 default:
6210 ai1->valence = 1;
6211 }
6212 ai1->chemFlag = true;
6213 }
6214 }
6215 }
6216
6217 /* now set up valences and geometries */
6218
6219 ai = I->AtomInfo.data();
6220 for(a = 0; a < I->NAtom; a++) {
6221 if(!ai->chemFlag) {
6222 expect = AtomInfoGetExpectedValence(I->G, ai);
6223 n = I->Neighbor[a];
6224 nn = I->Neighbor[n++];
6225
6226 // don't count "pseudo" atom neighbors
6227 if (!pseudo_neighbor_count.empty()) {
6228 nn -= pseudo_neighbor_count[a];
6229 }
6230
6231 if(ai->geom == 3) {
6232 ai->geom = cAtomInfoLinear;
6233 switch (ai->protons) {
6234 case cAN_C:
6235 ai->valence = 2;
6236 break;
6237 default:
6238 ai->valence = 1;
6239 }
6240 ai->chemFlag = true;
6241 } else {
6242 if(expect < 0)
6243 expect = -expect; /* for now, just ignore this issue */
6244 /* printf("%d %d %d %d\n",ai->geom,ai->valence,nn,expect); */
6245 if(ai->valence == expect) { /* sum of bond orders equals valence */
6246 ai->chemFlag = true;
6247 ai->valence = nn;
6248 switch (ai->geom) { /* max bond order observed */
6249 case 0:
6250 ai->geom = cAtomInfoNone;
6251 break;
6252 case 2:
6253 ai->geom = cAtomInfoPlanar;
6254 break;
6255 case 3:
6256 ai->geom = cAtomInfoLinear;
6257 break;
6258 default:
6259 if(expect == 1)
6260 ai->geom = cAtomInfoSingle;
6261 else
6262 ai->geom = cAtomInfoTetrahedral;
6263 break;
6264 }
6265 } else if(ai->valence < expect) { /* missing a bond */
6266 ai->chemFlag = true;
6267 ai->valence = nn + (expect - ai->valence);
6268 switch (ai->geom) {
6269 case 2:
6270 ai->geom = cAtomInfoPlanar;
6271 break;
6272 case 3:
6273 ai->geom = cAtomInfoLinear;
6274 break;
6275 default:
6276 if(expect == 1)
6277 ai->geom = cAtomInfoSingle;
6278 else
6279 ai->geom = cAtomInfoTetrahedral;
6280 break;
6281 }
6282 } else if(ai->valence > expect) {
6283 ai->chemFlag = true;
6284 ai->valence = nn;
6285 switch (ai->geom) {
6286 case 2:
6287 ai->geom = cAtomInfoPlanar;
6288 break;
6289 case 3:
6290 ai->geom = cAtomInfoLinear;
6291 break;
6292 default:
6293 if(expect == 1)
6294 ai->geom = cAtomInfoSingle;
6295 else
6296 ai->geom = cAtomInfoTetrahedral;
6297 break;
6298 }
6299 if(nn > 3)
6300 ai->geom = cAtomInfoTetrahedral;
6301 }
6302 }
6303 }
6304 ai++;
6305 }
6306
6307 /* now go through and make sure conjugated amines are planer */
6308 changedFlag = true;
6309 while(changedFlag) {
6310 changedFlag = false;
6311 ai = I->AtomInfo.data();
6312 for(a = 0; a < I->NAtom; a++) {
6313 if(ai->chemFlag) {
6314 if(ai->protons == cAN_N)
6315 if(ai->formalCharge < 1)
6316 if(ai->geom == cAtomInfoTetrahedral) {
6317 /* search for uncharged tetrahedral nitrogen */
6318 n = I->Neighbor[a] + 1;
6319 while(1) {
6320 a0 = I->Neighbor[n];
6321 n += 2;
6322 if(a0 < 0)
6323 break;
6324 ai0 = I->AtomInfo + a0;
6325 if((ai0->chemFlag) && (ai0->geom == cAtomInfoPlanar) &&
6326 ((ai0->protons == cAN_C) || (ai0->protons == cAN_N))) {
6327 ai->geom = cAtomInfoPlanar; /* found probable delocalization */
6328 if(ai->formalCharge == 0)
6329 ai->valence = 3; /* just in case... */
6330 changedFlag = true;
6331 break;
6332 }
6333 }
6334 }
6335 }
6336 ai++;
6337 }
6338 }
6339
6340 /* now go through and make sure conjugated anions are planer */
6341 changedFlag = true;
6342 while(changedFlag) {
6343 changedFlag = false;
6344 ai = I->AtomInfo.data();
6345 for(a = 0; a < I->NAtom; a++) {
6346 if(ai->chemFlag) {
6347 if(ai->protons == cAN_O)
6348 if(ai->formalCharge == -1)
6349 if((ai->geom == cAtomInfoTetrahedral) || (ai->geom == cAtomInfoSingle)) {
6350 /* search for anionic tetrahedral oxygen */
6351 n = I->Neighbor[a] + 1;
6352 while(1) {
6353 a0 = I->Neighbor[n];
6354 n += 2;
6355 if(a0 < 0)
6356 break;
6357 ai0 = I->AtomInfo + a0;
6358 if((ai0->chemFlag) && (ai0->geom == cAtomInfoPlanar) &&
6359 ((ai0->protons == cAN_C) || (ai0->protons == cAN_N))) {
6360 ai->geom = cAtomInfoPlanar; /* found probable delocalization */
6361 changedFlag = true;
6362 break;
6363 }
6364 }
6365 }
6366 }
6367 ai++;
6368 }
6369 }
6370
6371 }
6372
6373
6374 /*========================================================================*/
ObjectMoleculeTransformSelection(ObjectMolecule * I,int state,int sele,const float * matrix,int log,const char * sname,int homogenous,int global)6375 int ObjectMoleculeTransformSelection(ObjectMolecule * I, int state,
6376 int sele, const float *matrix, int log,
6377 const char *sname, int homogenous, int global)
6378 {
6379 /* called from "translate [5,5,5], objSele" */
6380 /* if sele == -1, then the whole object state is transformed */
6381 PyMOLGlobals *G = I->G;
6382 int a, s;
6383 int flag = false;
6384 CoordSet *cs;
6385 const AtomInfoType *ai;
6386 int logging;
6387 int all_states = false, inp_state;
6388 int ok = true;
6389 float homo_matrix[16], tmp_matrix[16];
6390 const float* input_matrix = matrix;
6391
6392 inp_state = state;
6393 if(state == -2)
6394 state = ObjectGetCurrentState(I, false);
6395 if(state < 0) {
6396 all_states = true;
6397 state = -1;
6398 }
6399 PRINTFD(G, FB_ObjectMolecule)
6400 "ObjMolTransSele-Debug: state %d\n", state ENDFD;
6401 while(1) {
6402 if(all_states) {
6403 state++;
6404 if(state >= I->NCSet)
6405 break;
6406 }
6407 if(state < I->NCSet) {
6408 cs = I->CSet[state];
6409 if(cs) {
6410 int use_matrices = SettingGet_i(G, I->Setting,
6411 NULL, cSetting_matrix_mode);
6412 if(use_matrices<0) use_matrices = 0;
6413
6414 if(global &&!homogenous) { /* convert matrix to homogenous */
6415 convertTTTfR44f(matrix, homo_matrix);
6416 matrix = homo_matrix;
6417 input_matrix = homo_matrix;
6418 homogenous = true;
6419 }
6420
6421 if(global &&((use_matrices && !cs->Matrix.empty()) || I->TTTFlag)) {
6422 /* if input coordinates are in the global system,
6423 they may need to be converted to local coordinates */
6424
6425 matrix = input_matrix;
6426
6427 /* global to object */
6428
6429 if(I->TTTFlag) {
6430 float ttt[16];
6431 if(matrix != tmp_matrix) {
6432 copy44f(matrix, tmp_matrix);
6433 }
6434 convertTTTfR44f(I->TTT, ttt);
6435 {
6436 float ttt_inv[16];
6437 invert_special44f44f(ttt, ttt_inv);
6438 left_multiply44f44f(ttt_inv, tmp_matrix);
6439 right_multiply44f44f(tmp_matrix, ttt);
6440 }
6441 matrix = tmp_matrix;
6442 }
6443
6444 /* object to state */
6445
6446 if(use_matrices) {
6447 if(!cs->Matrix.empty()) {
6448 double tmp_double[16], tmp_inv[16];
6449 copy44f44d(matrix, tmp_double);
6450 invert_special44d44d(cs->Matrix.data(), tmp_inv);
6451 left_multiply44d44d(tmp_inv, tmp_double);
6452 right_multiply44d44d(tmp_double, cs->Matrix.data());
6453 copy44d44f(tmp_double, tmp_matrix);
6454 matrix = tmp_matrix;
6455 }
6456 }
6457 }
6458
6459 if(sele >= 0) { /* transforming select atoms */
6460 ai = I->AtomInfo;
6461 for(a = 0; a < I->NAtom; a++) {
6462 s = ai->selEntry;
6463 if(!(ai->protekted == 1))
6464 if(SelectorIsMember(G, s, sele)) {
6465 if(homogenous)
6466 CoordSetTransformAtomR44f(cs, a, matrix);
6467 else
6468 CoordSetTransformAtomTTTf(cs, a, matrix);
6469 flag = true;
6470 }
6471 ai++;
6472 }
6473 } else { /* transforming whole coordinate set */
6474 if(!use_matrices) {
6475 ai = I->AtomInfo;
6476 for(a = 0; a < I->NAtom; a++) {
6477 if(!(ai->protekted == 1)) {
6478 if(homogenous)
6479 CoordSetTransformAtomR44f(cs, a, matrix);
6480 else
6481 CoordSetTransformAtomTTTf(cs, a, matrix);
6482 }
6483 ai++;
6484 }
6485 flag = true;
6486 CoordSetRecordTxfApplied(cs, matrix, homogenous);
6487 } else {
6488 ObjectMoleculeTransformState44f(I, state, matrix, false, homogenous, false);
6489 }
6490 }
6491 if(flag) {
6492 cs->invalidateRep(cRepAll, cRepInvCoord);
6493 ExecutiveUpdateCoordDepends(G, I);
6494 }
6495 }
6496 }
6497 if(!all_states)
6498 break;
6499 }
6500
6501 if(log) {
6502 OrthoLineType line;
6503 WordType sele_str = ",'";
6504 logging = SettingGetGlobal_i(G, cSetting_logging);
6505 if(sele >= 0) {
6506 strcat(sele_str, sname);
6507 }
6508 strcat(sele_str, "'");
6509 switch (logging) {
6510 case cPLog_pml:
6511 sprintf(line,
6512 "_ cmd.transform_object('%s',[\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f\\\n_ ],%d,%d%s,%d)\n",
6513 I->Name,
6514 matrix[0], matrix[1], matrix[2], matrix[3],
6515 matrix[4], matrix[5], matrix[6], matrix[7],
6516 matrix[8], matrix[9], matrix[10], matrix[11],
6517 matrix[12], matrix[13], matrix[14], matrix[15],
6518 inp_state + 1, 0, sele_str, homogenous);
6519 PLog(G, line, cPLog_no_flush);
6520 break;
6521 case cPLog_pym:
6522
6523 sprintf(line,
6524 "cmd.transform_object('%s',[\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f\n],%d,%d%s,%d)\n",
6525 I->Name,
6526 matrix[0], matrix[1], matrix[2], matrix[3],
6527 matrix[4], matrix[5], matrix[6], matrix[7],
6528 matrix[8], matrix[9], matrix[10], matrix[11],
6529 matrix[12], matrix[13], matrix[14], matrix[15],
6530 inp_state + 1, 0, sele_str, homogenous);
6531 PLog(G, line, cPLog_no_flush);
6532 break;
6533 default:
6534 break;
6535 }
6536 }
6537 return (ok);
6538 }
6539
6540
6541 /*========================================================================*/
ObjectMoleculeGetAtomIndex(ObjectMolecule * I,int sele)6542 int ObjectMoleculeGetAtomIndex(ObjectMolecule * I, int sele)
6543 {
6544 int a, s;
6545 if(sele < 0)
6546 return (-1);
6547 for(a = 0; a < I->NAtom; a++) {
6548 s = I->AtomInfo[a].selEntry;
6549 if(SelectorIsMember(I->G, s, sele))
6550 return (a);
6551 }
6552 return (-1);
6553 }
6554
6555
6556 /*========================================================================*/
6557 /*
6558 * Update all AtomInfoType.bonded
6559 *
6560 * Cost: O(NAtom + NBond)
6561 */
ObjectMoleculeUpdateNonbonded(ObjectMolecule * I)6562 void ObjectMoleculeUpdateNonbonded(ObjectMolecule * I)
6563 {
6564 int a;
6565 const BondType *b;
6566 AtomInfoType *ai;
6567 int nAtom = I->NAtom;
6568 int nBond = I->NBond;
6569
6570 ai = I->AtomInfo.data();
6571
6572 for(a = 0; a < nAtom; a++)
6573 (ai++)->bonded = false;
6574
6575 b = I->Bond;
6576 ai = I->AtomInfo.data();
6577 for(a = 0; a < nBond; a++) {
6578 ai[b->index[0]].bonded = true;
6579 ai[b->index[1]].bonded = true;
6580 b++;
6581 }
6582 }
6583
6584
6585 /*========================================================================*/
ObjectMoleculeGetTotalAtomValence(ObjectMolecule * I,int atom)6586 int ObjectMoleculeGetTotalAtomValence(ObjectMolecule * I, int atom)
6587 {
6588 int result = 0;
6589 int b, i;
6590 ObjectMoleculeUpdateNeighbors(I);
6591 if(atom < I->NAtom) {
6592 ITERNEIGHBORBONDS(I->Neighbor, atom, b, i) {
6593 result += I->Bond[b].order;
6594 }
6595 } else {
6596 result = -1; /* error */
6597 }
6598 return result;
6599 }
6600
6601
6602 /*
6603 * Generate I->Neighbor from I->Bond, but only if I->Neighbor is not NULL.
6604 *
6605 * Changed in PyMOL 2.1.1: Ignore zero-order bonds (PYMOL-3025)
6606 *
6607 * To force the update, call ObjectMoleculeInvalidate(level=cRepInvBonds) first.
6608 *
6609 */
6610 /*========================================================================*/
ObjectMoleculeUpdateNeighbors(ObjectMolecule * I)6611 int ObjectMoleculeUpdateNeighbors(ObjectMolecule * I)
6612 {
6613 /* neighbor storage structure: VERY COMPLICATED...
6614
6615 0 list offset for atom 0 = n
6616 1 list offset for atom 1 = n + m + 1
6617 ...
6618 n-1 list offset for atom n-1
6619
6620 n count for atom 0
6621 n+1 neighbor of atom 0
6622 n+2 bond index
6623 n+3 neighbor of atom 0
6624 n+4 bond index
6625 ...
6626 n+m -1 terminator for atom 0
6627
6628 n+m+1 count for atom 1
6629 n+m+2 neighbor of atom 1
6630 n+m+3 bond index
6631 n+m+4 neighbor of atom 1
6632 n+m+5 bond index
6633 etc.
6634
6635 NOTE: all atoms have an offset and a terminator whether or not they have any bonds:
6636
6637 Here's an example of an ALA
6638 [ offet for atom1, offset for atom2, ..., offset for atom9, \
6639 {10, 16, 26, 32, 36, 46, 50, 54, 58, 62, \
6640 (atom1) nbonds=2, neighbor index1=5, bond index=1, neighbor index2=1, bond index=0, null terminator=-1 \
6641 2, 5, 1, 1, 0, -1,
6642 (atom2) nbonds=4, nbr index1=6, bond id=4; nbr idx2=4, bond idx=3; nbr index3=2, bond idx=2; nbr index=0, bond index=0, null=-1 \
6643 4, 6, 4, 4, 3, 2, 2, 0, 0, -1,
6644 (atom3) ...
6645 2, 3, 5, 1, 2, -1, 1, 2, 5, -1,
6646 4, 9, 8, 8, 7, 7, 6, 1, 3, -1,
6647 1, 0, 1, -1,
6648 1, 1, 4, -1,
6649 1, 4, 6, -1,
6650 1, 4, 7, -1,
6651 1, 4, 8, -1 }
6652
6653 */
6654
6655 int size;
6656 int a, b, c, d, l0, l1, *l;
6657 const BondType *bnd;
6658 int ok = true;
6659
6660 /* If no neighbors have been calculated, fill in the table */
6661 if(!I->Neighbor) {
6662
6663 /* Create/check the VLA */
6664 size = (I->NAtom * 3) + (I->NBond * 4);
6665 if(I->Neighbor) {
6666 VLACheck(I->Neighbor, int, size);
6667 } else {
6668 I->Neighbor = VLAlloc(int, size);
6669 }
6670 CHECKOK(ok, I->Neighbor);
6671 if (!ok)
6672 return ok;
6673 /* initialize; zero out neighbors */
6674 l = I->Neighbor;
6675 for(a = 0; a < I->NAtom; a++)
6676 (*l++) = 0;
6677
6678 /* count neighbors for each atom */
6679 bnd = I->Bond;
6680 for(b = 0; b < I->NBond; b++) {
6681 if (bnd->order) {
6682 I->Neighbor[bnd->index[0]]++;
6683 I->Neighbor[bnd->index[1]]++;
6684 }
6685 bnd++;
6686 }
6687
6688 /* set up offsets and list terminators */
6689 c = I->NAtom;
6690 for(a = 0; a < I->NAtom; a++) {
6691 d = I->Neighbor[a]; /* get number of neighbors */
6692 I->Neighbor[c] = d; /* store neighbor count */
6693 I->Neighbor[a] = c + d + d + 1; /* set initial position to end of list, we'll fill backwards */
6694 I->Neighbor[I->Neighbor[a]] = -1; /* store terminator */
6695 c += d + d + 2;
6696 }
6697
6698 /* now load neighbors in a sequential list for each atom (reverse order) */
6699 bnd = I->Bond;
6700 for(b = 0; b < I->NBond; b++, ++bnd) {
6701 l0 = bnd->index[0];
6702 l1 = bnd->index[1];
6703
6704 if (!bnd->order)
6705 continue;
6706
6707 I->Neighbor[l0]--;
6708 I->Neighbor[I->Neighbor[l0]] = b; /* store bond indices (for I->Bond) */
6709 I->Neighbor[l0]--;
6710 I->Neighbor[I->Neighbor[l0]] = l1; /* store neighbor references (I->AtomInfo, etc.) */
6711
6712 I->Neighbor[l1]--;
6713 I->Neighbor[I->Neighbor[l1]] = b; /* store bond indices (for I->Bond) */
6714 I->Neighbor[l1]--;
6715 I->Neighbor[I->Neighbor[l1]] = l0; /* store neighbor references (I->AtomInfo, etc.) */
6716 }
6717 for(a = 0; a < I->NAtom; a++) { /* adjust down to point to the count, not the first entry */
6718 if(I->Neighbor[a] >= 0)
6719 I->Neighbor[a]--;
6720 }
6721 l = I->Neighbor;
6722 }
6723 return ok;
6724 }
6725
6726
6727 /*========================================================================*/
6728 #ifndef _PYMOL_NOPY
ObjectMoleculeChemPyModel2CoordSet(PyMOLGlobals * G,PyObject * model,AtomInfoType ** atInfoPtr)6729 static CoordSet *ObjectMoleculeChemPyModel2CoordSet(PyMOLGlobals * G,
6730 PyObject * model,
6731 AtomInfoType ** atInfoPtr)
6732 {
6733 int nAtom, nBond;
6734 int a, c;
6735 float *coord = NULL;
6736 CoordSet *cset = NULL;
6737 AtomInfoType *atInfo = NULL, *ai;
6738 float *f;
6739 BondType *ii, *bond = NULL;
6740 int ok = true;
6741 int auto_show = RepGetAutoShowMask(G);
6742 int hetatm;
6743 int ignore_ids;
6744 PyObject *atomList = NULL;
6745 PyObject *bondList = NULL;
6746 PyObject *atom = NULL;
6747 PyObject *bnd = NULL;
6748 PyObject *index = NULL;
6749 PyObject *crd = NULL;
6750 PyObject *tmp = NULL;
6751
6752 ignore_ids = !SettingGetGlobal_b(G, cSetting_preserve_chempy_ids);
6753
6754 nAtom = 0;
6755 nBond = 0;
6756 if(atInfoPtr)
6757 atInfo = *atInfoPtr;
6758
6759 atomList = PyObject_GetAttrString(model, "atom");
6760 if(atomList && PyList_Check(atomList))
6761 nAtom = PyList_Size(atomList);
6762 else
6763 ok = ErrMessage(G, __func__, "can't get atom list");
6764
6765 if(ok) {
6766 coord = VLAlloc(float, 3 * nAtom);
6767 if(atInfo)
6768 VLACheck(atInfo, AtomInfoType, nAtom);
6769 }
6770
6771 if(ok) {
6772
6773 f = coord;
6774 for(a = 0; a < nAtom; a++) {
6775
6776 atom = PyList_GetItem(atomList, a);
6777 if(!atom)
6778 ok = ErrMessage(G, __func__, "can't get atom");
6779 crd = PyObject_GetAttrString(atom, "coord");
6780 if(!crd)
6781 ok = ErrMessage(G, __func__, "can't get coordinates");
6782 else {
6783 for(c = 0; c < 3; c++) {
6784 tmp = PySequence_GetItem(crd, c);
6785 if(tmp)
6786 ok = PConvPyObjectToFloat(tmp, f++);
6787 Py_XDECREF(tmp);
6788 if(!ok) {
6789 ErrMessage(G, __func__, "can't read coordinates");
6790 break;
6791 }
6792 }
6793 }
6794 Py_XDECREF(crd);
6795
6796 ai = atInfo + a;
6797 ai->id = a; /* chempy models are zero-based */
6798 if(!ignore_ids) {
6799 if(ok) { /* get chempy atom id if extant */
6800 if(PTruthCallStr(atom, "has", "id")) {
6801 tmp = PyObject_GetAttrString(atom, "id");
6802 if(tmp)
6803 ok = PConvPyObjectToInt(tmp, &ai->id);
6804 if(!ok)
6805 ErrMessage(G, __func__,
6806 "can't read atom identifier");
6807 Py_XDECREF(tmp);
6808 } else {
6809 ai->id = -1;
6810 }
6811 }
6812 }
6813 ai->rank = a; /* ranks are always zero-based */
6814
6815 if(ok) {
6816 tmp = PyObject_GetAttrString(atom, "name");
6817 if(tmp) {
6818 WordType tmp_word;
6819
6820 ok = PConvPyObjectToStrMaxClean(tmp, tmp_word, sizeof(WordType) - 1);
6821 AtomInfoCleanAtomName(tmp_word);
6822 ai->name = LexIdx(G, tmp_word);
6823 }
6824 if(!ok)
6825 ErrMessage(G, __func__, "can't read name");
6826 Py_XDECREF(tmp);
6827 }
6828
6829 if(ok) {
6830 ai->textType = 0;
6831 if(PTruthCallStr(atom, "has", "text_type")) {
6832 tmp = PyObject_GetAttrString(atom, "text_type");
6833 if(tmp) {
6834 OrthoLineType temp;
6835 ok = PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
6836 ai->textType = LexIdx(G, temp);
6837 }
6838 if(!ok)
6839 ErrMessage(G, __func__, "can't read text_type");
6840 Py_XDECREF(tmp);
6841 }
6842 }
6843
6844 if(ok) {
6845 ai->custom = 0;
6846 if(PTruthCallStr(atom, "has", "custom")) {
6847 tmp = PyObject_GetAttrString(atom, "custom");
6848 if(tmp) {
6849 OrthoLineType temp;
6850 ok = PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
6851 ai->custom = LexIdx(G, temp);
6852 }
6853 if(!ok)
6854 ErrMessage(G, __func__, "can't read custom");
6855 Py_XDECREF(tmp);
6856 }
6857 }
6858
6859 if(ok) {
6860 if(PTruthCallStr(atom, "has", "vdw")) {
6861 tmp = PyObject_GetAttrString(atom, "vdw");
6862 if(tmp)
6863 ok = PConvPyObjectToFloat(tmp, &ai->vdw);
6864 if(!ok)
6865 ErrMessage(G, __func__, "can't read vdw radius");
6866 Py_XDECREF(tmp);
6867 } else {
6868 ai->vdw = 0.0f;
6869 }
6870 }
6871 if(ok) {
6872 if(PTruthCallStr(atom, "has", "bohr")) {
6873 tmp = PyObject_GetAttrString(atom, "bohr");
6874 if(tmp)
6875 ok = PConvPyObjectToFloat(tmp, &ai->elec_radius);
6876 if(!ok)
6877 ErrMessage(G, __func__,
6878 "can't read elec. radius");
6879 Py_XDECREF(tmp);
6880 } else {
6881 ai->elec_radius = 0.0F;
6882 }
6883 }
6884
6885 if(ok) {
6886 if(PTruthCallStr(atom, "has", "stereo")) {
6887 tmp = PyObject_GetAttrString(atom, "stereo");
6888 if(tmp) {
6889 char tmp_char;
6890 ok = PConvPyObjectToChar(tmp, &tmp_char);
6891 ai->stereo = tmp_char;
6892 }
6893 if(!ok)
6894 ErrMessage(G, __func__, "can't read stereo");
6895 Py_XDECREF(tmp);
6896 } else {
6897 ai->stereo = 0;
6898 }
6899 }
6900
6901 if(ok) {
6902 if(PTruthCallStr(atom, "has", "numeric_type")) {
6903 tmp = PyObject_GetAttrString(atom, "numeric_type");
6904 if(tmp)
6905 ok = PConvPyObjectToInt(tmp, &ai->customType);
6906 if(!ok)
6907 ErrMessage(G, __func__,
6908 "can't read numeric_type");
6909 Py_XDECREF(tmp);
6910 } else {
6911 ai->customType = cAtomInfoNoType;
6912 }
6913 }
6914
6915 if(ok) {
6916 if(PTruthCallStr(atom, "has", "formal_charge")) {
6917 tmp = PyObject_GetAttrString(atom, "formal_charge");
6918 if(tmp)
6919 ok = PConvPyObjectToChar(tmp, (char *) &ai->formalCharge);
6920 if(!ok)
6921 ErrMessage(G, __func__,
6922 "can't read formal_charge");
6923 Py_XDECREF(tmp);
6924 } else {
6925 ai->formalCharge = 0;
6926 }
6927 }
6928
6929 if(ok) {
6930 if(PTruthCallStr(atom, "has", "partial_charge")) {
6931 tmp = PyObject_GetAttrString(atom, "partial_charge");
6932 if(tmp)
6933 ok = PConvPyObjectToFloat(tmp, &ai->partialCharge);
6934 if(!ok)
6935 ErrMessage(G, __func__,
6936 "can't read partial_charge");
6937 Py_XDECREF(tmp);
6938 } else {
6939 ai->partialCharge = 0.0;
6940 }
6941 }
6942
6943 if(ok) {
6944 if(PTruthCallStr(atom, "has", "flags")) {
6945 tmp = PyObject_GetAttrString(atom, "flags");
6946 if(tmp)
6947 ok = PConvPyObjectToInt(tmp, (int *) &ai->flags);
6948 if(!ok)
6949 ErrMessage(G, __func__, "can't read flags");
6950 Py_XDECREF(tmp);
6951 } else {
6952 ai->flags = 0;
6953 }
6954 }
6955
6956 if(ok) {
6957 char buf[8] = "";
6958 tmp = PyObject_GetAttrString(atom, "resn");
6959 if(tmp)
6960 ok = PConvPyObjectToStrMaxClean(tmp, buf, sizeof(buf) - 1);
6961 ai->resn = LexIdx(G, buf);
6962 if(!ok)
6963 ErrMessage(G, __func__, "can't read resn");
6964 Py_XDECREF(tmp);
6965 }
6966
6967 if(ok) {
6968 tmp = PyObject_GetAttrString(atom, "ins_code");
6969 if(tmp) {
6970 ResIdent tmp_ins_code;
6971 ok = PConvPyObjectToStrMaxClean(tmp, tmp_ins_code, sizeof(ResIdent) - 1);
6972 if(!ok)
6973 ErrMessage(G, __func__, "can't read ins_code");
6974 else if(tmp_ins_code[0] != '?') {
6975 ai->setInscode(tmp_ins_code[0]);
6976 }
6977 }
6978 Py_XDECREF(tmp);
6979 }
6980 if(ok) {
6981 if(PTruthCallStr(atom, "has", "resi_number")) {
6982 tmp = PyObject_GetAttrString(atom, "resi_number");
6983 if(tmp)
6984 ok = PConvPyObjectToInt(tmp, &ai->resv);
6985 if(!ok)
6986 ErrMessage(G, __func__, "can't read resi_number");
6987 Py_XDECREF(tmp);
6988 }
6989 }
6990
6991 if(ok) {
6992 OrthoLineType temp = "";
6993 tmp = PyObject_GetAttrString(atom, "segi");
6994 if(tmp)
6995 PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
6996 ai->segi = LexIdx(G, temp);
6997 if(!ok)
6998 ErrMessage(G, __func__, "can't read segi");
6999 Py_XDECREF(tmp);
7000 }
7001
7002 if(ok) {
7003 tmp = PyObject_GetAttrString(atom, "b");
7004 if(tmp)
7005 ok = PConvPyObjectToFloat(tmp, &ai->b);
7006 if(!ok)
7007 ErrMessage(G, __func__, "can't read b value");
7008 Py_XDECREF(tmp);
7009 }
7010
7011 if(ok) {
7012 tmp = PyObject_GetAttrString(atom, "u");
7013 if(tmp) {
7014 ok = PConvPyObjectToFloat(tmp, &ai->b);
7015 if(!ok)
7016 ErrMessage(G, __func__, "can't read u value");
7017 else
7018 ai->b *= (8 * cPI * cPI); /* B-factor = 8 pi^2 U-factor */
7019 } else {
7020 assert(PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError));
7021 PyErr_Clear();
7022 }
7023 Py_XDECREF(tmp);
7024 }
7025
7026 if(ok) {
7027 tmp = PyObject_GetAttrString(atom, "u_aniso");
7028 if(tmp) {
7029 float u[6];
7030 if(PConvPyListToFloatArrayInPlace(tmp, u, 6)) {
7031 // only allocate if not all zero
7032 if(u[0] || u[1] || u[2] || u[3] || u[4] || u[5])
7033 std::copy_n(u, 6, ai->get_anisou());
7034 }
7035 Py_DECREF(tmp);
7036 } else {
7037 assert(PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError));
7038 PyErr_Clear();
7039 }
7040 }
7041
7042 if(ok) {
7043 tmp = PyObject_GetAttrString(atom, "q");
7044 if(tmp)
7045 ok = PConvPyObjectToFloat(tmp, &ai->q);
7046 if(!ok)
7047 ErrMessage(G, __func__, "can't read occupancy");
7048 Py_XDECREF(tmp);
7049 }
7050
7051 if(ok) {
7052 OrthoLineType temp = "";
7053 tmp = PyObject_GetAttrString(atom, "chain");
7054 if(tmp)
7055 PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
7056 ai->chain = LexIdx(G, temp);
7057 if(!ok)
7058 ErrMessage(G, __func__, "can't read chain");
7059 Py_XDECREF(tmp);
7060 }
7061
7062 if(ok) {
7063 tmp = PyObject_GetAttrString(atom, "hetatm");
7064 if(tmp)
7065 ok = PConvPyObjectToInt(tmp, &hetatm);
7066 if(!ok)
7067 ErrMessage(G, __func__, "can't read hetatm");
7068 else {
7069 ai->hetatm = hetatm;
7070 if(!PTruthCallStr(atom, "has", "flags")) {
7071 if(ai->hetatm)
7072 ai->flags = cAtomFlag_ignore;
7073 }
7074 }
7075 Py_XDECREF(tmp);
7076 }
7077
7078 if(ok) {
7079 tmp = PyObject_GetAttrString(atom, "alt");
7080 if(tmp)
7081 ok = PConvPyObjectToStrMaxClean(tmp, ai->alt, sizeof(Chain) - 1);
7082 if(!ok)
7083 ErrMessage(G, __func__,
7084 "can't read alternate conformation");
7085 Py_XDECREF(tmp);
7086 }
7087
7088 if(ok) {
7089 tmp = PyObject_GetAttrString(atom, "symbol");
7090 if(tmp)
7091 ok = PConvPyObjectToStrMaxClean(tmp, ai->elem, sizeof(ElemName) - 1);
7092 if(!ok)
7093 ErrMessage(G, __func__, "can't read symbol");
7094 Py_XDECREF(tmp);
7095 }
7096
7097 if(ok) {
7098 tmp = PyObject_GetAttrString(atom, "ss");
7099 if(tmp)
7100 ok = PConvPyObjectToStrMaxClean(tmp, ai->ssType, sizeof(SSType) - 1);
7101 if(!ok)
7102 ErrMessage(G, __func__,
7103 "can't read secondary structure");
7104 Py_XDECREF(tmp);
7105 }
7106
7107 if(ok && PyObject_HasAttrString(atom, "label")) {
7108 tmp = PyObject_GetAttrString(atom, "label");
7109 if(tmp) {
7110 if (PyString_Check(tmp))
7111 ai->label = LexIdx(G, PyString_AsString(tmp));
7112 }
7113 Py_XDECREF(tmp);
7114 }
7115
7116 if(ok && PyObject_HasAttrString(atom, "sphere_scale")) {
7117 tmp = PyObject_GetAttrString(atom, "sphere_scale");
7118 if(tmp) {
7119 float value;
7120 if(PConvPyFloatToFloat(tmp, &value)) {
7121 SettingSet(G, cSetting_sphere_scale, value, ai);
7122 }
7123 }
7124 Py_XDECREF(tmp);
7125 }
7126 if(ok && PyObject_HasAttrString(atom, "cartoon_color")) {
7127 tmp = PyObject_GetAttrString(atom, "cartoon_color");
7128 if(tmp) {
7129 int color_index;
7130 ok = PConvPyObjectToInt(tmp, &color_index);
7131 if(!ok)
7132 ErrMessage(G, __func__, "bad cartoon color info");
7133 else {
7134 SettingSet(G, cSetting_cartoon_color, color_index, ai);
7135 }
7136 }
7137 Py_XDECREF(tmp);
7138 }
7139 if(ok && PyObject_HasAttrString(atom, "cartoon_transparency")) {
7140 tmp = PyObject_GetAttrString(atom, "cartoon_transparency");
7141 if(tmp) {
7142 float alpha_val;
7143 ok = PConvPyObjectToFloat(tmp, &alpha_val);
7144 if(!ok)
7145 ErrMessage(G, __FUNCTION__, "bad alpha value");
7146 else {
7147 SettingSet(G, cSetting_cartoon_transparency, alpha_val, ai);
7148 }
7149 }
7150 Py_XDECREF(tmp);
7151 }
7152 if(ok && PyObject_HasAttrString(atom, "cartoon_trgb")) {
7153 tmp = PyObject_GetAttrString(atom, "cartoon_trgb");
7154 if(tmp) {
7155 unsigned int trgb;
7156 ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
7157 if(!ok)
7158 ErrMessage(G, __func__, "bad cartoon color info");
7159 else {
7160 char color_name[24];
7161 sprintf(color_name, "0x%08x", trgb);
7162 SettingSet(G, cSetting_cartoon_color, ColorGetIndex(G, color_name), ai);
7163 }
7164 }
7165 Py_XDECREF(tmp);
7166 }
7167 if(ok && PyObject_HasAttrString(atom, "label_trgb")) {
7168 tmp = PyObject_GetAttrString(atom, "label_trgb");
7169 if(tmp) {
7170 unsigned int trgb;
7171 ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
7172 if(!ok)
7173 ErrMessage(G, __func__, "bad label color info");
7174 else {
7175 char color_name[24];
7176 sprintf(color_name, "0x%08x", trgb);
7177 SettingSet(G, cSetting_label_color, ColorGetIndex(G, color_name), ai);
7178 }
7179 }
7180 Py_XDECREF(tmp);
7181 }
7182 if(ok && PyObject_HasAttrString(atom, "ribbon_color")) {
7183 tmp = PyObject_GetAttrString(atom, "ribbon_color");
7184 if(tmp) {
7185 int color_index;
7186 ok = PConvPyObjectToInt(tmp, &color_index);
7187 if(!ok)
7188 ErrMessage(G, __func__, "bad ribbon color info");
7189 else {
7190 SettingSet(G, cSetting_ribbon_color, color_index, ai);
7191 }
7192 }
7193 Py_XDECREF(tmp);
7194 }
7195
7196 if(ok && PyObject_HasAttrString(atom, "ribbon_trgb")) {
7197 tmp = PyObject_GetAttrString(atom, "ribbon_trgb");
7198 if(tmp) {
7199 unsigned int trgb;
7200 ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
7201 if(!ok)
7202 ErrMessage(G, __func__, "bad cartoon color info");
7203 else {
7204 char color_name[24];
7205 sprintf(color_name, "0x%08x", trgb);
7206 SettingSet(G, cSetting_ribbon_color, ColorGetIndex(G, color_name), ai);
7207 }
7208 }
7209 Py_XDECREF(tmp);
7210 }
7211
7212 atInfo[a].visRep = auto_show;
7213
7214 if(ok && PyObject_HasAttrString(atom, "visible")) {
7215 unsigned int vis;
7216 tmp = PyObject_GetAttrString(atom, "visible");
7217 if(tmp) {
7218 ok = PConvPyObjectToInt(tmp, (signed int *) &vis);
7219 if(!ok)
7220 ErrMessage(G, __func__, "bad visibility info");
7221 else {
7222 atInfo[a].visRep = vis;
7223
7224 if (!(ai->flags & cAtomFlag_class)) {
7225 ai->flags |= cAtomFlag_inorganic; // suppress auto_show_classified
7226 }
7227 }
7228 }
7229 Py_XDECREF(tmp);
7230 }
7231
7232 if(ok && atInfo) {
7233 AtomInfoAssignParameters(G, ai);
7234 AtomInfoAssignColors(G, ai);
7235 }
7236
7237 if(ok && PyObject_HasAttrString(atom, "trgb")) {
7238 /* were we given a Transparency-Red-Blue-Green value? */
7239 unsigned int trgb;
7240 tmp = PyObject_GetAttrString(atom, "trgb");
7241 if(tmp) {
7242 ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
7243 if(!ok)
7244 ErrMessage(G, __func__, "bad color info");
7245 else {
7246 char color_name[24];
7247 sprintf(color_name, "0x%08x", trgb);
7248 atInfo[a].color = ColorGetIndex(G, color_name);
7249 }
7250 Py_DECREF(tmp);
7251 }
7252 }
7253
7254 if(!ok)
7255 break;
7256
7257 assert(!PyErr_Occurred());
7258 }
7259 }
7260
7261 if(nAtom) {
7262 bondList = PyObject_GetAttrString(model, "bond");
7263 if(bondList && PyList_Check(bondList))
7264 nBond = PyList_Size(bondList);
7265 else
7266 ok = ErrMessage(G, __func__, "can't get bond list");
7267
7268 if(ok) {
7269 bond = VLACalloc(BondType, nBond);
7270 ii = bond;
7271 for(a = 0; a < nBond; a++) {
7272 bnd = PyList_GetItem(bondList, a);
7273 if(!bnd)
7274 ok = ErrMessage(G, __func__, "can't get bond");
7275 index = PyObject_GetAttrString(bnd, "index");
7276 if(!index)
7277 ok =
7278 ErrMessage(G, __func__, "can't get bond indices");
7279 else {
7280 for(c = 0; c < 2; c++) {
7281 tmp = PyList_GetItem(index, c);
7282 if(tmp)
7283 ok = PConvPyObjectToInt(tmp, &ii->index[c]);
7284 if(!ok) {
7285 ErrMessage(G, __func__,
7286 "can't read coordinates");
7287 break;
7288 }
7289 }
7290 }
7291 if(ok) {
7292 int order = 0;
7293 tmp = PyObject_GetAttrString(bnd, "order");
7294 if(tmp)
7295 ok = PConvPyObjectToInt(tmp, &order);
7296 if(!ok)
7297 ErrMessage(G, __func__, "can't read bond order");
7298 Py_XDECREF(tmp);
7299 ii->order = order;
7300 }
7301
7302 if(ok && PyObject_HasAttrString(bnd, "stick_radius")) {
7303 tmp = PyObject_GetAttrString(bnd, "stick_radius");
7304 if(tmp) {
7305 float value;
7306 if(PConvPyFloatToFloat(tmp, &value)) {
7307 SettingSet(G, cSetting_stick_radius, value, ii);
7308 }
7309 }
7310 Py_XDECREF(tmp);
7311 }
7312 if(ok && PyObject_HasAttrString(bnd, "valence")) {
7313 tmp = PyObject_GetAttrString(bnd, "valence");
7314 if(tmp) {
7315 int value;
7316 if(PConvPyIntToInt(tmp, &value)) {
7317 SettingSet(G, cSetting_valence, value, ii);
7318 }
7319 }
7320 Py_XDECREF(tmp);
7321 }
7322
7323 if(ok) {
7324 int stereo;
7325 tmp = PyObject_GetAttrString(bnd, "stereo");
7326 if(tmp)
7327 ok = PConvPyObjectToInt(tmp, &stereo);
7328 else
7329 ii->stereo = 0;
7330 if(!ok)
7331 ii->stereo = 0;
7332 else
7333 ii->stereo = stereo;
7334 Py_XDECREF(tmp);
7335 }
7336
7337 ii->id = a;
7338 if(!ignore_ids) {
7339 if(ok) { /* get unique chempy bond id if present */
7340 if(PTruthCallStr(bnd, "has", "id")) {
7341 tmp = PyObject_GetAttrString(bnd, "id");
7342 if(tmp)
7343 ok = PConvPyObjectToInt(tmp, &ii->id);
7344 if(!ok)
7345 ErrMessage(G, __func__,
7346 "can't read bond identifier");
7347 Py_XDECREF(tmp);
7348 } else {
7349 ii->id = -1;
7350 }
7351 }
7352 }
7353 Py_XDECREF(index);
7354 ii++;
7355 }
7356 }
7357 }
7358
7359 Py_XDECREF(atomList);
7360 Py_XDECREF(bondList);
7361
7362 if(ok) {
7363 cset = CoordSetNew(G);
7364 cset->NIndex = nAtom;
7365 cset->Coord = pymol::vla_take_ownership(coord);
7366 cset->NTmpBond = nBond;
7367 cset->TmpBond = pymol::vla_take_ownership(bond);
7368
7369 #ifdef _PYMOL_IP_PROPERTIES
7370 #endif
7371 } else {
7372 VLAFreeP(bond);
7373 VLAFreeP(coord);
7374 }
7375 if(atInfoPtr)
7376 *atInfoPtr = atInfo;
7377
7378 if(PyErr_Occurred())
7379 PyErr_Print();
7380 return (cset);
7381 }
7382 #endif
7383
7384
7385 /*========================================================================*/
ObjectMoleculeLoadChemPyModel(PyMOLGlobals * G,ObjectMolecule * I,PyObject * model,int frame,int discrete)7386 ObjectMolecule *ObjectMoleculeLoadChemPyModel(PyMOLGlobals * G,
7387 ObjectMolecule * I,
7388 PyObject * model, int frame, int discrete)
7389 {
7390 #ifdef _PYMOL_NOPY
7391 return NULL;
7392 #else
7393 CoordSet *cset = NULL;
7394 auto atInfo = pymol::vla<AtomInfoType>(10);
7395 int ok = true;
7396 int isNew = true;
7397 unsigned int nAtom = 0;
7398 int fractional = false;
7399 int connect_mode = -1;
7400 int auto_bond = false;
7401 PyObject *tmp, *mol;
7402
7403 if(!I)
7404 isNew = true;
7405 else
7406 isNew = false;
7407
7408 if(ok) {
7409
7410 if(isNew) {
7411 I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
7412 std::swap(atInfo, I->AtomInfo);
7413 isNew = true;
7414 } else {
7415 if (discrete)
7416 ObjectMoleculeSetDiscrete(G, I, true);
7417 }
7418
7419 if(isNew) {
7420 I->Color = AtomInfoUpdateAutoColor(G);
7421 }
7422 cset = ObjectMoleculeChemPyModel2CoordSet(G, model, &atInfo);
7423
7424 if(!cset)
7425 ok = false;
7426 else {
7427 mol = PyObject_GetAttrString(model, "molecule");
7428 if(mol) {
7429 if(PyObject_HasAttrString(mol, "title")) {
7430 tmp = PyObject_GetAttrString(mol, "title");
7431 if(tmp) {
7432 UtilNCopy(cset->Name, PyString_AsString(tmp), sizeof(WordType));
7433 Py_DECREF(tmp);
7434 if(!strcmp(cset->Name, "untitled")) /* ignore untitled */
7435 cset->Name[0] = 0;
7436 }
7437 }
7438 Py_DECREF(mol);
7439 }
7440 if(PyObject_HasAttrString(model, "spheroid") &&
7441 PyObject_HasAttrString(model, "spheroid_normals")) {
7442 tmp = PyObject_GetAttrString(model, "spheroid");
7443 if(tmp) {
7444 PConvFromPyObject(G, tmp, cset->Spheroid);
7445 Py_DECREF(tmp);
7446 }
7447 tmp = PyObject_GetAttrString(model, "spheroid_normals");
7448 if(tmp) {
7449 PConvFromPyObject(G, tmp, cset->SpheroidNormal);
7450 Py_DECREF(tmp);
7451 }
7452 }
7453 if(PyObject_HasAttrString(model, "spacegroup") &&
7454 PyObject_HasAttrString(model, "cell")) {
7455 CSymmetry *symmetry = new CSymmetry(G);
7456 if(symmetry) {
7457 tmp = PyObject_GetAttrString(model, "spacegroup");
7458 if(tmp) {
7459 const char *tmp_str = NULL;
7460 if(PConvPyStrToStrPtr(tmp, &tmp_str)) {
7461 UtilNCopy(symmetry->SpaceGroup, tmp_str, sizeof(WordType));
7462 }
7463 Py_DECREF(tmp);
7464 }
7465 tmp = PyObject_GetAttrString(model, "cell");
7466 if(tmp) {
7467 float cell[6];
7468 if(PConvPyListToFloatArrayInPlace(tmp, cell, 6)) {
7469 copy3f(cell, symmetry->Crystal.Dim);
7470 copy3f(cell + 3, symmetry->Crystal.Angle);
7471 }
7472 Py_DECREF(tmp);
7473 }
7474 cset->Symmetry = std::unique_ptr<CSymmetry>(symmetry);
7475 }
7476 }
7477 if(PyObject_HasAttrString(model, "fractional")) {
7478 tmp = PyObject_GetAttrString(model, "fractional");
7479 if(tmp) {
7480 int tmp_int = 0;
7481 if(PConvPyIntToInt(tmp, &tmp_int)) {
7482 fractional = tmp_int;
7483 }
7484 Py_DECREF(tmp);
7485 }
7486 }
7487 if(PyObject_HasAttrString(model, "connect_mode")) {
7488 tmp = PyObject_GetAttrString(model, "connect_mode");
7489 if(tmp) {
7490 int tmp_int = 0;
7491 if(PConvPyIntToInt(tmp, &tmp_int)) {
7492 auto_bond = true;
7493 connect_mode = tmp_int;
7494 }
7495 Py_DECREF(tmp);
7496 }
7497 }
7498 nAtom = cset->NIndex;
7499 }
7500 }
7501 /* include coordinate set */
7502 if(ok) {
7503 if(frame < 0)
7504 frame = I->NCSet;
7505
7506 if(I->DiscreteFlag && atInfo) {
7507 unsigned int a;
7508 int fp1 = frame + 1;
7509 AtomInfoType *ai = atInfo.data();
7510 for(a = 0; a < nAtom; a++) {
7511 (ai++)->discrete_state = fp1;
7512 }
7513 }
7514
7515 cset->Obj = I;
7516 cset->enumIndices();
7517 cset->invalidateRep(cRepAll, cRepInvRep);
7518 if(isNew) {
7519 std::swap(I->AtomInfo, atInfo);
7520 } else {
7521 ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_AllMask, true);
7522 }
7523 if(isNew)
7524 I->NAtom = nAtom;
7525 VLACheck(I->CSet, CoordSet *, frame);
7526 if(I->NCSet <= frame)
7527 I->NCSet = frame + 1;
7528 if(I->CSet[frame])
7529 I->CSet[frame]->fFree();
7530 I->CSet[frame] = cset;
7531 if (fractional && cset->Symmetry) {
7532 CrystalUpdate(&cset->Symmetry->Crystal);
7533 CoordSetFracToReal(cset, &cset->Symmetry->Crystal);
7534 }
7535 if(ok && isNew)
7536 ok &= ObjectMoleculeConnect(I, cset, auto_bond, connect_mode);
7537 if(cset->Symmetry && (!I->Symmetry)) {
7538 I->Symmetry = new CSymmetry(*cset->Symmetry);
7539 SymmetryUpdate(I->Symmetry);
7540 }
7541 SceneCountFrames(G);
7542 if (ok)
7543 ok &= ObjectMoleculeExtendIndices(I, frame);
7544 if (ok)
7545 ok &= ObjectMoleculeSort(I);
7546 if (ok){
7547 ObjectMoleculeUpdateIDNumbers(I);
7548 ObjectMoleculeUpdateNonbonded(I);
7549 }
7550 }
7551 return (I);
7552 #endif
7553 }
7554
7555
7556 /*========================================================================*/
7557 /*
7558 * Update coordinates of an exisiting coordset or insert/append a new
7559 * coordset.
7560 *
7561 * coords: flat coordinate array of length NIndex * 3
7562 * coords_len: must be NIndex * 3
7563 * frame: coordset index
7564 */
ObjectMoleculeLoadCoords(PyMOLGlobals * G,ObjectMolecule * I,const float * coords,int coords_len,int frame)7565 ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, ObjectMolecule * I,
7566 const float * coords, int coords_len, int frame)
7567 {
7568 CoordSet *cset = NULL;
7569 int a;
7570 bool is_new = false;
7571
7572 if(frame < 0) {
7573 frame = I->NCSet;
7574 } else if (frame < I->NCSet) {
7575 cset = I->CSet[frame];
7576 }
7577
7578 if (!cset) {
7579 // template coordinate set, if available
7580 cset = I->CSTmpl;
7581
7582 // find any coordinate set
7583 for(a = 0; !cset && a < I->NCSet; ++a)
7584 cset = I->CSet[a];
7585 ok_assert(1, cset);
7586 cset = CoordSetCopy(cset);
7587 is_new = true;
7588 }
7589
7590 // check atom count
7591 if(coords_len != cset->NIndex * 3) {
7592 ErrMessage(G, "LoadCoords", "atom count mismatch");
7593 ok_raise(1);
7594 }
7595
7596 // copy coordinates
7597 for(a = 0; a < coords_len; ++a) {
7598 cset->Coord[a] = coords[a];
7599 }
7600
7601 cset->invalidateRep(cRepAll, cRepInvRep);
7602
7603 // include coordinate set
7604 if (is_new) {
7605 VLACheck(I->CSet, CoordSet *, frame);
7606 if(I->NCSet <= frame)
7607 I->NCSet = frame + 1;
7608 I->CSet[frame] = cset;
7609 SceneCountFrames(G);
7610 }
7611
7612 // success
7613 return (I);
7614
7615 // error handling
7616 ok_except1:
7617 if(is_new && cset)
7618 cset->fFree();
7619 ErrMessage(G, "LoadCoords", "failed");
7620 return NULL;
7621 }
7622
7623 /*========================================================================*/
7624 /* see above... but look up object by name
7625 */
ObjectMoleculeLoadCoords(PyMOLGlobals * G,const char * name,const float * coords,int coords_len,int frame)7626 ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, const char * name,
7627 const float * coords, int coords_len, int frame)
7628 {
7629 CObject * cobj = ExecutiveFindObjectByName(G, name);
7630
7631 if(!cobj || cobj->type != cObjectMolecule) {
7632 ErrMessage(G, "LoadCoords", "named object molecule not found.");
7633 return NULL;
7634 }
7635
7636 return ObjectMoleculeLoadCoords(G, (ObjectMolecule *) cobj,
7637 coords, coords_len, frame);
7638 }
7639
7640
7641 /*========================================================================*/
7642 /* see above... but take coordinates from Python list
7643 *
7644 * coords: 2d Python float sequence with shape (NIndex, 3)
7645 */
ObjectMoleculeLoadCoords(PyMOLGlobals * G,ObjectMolecule * I,PyObject * coords,int frame)7646 ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, ObjectMolecule * I,
7647 PyObject * coords, int frame)
7648 {
7649 #ifdef _PYMOL_NOPY
7650 return NULL;
7651 #else
7652 CoordSet *cset = NULL;
7653 int a, b, l;
7654 PyObject *v, *w;
7655 float *f;
7656 bool is_new = false;
7657
7658 if(!PySequence_Check(coords)) {
7659 ErrMessage(G, "LoadCoords", "passed argument is not a sequence");
7660 ok_raise(1);
7661 }
7662
7663 if(frame < 0) {
7664 frame = I->NCSet;
7665 } else if (frame < I->NCSet) {
7666 cset = I->CSet[frame];
7667 }
7668
7669 if (!cset) {
7670 // template coordinate set, if available
7671 cset = I->CSTmpl;
7672
7673 // find any coordinate set
7674 for(a = 0; !cset && a < I->NCSet; ++a)
7675 cset = I->CSet[a];
7676 ok_assert(1, cset);
7677 cset = CoordSetCopy(cset);
7678 is_new = true;
7679 }
7680
7681 // check atom count
7682 l = PySequence_Size(coords);
7683 if(l != cset->NIndex) {
7684 ErrMessage(G, "LoadCoords", "atom count mismatch");
7685 ok_raise(1);
7686 }
7687
7688 // copy coordinates
7689 f = cset->Coord.data();
7690 for(a = 0; a < l; a++) {
7691 v = PySequence_ITEM(coords, a);
7692
7693 for(b = 0; b < 3; b++) {
7694 if(!(w = PySequence_GetItem(v, b)))
7695 break;
7696
7697 f[a * 3 + b] = (float) PyFloat_AsDouble(w);
7698 Py_DECREF(w);
7699 }
7700
7701 Py_DECREF(v);
7702 ok_assert(2, !PyErr_Occurred());
7703 }
7704
7705 cset->invalidateRep(cRepAll, cRepInvRep);
7706
7707 // include coordinate set
7708 if (is_new) {
7709 VLACheck(I->CSet, CoordSet *, frame);
7710 if(I->NCSet <= frame)
7711 I->NCSet = frame + 1;
7712 I->CSet[frame] = cset;
7713 SceneCountFrames(G);
7714 }
7715
7716 // success
7717 return (I);
7718
7719 // error handling
7720 ok_except2:
7721 PyErr_Print();
7722 ok_except1:
7723 if(is_new && cset)
7724 cset->fFree();
7725 ErrMessage(G, "LoadCoords", "failed");
7726 return NULL;
7727 #endif
7728 }
7729
7730
7731 /*========================================================================*/
ObjectMoleculeExtendIndices(ObjectMolecule * I,int state)7732 int ObjectMoleculeExtendIndices(ObjectMolecule * I, int state)
7733 {
7734 int a;
7735 CoordSet *cs;
7736
7737 if(I->DiscreteFlag && (state >= 0)) {
7738 /* if object is discrete, then we don't need to extend each state,
7739 just the current one (which updates object DiscreteAtmToIdx) */
7740 cs = I->CSTmpl;
7741 ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
7742 if(state < I->NCSet) {
7743 cs = I->CSet[state];
7744 ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
7745 }
7746 } else { /* do all states */
7747 for(a = -1; a < I->NCSet; a++) {
7748 cs = (a < 0) ? I->CSTmpl : I->CSet[a];
7749 ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
7750 }
7751 }
7752 return true;
7753 ok_except1:
7754 return false;
7755 }
7756
7757
7758 /*========================================================================*/
7759
ObjectMoleculeMOLStr2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr,const char ** restart)7760 static CoordSet *ObjectMoleculeMOLStr2CoordSet(PyMOLGlobals * G, const char *buffer,
7761 AtomInfoType ** atInfoPtr, const char **restart)
7762 {
7763 const char *p;
7764 int nAtom, nBond;
7765 int a, cnt, atm, chg;
7766 float *coord = NULL;
7767 CoordSet *cset = NULL;
7768 AtomInfoType *atInfo = NULL;
7769 char cc[MAXLINELEN], cc1[MAXLINELEN], resn[MAXLINELEN] = "UNK";
7770 float *f;
7771 BondType *ii;
7772 BondType *bond = NULL;
7773 int ok = true;
7774 int auto_show = RepGetAutoShowMask(G);
7775 WordType nameTmp;
7776
7777 p = buffer;
7778 nAtom = 0;
7779 if(atInfoPtr)
7780 atInfo = *atInfoPtr;
7781
7782 /* p=ParseWordCopy(nameTmp,p,sizeof(WordType)-1); */
7783 p = ParseNCopy(nameTmp, p, sizeof(WordType) - 1);
7784 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
7785 " ObjMolMOLStr2CoordSet: title '%s'\n", nameTmp ENDFB(G)
7786 p = nextline(p);
7787 p = nextline(p);
7788 p = nextline(p);
7789
7790 if(ok) {
7791 p = ncopy(cc, p, 3);
7792 if(sscanf(cc, "%d", &nAtom) != 1)
7793 ok = ErrMessage(G, "ReadMOLFile", "bad atom count");
7794 }
7795
7796 if(ok) {
7797 p = ncopy(cc, p, 3);
7798 if(sscanf(cc, "%d", &nBond) != 1)
7799 ok = ErrMessage(G, "ReadMOLFile", "bad bond count");
7800 }
7801
7802 if(ok) {
7803 coord = VLAlloc(float, 3 * nAtom);
7804 if(atInfo)
7805 VLACheck(atInfo, AtomInfoType, nAtom);
7806 }
7807
7808 p = nextline(p);
7809
7810 /* read coordinates and atom names */
7811
7812 if(ok) {
7813 f = coord;
7814 for(a = 0; a < nAtom; a++) {
7815 if(ok) {
7816 p = ncopy(cc, p, 10);
7817 if(sscanf(cc, "%f", f++) != 1)
7818 ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
7819 }
7820 if(ok) {
7821 p = ncopy(cc, p, 10);
7822 if(sscanf(cc, "%f", f++) != 1)
7823 ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
7824 }
7825 if(ok) {
7826 p = ncopy(cc, p, 10);
7827 if(sscanf(cc, "%f", f++) != 1)
7828 ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
7829 }
7830 if(ok) {
7831 p = nskip(p, 1);
7832 p = ntrim(cc, p, 3);
7833 strncpy(atInfo[a].elem, cc, cElemNameLen);
7834 atInfo[a].name = LexIdx(G, cc);
7835 atInfo[a].visRep = auto_show;
7836 }
7837 if(ok) {
7838 int tmp_int;
7839 p = nskip(p, 2);
7840 p = ncopy(cc, p, 3);
7841 if(sscanf(cc, "%hhi", &atInfo[a].formalCharge) == 1) {
7842 if(atInfo[a].formalCharge) {
7843 atInfo[a].formalCharge = 4 - atInfo[a].formalCharge;
7844 }
7845 }
7846 p = ncopy(cc, p, 3);
7847 if(sscanf(cc, "%d", &tmp_int) != 1)
7848 atInfo[a].stereo = 0;
7849 else
7850 atInfo[a].stereo = tmp_int;
7851 }
7852 if(ok && atInfo) {
7853 atInfo[a].id = a + 1;
7854 atInfo[a].rank = a;
7855 atInfo[a].resn = LexIdx(G, resn);
7856 atInfo[a].hetatm = true;
7857 AtomInfoAssignParameters(G, atInfo + a);
7858 AtomInfoAssignColors(G, atInfo + a);
7859 atInfo[a].alt[0] = 0;
7860 atInfo[a].segi = 0;
7861 atInfo[a].inscode = 0;
7862 }
7863 p = nextline(p);
7864 if(!ok)
7865 break;
7866 }
7867 }
7868 if(ok) {
7869 bond = VLACalloc(BondType, nBond);
7870 ii = bond;
7871 for(a = 0; a < nBond; a++) {
7872 if(ok) {
7873 p = ncopy(cc, p, 3);
7874 if(sscanf(cc, "%d", &ii->index[0]) != 1)
7875 ok = ErrMessage(G, "ReadMOLFile", "bad bond atom");
7876 }
7877
7878 if(ok) {
7879 p = ncopy(cc, p, 3);
7880 if(sscanf(cc, "%d", &ii->index[1]) != 1)
7881 ok = ErrMessage(G, "ReadMOLFile", "bad bond atom");
7882 }
7883
7884 if(ok) {
7885 int order = 0;
7886 p = ncopy(cc, p, 3);
7887 if(sscanf(cc, "%d", &order) != 1)
7888 ok = ErrMessage(G, "ReadMOLFile", "bad bond order");
7889 ii->order = order;
7890 }
7891 if(ok) {
7892 int stereo;
7893 p = ncopy(cc, p, 3);
7894 if(sscanf(cc, "%d", &stereo) != 1)
7895 ii->stereo = 0;
7896 else
7897 ii->stereo = stereo;
7898 }
7899 ii++;
7900 if(!ok)
7901 break;
7902 p = nextline(p);
7903 }
7904 ii = bond;
7905 for(a = 0; a < nBond; a++) {
7906 ii->index[0]--; /* adjust bond indexs down one */
7907 ii->index[1]--;
7908 ii++;
7909 }
7910 }
7911 while(*p) { /* read M CHG records */
7912 auto p_line = p;
7913 p = ncopy(cc, p, 6);
7914 if(cc[5] == ' ')
7915 cc[5] = 0;
7916 if((!strcmp(cc, "M END")) || (!strcmp(cc, "M END"))) {
7917 /* denotes end of MOL block */
7918 break;
7919 }
7920 if((!strcmp(cc, "M CHG")) || (!strcmp(cc, "M CHG"))) {
7921 p = ncopy(cc, p, 3);
7922 if(sscanf(cc, "%d", &cnt) == 1) {
7923 while(cnt--) {
7924 p = ncopy(cc, p, 4);
7925 p = ncopy(cc1, p, 4);
7926 if(!((*cc) || (*cc1)))
7927 break;
7928 if((sscanf(cc, "%d", &atm) == 1) && (sscanf(cc1, "%d", &chg) == 1)) {
7929 atm--;
7930 if((atm >= 0) && (atm < nAtom))
7931 atInfo[atm].formalCharge = chg;
7932 }
7933 }
7934 }
7935 } else if (!strcmp(cc, "M V30")) {
7936 p = MOLV3000Parse(G, p_line, atInfo, bond, coord, nAtom, nBond);
7937 if (!p) {
7938 ok = false;
7939 break;
7940 }
7941 continue;
7942 }
7943 p = nextline(p);
7944 }
7945 if(ok) {
7946 (*restart) = p;
7947 cset = CoordSetNew(G);
7948 cset->NIndex = nAtom;
7949 cset->Coord = pymol::vla_take_ownership(coord);
7950 cset->NTmpBond = nBond;
7951 cset->TmpBond = pymol::vla_take_ownership(bond);
7952 strcpy(cset->Name, nameTmp);
7953 } else {
7954 VLAFreeP(bond);
7955 VLAFreeP(coord);
7956 (*restart) = NULL;
7957 }
7958 if(atInfoPtr)
7959 *atInfoPtr = atInfo;
7960 return (cset);
7961 }
7962
7963
7964 /*========================================================================*/
7965
ObjectMoleculeSDF2Str2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr,const char ** next_mol)7966 static CoordSet *ObjectMoleculeSDF2Str2CoordSet(PyMOLGlobals * G, const char *buffer,
7967 AtomInfoType ** atInfoPtr,
7968 const char **next_mol)
7969 {
7970 char cc[MAXLINELEN];
7971 const char *p;
7972 CoordSet *result = NULL;
7973 result = ObjectMoleculeMOLStr2CoordSet(G, buffer, atInfoPtr, next_mol);
7974 p = *next_mol;
7975 if(p) {
7976 while(*p) { /* we simply need to skip until we've read past the end of the SDF record */
7977 p = ncopy(cc, p, 4);
7978 p = nextline(p);
7979 if(!strcmp(cc, "$$$$")) { /* SDF record separator... */
7980 break;
7981 }
7982 }
7983 if(!*p)
7984 p = NULL;
7985 }
7986 *next_mol = p;
7987 return result;
7988 }
7989
7990 /*
7991 * Get the sum of bond orders for every atom. For this purpose, aromatic
7992 * bonds will be considered either single or double bond, based on
7993 * the entire system (heuristic method, room for improvement).
7994 */
7995 static
get_bond_order_sums(ObjectMolecule * obj)7996 std::vector<signed char> get_bond_order_sums(ObjectMolecule * obj) {
7997 std::vector<signed char> valences(obj->NAtom);
7998 std::vector<signed char> freevalences(obj->NAtom);
7999 std::vector<signed char> orders(obj->NBond);
8000
8001 // bond order sums as if all aromatic bonds were single bonds
8002 for (int b = 0; b < obj->NBond; ++b) {
8003 auto bond = obj->Bond + b;
8004 auto order = bond->order;
8005 orders[b] = order;
8006 if (order > 3)
8007 order = 1;
8008 valences[bond->index[0]] += order;
8009 valences[bond->index[1]] += order;
8010 }
8011
8012 // determine free valences as if all aromatic bonds were single bonds
8013 for (int atm = 0; atm < obj->NAtom; ++atm) {
8014 int tmp = 0;
8015 switch (obj->AtomInfo[atm].protons) {
8016 case cAN_C: tmp = 4; break;
8017 case cAN_N: case cAN_P: tmp = 5; break;
8018 case cAN_O: case cAN_S: tmp = 6; break;
8019 }
8020 if (tmp) {
8021 tmp -= valences[atm];
8022 if (tmp) {
8023 freevalences[atm] = (tmp - 1) % 2 + 1;
8024 }
8025 }
8026 }
8027
8028 // (This is a heuristic, gets most of the ZINC dataset correct)
8029 // Do two passes over all aromatic bonds.
8030 // 1st pass: assign double bonds between atoms with `freevalence == 1`
8031 // 2nd pass: assign double bonds between atoms with `freevalence >= 1`
8032 for (int secondpass = 0; secondpass < 2; ++secondpass) {
8033 for (int b = 0; b < obj->NBond; ++b) {
8034 if (orders[b] == 4) {
8035 auto atm1 = obj->Bond[b].index[0];
8036 auto atm2 = obj->Bond[b].index[1];
8037 if (secondpass ?
8038 (freevalences[atm1] >= 1 && freevalences[atm2] >= 1 ) :
8039 (freevalences[atm1] == 1 && freevalences[atm2] == 1)) {
8040 freevalences[atm1] = 0;
8041 freevalences[atm2] = 0;
8042 valences[atm1] += 1;
8043 valences[atm2] += 1;
8044 orders[b] = 2;
8045 }
8046 }
8047 }
8048 }
8049
8050 return valences;
8051 }
8052
8053 /*
8054 * For each atom, set `formalCharge` based on mol2 `textType`
8055 *
8056 * See also: getMOL2Type() in layer2/Mol2Typing.cpp
8057 */
ObjectMoleculeMOL2SetFormalCharges(PyMOLGlobals * G,ObjectMolecule * obj)8058 static void ObjectMoleculeMOL2SetFormalCharges(PyMOLGlobals *G, ObjectMolecule *obj){
8059
8060 // check if structure has explicit hydrogens
8061 bool has_hydrogens = false;
8062 for (int at = 0; at < obj->NAtom; ++at) {
8063 auto ai = obj->AtomInfo + at;
8064 if (ai->isHydrogen()) {
8065 has_hydrogens = true;
8066 break;
8067 }
8068 }
8069
8070 if (!has_hydrogens) {
8071 // could eventually do PDB nomenclature charge assignment here...
8072 return;
8073 }
8074
8075 // Unfortunately, aromatic bonds (type 4) can't be used to determine
8076 // formal charges. The following uses a heuristic to guess bond orders
8077 // for aromatic bonds.
8078 auto valences = get_bond_order_sums(obj);
8079
8080 // (period currently incompatible with G->lex_const)
8081 auto lex_N_4 = LexBorrow(G, "N.4");
8082
8083 for (int at = 0; at < obj->NAtom; ++at) {
8084 int fcharge = 0;
8085 auto ai = obj->AtomInfo + at;
8086
8087 if (ai->protons == cAN_N) {
8088 if (ai->textType == lex_N_4) {
8089 fcharge = 1;
8090 } else if (valences[at] == 2) {
8091 fcharge = -1;
8092 } else if (valences[at] == 4) {
8093 fcharge = 1;
8094 }
8095 } else if (ai->protons == cAN_O) {
8096 if (valences[at] == 1) {
8097 fcharge = -1;
8098 }
8099 }
8100
8101 ai->formalCharge = fcharge;
8102 }
8103 }
8104
8105 /*========================================================================*/
8106
ObjectMoleculeMOL2Str2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr,const char ** next_mol)8107 static CoordSet *ObjectMoleculeMOL2Str2CoordSet(PyMOLGlobals * G,
8108 const char *buffer,
8109 AtomInfoType ** atInfoPtr,
8110 const char **next_mol)
8111 {
8112 const char *p;
8113 int nAtom, nBond, nSubst, nFeat, nSet;
8114 int a;
8115 float *coord = NULL;
8116 CoordSet *cset = NULL;
8117 AtomInfoType *atInfo = NULL;
8118 char cc[MAXLINELEN];
8119 float *f;
8120 const char *last_p;
8121 BondType *ii;
8122 BondType *bond = NULL;
8123 int ok = true;
8124 int auto_show = RepGetAutoShowMask(G);
8125 int have_molecule = false;
8126 WordType nameTmp;
8127 bool has_non_hetatm = false;
8128
8129 // Maestro writes <0> as subst_name for waters
8130 const lexidx_t empty_subst_name = LexIdx(G, "<0>");
8131
8132 p = buffer;
8133 nAtom = 0;
8134 if(atInfoPtr)
8135 atInfo = *atInfoPtr;
8136
8137 while((*p) && ok) {
8138
8139 last_p = p;
8140 /* top level -- looking for an RTI, assuming p points to the beginning of a line */
8141 p = ParseWordCopy(cc, p, MAXLINELEN);
8142 if(cc[0] == '@') {
8143 if(WordMatchExact(G, cc, "@<TRIPOS>MOLECULE", true)
8144 || WordMatchExact(G, cc, "@MOLECULE", true)) {
8145 if(have_molecule) {
8146 *next_mol = last_p;
8147 break; /* next record of multi-mol2 */
8148 }
8149 p = ParseNextLine(p);
8150 p = ParseNTrim(nameTmp, p, sizeof(WordType) - 1); /* get mol name */
8151 p = ParseNextLine(p);
8152 p = ParseWordCopy(cc, p, MAXLINELEN);
8153 if(sscanf(cc, "%d", &nAtom) != 1)
8154 ok = ErrMessage(G, "ReadMOL2File", "bad atom count");
8155 else {
8156 coord = VLAlloc(float, 3 * nAtom);
8157 if(atInfo)
8158 VLACheck(atInfo, AtomInfoType, nAtom);
8159
8160 p = ParseWordCopy(cc, p, MAXLINELEN);
8161 if(sscanf(cc, "%d", &nBond) != 1) {
8162 nBond = 0;
8163 }
8164 p = ParseWordCopy(cc, p, MAXLINELEN);
8165 if(sscanf(cc, "%d", &nSubst) != 1) {
8166 nSubst = 0;
8167 }
8168 p = ParseWordCopy(cc, p, MAXLINELEN);
8169 if(sscanf(cc, "%d", &nFeat) != 1) {
8170 nFeat = 0;
8171 }
8172 p = ParseWordCopy(cc, p, MAXLINELEN);
8173 if(sscanf(cc, "%d", &nSet) != 1) {
8174 nSet = 0;
8175 }
8176
8177 }
8178 p = ParseNextLine(p);
8179 p = ParseNextLine(p);
8180 have_molecule = true;
8181 } else if(WordMatchExact(G, cc, "@<TRIPOS>ATOM", true)
8182 || WordMatchExact(G, cc, "@ATOM", true)) {
8183 if(!have_molecule) {
8184 ok = ErrMessage(G, "ReadMOL2File", "@ATOM before @MOLECULE!");
8185 break;
8186 }
8187 p = ParseNextLine(p);
8188 f = coord;
8189 for(a = 0; a < nAtom; a++) {
8190 AtomInfoType *ai = atInfo + a;
8191
8192 if(ok) {
8193 p = ParseWordCopy(cc, p, MAXLINELEN);
8194 if(sscanf(cc, "%d", &ai->id) != 1) {
8195 ok = ErrMessage(G, "ReadMOL2File", "bad atom id");
8196 }
8197 }
8198 if(ok) {
8199 p = ParseWordCopy(cc, p, MAXLINELEN);
8200 cc[cAtomNameLen] = 0;
8201 UtilCleanStr(cc);
8202 if(cc[0])
8203 LexAssign(G, ai->name, cc);
8204 else
8205 ok = ErrMessage(G, "ReadMOL2File", "bad atom name");
8206 }
8207 if(ok) {
8208 p = ParseWordCopy(cc, p, MAXLINELEN);
8209 if(sscanf(cc, "%f", f++) != 1)
8210 ok = ErrMessage(G, "ReadMOL2File", "bad x coordinate");
8211 }
8212 if(ok) {
8213 p = ParseWordCopy(cc, p, MAXLINELEN);
8214 if(sscanf(cc, "%f", f++) != 1)
8215 ok = ErrMessage(G, "ReadMOL2File", "bad y coordinate");
8216 }
8217 if(ok) {
8218 p = ParseWordCopy(cc, p, MAXLINELEN);
8219 if(sscanf(cc, "%f", f++) != 1)
8220 ok = ErrMessage(G, "ReadMOL2File", "bad z coordinate");
8221 }
8222 if(ok) {
8223 OrthoLineType temp;
8224 p = ParseWordCopy(cc, p, OrthoLineLength - 1);
8225 if(sscanf(cc, "%s", temp) != 1)
8226 ok = ErrMessage(G, "ReadMOL2File", "bad atom type");
8227 else { /* convert atom type to elem symbol */
8228 char *tt = temp;
8229 char *el = ai->elem;
8230 int elc = 0;
8231 while(*tt && ((*tt) != '.')) {
8232 *(el++) = *(tt++);
8233 elc++;
8234 if(elc > cElemNameLen)
8235 break;
8236 }
8237 *el = 0;
8238 if(el[2])
8239 el[0] = 0;
8240
8241 ai->textType = LexIdx(G, temp);
8242 }
8243 }
8244 if(ok) {
8245 char cc_resi[8];
8246 p = ParseWordCopy(cc_resi, p, sizeof(cc_resi) - 1);
8247 if(cc_resi[0]) { /* subst_id is residue identifier */
8248 ai->setResi(cc_resi);
8249 p = ParseWordCopy(cc, p, MAXLINELEN);
8250 if(cc[0]) {
8251 if (nSubst == 0) {
8252 // without substructure information (e.g. OpenBabel exported MOL2):
8253 // if subst_name includes the subst_id (e.g. 5 ALA5) then strip the number
8254 int len_resi = strlen(cc_resi);
8255 int len_resn = strlen(cc);
8256 if (len_resn > len_resi) {
8257 if (strcmp(cc_resi, cc + len_resn - len_resi) == 0) {
8258 cc[len_resn - len_resi] = 0;
8259 }
8260 }
8261 }
8262
8263 LexAssign(G, ai->resn, cc);
8264
8265 p = ParseWordCopy(cc, p, MAXLINELEN);
8266 if(cc[0]) {
8267 if(sscanf(cc, "%f", &ai->partialCharge) != 1)
8268 ok = ErrMessage(G, "ReadMOL2File", "bad atom charge");
8269 }
8270
8271 // status_bit
8272 p = ParseWordCopy(cc, p, MAXLINELEN);
8273 if (cc[0]) {
8274 // for water, substitute resn "<0>" with "HOH" for
8275 // correct classification as "solvent"
8276 if (ai->resn == empty_subst_name && strstr(cc, "WATER")) {
8277 LexAssign(G, ai->resn, G->lex_const.HOH);
8278 }
8279 }
8280 }
8281 }
8282 }
8283 p = ParseNextLine(p);
8284
8285 ai->visRep = auto_show;
8286
8287 ai->id = a + 1;
8288 ai->rank = a;
8289 ai->hetatm = true;
8290 AtomInfoAssignParameters(G, atInfo + a);
8291 AtomInfoAssignColors(G, atInfo + a);
8292 ai->alt[0] = 0;
8293 ai->segi = 0;
8294
8295 }
8296 } else if(WordMatchExact(G, cc, "@<TRIPOS>SET", true)
8297 || WordMatchExact(G, cc, "@SET", true)) {
8298 if(!have_molecule) {
8299 ok = ErrMessage(G, "ReadMOL2File", "@SET before @MOLECULE!");
8300 break;
8301 }
8302 p = ParseNextLine(p);
8303 if(ok) {
8304 for(a = 0; a < nSet; a++) {
8305 if(ok) {
8306 char ss = 0;
8307 p = ParseWordCopy(cc, p, 50);
8308 cc[5] = 0;
8309 if(!strcmp("HELIX", cc))
8310 ss = 'H';
8311 if(!strcmp("SHEET", cc))
8312 ss = 'S';
8313 p = ParseWordCopy(cc, p, 50);
8314 if(!strcmp("STATIC", cc)) {
8315 int nMember;
8316
8317 p = ParseNextLine(p);
8318 p = ParseWordCopy(cc, p, 20);
8319 if(sscanf(cc, "%d", &nMember) != 1)
8320 ok = ErrMessage(G, "ReadMOL2File", "bad member count");
8321 else {
8322 while(ok && (nMember > 0)) {
8323 p = ParseWordCopy(cc, p, 20);
8324 if((!cc[0]) && (!*p)) {
8325 ok = false;
8326 break;
8327 }
8328 if(cc[0] != '\\') {
8329 int atom_id;
8330 if(sscanf(cc, "%d", &atom_id) != 1) {
8331 ok = ErrMessage(G, "ReadMOL2File", "bad member");
8332 } else {
8333 atom_id--;
8334 if((atom_id >= 0) && (atom_id < nAtom)) {
8335 atInfo[atom_id].ssType[0] = ss;
8336 atInfo[atom_id].ssType[1] = 0;
8337 }
8338 nMember--;
8339 }
8340 } else {
8341 p = ParseNextLine(p);
8342 }
8343 }
8344 }
8345 p = ParseNextLine(p);
8346 } else {
8347 p = ParseNextLine(p);
8348 p = ParseNextLine(p);
8349 }
8350 }
8351 }
8352 }
8353 } else if(WordMatchExact(G, cc, "@<TRIPOS>BOND", true)
8354 || WordMatchExact(G, cc, "@BOND", true)) {
8355 if(!have_molecule) {
8356 ok = ErrMessage(G, "ReadMOL2File", "@BOND before @MOLECULE!");
8357 break;
8358 }
8359 p = ParseNextLine(p);
8360
8361 if(ok) {
8362 bond = VLACalloc(BondType, nBond);
8363 ii = bond;
8364 for(a = 0; a < nBond; a++) {
8365 if(ok) {
8366 p = ParseWordCopy(cc, p, 20);
8367 if(sscanf(cc, "%d", &ii->id) != 1)
8368 ok = ErrMessage(G, "ReadMOL2File", "bad atom id");
8369 }
8370
8371 if(ok) {
8372 p = ParseWordCopy(cc, p, 20);
8373 if(sscanf(cc, "%d", &ii->index[0]) != 1)
8374 ok = ErrMessage(G, "ReadMOL2File", "bad source atom id");
8375 }
8376
8377 if(ok) {
8378 p = ParseWordCopy(cc, p, 20);
8379 if(sscanf(cc, "%d", &ii->index[1]) != 1)
8380 ok = ErrMessage(G, "ReadMOL2File", "bad target atom id");
8381 }
8382
8383 if(ok) {
8384 p = ParseWordCopy(cc, p, 20);
8385 if(!cc[1]) {
8386 switch (cc[0]) {
8387 case '1':
8388 ii->order = 1;
8389 break;
8390 case '2':
8391 ii->order = 2;
8392 break;
8393 case '3':
8394 ii->order = 3;
8395 break;
8396 case '4':
8397 ii->order = 4;
8398 break;
8399 }
8400 } else if(WordMatchExact(G, "ar", cc, true)) {
8401 ii->order = 4;
8402 } else if(WordMatchExact(G, "am", cc, true)) {
8403 ii->order = 1;
8404 } else if(WordMatchExact(G, "un", cc, true)) {
8405 ii->order = 1;
8406 } else if(WordMatchExact(G, "nc", cc, true)) {
8407 ii->order = 0; /* is this legal in PyMOL? */
8408 } else if(WordMatchExact(G, "du", cc, true)) {
8409 ii->order = 1;
8410 } else
8411 ok = ErrMessage(G, "ReadMOL2File", "bad bond type");
8412 }
8413 ii->stereo = 0;
8414 ii++;
8415 if(!ok)
8416 break;
8417 p = ParseNextLine(p);
8418 }
8419 ii = bond;
8420 for(a = 0; a < nBond; a++) {
8421 ii->index[0]--; /* adjust bond indexs down one */
8422 ii->index[1]--;
8423 ii++;
8424 }
8425 }
8426 } else if(WordMatchExact(G, cc, "@<TRIPOS>SUBSTRUCTURE", true)
8427 || WordMatchExact(G, cc, "@SUBSTRUCTURE", true)) {
8428 if(!have_molecule) {
8429 ok = ErrMessage(G, "ReadMOL2File", "@SUBSTSRUCTURE before @MOLECULE!");
8430 break;
8431 }
8432 p = ParseNextLine(p);
8433 if(ok) {
8434 WordType subst_name;
8435 SegIdent segment; /* what MOL2 calls chain */
8436 lexidx_t chain = 0;
8437 WordType subst_type;
8438 ResIdent resi;
8439 ResName resn;
8440 int id, dict_type, root, resv;
8441 int end_line, seg_flag, subst_flag, resi_flag;
8442 int chain_flag, resn_flag;
8443 OVOneToOne *o2o = OVOneToOne_New(G->Context->heap);
8444
8445 {
8446 /* create linked list of atoms for each residue id */
8447 OVreturn_word result;
8448 int b;
8449 AtomInfoType *ai = atInfo;
8450 for(b = 0; b < nAtom; b++) {
8451 ai->temp1 = -1;
8452 ai++;
8453 }
8454 ai = atInfo;
8455 for(b = 0; b < nAtom; b++) {
8456 if(OVreturn_IS_OK((result = OVOneToOne_GetForward(o2o, ai->resv)))) {
8457 atInfo[b].temp1 = atInfo[result.word].temp1;
8458 atInfo[result.word].temp1 = b;
8459 } else {
8460 OVOneToOne_Set(o2o, ai->resv, b);
8461 }
8462 ai++;
8463 }
8464 }
8465
8466 for(a = 0; a < nSubst; a++) {
8467 bool hetatm = true;
8468 segment[0] = 0;
8469 subst_name[0] = 0;
8470 LexAssign(G, chain, 0);
8471 resn[0] = 0;
8472 resi[0] = 0;
8473 end_line = false;
8474 seg_flag = false;
8475 subst_flag = false;
8476
8477 resi_flag = false;
8478 chain_flag = false;
8479 resn_flag = false;
8480
8481 if(ok) {
8482 const char *save_p = p;
8483 p = ParseWordCopy(cc, p, 20);
8484 if(sscanf(cc, "%d", &id) != 1) {
8485 if(cc[0] != '@')
8486 ok = ErrMessage(G, "ReadMOL2File", "bad substructure id");
8487 else {
8488 p = save_p;
8489 break; /* missing substructure... */
8490 }
8491 }
8492 }
8493 if(ok) {
8494 p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
8495 if(sscanf(cc, "%s", subst_name) != 1)
8496 ok = ErrMessage(G, "ReadMOL2File", "bad substructure name");
8497 }
8498 if(ok) {
8499 p = ParseWordCopy(cc, p, 20);
8500 if(sscanf(cc, "%d", &root) != 1)
8501 ok = ErrMessage(G, "ReadMOL2File", "bad target root atom id");
8502 else
8503 root--; /* convert 1-based to 0-based */
8504 }
8505
8506 /* optional data */
8507 if(ok && (!end_line)) {
8508 p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
8509 if(sscanf(cc, "%s", subst_type) != 1) {
8510 end_line = true;
8511 subst_type[0] = 0;
8512 } else {
8513 subst_flag = 1;
8514 }
8515 }
8516
8517 if(ok && (!end_line)) {
8518 p = ParseWordCopy(cc, p, 20);
8519 if(sscanf(cc, "%d", &dict_type) != 1)
8520 end_line = true;
8521 }
8522
8523 if(ok && (!end_line)) {
8524 p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
8525 cc[cSegiLen] = 0;
8526 if(sscanf(cc, "%s", segment) != 1) {
8527 end_line = true;
8528 segment[0] = 0;
8529 } else if(strcmp(segment, "****")) {
8530 seg_flag = true;
8531 if(!segment[1]) { /* if segment is single letter, then also assign to chain field */
8532 LexAssign(G, chain, segment);
8533 chain_flag = true;
8534 }
8535 }
8536 }
8537
8538 if(ok && (!end_line)) {
8539 p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
8540 cc[cResnLen] = 0;
8541 if(!strcmp(cc, "****")) { /* whoops -- no residue name... */
8542 char *pp;
8543 UtilNCopy(resn, subst_name, sizeof(ResName));
8544 pp = resn;
8545 /* any numbers? */
8546 while(*pp) {
8547 if(((*pp) >= '0') && ((*pp) <= '9'))
8548 break;
8549 pp++;
8550 }
8551 /* trim them off */
8552 while(pp >= resn) {
8553 if(((*pp) >= '0') && ((*pp) <= '9'))
8554 *pp = 0;
8555 else
8556 break;
8557 pp--;
8558 }
8559
8560 if(resn[0])
8561 resn_flag = true;
8562 } else {
8563 if(sscanf(cc, "%s", resn) != 1) {
8564 end_line = true;
8565 resn[0] = 0;
8566 } else {
8567 resn_flag = true;
8568 }
8569 }
8570 }
8571
8572 if(ok && (root >= 0) && (root < nAtom)) {
8573
8574 if((strlen(subst_name) == 4) &&
8575 ((subst_name[0] >= '1') && (subst_name[0] <= '9')) &&
8576 ((((subst_name[1] >= 'A') && (subst_name[1] <= 'Z')) ||
8577 ((subst_name[1] >= 'a') && (subst_name[1] <= 'z'))) ||
8578 (((subst_name[2] >= 'A') && (subst_name[2] <= 'Z')) ||
8579 ((subst_name[2] >= 'a') && (subst_name[2] <= 'z'))) ||
8580 (((subst_name[3] >= 'A') && (subst_name[3] <= 'Z')) ||
8581 ((subst_name[3] >= 'a') && (subst_name[3] <= 'z'))))) {
8582
8583 /* if subst_name looks like a PDB code, then it isn't a residue identifier
8584 so do nothing... */
8585 } else {
8586
8587 if(!subst_flag) {
8588
8589 /* Merck stuffs the chain ID and the residue ID in the
8590 subst_name field, so handle that case first */
8591
8592 /* get the residue value */
8593
8594 ParseIntCopy(cc, subst_name, 20);
8595 if(sscanf(cc, "%d", &resv) == 1) {
8596 /* we have the resv, so now get the chain if there is one */
8597 char *pp = subst_name;
8598 if(!((pp[0] >= '0') && (pp[0] <= '9'))) {
8599 char tmp[2] = {pp[0], 0};
8600 LexAssign(G, chain, tmp);
8601 chain_flag = true;
8602 pp++;
8603 }
8604 UtilNCopy(resi, pp, cResiLen); /* now get the textual residue identifier */
8605
8606 if(resi[0]) {
8607 resi_flag = true;
8608 }
8609 }
8610 } else {
8611 /* normal mode: assume that the substructure ID is a vanilla residue identifier */
8612
8613 char *pp = subst_name;
8614
8615 if(resn_flag) { /* if we have a resn, then remove it from the subst_name */
8616 char *qq = resn;
8617 while((*pp) && (*qq)) {
8618 if(*pp == *qq) {
8619 pp++;
8620 qq++;
8621 } else
8622 break;
8623 }
8624 }
8625
8626 ParseIntCopy(cc, pp, 20);
8627 if(sscanf(cc, "%d", &resv) == 1) {
8628 UtilNCopy(resi, pp, cResiLen); /* now get the textual residue identifier */
8629 if(resi[0]) {
8630 resi_flag = true;
8631 }
8632 }
8633
8634 if (strcmp(subst_type, "RESIDUE") == 0) {
8635 hetatm = false;
8636 has_non_hetatm = true;
8637 }
8638 }
8639 }
8640
8641 /* for now, assume that atom ids are 1-based and sequential */
8642
8643 if(ok) {
8644 if(resi_flag || chain_flag || resn_flag || seg_flag) {
8645 OVreturn_word result;
8646 {
8647 if(OVreturn_IS_OK((result = OVOneToOne_GetForward(o2o, id)))) {
8648 /* traverse linked list for resi */
8649 int b = result.word;
8650 while((b >= 0) && (b < nAtom)) {
8651 AtomInfoType *ai = atInfo + b;
8652 if(resi_flag)
8653 ai->setResi(resi);
8654 if(chain_flag) {
8655 LexAssign(G, ai->chain, chain);
8656 }
8657 if(resn_flag)
8658 LexAssign(G, ai->resn, resn);
8659 if(seg_flag)
8660 LexAssign(G, ai->segi, segment);
8661 ai->hetatm = hetatm;
8662 b = ai->temp1;
8663 }
8664 }
8665 }
8666 }
8667 }
8668 }
8669 if(!ok)
8670 break;
8671 p = ParseNextLine(p);
8672 }
8673 LexDec(G, chain);
8674 OVOneToOne_DEL_AUTO_NULL(o2o);
8675 }
8676 } else
8677 p = ParseNextLine(p);
8678 } else
8679 p = ParseNextLine(p);
8680 }
8681
8682 LexDec(G, empty_subst_name);
8683
8684 if (has_non_hetatm) {
8685 // contains "residues" (assume polymer) so ignore hetatm for surfacing
8686 for (auto ai = atInfo, ai_end = atInfo + nAtom; ai != ai_end; ++ai) {
8687 if (ai->hetatm) {
8688 ai->flags |= cAtomFlag_ignore;
8689 }
8690 }
8691 }
8692
8693 if(ok) {
8694 cset = CoordSetNew(G);
8695 cset->NIndex = nAtom;
8696 cset->Coord = pymol::vla_take_ownership(coord);
8697 cset->NTmpBond = nBond;
8698 cset->TmpBond = pymol::vla_take_ownership(bond);
8699 strcpy(cset->Name, nameTmp);
8700 } else {
8701 VLAFreeP(bond);
8702 VLAFreeP(coord);
8703 }
8704 if(atInfoPtr)
8705 *atInfoPtr = atInfo;
8706 return (cset);
8707 }
8708
8709 /*
8710 * Read one molecule in MOL2, MOL, SDF, MMD or XYZ format from the string pointed
8711 * to by (*next_entry). All these formats (except MOL) support multiple
8712 * concatenated entries in one file. If multiplex=1, then read only one
8713 * molecule (one state) and set the next_entry pointer to the beginning of the
8714 * next entry. Otherwise, read a multi-state molecule.
8715 */
ObjectMoleculeReadStr(PyMOLGlobals * G,ObjectMolecule * I,const char ** next_entry,cLoadType_t content_format,int frame,int discrete,int quiet,int multiplex,char * new_name,short loadpropertiesall,OVLexicon * loadproplex)8716 ObjectMolecule *ObjectMoleculeReadStr(PyMOLGlobals * G, ObjectMolecule * I,
8717 const char **next_entry,
8718 cLoadType_t content_format, int frame,
8719 int discrete, int quiet, int multiplex,
8720 char *new_name,
8721 short loadpropertiesall, OVLexicon *loadproplex)
8722 {
8723 int ok = true;
8724 CoordSet *cset = NULL;
8725 pymol::vla<AtomInfoType> atInfo;
8726 int isNew;
8727 int nAtom;
8728 const char *restart = NULL, *start = *next_entry;
8729 int repeatFlag = true;
8730 int successCnt = 0;
8731 char tmpName[WordLength];
8732 int deferred_tasks = false;
8733 int skip_out;
8734 int connect = false;
8735 int set_formal_charges = false;
8736 *next_entry = NULL;
8737 int aic_mask = cAIC_MOLMask;
8738
8739 while(repeatFlag) {
8740 repeatFlag = false;
8741 skip_out = false;
8742
8743 if(!I)
8744 isNew = true;
8745 else
8746 isNew = false;
8747
8748 if(isNew) {
8749 I = (ObjectMolecule *) new ObjectMolecule(G, (discrete > 0));
8750 std::swap(atInfo, I->AtomInfo);
8751 } else {
8752 atInfo = pymol::vla<AtomInfoType>(10);
8753 }
8754
8755 if(isNew) {
8756 I->Color = AtomInfoUpdateAutoColor(G);
8757 }
8758
8759 restart = NULL;
8760 switch (content_format) {
8761 case cLoadTypeMOL2:
8762 case cLoadTypeMOL2Str:
8763 cset = ObjectMoleculeMOL2Str2CoordSet(G, start, &atInfo, &restart);
8764 if (cset){
8765 set_formal_charges = true;
8766 }
8767 break;
8768 case cLoadTypeMOL:
8769 case cLoadTypeMOLStr:
8770 cset = ObjectMoleculeMOLStr2CoordSet(G, start, &atInfo, &restart);
8771 restart = NULL;
8772 break;
8773 case cLoadTypeSDF2:
8774 case cLoadTypeSDF2Str:
8775 cset = ObjectMoleculeSDF2Str2CoordSet(G, start, &atInfo, &restart);
8776 break;
8777 case cLoadTypeXYZ:
8778 case cLoadTypeXYZStr:
8779 cset = ObjectMoleculeXYZStr2CoordSet(G, start, &atInfo, &restart);
8780 if(!cset->TmpBond)
8781 connect = true;
8782 break;
8783 case cLoadTypeMMD:
8784 case cLoadTypeMMDStr:
8785 cset = ObjectMoleculeMMDStr2CoordSet(G, start, &atInfo, &restart);
8786 aic_mask = cAIC_MMDMask;
8787 break;
8788 }
8789
8790
8791 if(!cset) {
8792 if(!isNew)
8793 VLAFreeP(atInfo);
8794 if(!successCnt) {
8795 if (isNew)
8796 std::swap(I->AtomInfo, atInfo);
8797 DeleteP(I);
8798 ok = false;
8799 } else {
8800 skip_out = true;
8801 }
8802 }
8803
8804 if(ok && !skip_out) {
8805
8806 if((discrete>0 && !I->DiscreteFlag) || // should set to discrete if explicitly defined
8807 ((discrete < 0) && (restart) && isNew && (multiplex <= 0))) {
8808 /* if default discrete behavior and new object, with
8809 multi-coordinate set file, and not multiplex, then set
8810 discrete */
8811
8812 ObjectMoleculeSetDiscrete(G, I, true);
8813 }
8814
8815 if(frame < 0)
8816 frame = I->NCSet;
8817 if(I->NCSet <= frame)
8818 I->NCSet = frame + 1;
8819 VLACheck(I->CSet, CoordSet *, frame);
8820
8821 nAtom = cset->NIndex;
8822
8823 if(I->DiscreteFlag && atInfo) {
8824 int a;
8825 int fp1 = frame + 1;
8826 AtomInfoType *ai = atInfo.data();
8827 for(a = 0; a < nAtom; a++) {
8828 (ai++)->discrete_state = fp1;
8829 }
8830 }
8831
8832 if(multiplex > 0)
8833 UtilNCopy(tmpName, cset->Name, WordLength);
8834
8835 cset->Obj = I;
8836 cset->enumIndices();
8837 cset->invalidateRep(cRepAll, cRepInvRep);
8838 if(isNew) {
8839 std::swap(I->AtomInfo, atInfo);
8840 } else {
8841 ObjectMoleculeMerge(I, std::move(atInfo), cset, false, aic_mask, false);
8842 /* NOTE: will release atInfo */
8843 }
8844
8845 if(isNew)
8846 I->NAtom = nAtom;
8847 if(frame < 0)
8848 frame = I->NCSet;
8849 VLACheck(I->CSet, CoordSet *, frame);
8850 if(I->NCSet <= frame)
8851 I->NCSet = frame + 1;
8852 if(I->CSet[frame])
8853 I->CSet[frame]->fFree();
8854 I->CSet[frame] = cset;
8855
8856 if(ok && isNew)
8857 ok &= ObjectMoleculeConnect(I, cset, connect);
8858
8859 if (ok)
8860 ok &= ObjectMoleculeExtendIndices(I, frame);
8861 if (ok)
8862 ok &= ObjectMoleculeSort(I);
8863
8864 deferred_tasks = true;
8865 successCnt++;
8866 if(!quiet) {
8867 if(successCnt > 1) {
8868 if(successCnt == 2) {
8869 PRINTFB(G, FB_ObjectMolecule, FB_Actions)
8870 " %s: read through molecule %d.\n", __func__, 1 ENDFB(G);
8871 }
8872 if(UtilShouldWePrintQuantity(successCnt)) {
8873 PRINTFB(G, FB_ObjectMolecule, FB_Actions)
8874 " %s: read through molecule %d.\n", __func__, successCnt ENDFB(G);
8875 }
8876 }
8877 }
8878 if(multiplex > 0) {
8879 UtilNCopy(new_name, tmpName, WordLength);
8880 if(restart) {
8881 *next_entry = restart;
8882 }
8883 } else if(restart) {
8884 repeatFlag = true;
8885 start = restart;
8886 frame = frame + 1;
8887 }
8888 }
8889 }
8890 if(deferred_tasks && I) { /* defer time-consuming tasks until all states have been loaded */
8891 if (set_formal_charges){
8892 ObjectMoleculeMOL2SetFormalCharges(G, I);
8893 }
8894 SceneCountFrames(G);
8895 I->invalidate(cRepAll, cRepInvAll, -1);
8896 ObjectMoleculeUpdateIDNumbers(I);
8897 ObjectMoleculeUpdateNonbonded(I);
8898 }
8899 return (I);
8900 }
8901
8902
8903 /*========================================================================*/
8904 typedef int CompareFn(PyMOLGlobals *, const AtomInfoType *, const AtomInfoType *);
ObjectMoleculeMerge(ObjectMolecule * I,pymol::vla<AtomInfoType> && ai,CoordSet * cs,int bondSearchFlag,int aic_mask,int invalidate)8905 int ObjectMoleculeMerge(ObjectMolecule * I, pymol::vla<AtomInfoType>&& ai,
8906 CoordSet * cs, int bondSearchFlag, int aic_mask, int invalidate)
8907 {
8908 PyMOLGlobals *G = I->G;
8909 int *index, *outdex, *a2i = NULL, *i2a = NULL;
8910 int a, b, lb = 0, ac;
8911 int c, nb, a1, a2;
8912 int found;
8913 int nAt, nBd, nBond;
8914 int expansionFlag = false;
8915 int oldNAtom, oldNBond;
8916 int ok = true;
8917
8918 oldNAtom = I->NAtom;
8919 oldNBond = I->NBond;
8920
8921 /* first, sort the coodinate set */
8922
8923 index = AtomInfoGetSortedIndex(G, I, ai, cs->NIndex, &outdex);
8924 CHECKOK(ok, index);
8925 if (!ok)
8926 return false;
8927 for(b = 0; b < cs->NIndex; b++)
8928 cs->IdxToAtm[b] = outdex[cs->IdxToAtm[b]];
8929 for(b = 0; b < cs->NIndex; b++)
8930 cs->AtmToIdx[b] = -1;
8931 for(b = 0; b < cs->NIndex; b++)
8932 cs->AtmToIdx[cs->IdxToAtm[b]] = b;
8933
8934 auto ai2 = pymol::vla<AtomInfoType>(cs->NIndex);
8935 if (ok){
8936 for(a = 0; a < cs->NIndex; a++)
8937 ai2[a] = std::move(ai[index[a]]); /* creates a sorted list of atom info records */
8938 }
8939 ai = std::move(ai2);
8940
8941 /* now, match it up with the current object's atomic information */
8942
8943 if (ok){
8944 for(a = 0; a < cs->NIndex; a++) {
8945 index[a] = -1;
8946 outdex[a] = -1;
8947 }
8948 }
8949
8950 if (ok) {
8951 int n_index = cs->NIndex;
8952 int n_atom = I->NAtom;
8953 const AtomInfoType *atInfo = I->AtomInfo.data(), *ai_a;
8954 CompareFn *fCompare;
8955
8956 if(SettingGetGlobal_b(G, cSetting_pdb_hetatm_sort)) {
8957 fCompare = AtomInfoCompareIgnoreRank;
8958 } else {
8959 fCompare = AtomInfoCompareIgnoreRankHet;
8960 }
8961
8962 c = 0;
8963 b = 0;
8964 lb = 0;
8965
8966 for(a = 0; a < n_index; a++) {
8967 int reverse = false;
8968 ai_a = ai + a;
8969 found = false;
8970 if(!I->DiscreteFlag) { /* don't even try matching for discrete objects */
8971 while(b < n_atom) {
8972 ac = (fCompare(G, ai_a, atInfo + b));
8973 if(!ac) {
8974 found = true;
8975 break;
8976 } else if(ac < 0) { /* atom is before current, so try going the other way */
8977 reverse = true;
8978 break;
8979 } else if(!b) {
8980 int idx;
8981 ac = (fCompare(G, ai_a, atInfo + n_atom - 1));
8982 if(ac > 0) { /* atom is after all atoms in list, so don't even bother searching */
8983 break;
8984 }
8985 /* next, try to jump to an appropriate position in the list */
8986 idx = ((a * n_atom) / n_index) - 1;
8987 if((idx > 0) && (b != idx) && (idx < n_atom)) {
8988 ac = (fCompare(G, ai_a, atInfo + idx));
8989 if(ac > 0)
8990 b = idx;
8991 }
8992 }
8993 lb = b; /* last b before atom */
8994 b++;
8995 }
8996 if(reverse && !found) { /* searching going backwards... */
8997 while(b >= 0) {
8998 ac = (fCompare(G, ai_a, atInfo + b));
8999 if(!ac) {
9000 found = true;
9001 break;
9002 } else if(ac > 0) { /* atom after current -- no good */
9003 break;
9004 }
9005 lb = b;
9006 b--;
9007 }
9008 if(b < 0)
9009 b = 0;
9010 }
9011 }
9012 if(found) {
9013 index[a] = b; /* store real atom index b for a in index[a] */
9014 b++;
9015 } else {
9016 index[a] = I->NAtom + c; /* otherwise, this is a new atom */
9017 c++;
9018 b = lb;
9019 }
9020 }
9021 }
9022
9023 /* first, reassign atom info for matched atoms */
9024
9025 /* allocate additional space */
9026 if (ok){
9027 if(c) {
9028 expansionFlag = true;
9029 nAt = I->NAtom + c;
9030 } else {
9031 nAt = I->NAtom;
9032 }
9033 if(expansionFlag) {
9034 VLACheck(I->AtomInfo, AtomInfoType, nAt);
9035 CHECKOK(ok, I->AtomInfo);
9036 }
9037 }
9038
9039 /* allocate our new x-ref tables */
9040 if (ok){
9041 if(nAt < I->NAtom)
9042 nAt = I->NAtom;
9043 a2i = VLACalloc(int, nAt);
9044 CHECKOK(ok, a2i);
9045 if (ok)
9046 i2a = VLACalloc(int, cs->NIndex);
9047 CHECKOK(ok, i2a);
9048 }
9049
9050 if (ok){
9051 for(a = 0; a < cs->NIndex; a++) { /* a is in original file space */
9052 a1 = cs->IdxToAtm[a]; /* a1 is in sorted atom info space */
9053 a2 = index[a1];
9054 i2a[a] = a2; /* a2 is in object space */
9055 if(a2 < oldNAtom)
9056 AtomInfoCombine(G, I->AtomInfo + a2, std::move(ai[a1]), aic_mask);
9057 else
9058 I->AtomInfo[a2] = std::move(ai[a1]);
9059 }
9060 }
9061
9062 if(ok && I->DiscreteFlag) {
9063 ok = I->setNDiscrete(nAt);
9064 }
9065
9066 cs->NAtIndex = nAt;
9067 I->NAtom = nAt;
9068
9069 VLAFreeP(cs->AtmToIdx);
9070 VLAFreeP(cs->IdxToAtm);
9071 cs->IdxToAtm = pymol::vla_take_ownership(i2a);
9072
9073 if (ok){
9074 cs->AtmToIdx = pymol::vla_take_ownership(a2i);
9075 for(a = 0; a < cs->NAtIndex; a++)
9076 cs->AtmToIdx[a] = -1;
9077 for(a = 0; a < cs->NIndex; a++)
9078 cs->AtmToIdx[cs->IdxToAtm[a]] = a;
9079 }
9080
9081 VLAFreeP(ai); /* note that we're trusting AtomInfoCombine to have
9082 already purged all atom-dependent storage (such as strings) */
9083
9084 AtomInfoFreeSortedIndexes(G, &index, &outdex);
9085
9086 /* now find and integrate and any new bonds */
9087 if(ok && expansionFlag) { /* expansion flag means we have introduced at least 1 new atom */
9088 pymol::vla<BondType> bond;
9089 ok &= ObjectMoleculeConnect(I, nBond, bond, cs, bondSearchFlag, -1);
9090 if(nBond) {
9091 index = pymol::malloc<int>(nBond);
9092 CHECKOK(ok, index);
9093 c = 0;
9094 b = 0;
9095 nb = 0;
9096 if (ok){
9097 for(a = 0; a < nBond; a++) { /* iterate over new bonds */
9098 found = false;
9099 if(!I->DiscreteFlag) { /* don't even try matching for discrete objects */
9100 b = nb; /* pick up where we left off */
9101 while(b < I->NBond) {
9102 ac = BondCompare(bond + a, I->Bond + b);
9103 if(!ac) { /* zero is a match */
9104 found = true;
9105 break;
9106 } else if(ac < 0) { /* gone past position of this bond */
9107 break;
9108 } else if(!b) {
9109 int idx;
9110 ac = BondCompare(bond + a, I->Bond + I->NBond - 1);
9111
9112 if(ac > 0) /* bond is after all bonds in list, so don't even bother searching */
9113 break;
9114 /* next, try to jump to an appropriate position in the list */
9115 idx = ((a * I->NBond) / nBond) - 1;
9116 if((idx > 0) && (b != idx) && (idx < I->NBond)) {
9117 ac = BondCompare(bond + a, I->Bond + idx);
9118 if(ac > 0)
9119 b = idx;
9120 }
9121 }
9122 b++; /* no match yet, keep looking */
9123 }
9124 }
9125 if(found) {
9126 index[a] = b; /* existing bond... */
9127 nb = b + 1;
9128 } else { /* this is a new bond, save index and increment */
9129 index[a] = I->NBond + c;
9130 c++;
9131 }
9132 }
9133 }
9134 /* first, reassign atom info for matched atoms */
9135 if(c) {
9136 /* allocate additional space */
9137 nBd = I->NBond + c;
9138
9139 if(!I->Bond)
9140 I->Bond = pymol::vla<BondType>(1);
9141 CHECKOK(ok, I->Bond);
9142 if (ok)
9143 VLACheck(I->Bond, BondType, nBd);
9144 CHECKOK(ok, I->Bond);
9145 for(a = 0; a < nBond; a++) { /* copy the new bonds */
9146 a2 = index[a];
9147 if(a2 >= I->NBond) {
9148 I->Bond[a2] = bond[a];
9149 }
9150 }
9151 I->NBond = nBd;
9152 }
9153 FreeP(index);
9154 }
9155 }
9156 if(invalidate) {
9157 if(oldNAtom) {
9158 if(oldNAtom == I->NAtom) {
9159 if(oldNBond != I->NBond) {
9160 I->invalidate(cRepAll, cRepInvBonds, -1);
9161 }
9162 } else {
9163 I->invalidate(cRepAll, cRepInvAtoms, -1);
9164 }
9165 }
9166 }
9167 return ok;
9168 }
9169
9170
9171 /*========================================================================*/
9172 /**
9173 * @param state Object state or -2 for current state
9174 * @return NULL if there is no CoordSet for the given state
9175 */
getCoordSet(int state)9176 CoordSet* ObjectMolecule::getCoordSet(int state)
9177 {
9178 return static_cast<CoordSet*>(getObjectState(state));
9179 }
getCoordSet(int state) const9180 const CoordSet* ObjectMolecule::getCoordSet(int state) const
9181 {
9182 return static_cast<const CoordSet*>(getObjectState(state));
9183 }
9184
9185
9186 /*========================================================================*/
ObjectMoleculeTransformTTTf(ObjectMolecule * I,float * ttt,int frame)9187 void ObjectMoleculeTransformTTTf(ObjectMolecule * I, float *ttt, int frame)
9188 {
9189 int b;
9190 CoordSet *cs;
9191 for(b = 0; b < I->NCSet; b++) {
9192 if((frame < 0) || (frame == b)) {
9193 cs = I->CSet[b];
9194 if(cs) {
9195 cs->invalidateRep(cRepAll, cRepInvCoord);
9196 MatrixTransformTTTfN3f(cs->NIndex, cs->Coord.data(), ttt, cs->Coord.data());
9197 CoordSetRecordTxfApplied(cs, ttt, false);
9198 }
9199 }
9200 }
9201 }
9202
9203
9204 /*========================================================================*/
ObjectMoleculeSeleOp(ObjectMolecule * I,int sele,ObjectMoleculeOpRec * op)9205 void ObjectMoleculeSeleOp(ObjectMolecule * I, int sele, ObjectMoleculeOpRec * op)
9206 {
9207 float *coord;
9208 int a, b, s;
9209 int c, d, t_i;
9210 int a1 = 0, ind;
9211 float rms;
9212 float v1[3], v2, *vv1, *vv2, *vt, *vt1, *vt2;
9213 int hit_flag = false;
9214 int ok = true;
9215 int cnt;
9216 int skip_flag;
9217 int match_flag = false;
9218 int offset;
9219 int priority;
9220 int use_matrices = false;
9221 CoordSet *cs;
9222 AtomInfoType *ai, *ai0;
9223 PyMOLGlobals *G = I->G;
9224 #ifndef _PYMOL_NOPY
9225 PyCodeObject *expr_co = NULL;
9226 int compileType = Py_single_input;
9227 #endif
9228 #ifdef _WEBGL
9229 #endif
9230 PRINTFD(G, FB_ObjectMolecule)
9231 " %s-DEBUG: sele %d op->code %d\n", __func__, sele, op->code ENDFD;
9232 if(sele >= 0) {
9233 const char *errstr = "Alter";
9234 /* always run on entry */
9235 switch (op->code) {
9236 case OMOP_LABL:
9237 errstr = "Label";
9238 if (op->i2 != cExecutiveLabelEvalOn){
9239 break;
9240 }
9241 #ifndef _PYMOL_NOPY
9242 compileType = Py_eval_input;
9243 #endif
9244 case OMOP_ALTR:
9245 case OMOP_AlterState:
9246 PBlock(G);
9247 if (op->s1 && op->s1[0]){
9248 #ifndef _PYMOL_NOPY
9249 expr_co = (PyCodeObject*)Py_CompileString(op->s1, "", compileType);
9250 if(expr_co == NULL) {
9251 if(PyErr_Occurred())
9252 PyErr_Print();
9253 ok = ErrMessage(G, errstr, "failed to compile expression");
9254 }
9255 #else
9256 #ifndef _WEBGL
9257 ok = ErrMessage(G, errstr, "failed to compile expression");
9258 #endif
9259 #endif
9260 }
9261 /* PBlockAndUnlockAPI() is not safe.
9262 * what if "v" is invalidated by another thread? */
9263 break;
9264 }
9265 switch (op->code) {
9266 case OMOP_ReferenceStore:
9267 case OMOP_ReferenceRecall:
9268 case OMOP_ReferenceValidate:
9269 case OMOP_ReferenceSwap:
9270 for(b = 0; b < I->NCSet; b++) {
9271 if(I->CSet[b]) {
9272 if((b == op->i1) || (op->i1 < 0)) {
9273 int inv_flag = false;
9274 cs = I->CSet[b];
9275 if(cs && CoordSetValidateRefPos(cs)) {
9276 for(a = 0; a < I->NAtom; a++) {
9277 s = I->AtomInfo[a].selEntry;
9278 if(SelectorIsMember(G, s, sele)) {
9279 ind = cs->atmToIdx(a);
9280 if(ind >= 0) {
9281 float *v = cs->coordPtr(ind);
9282 RefPosType *rp = cs->RefPos + ind;
9283 switch (op->code) {
9284 case OMOP_ReferenceStore:
9285 copy3f(v, rp->coord);
9286 rp->specified = true;
9287 break;
9288 case OMOP_ReferenceRecall:
9289 if(rp->specified) {
9290 copy3f(rp->coord, v);
9291 inv_flag = true;
9292 }
9293 break;
9294 case OMOP_ReferenceValidate:
9295 if(!rp->specified) {
9296 copy3f(v, rp->coord);
9297 rp->specified = true;
9298 }
9299 break;
9300 case OMOP_ReferenceSwap:
9301 if(rp->specified) {
9302 copy3f(rp->coord, v1);
9303 copy3f(v, rp->coord);
9304 copy3f(v1, v);
9305 inv_flag = true;
9306 }
9307 break;
9308 }
9309 op->i2++;
9310 }
9311 }
9312 }
9313 }
9314 if(inv_flag && cs) {
9315 cs->invalidateRep(cRepAll, cRepInvRep);
9316 }
9317 }
9318 }
9319 }
9320 break;
9321 case OMOP_AddHydrogens:
9322 if (ok) {
9323 ok &= ObjectMoleculeAddSeleHydrogensRefactored(I, sele, op->i1);
9324 }
9325 break;
9326 case OMOP_FixHydrogens:
9327 if (ok)
9328 ok &= ObjectMoleculeFixSeleHydrogens(I, sele, -1); /* state? */
9329 break;
9330 case OMOP_RevalenceFromSource:
9331 case OMOP_RevalenceByGuessing:
9332 ai = I->AtomInfo.data();
9333 for(a = 0; a < I->NAtom; a++) {
9334 if(SelectorIsMember(G, ai->selEntry, sele)) {
9335 hit_flag = true;
9336 break;
9337 }
9338 ai++;
9339 }
9340 break;
9341 case OMOP_PrepareFromTemplate:
9342 ai0 = op->ai; /* template atom */
9343 for(a = 0; a < I->NAtom; a++) {
9344 s = I->AtomInfo[a].selEntry;
9345 if(SelectorIsMember(G, s, sele)) {
9346 ai = I->AtomInfo + a;
9347 if(op->i1 != 3) {
9348 ai->hetatm = ai0->hetatm;
9349 ai->flags = ai0->flags;
9350 LexAssign(G, ai->chain, ai0->chain);
9351 strcpy(ai->alt, ai0->alt);
9352 LexAssign(G, ai->segi, ai0->segi);
9353 }
9354 if(op->i1 == 1) { /* mode 1, merge residue information */
9355 ai->inscode = ai0->inscode;
9356 ai->resv = ai0->resv;
9357 LexAssign(G, ai->resn, ai0->resn);
9358 }
9359 AtomInfoAssignColors(G, ai);
9360 if(op->i3) {
9361 if((ai->elem[0] == ai0->elem[0]) && (ai->elem[1] == ai0->elem[1]))
9362 ai->color = ai0->color;
9363 else if(ai->protons == cAN_C) {
9364 ai->color = op->i4;
9365 }
9366 }
9367 ai->visRep = ai0->visRep;
9368 ai->id = -1;
9369 ai->rank = -1;
9370 op->i2++;
9371 }
9372 }
9373 break;
9374 case OMOP_Sort:
9375 for(a = 0; ok && a < I->NAtom; a++) {
9376 s = I->AtomInfo[a].selEntry;
9377 if(SelectorIsMember(G, s, sele)) {
9378 if (ok)
9379 ok &= ObjectMoleculeSort(I);
9380 break;
9381 }
9382 }
9383 break;
9384 case OMOP_SetAtomicSetting:
9385 ai = I->AtomInfo.data();
9386 for(a = 0; a < I->NAtom; a++) {
9387 if(SelectorIsMember(G, ai->selEntry, sele)) {
9388 int uid = AtomInfoCheckUniqueID(G, ai);
9389 ai->has_setting = true;
9390 if (SettingUniqueSetTypedValue(G, uid, op->i1, op->i2, op->ii1))
9391 op->i4++;
9392 }
9393 ai++;
9394 }
9395 break;
9396 case OMOP_Pop:
9397 for(a = 0; a < I->NAtom; a++) {
9398 s = I->AtomInfo[a].selEntry;
9399 if(SelectorIsMember(G, s, sele)) {
9400 if(SelectorMoveMember(G, s, sele, op->i1)) {
9401 op->i2--;
9402 op->i3++;
9403 }
9404 if(!op->i2)
9405 break;
9406 }
9407 }
9408 break;
9409 case OMOP_AVRT: /* average vertex coordinate */
9410 case OMOP_StateVRT: /* state vertex coordinate */
9411 {
9412 int op_i2 = op->i2;
9413 int obj_TTTFlag = I->TTTFlag;
9414 int b_end = I->NCSet;
9415 if (op->code == OMOP_StateVRT && op->i1 < b_end) {
9416 b_end = op->i1 + 1;
9417 }
9418 if(op_i2) {
9419 use_matrices =
9420 SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
9421 if(use_matrices<0) use_matrices = 0;
9422 }
9423 for(a = 0; a < I->NAtom; a++) {
9424 s = I->AtomInfo[a].selEntry;
9425 if(!(priority = SelectorIsMember(G, s, sele)))
9426 continue;
9427
9428 cnt = 0;
9429
9430 // all states for AVRT, one state for StateVRT (don't use
9431 // StateIterator which depends on settings)
9432 for(b = op->i1; b < b_end; ++b) {
9433 if(!(cs = I->CSet[b]))
9434 continue;
9435
9436 if((a1 = cs->atmToIdx(a)) == -1)
9437 continue;
9438
9439 if(!cnt) {
9440 VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
9441 }
9442 cnt++;
9443 vv2 = cs->coordPtr(a1);
9444
9445 if(op_i2) { /* do we want transformed coordinates? */
9446 if(use_matrices) {
9447 if(!cs->Matrix.empty()) { /* state transformation */
9448 transform44d3f(cs->Matrix.data(), vv2, v1);
9449 vv2 = v1;
9450 }
9451 }
9452 if(obj_TTTFlag) {
9453 transformTTT44f3f(I->TTT, vv2, v1);
9454 vv2 = v1;
9455 }
9456 }
9457
9458 // sum up coordinates over states
9459 vv1 = op->vv1 + (op->nvv1 * 3);
9460 add3f(vv1, vv2, vv1);
9461 }
9462
9463 // number of summed up coordinates (states) for this atom
9464 VLACheck(op->vc1, int, op->nvv1);
9465 op->vc1[op->nvv1] = cnt;
9466
9467 // ordered_selections
9468 if(op->vp1) {
9469 VLACheck(op->vp1, int, op->nvv1);
9470 op->vp1[op->nvv1] = priority;
9471 }
9472
9473 // atom pointer VLA
9474 if(op->ai1VLA) {
9475 VLACheck(op->ai1VLA, AtomInfoType *, op->nvv1);
9476 op->ai1VLA[op->nvv1] = I->AtomInfo + a;
9477 I->AtomInfo[a].temp1 = a;
9478 /* KLUDGE ALERT!!! storing atom index in the temp1 field... */
9479 }
9480
9481 // number of atoms in selection (incl. the ones with no coordinates)
9482 op->nvv1++;
9483 }
9484 }
9485 break;
9486 case OMOP_SFIT: /* state fitting within a single object */
9487 vt = pymol::malloc<float>(3 * op->nvv2); /* temporary (matching) target vertex pointers */
9488 cnt = 0;
9489 for(a = 0; a < I->NAtom; a++) {
9490 s = I->AtomInfo[a].selEntry;
9491 if(SelectorIsMember(G, s, sele)) {
9492 cnt++;
9493 break;
9494 }
9495 }
9496 if(cnt) { /* only perform action for selected object */
9497
9498 for(b = 0; b < I->NCSet; b++) {
9499 rms = -1.0;
9500 vt1 = vt; /* reset target vertex pointers */
9501 vt2 = op->vv2;
9502 t_i = 0; /* original target vertex index */
9503 if(I->CSet[b] && (b != op->i2)) {
9504 op->nvv1 = 0;
9505 for(a = 0; a < I->NAtom; a++) {
9506 s = I->AtomInfo[a].selEntry;
9507 if(SelectorIsMember(G, s, sele)) {
9508 a1 = I->CSet[b]->atmToIdx(a);
9509 if(a1 >= 0) {
9510
9511 match_flag = false;
9512 while(t_i < op->nvv2) {
9513 if(op->i1VLA[t_i] == a) { /* same atom? */
9514 match_flag = true;
9515 break;
9516 }
9517 if(op->i1VLA[t_i] < a) { /* catch up? */
9518 t_i++;
9519 vt2 += 3;
9520 } else
9521 break;
9522 }
9523 if(match_flag) {
9524 VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
9525 vv2 = I->CSet[b]->coordPtr(a1);
9526 vv1 = op->vv1 + (op->nvv1 * 3);
9527 *(vv1++) = *(vv2++);
9528 *(vv1++) = *(vv2++);
9529 *(vv1++) = *(vv2++);
9530 *(vt1++) = *(vt2);
9531 *(vt1++) = *(vt2 + 1);
9532 *(vt1++) = *(vt2 + 2);
9533 op->nvv1++;
9534 }
9535 }
9536 }
9537 }
9538 if(op->nvv1 != op->nvv2) {
9539 PRINTFB(G, FB_Executive, FB_Warnings)
9540 "Executive-Warning: Missing atoms in state %d (%d instead of %d).\n",
9541 b + 1, op->nvv1, op->nvv2 ENDFB(G);
9542 }
9543 if(op->nvv1) {
9544 if(op->i1 != 0) /* fitting flag */
9545 rms = MatrixFitRMSTTTf(G, op->nvv1, op->vv1, vt, NULL, op->ttt);
9546 else
9547 rms = MatrixGetRMS(G, op->nvv1, op->vv1, vt, NULL);
9548 if(op->i1 == 2) {
9549 ObjectMoleculeTransformTTTf(I, op->ttt, b);
9550
9551 if(op->i3) {
9552 const float divisor = (float) op->i3;
9553 const float premult = (float) op->i3 - 1.0F;
9554
9555 /* mix flag is set, so average the prior target
9556 coordinates with these coordinates */
9557
9558 vt2 = op->vv2;
9559 t_i = 0; /* original target vertex index */
9560 for(a = 0; a < I->NAtom; a++) {
9561 s = I->AtomInfo[a].selEntry;
9562 if(SelectorIsMember(G, s, sele)) {
9563 a1 = I->CSet[b]->atmToIdx(a);
9564 if(a1 >= 0) {
9565
9566 match_flag = false;
9567 while(t_i < op->nvv2) {
9568 if(op->i1VLA[t_i] == a) { /* same atom? */
9569 match_flag = true;
9570 break;
9571 }
9572 if(op->i1VLA[t_i] < a) { /* catch up? */
9573 t_i++;
9574 vt2 += 3;
9575 } else
9576 break;
9577 }
9578 if(match_flag) {
9579 vv2 = I->CSet[b]->coordPtr(a1);
9580 *(vt2) = ((premult * (*vt2)) + *(vv2++)) / divisor;
9581 *(vt2 + 1) = ((premult * (*(vt2 + 1))) + *(vv2++)) / divisor;
9582 *(vt2 + 2) = ((premult * (*(vt2 + 2))) + *(vv2++)) / divisor;
9583 }
9584 }
9585 }
9586 }
9587 }
9588 }
9589 } else {
9590 PRINTFB(G, FB_Executive, FB_Warnings)
9591 "Executive-Warning: No matches found for state %d.\n", b + 1 ENDFB(G);
9592 }
9593 }
9594 VLACheck(op->f1VLA, float, b);
9595 op->f1VLA[b] = rms;
9596 }
9597 VLASize(op->f1VLA, float, I->NCSet); /* NOTE this action is object-specific! */
9598 }
9599 FreeP(vt);
9600 break;
9601 case OMOP_OnOff:
9602 for(a = 0; a < I->NAtom; a++) {
9603 s = I->AtomInfo[a].selEntry;
9604 if(SelectorIsMember(G, s, sele)) {
9605 hit_flag = true;
9606 break;
9607 }
9608 }
9609 break;
9610 case OMOP_SaveUndo: /* save undo */
9611 for(a = 0; a < I->NAtom; a++) {
9612 s = I->AtomInfo[a].selEntry;
9613 if(SelectorIsMember(G, s, sele)) {
9614 hit_flag = true;
9615 break;
9616 }
9617 }
9618 break;
9619 case OMOP_IdentifyObjects: /* identify atoms */
9620 for(a = 0; a < I->NAtom; a++) {
9621 s = I->AtomInfo[a].selEntry;
9622 if(SelectorIsMember(G, s, sele)) {
9623 VLACheck(op->i1VLA, int, op->i1);
9624 op->i1VLA[op->i1] = I->AtomInfo[a].id;
9625 if (op->obj1VLA != nullptr) {
9626 VLACheck(op->obj1VLA, ObjectMolecule*, op->i1);
9627 op->obj1VLA[op->i1] = I;
9628 }
9629 op->i1++;
9630 }
9631 }
9632 break;
9633 case OMOP_Index: /* identify atoms */
9634 for(a = 0; a < I->NAtom; a++) {
9635 s = I->AtomInfo[a].selEntry;
9636 if(SelectorIsMember(G, s, sele)) {
9637 VLACheck(op->i1VLA, int, op->i1);
9638 op->i1VLA[op->i1] = a; /* NOTE: need to incr by 1 before python */
9639 VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
9640 op->obj1VLA[op->i1] = I;
9641 op->i1++;
9642 }
9643 }
9644 break;
9645 case OMOP_GetObjects: /* identify atoms */
9646 for(a = 0; a < I->NAtom; a++) {
9647 s = I->AtomInfo[a].selEntry;
9648 if(SelectorIsMember(G, s, sele)) {
9649 VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
9650 op->obj1VLA[op->i1] = I;
9651 op->i1++;
9652 break;
9653 }
9654 }
9655 break;
9656 case OMOP_CountAtoms: /* count atoms in object, in selection */
9657 ai = I->AtomInfo.data();
9658 for(a = 0; a < I->NAtom; a++) {
9659 s = ai->selEntry;
9660 if(SelectorIsMember(G, s, sele))
9661 op->i1++;
9662 ai++;
9663 }
9664 break;
9665 case OMOP_PhiPsi:
9666 ai = I->AtomInfo.data();
9667 for(a = 0; a < I->NAtom; a++) {
9668 s = ai->selEntry;
9669 if(SelectorIsMember(G, s, sele)) {
9670 VLACheck(op->i1VLA, int, op->i1);
9671 op->i1VLA[op->i1] = a;
9672 VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
9673 op->obj1VLA[op->i1] = I;
9674 VLACheck(op->f1VLA, float, op->i1);
9675 VLACheck(op->f2VLA, float, op->i1);
9676 if(ObjectMoleculeGetPhiPsi
9677 (I, a, op->f1VLA + op->i1, op->f2VLA + op->i1, op->i2))
9678 op->i1++;
9679 }
9680 ai++;
9681 }
9682 break;
9683 case OMOP_Cartoon: /* adjust cartoon type */
9684 ai = I->AtomInfo.data();
9685 for(a = 0; a < I->NAtom; a++) {
9686 s = ai->selEntry;
9687 if(SelectorIsMember(G, s, sele)) {
9688 if (ai->cartoon!=op->i1){
9689 op->i3++;
9690 }
9691 ai->cartoon = op->i1;
9692 op->i2++;
9693 }
9694 ai++;
9695 }
9696 break;
9697 case OMOP_Protect: /* protect atoms from movement */
9698 ai = I->AtomInfo.data();
9699 for(a = 0; a < I->NAtom; a++) {
9700 s = ai->selEntry;
9701 if(SelectorIsMember(G, s, sele)) {
9702 ai->protekted = op->i1;
9703 op->i2++;
9704 }
9705 ai++;
9706 }
9707 break;
9708 case OMOP_Mask: /* protect atoms from selection */
9709 ai = I->AtomInfo.data();
9710 for(a = 0; a < I->NAtom; a++) {
9711 s = ai->selEntry;
9712 if(SelectorIsMember(G, s, sele)) {
9713 ai->masked = op->i1;
9714 op->i2++;
9715 }
9716 ai++;
9717 }
9718 break;
9719 case OMOP_SetB: /* set B-value */
9720 ai = I->AtomInfo.data();
9721 for(a = 0; a < I->NAtom; a++) {
9722 s = ai->selEntry;
9723 if(SelectorIsMember(G, s, sele)) {
9724 ai->b = op->f1;
9725 op->i2++;
9726 }
9727 ai++;
9728 }
9729 break;
9730 case OMOP_Remove: /* flag atoms for deletion */
9731 ai = I->AtomInfo.data();
9732 for(a = 0; a < I->NAtom; a++) {
9733 ai->deleteFlag = false;
9734 s = ai->selEntry;
9735 if(SelectorIsMember(G, s, sele)) {
9736 ai->deleteFlag = true;
9737 op->i1++;
9738 }
9739 ai++;
9740 }
9741 break;
9742 case OMOP_GetChains:
9743 ai = I->AtomInfo.data();
9744 for(a = 0; a < I->NAtom; a++) {
9745 s = ai->selEntry;
9746 if(SelectorIsMember(G, s, sele)) {
9747 // pointer hack
9748 ((std::set<lexidx_t> *) (void*) op->ii1)->insert(ai->chain);
9749 op->i1++;
9750 }
9751 ai++;
9752 }
9753 break;
9754
9755 case OMOP_Spectrum:
9756 ai = I->AtomInfo.data();
9757 ai0 = NULL;
9758 for(a = 0; a < I->NAtom; a++) {
9759 s = ai->selEntry;
9760 if(SelectorIsMember(G, s, sele)) {
9761 skip_flag = false;
9762 if(op->i4 && ai0) /* byres and we've done a residue */
9763 if(AtomInfoSameResidue(G, ai, ai0))
9764 skip_flag = true;
9765 if(!skip_flag) {
9766 c = (int) (0.49999 + op->i1 * (op->ff1[op->i3] - op->f1) / op->f2);
9767 if(c < 0)
9768 c = 0;
9769 if(c >= op->i1)
9770 c = op->i1 - 1;
9771 ai->color = op->ii1[c];
9772
9773 /* printf("%8.3 %8.3\n",ai->partial_charge, */
9774 if(op->i4) { /* byres */
9775 offset = -1;
9776 while((a + offset) >= 0) {
9777 ai0 = I->AtomInfo + a + offset;
9778 if(AtomInfoSameResidue(G, ai, ai0)) {
9779 ai0->color = op->ii1[c];
9780 hit_flag = true;
9781 } else
9782 break;
9783 offset--;
9784 }
9785 offset = 1;
9786 while((a + offset) < I->NAtom) {
9787 ai0 = I->AtomInfo + a + offset;
9788 if(AtomInfoSameResidue(G, ai, ai0)) {
9789 ai0->color = op->ii1[c];
9790 hit_flag = true;
9791 } else
9792 break;
9793 offset++;
9794 }
9795 }
9796 ai0 = ai;
9797 }
9798 op->i3++;
9799
9800 }
9801 ai++;
9802 }
9803 break;
9804
9805 case OMOP_SingleStateVertices: /* same as OMOP_VERT for a single state */
9806 ai = I->AtomInfo.data();
9807 if(op->cs1 < I->NCSet) {
9808 if(I->CSet[op->cs1]) {
9809 b = op->cs1;
9810 for(a = 0; a < I->NAtom; a++) {
9811 s = ai->selEntry;
9812 if(SelectorIsMember(G, s, sele)) {
9813 op->i1++;
9814 a1 = I->CSet[b]->atmToIdx(a);
9815 if(a1 >= 0) {
9816 VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
9817 vv2 = I->CSet[b]->coordPtr(a1);
9818 vv1 = op->vv1 + (op->nvv1 * 3);
9819 *(vv1++) = *(vv2++);
9820 *(vv1++) = *(vv2++);
9821 *(vv1++) = *(vv2++);
9822 op->nvv1++;
9823 }
9824 }
9825 ai++;
9826 }
9827 }
9828 }
9829 break;
9830 case OMOP_CSetIdxGetAndFlag:
9831 ai = I->AtomInfo.data();
9832 for(a = 0; a < I->NAtom; a++) {
9833 s = ai->selEntry;
9834 if(SelectorIsMember(G, s, sele)) {
9835 for(b = op->cs1; b <= op->cs2; b++) {
9836 offset = b - op->cs1;
9837 if(b < I->NCSet) {
9838 if(I->CSet[b]) {
9839 a1 = I->CSet[b]->atmToIdx(a);
9840 if(a1 >= 0) {
9841 op->ii1[op->i1 * offset + op->i2] = 1; /* presence flag */
9842 vv1 = op->vv1 + 3 * (op->i1 * offset + op->i2); /* atom-based offset */
9843 vv2 = I->CSet[b]->coordPtr(a1);
9844 *(vv1++) = *(vv2++);
9845 *(vv1++) = *(vv2++);
9846 *(vv1++) = *(vv2++);
9847 op->nvv1++;
9848 }
9849 }
9850 }
9851 }
9852 op->i2++; /* atom index field for atoms within selection... */
9853 }
9854 ai++;
9855 }
9856 break;
9857 case OMOP_CSetIdxSetFlagged:
9858 ai = I->AtomInfo.data();
9859 hit_flag = false;
9860 for(a = 0; a < I->NAtom; a++) {
9861 s = ai->selEntry;
9862 if(SelectorIsMember(G, s, sele)) {
9863 for(b = op->cs1; b <= op->cs2; b++) {
9864 offset = b - op->cs1;
9865 if(b < I->NCSet) {
9866 if(I->CSet[b]) {
9867 a1 = I->CSet[b]->atmToIdx(a);
9868 if(a1 >= 0) {
9869 if(op->ii1[op->i1 * offset + op->i2]) { /* copy flag */
9870 vv1 = op->vv1 + 3 * (op->i1 * offset + op->i2); /* atom-based offset */
9871 vv2 = I->CSet[b]->coordPtr(a1);
9872 *(vv2++) = *(vv1++);
9873 *(vv2++) = *(vv1++);
9874 *(vv2++) = *(vv1++);
9875 op->nvv1++;
9876 hit_flag = true;
9877 }
9878 }
9879 }
9880 }
9881 }
9882 op->i2++; /* atom index field for atoms within selection... */
9883 }
9884 ai++;
9885 }
9886 break;
9887 case OMOP_SUMC: /* performance optimized to speed center & zoom actions */
9888 {
9889 /* given a selection, sum up all the coordinates (for centering) */
9890 float *op_v1 = op->v1;
9891 int op_i1 = op->i1;
9892 int op_i2 = op->i2;
9893 int obj_TTTFlag = I->TTTFlag;
9894 int i_NCSet = I->NCSet;
9895 int i_NAtom = I->NAtom;
9896 int i_DiscreteFlag = I->DiscreteFlag;
9897 CoordSet* const* i_CSet = I->CSet.data();
9898 if(op_i2) {
9899 use_matrices =
9900 SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
9901 if(use_matrices<0) use_matrices = 0;
9902 }
9903 ai = I->AtomInfo.data();
9904 for(a = 0; a < i_NAtom; a++) {
9905 s = ai->selEntry;
9906 /* for each atom, if this current atom is in the selection */
9907 if(SelectorIsMember(G, s, sele)) {
9908 /* loop over all atoms; regardless of state */
9909 for(b = 0; b < i_NCSet; b++) {
9910 if(i_DiscreteFlag) {
9911 if((cs = I->DiscreteCSet[a]))
9912 a1 = I->DiscreteAtmToIdx[a];
9913 } else {
9914 if((cs = i_CSet[b]))
9915 a1 = cs->AtmToIdx[a];
9916 }
9917 /* if valid coordinate set and atom info for this atom */
9918 if(cs && (a1 >= 0)) {
9919 coord = cs->coordPtr(a1);
9920 if(op_i2) { /* do we want transformed coordinates? */
9921 if(use_matrices) {
9922 if(!cs->Matrix.empty()) { /* state transformation */
9923 transform44d3f(cs->Matrix.data(), coord, v1);
9924 coord = v1;
9925 }
9926 }
9927 if(obj_TTTFlag) {
9928 transformTTT44f3f(I->TTT, coord, v1);
9929 coord = v1;
9930 }
9931 }
9932 /* op_v1 += coord */
9933 add3f(op_v1, coord, op_v1);
9934 /* count += 1 */
9935 op_i1++;
9936 }
9937 if(i_DiscreteFlag)
9938 break;
9939 }
9940 }
9941 /* next atom */
9942 ai++;
9943 }
9944 op->i1 = op_i1;
9945 }
9946 break;
9947 case OMOP_MNMX: /* performance optimized to speed center & zoom actions */
9948 {
9949 float *op_v1 = op->v1;
9950 float *op_v2 = op->v2;
9951 int op_i1 = op->i1;
9952 int op_i2 = op->i2;
9953 int obj_TTTFlag = I->TTTFlag;
9954 int i_NCSet = I->NCSet;
9955 int i_NAtom = I->NAtom;
9956 int i_DiscreteFlag = I->DiscreteFlag;
9957 CoordSet* const* i_CSet = I->CSet.data();
9958 if(op_i2) {
9959 use_matrices =
9960 SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
9961 if(use_matrices<0) use_matrices = 0;
9962 }
9963 ai = I->AtomInfo.data();
9964 for(a = 0; a < i_NAtom; a++) {
9965 s = ai->selEntry;
9966 if(SelectorIsMember(G, s, sele)) {
9967 for(b = 0; b < i_NCSet; b++) {
9968 if(i_DiscreteFlag) {
9969 if((cs = I->DiscreteCSet[a]))
9970 a1 = I->DiscreteAtmToIdx[a];
9971 } else {
9972 if((cs = i_CSet[b]))
9973 a1 = cs->AtmToIdx[a];
9974 }
9975 if(cs && (a1 >= 0)) {
9976 coord = cs->coordPtr(a1);
9977 if(op_i2) { /* do we want transformed coordinates? */
9978 if(use_matrices) {
9979 if(!cs->Matrix.empty()) { /* state transformation */
9980 transform44d3f(cs->Matrix.data(), coord, v1);
9981 coord = v1;
9982 }
9983 }
9984 if(obj_TTTFlag) {
9985 transformTTT44f3f(I->TTT, coord, v1);
9986 coord = v1;
9987 }
9988 }
9989 if(op_i1) {
9990 if(op_v1[0] > coord[0])
9991 op_v1[0] = coord[0];
9992 if(op_v1[1] > coord[1])
9993 op_v1[1] = coord[1];
9994 if(op_v1[2] > coord[2])
9995 op_v1[2] = coord[2];
9996 if(op_v2[0] < coord[0])
9997 op_v2[0] = coord[0];
9998 if(op_v2[1] < coord[1])
9999 op_v2[1] = coord[1];
10000 if(op_v2[2] < coord[2])
10001 op_v2[2] = coord[2];
10002 } else {
10003 op_v1[0] = coord[0];
10004 op_v1[1] = coord[1];
10005 op_v1[2] = coord[2];
10006 op_v2[0] = coord[0];
10007 op_v2[1] = coord[1];
10008 op_v2[2] = coord[2];
10009 }
10010 op_i1++;
10011 }
10012 if(i_DiscreteFlag)
10013 break;
10014 }
10015 }
10016 ai++;
10017 }
10018 op->i1 = op_i1;
10019 }
10020 break;
10021 default:
10022 {
10023 int inv_flag;
10024 #ifdef _PYMOL_IP_EXTRAS
10025 int use_stereo = 0, use_text_type = 0;
10026 #endif
10027
10028 switch (op->code) {
10029 case OMOP_INVA:
10030 /* set up an important optimization... */
10031 for(b = 0; b < I->NCSet; b++) {
10032 cs = I->CSet[b];
10033 if(cs)
10034 cs->objMolOpInvalidated = false;
10035 }
10036 break;
10037 }
10038 use_matrices = SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
10039 if(use_matrices<0) use_matrices = 0;
10040 ai = I->AtomInfo.data();
10041
10042 #ifdef _PYMOL_IP_EXTRAS
10043 // use stereo or text_type ?
10044 // only do this for "label2" command (better logic in WrapperObjectSubScript)
10045 if (op->code == OMOP_LABL && op->i2 == cExecutiveLabelEvalAlt) {
10046 use_stereo = PLabelExprUsesVariable(G, op->s1, "stereo");
10047 use_text_type = PLabelExprUsesVariable(G, op->s1, "text_type");
10048 }
10049
10050 #ifdef NO_MMLIBS
10051 if (use_stereo) {
10052 PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
10053 " NO_MMLIBS-Warning: stereochemistry not supported in this PyMOL build.\n" ENDFB(G);
10054 }
10055 if (use_text_type) {
10056 PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
10057 " NO_MMLIBS-Warning: automatic 'text_type' assignment not supported in this PyMOL build.\n" ENDFB(G);
10058 }
10059 #endif
10060 #endif
10061
10062 for(a = 0; a < I->NAtom; a++) {
10063 switch (op->code) {
10064 case OMOP_Flag:
10065 ai->flags &= op->i2; /* clear flag using mask */
10066 op->i4++;
10067 /* no break here is intentional! */
10068 case OMOP_FlagSet:
10069 case OMOP_FlagClear:
10070 case OMOP_COLR: /* normal atom based loops */
10071 case OMOP_VISI:
10072 case OMOP_CheckVis:
10073 case OMOP_TTTF:
10074 case OMOP_ALTR:
10075 case OMOP_LABL:
10076 case OMOP_AlterState:
10077 s = ai->selEntry;
10078 if(SelectorIsMember(G, s, sele)) {
10079 switch (op->code) {
10080 case OMOP_Flag:
10081 ai->flags |= op->i1; /* set flag */
10082 op->i3++;
10083 break;
10084 case OMOP_FlagSet:
10085 ai->flags |= op->i1; /* set flag */
10086 op->i3++;
10087 break;
10088 case OMOP_FlagClear:
10089 ai->flags &= op->i2; /* clear flag */
10090 op->i3++;
10091 break;
10092 case OMOP_VISI:
10093 switch (op->i2) {
10094 case cVis_HIDE:
10095 ai->visRep &= ~(op->i1);
10096 I->visRep &= ~(op->i1); // cell
10097 break;
10098 case cVis_SHOW:
10099 ai->visRep |= op->i1;
10100 I->visRep |= op->i1; // cell
10101 break;
10102 case cVis_AS:
10103 ai->visRep = op->i1;
10104 I->visRep = op->i1; // cell
10105 break;
10106 }
10107 break;
10108 case OMOP_CheckVis:
10109 if((ai->visRep & op->i1)) {
10110 op->i2 = true;
10111 }
10112 break;
10113 case OMOP_COLR:
10114 if(op->i1 == cColorAtomic)
10115 ai->color = AtomInfoGetColor(G, ai);
10116 else
10117 ai->color = op->i1;
10118 hit_flag = true;
10119 op->i2++;
10120 break;
10121 case OMOP_TTTF:
10122 hit_flag = true;
10123 break;
10124 case OMOP_LABL:
10125 if(ok) {
10126 if(!op->s1[0]) {
10127 if(ai->label) {
10128 op->i1--; /* negative if unlabelling */
10129 LexAssign(G, ai->label, 0);
10130 }
10131 ai->visRep &= ~cRepLabelBit;
10132 hit_flag = true;
10133 } else {
10134 switch (op->i2) {
10135 case cExecutiveLabelEvalOn:
10136 {
10137 /* python label expression evaluation */
10138 CoordSet *cs = NULL;
10139 if(I->DiscreteFlag && I->DiscreteCSet) {
10140 cs = I->DiscreteCSet[a];
10141 } else if (I->NCSet == 1){
10142 cs = I->CSet[0];
10143 }
10144 #ifndef _PYMOL_NOPY
10145 if(PLabelAtom(I->G, I, cs, expr_co, a)) {
10146 if (ai->label){
10147 op->i1++; /* only if the string has been set, report labelled */
10148 }
10149 ai->visRep |= cRepLabelBit;
10150 hit_flag = true;
10151 } else {
10152 ok = false;
10153 }
10154 #else
10155 ok = false;
10156 #endif
10157 }
10158 break;
10159 case cExecutiveLabelEvalAlt:
10160 {
10161 if(PLabelAtomAlt(I->G, &I->AtomInfo[a], I->Name, op->s1, a)) {
10162 if (ai->label){
10163 op->i1++; /* only if the string has been set, report labelled */
10164 }
10165 ai->visRep |= cRepLabelBit;
10166 hit_flag = true;
10167 } else {
10168 ok = false;
10169 }
10170 }
10171 break;
10172 case cExecutiveLabelEvalOff:
10173 {
10174 /* simple string label text */
10175 AtomInfoType *ai = I->AtomInfo + a;
10176 LexDec(G, ai->label);
10177 ai->label = LexIdx(G, op->s1);
10178 }
10179 break;
10180 }
10181 }
10182 }
10183 break;
10184 case OMOP_ALTR:
10185 if(ok) {
10186 CoordSet *cs = NULL;
10187 if(I->DiscreteFlag && I->DiscreteCSet) {
10188 cs = I->DiscreteCSet[a];
10189 } else if (I->NCSet == 1){
10190 cs = I->CSet[0];
10191 }
10192 #ifndef _PYMOL_NOPY
10193 if(PAlterAtom
10194 (I->G, I, cs, expr_co, op->i2, a,
10195 op->py_ob1))
10196 op->i1++;
10197 else
10198 #endif
10199 #ifdef _WEBGL
10200 #endif
10201 ok = false;
10202 }
10203 break;
10204 case OMOP_AlterState:
10205 if(ok) {
10206 if(op->i2 < I->NCSet) {
10207 cs = I->CSet[op->i2];
10208 if(cs) {
10209 a1 = cs->atmToIdx(a);
10210 if(a1 >= 0) {
10211 #ifndef _PYMOL_NOPY
10212 if(PAlterAtomState(I->G, expr_co, op->i3,
10213 I, cs, a, a1, op->i2, op->py_ob1)) {
10214 op->i1++;
10215 hit_flag = true;
10216 } else
10217 #endif
10218 #ifdef _WEBGL
10219 #endif
10220 ok = false;
10221 }
10222 }
10223 }
10224 }
10225 break;
10226 }
10227 break;
10228 }
10229 break;
10230
10231 /* coord-set based properties, iterating only a single coordinate set */
10232 case OMOP_CSetMinMax:
10233 case OMOP_CSetCameraMinMax:
10234 case OMOP_CSetMaxDistToPt:
10235 case OMOP_CSetSumSqDistToPt:
10236 case OMOP_CSetSumVertices:
10237 case OMOP_CSetMoment:
10238 cs = NULL;
10239 if((op->cs1 >= 0) && (op->cs1 < I->NCSet)) {
10240 cs = I->CSet[op->cs1];
10241 } else if(op->include_static_singletons) {
10242 if((I->NCSet == 1)
10243 && (SettingGet_b(G, NULL, I->Setting, cSetting_static_singletons))) {
10244 cs = I->CSet[0]; /*treat static singletons as present in each state */
10245 }
10246 }
10247
10248 if(cs) {
10249 s = ai->selEntry;
10250 if(SelectorIsMember(G, s, sele)) {
10251 switch (op->code) {
10252 case OMOP_CSetSumVertices:
10253 a1 = cs->atmToIdx(a);
10254 if(a1 >= 0) {
10255 coord = cs->coordPtr(a1);
10256 if(op->i2) { /* do we want transformed coordinates? */
10257 if(use_matrices) {
10258 if(!cs->Matrix.empty()) { /* state transformation */
10259 transform44d3f(cs->Matrix.data(), coord, v1);
10260 coord = v1;
10261 }
10262 }
10263 if(I->TTTFlag) {
10264 transformTTT44f3f(I->TTT, coord, v1);
10265 coord = v1;
10266 }
10267 }
10268 add3f(op->v1, coord, op->v1);
10269 op->i1++;
10270 }
10271 break;
10272 case OMOP_CSetMinMax:
10273 a1 = cs->atmToIdx(a);
10274 if(a1 >= 0) {
10275 coord = cs->coordPtr(a1);
10276 if(op->i2) { /* do we want transformed coordinates? */
10277 if(use_matrices) {
10278 if(!cs->Matrix.empty()) { /* state transformation */
10279 transform44d3f(cs->Matrix.data(), coord, v1);
10280 coord = v1;
10281 }
10282 }
10283 if(I->TTTFlag) {
10284 transformTTT44f3f(I->TTT, coord, v1);
10285 coord = v1;
10286 }
10287 }
10288 if(op->i1) {
10289 for(c = 0; c < 3; c++) {
10290 if(*(op->v1 + c) > *(coord + c))
10291 *(op->v1 + c) = *(coord + c);
10292 if(*(op->v2 + c) < *(coord + c))
10293 *(op->v2 + c) = *(coord + c);
10294 }
10295 } else {
10296 for(c = 0; c < 3; c++) {
10297 *(op->v1 + c) = *(coord + c);
10298 *(op->v2 + c) = *(coord + c);
10299 }
10300 }
10301 op->i1++;
10302 }
10303 break;
10304 case OMOP_CSetCameraMinMax:
10305 a1 = cs->atmToIdx(a);
10306 if(a1 >= 0) {
10307 coord = cs->coordPtr(a1);
10308 if(op->i2) { /* do we want transformed coordinates? */
10309 if(use_matrices) {
10310 if(!cs->Matrix.empty()) { /* state transformation */
10311 transform44d3f(cs->Matrix.data(), coord, v1);
10312 coord = v1;
10313 }
10314 }
10315 if(I->TTTFlag) {
10316 transformTTT44f3f(I->TTT, coord, v1);
10317 coord = v1;
10318 }
10319 }
10320 MatrixTransformC44fAs33f3f(op->mat1, coord, v1);
10321 /* convert to view-space */
10322 coord = v1;
10323 if(op->i1) {
10324 for(c = 0; c < 3; c++) {
10325 if(*(op->v1 + c) > *(coord + c))
10326 *(op->v1 + c) = *(coord + c);
10327 if(*(op->v2 + c) < *(coord + c))
10328 *(op->v2 + c) = *(coord + c);
10329 }
10330 } else {
10331 for(c = 0; c < 3; c++) {
10332 *(op->v1 + c) = *(coord + c);
10333 *(op->v2 + c) = *(coord + c);
10334 }
10335 }
10336 op->i1++;
10337 }
10338 break;
10339 case OMOP_CSetSumSqDistToPt:
10340 a1 = cs->atmToIdx(a);
10341 if(a1 >= 0) {
10342 float dist;
10343 coord = cs->coordPtr(a1);
10344 if(op->i2) { /* do we want transformed coordinates? */
10345 if(use_matrices) {
10346 if(!cs->Matrix.empty()) { /* state transformation */
10347 transform44d3f(cs->Matrix.data(), coord, v1);
10348 coord = v1;
10349 }
10350 }
10351 if(I->TTTFlag) {
10352 transformTTT44f3f(I->TTT, coord, v1);
10353 coord = v1;
10354 }
10355 }
10356 dist = (float) diff3f(op->v1, coord);
10357 op->d1 += dist * dist;
10358 op->i1++;
10359 }
10360 break;
10361 case OMOP_CSetMaxDistToPt:
10362 a1 = cs->atmToIdx(a);
10363 if(a1 >= 0) {
10364 float dist;
10365 coord = cs->coordPtr(a1);
10366 if(op->i2) { /* do we want transformed coordinates? */
10367 if(use_matrices) {
10368 if(!cs->Matrix.empty()) { /* state transformation */
10369 transform44d3f(cs->Matrix.data(), coord, v1);
10370 coord = v1;
10371 }
10372 }
10373 if(I->TTTFlag) {
10374 transformTTT44f3f(I->TTT, coord, v1);
10375 coord = v1;
10376 }
10377 }
10378 dist = (float) diff3f(op->v1, coord);
10379 if(dist > op->f1)
10380 op->f1 = dist;
10381 op->i1++;
10382 }
10383 break;
10384 case OMOP_CSetMoment:
10385 a1 = cs->atmToIdx(a);
10386 if(a1 >= 0) {
10387 subtract3f(cs->coordPtr(a1), op->v1, v1);
10388 v2 = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
10389 op->d[0][0] += v2 - v1[0] * v1[0];
10390 op->d[0][1] += -v1[0] * v1[1];
10391 op->d[0][2] += -v1[0] * v1[2];
10392 op->d[1][0] += -v1[1] * v1[0];
10393 op->d[1][1] += v2 - v1[1] * v1[1];
10394 op->d[1][2] += -v1[1] * v1[2];
10395 op->d[2][0] += -v1[2] * v1[0];
10396 op->d[2][1] += -v1[2] * v1[1];
10397 op->d[2][2] += v2 - v1[2] * v1[2];
10398 }
10399 break;
10400
10401 }
10402 }
10403 }
10404 break;
10405 default:
10406 /* coord-set based properties, iterating as all coordsets within atoms */
10407 for(b = 0; b < I->NCSet; b++) {
10408 if(I->DiscreteFlag) {
10409 cs = I->DiscreteCSet[a];
10410 } else {
10411 cs = I->CSet[b];
10412 }
10413 if(cs) {
10414 s = ai->selEntry;
10415 inv_flag = false;
10416 if(SelectorIsMember(G, s, sele)) {
10417 switch (op->code) {
10418 case OMOP_CameraMinMax:
10419 a1 = cs->atmToIdx(a);
10420 if(a1 >= 0) {
10421 coord = cs->coordPtr(a1);
10422 if(op->i2) { /* do we want transformed coordinates? */
10423 if(use_matrices) {
10424 if(!cs->Matrix.empty()) { /* state transformation */
10425 transform44d3f(cs->Matrix.data(), coord, v1);
10426 coord = v1;
10427 }
10428 }
10429 if(I->TTTFlag) {
10430 transformTTT44f3f(I->TTT, coord, v1);
10431 coord = v1;
10432 }
10433 }
10434 MatrixTransformC44fAs33f3f(op->mat1, coord, v1);
10435 /* convert to view-space */
10436 coord = v1;
10437 if(op->i1) {
10438 for(c = 0; c < 3; c++) {
10439 if(*(op->v1 + c) > *(coord + c))
10440 *(op->v1 + c) = *(coord + c);
10441 if(*(op->v2 + c) < *(coord + c))
10442 *(op->v2 + c) = *(coord + c);
10443 }
10444 } else {
10445 for(c = 0; c < 3; c++) {
10446 *(op->v1 + c) = *(coord + c);
10447 *(op->v2 + c) = *(coord + c);
10448 }
10449 }
10450 op->i1++;
10451 }
10452 break;
10453 case OMOP_MaxDistToPt:
10454 a1 = cs->atmToIdx(a);
10455 if(a1 >= 0) {
10456 float dist;
10457 coord = cs->coordPtr(a1);
10458 if(op->i2) { /* do we want transformed coordinates? */
10459 if(use_matrices) {
10460 if(!cs->Matrix.empty()) { /* state transformation */
10461 transform44d3f(cs->Matrix.data(), coord, v1);
10462 coord = v1;
10463 }
10464 }
10465 if(I->TTTFlag) {
10466 transformTTT44f3f(I->TTT, coord, v1);
10467 coord = v1;
10468 }
10469 }
10470 dist = (float) diff3f(coord, op->v1);
10471 if(dist > op->f1)
10472 op->f1 = dist;
10473 op->i1++;
10474 }
10475 break;
10476 case OMOP_INVA:
10477 if(!cs->objMolOpInvalidated) {
10478 /* performance optimization: avoid repeatedly
10479 calling invalidate on the same coord sets */
10480 a1 = cs->atmToIdx(a);
10481 if(a1 >= 0) /* selection touches this coordinate set */
10482 inv_flag = true; /* so set the invalidation flag */
10483 }
10484 break;
10485 case OMOP_VERT:
10486 /* get the atom index whether it's discrete or not */
10487 a1 = cs->atmToIdx(a);
10488 if(a1 >= 0) {
10489 /* if a1 is a valid atom index, then copy it's xyz coordinates
10490 * into vv1; increment the counter, nvv1 */
10491 VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
10492 vv2 = cs->coordPtr(a1);
10493 vv1 = op->vv1 + (op->nvv1 * 3);
10494 *(vv1++) = *(vv2++);
10495 *(vv1++) = *(vv2++);
10496 *(vv1++) = *(vv2++);
10497 op->nvv1++;
10498 }
10499 break;
10500 case OMOP_SVRT:
10501 /* gives us only vertices for a specific coordinate set */
10502 if(b == op->i1) {
10503 a1 = cs->atmToIdx(a);
10504 if(a1 >= 0) {
10505 VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
10506 VLACheck(op->i1VLA, int, op->nvv1);
10507 op->i1VLA[op->nvv1] = a; /* save atom index for later comparisons */
10508 vv2 = cs->coordPtr(a1);
10509 vv1 = op->vv1 + (op->nvv1 * 3);
10510 *(vv1++) = *(vv2++);
10511 *(vv1++) = *(vv2++);
10512 *(vv1++) = *(vv2++);
10513 op->nvv1++;
10514 }
10515 }
10516 break;
10517 case OMOP_MOME:
10518 /* Moment of inertia tensor - unweighted - assumes v1 is center of molecule */
10519 a1 = cs->atmToIdx(a);
10520 if(a1 >= 0) {
10521 subtract3f(cs->coordPtr(a1), op->v1, v1);
10522 v2 = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
10523 op->d[0][0] += v2 - v1[0] * v1[0];
10524 op->d[0][1] += -v1[0] * v1[1];
10525 op->d[0][2] += -v1[0] * v1[2];
10526 op->d[1][0] += -v1[1] * v1[0];
10527 op->d[1][1] += v2 - v1[1] * v1[1];
10528 op->d[1][2] += -v1[1] * v1[2];
10529 op->d[2][0] += -v1[2] * v1[0];
10530 op->d[2][1] += -v1[2] * v1[1];
10531 op->d[2][2] += v2 - v1[2] * v1[2];
10532 }
10533 break;
10534 }
10535 }
10536 switch (op->code) {
10537 /* full coord-set based */
10538 case OMOP_INVA:
10539 /* shouldn't this be calling the object invalidation routine instead? */
10540 if(inv_flag) {
10541 cs->objMolOpInvalidated = true;
10542 for(d = 0; d < cRepCnt; d++) {
10543 if ((1 << d) & op->i1) {
10544 cs->invalidateRep(d, op->i2);
10545 }
10546 }
10547 }
10548 break;
10549 }
10550 if(I->DiscreteFlag) {
10551 /* don't iterate every coordinate set for discrete objects! */
10552 break;
10553 }
10554 }
10555 } /* end coordset section */
10556 break;
10557 }
10558 ai++;
10559 }
10560 } /* case: default */
10561 break;
10562 }
10563 if(hit_flag) {
10564 switch (op->code) {
10565 case OMOP_COLR:
10566 ExecutiveUpdateColorDepends(I->G, I);
10567 break;
10568 case OMOP_TTTF:
10569 ObjectMoleculeTransformTTTf(I, op->ttt, -1);
10570 break;
10571 case OMOP_LABL:
10572 I->invalidate(cRepLabel, cRepInvText, -1);
10573 break;
10574 case OMOP_AlterState: /* overly coarse - doing all states, could do just 1 */
10575 if(!op->i3) { /* not read_only? */
10576 I->invalidate(-1, cRepInvRep, -1);
10577 SceneChanged(G);
10578 }
10579 break;
10580 case OMOP_CSetIdxSetFlagged:
10581 I->invalidate(-1, cRepInvRep, -1);
10582 SceneChanged(G);
10583 break;
10584 case OMOP_SaveUndo:
10585 op->i2 = true;
10586 ObjectMoleculeSaveUndo(I, op->i1, false);
10587 break;
10588 case OMOP_OnOff:
10589 ExecutiveSetObjVisib(G, I->Name, op->i1, false);
10590 break;
10591 case OMOP_RevalenceFromSource:
10592 if(ObjectMoleculeXferValences(I, op->i1, op->i2,
10593 op->i3, op->obj3, op->i4, op->i5, op->i6)) {
10594 ObjectMoleculeVerifyChemistry(I, op->i3);
10595 I->invalidate(cRepAll, cRepInvBonds, op->i3);
10596 }
10597 break;
10598 case OMOP_RevalenceByGuessing:
10599 {
10600 int *flag1 = pymol::calloc<int>(I->NAtom);
10601 int *flag2 = pymol::calloc<int>(I->NAtom);
10602 if(flag1 && flag2) {
10603 int a;
10604 int *f1 = flag1;
10605 int *f2 = flag2;
10606 const AtomInfoType *ai = I->AtomInfo.data();
10607 for(a = 0; a < I->NAtom; a++) {
10608 *(f1++) = SelectorIsMember(G, ai->selEntry, op->i1);
10609 *(f2++) = SelectorIsMember(G, ai->selEntry, op->i2);
10610 ai++;
10611 }
10612 {
10613 int target_state = op->i3;
10614 if(target_state < 0)
10615 target_state = 0; /* TO DO */
10616 ObjectMoleculeGuessValences(I, target_state, flag1, flag2, op->i4);
10617 ObjectMoleculeVerifyChemistry(I, target_state);
10618 I->invalidate(cRepAll, cRepInvBonds, target_state);
10619 }
10620 FreeP(flag1);
10621 FreeP(flag2);
10622 }
10623 }
10624 break;
10625 }
10626 }
10627
10628 /* always run on exit... */
10629 switch (op->code) {
10630 case OMOP_LABL:
10631 if (op->i2 != cExecutiveLabelEvalOn){
10632 break;
10633 }
10634 case OMOP_ALTR:
10635 case OMOP_AlterState:
10636 Py_XDECREF(expr_co);
10637 PUnblock(G);
10638 break;
10639 }
10640 /* */
10641 }
10642 }
10643
10644
10645 /*========================================================================*/
ObjectMoleculeGetAtomSele(const ObjectMolecule * I,int index,char * buffer)10646 void ObjectMoleculeGetAtomSele(const ObjectMolecule * I, int index, char *buffer)
10647 {
10648 PyMOLGlobals * G = I->G;
10649 auto* ai = I->AtomInfo + index;
10650 char inscode_str[2] = { ai->inscode, '\0' };
10651
10652 snprintf(buffer, OrthoLineLength, "/%s/%s/%s/%s`%d%s/%s`%s", I->Name,
10653 LexStr(G, ai->segi),
10654 LexStr(G, ai->chain),
10655 LexStr(G, ai->resn), ai->resv, inscode_str,
10656 LexStr(G, ai->name), ai->alt);
10657 }
10658
10659
10660 /*========================================================================*/
describeElement(int index,char * buffer) const10661 void ObjectMolecule::describeElement(int index, char *buffer) const
10662 {
10663 auto I = this;
10664 ObjectMoleculeGetAtomSele(I, index, buffer);
10665 if(!I->AtomInfo[index].alt[0]) {
10666 // don't include the trailing backtick
10667 buffer[strlen(buffer) - 1] = 0;
10668 }
10669 }
10670
10671
10672 /*========================================================================*/
ObjectMoleculeGetAtomSeleLog(const ObjectMolecule * I,int index,char * buffer,int quote)10673 void ObjectMoleculeGetAtomSeleLog(const ObjectMolecule * I, int index, char *buffer, int quote)
10674 {
10675 char *p = quote ? buffer + 1 : buffer;
10676
10677 if(SettingGetGlobal_b(I->G, cSetting_robust_logs)) {
10678 ObjectMoleculeGetAtomSele(I, index, p);
10679 } else {
10680 sprintf(p, "(%s`%d)", I->Name, index + 1);
10681 }
10682
10683 if (quote) {
10684 int len = strlen(p);
10685 buffer[0] = buffer[len + 1] = '"';
10686 buffer[len + 2] = 0;
10687 }
10688 }
10689
ObjectMoleculeGetAtomSeleFast(const ObjectMolecule * I,int index)10690 std::string ObjectMoleculeGetAtomSeleFast(const ObjectMolecule* I, int index)
10691 {
10692 auto G = I->G;
10693 auto* ai = I->AtomInfo + index;
10694 char inscodestr[2] = {ai->inscode, 0};
10695 return pymol::string_format("(/'%s'/'%s'/'%s'/'%s'`%d%s/'%s'`'%s')", I->Name,
10696 LexStr(G, ai->segi), LexStr(G, ai->chain), LexStr(G, ai->resn), ai->resv,
10697 inscodestr, ai->name, ai->alt);
10698 }
10699
10700 /*========================================================================*/
getNFrame() const10701 int ObjectMolecule::getNFrame() const
10702 {
10703 return NCSet;
10704 }
10705
10706 struct _CCoordSetUpdateThreadInfo {
10707 CoordSet *cs;
10708 int a;
10709 };
10710
CoordSetUpdateThread(CCoordSetUpdateThreadInfo * T)10711 void CoordSetUpdateThread(CCoordSetUpdateThreadInfo * T)
10712 {
10713 if(T->cs) {
10714 T->cs->update(T->a);
10715 }
10716 }
10717
10718 #ifndef _PYMOL_NOPY
ObjMolCoordSetUpdateSpawn(PyMOLGlobals * G,CCoordSetUpdateThreadInfo * Thread,int n_thread,int n_total)10719 static void ObjMolCoordSetUpdateSpawn(PyMOLGlobals * G,
10720 CCoordSetUpdateThreadInfo * Thread, int n_thread,
10721 int n_total)
10722 {
10723 if(n_total == 1) {
10724 CoordSetUpdateThread(Thread);
10725 } else if(n_total) {
10726 int blocked;
10727 PyObject *info_list;
10728 int a, n = 0;
10729 blocked = PAutoBlock(G);
10730
10731 PRINTFB(G, FB_Scene, FB_Blather)
10732 " Scene: updating coordinate sets with %d threads...\n", n_thread ENDFB(G);
10733 info_list = PyList_New(n_total);
10734 for(a = 0; a < n_total; a++) {
10735 PyList_SetItem(info_list, a, PyCObject_FromVoidPtr(Thread + a, NULL));
10736 n++;
10737 }
10738 PXDecRef(PYOBJECT_CALLMETHOD
10739 (G->P_inst->cmd, "_coordset_update_spawn", "Oi", info_list, n_thread));
10740 Py_DECREF(info_list);
10741 PAutoUnblock(G, blocked);
10742 }
10743 }
10744 #endif
10745
10746
10747 /*========================================================================*/
update()10748 void ObjectMolecule::update()
10749 {
10750 auto I = this;
10751 int a; /*, ok; */
10752
10753 OrthoBusyPrime(G);
10754 /* if the cached representation is invalid, reset state */
10755 if(!I->RepVisCacheValid) {
10756 /* note which representations are active */
10757 /* for each atom in each coordset, blank out the representation cache */
10758 if(I->NCSet > 1) {
10759 const AtomInfoType *ai = I->AtomInfo.data();
10760 I->RepVisCache = 0;
10761 for(a = 0; a < I->NAtom; a++) {
10762 I->RepVisCache |= ai->visRep;
10763 ai++;
10764 }
10765 } else {
10766 I->RepVisCache = cRepBitmask; /* if only one coordinate set, then
10767 * there's no benefit to pre-filtering
10768 * the representations... */
10769 }
10770 I->RepVisCacheValid = true;
10771 }
10772 {
10773 /* determine the start/stop states */
10774 int start = 0;
10775 int stop = I->NCSet;
10776 /* set start and stop given an object */
10777 ObjectAdjustStateRebuildRange(I, &start, &stop);
10778 if((I->NCSet == 1)
10779 && (SettingGet_b(G, I->Setting, NULL, cSetting_static_singletons))) {
10780 start = 0;
10781 stop = 1;
10782 }
10783 if(stop > I->NCSet)
10784 stop = I->NCSet;
10785
10786 /* single and multithreaded coord set updates */
10787 {
10788 #ifndef _PYMOL_NOPY
10789 int n_thread = SettingGetGlobal_i(G, cSetting_max_threads);
10790 int multithread = SettingGetGlobal_i(G, cSetting_async_builds);
10791
10792 if(multithread && (n_thread) && (stop - start) > 1) {
10793 int cnt = 0;
10794
10795 ObjectMoleculeUpdateNeighbors(I);
10796 /* must precalculate to avoid race-condition since this isn't
10797 mutexed yet and neighbors are needed by cartoons */
10798
10799 for(a = start; a < stop; a++)
10800 if((a<I->NCSet) && I->CSet[a])
10801 cnt++;
10802 {
10803 CCoordSetUpdateThreadInfo *thread_info = pymol::malloc<CCoordSetUpdateThreadInfo>(cnt);
10804 if(thread_info) {
10805 cnt = 0;
10806 for(a = start; a < stop; a++) {
10807 if((a<I->NCSet) && I->CSet[a]) {
10808 thread_info[cnt].cs = I->CSet[a];
10809 thread_info[cnt].a = a;
10810 cnt++;
10811 }
10812 }
10813 ObjMolCoordSetUpdateSpawn(G, thread_info, n_thread, cnt);
10814 FreeP(thread_info);
10815 }
10816
10817 }
10818
10819 } else
10820 #endif
10821 { /* single thread */
10822 for(a = start; a < stop; a++) {
10823 if((a<I->NCSet) && I->CSet[a] && (!G->Interrupt)) {
10824 /* status bar */
10825 OrthoBusySlow(G, a, I->NCSet);
10826 PRINTFB(G, FB_ObjectMolecule, FB_Blather)
10827 " ObjectMolecule-DEBUG: updating representations for state %d of \"%s\".\n",
10828 a + 1, I->Name ENDFB(G);
10829 I->CSet[a]->update(a);
10830 }
10831 }
10832 }
10833 }
10834 /* if the unit cell is shown, redraw it */
10835 if((I->visRep & cRepCellBit)) {
10836 if (I->Symmetry) {
10837 CGOFree(I->UnitCellCGO);
10838 I->UnitCellCGO = CrystalGetUnitCellCGO(&I->Symmetry->Crystal);
10839 }
10840 }
10841 } /* end block */
10842
10843 PRINTFD(G, FB_ObjectMolecule)
10844 " ObjectMolecule: updates complete for object %s.\n", I->Name ENDFD;
10845 }
10846
10847 /*========================================================================*/
invalidate(int rep,int level,int state)10848 void ObjectMolecule::invalidate(int rep, int level, int state)
10849 {
10850 auto I = this;
10851 int a;
10852 PRINTFD(I->G, FB_ObjectMolecule)
10853 " %s: entered. rep: %d level: %d\n", __func__, rep, level ENDFD;
10854
10855 if(level >= cRepInvVisib) {
10856 I->RepVisCacheValid = false;
10857 }
10858
10859 if (level >= cRepInvBondsNoNonbonded) {
10860 if (level < cRepInvBonds) {
10861 level = cRepInvBonds;
10862 } else {
10863 ObjectMoleculeUpdateNonbonded(I);
10864 }
10865 }
10866
10867 if(level >= cRepInvBonds) {
10868 VLAFreeP(I->Neighbor); /* set I->Neighbor to NULL */
10869 if(I->Sculpt) {
10870 DeleteP(I->Sculpt);
10871 }
10872 if(level >= cRepInvAtoms) {
10873 SelectorUpdateObjectSele(I->G, I);
10874 }
10875 }
10876 PRINTFD(I->G, FB_ObjectMolecule)
10877 " %s: invalidating representations...\n", __func__ ENDFD;
10878
10879 if ( level >= cRepInvColor ) {
10880 /* after label, this gets called, so we shouldn't invalidate types b/c PYMOL-317
10881 not sure if that is the exact level we should cut this off at
10882 1/28/13 BB: changed from >cRepInvText to >=cRepInvColor for invalidating surface
10883 colors during gadget invalidation */
10884
10885 int start = 0;
10886 int stop = I->NCSet;
10887
10888 if(state >= 0) {
10889 start = state;
10890 stop = state + 1;
10891 }
10892 if(stop > I->NCSet)
10893 stop = I->NCSet;
10894 for(a = start; a < stop; a++) {
10895 CoordSet *cset = 0;
10896 cset = I->CSet[a];
10897 if(cset) {
10898 cset->invalidateRep(rep, level);
10899 }
10900 }
10901 }
10902
10903 PRINTFD(I->G, FB_ObjectMolecule)
10904 " %s: leaving...\n", __func__ ENDFD;
10905
10906 }
10907
ObjectMoleculeInvalidateAtomType(ObjectMolecule * I,int state)10908 void ObjectMoleculeInvalidateAtomType(ObjectMolecule *I, int state){
10909 CoordSet *cset = 0;
10910 int ai, atm;
10911 AtomInfoType *at;
10912 cset = I->CSet[state];
10913 if (state < 0){
10914 for (ai=0; ai < I->NAtom; ai++){
10915 at = &I->AtomInfo[ai];
10916 at->textType = 0;
10917 }
10918 } else {
10919 for (ai=0; ai < cset->NIndex; ai++){
10920 atm = cset->IdxToAtm[ai];
10921 if (atm>=0){
10922 at = &I->AtomInfo[ai];
10923 at->textType = 0;
10924 }
10925 }
10926 }
10927 }
10928
10929 /*========================================================================*/
ObjectMoleculeMoveAtom(ObjectMolecule * I,int state,int index,const float * v,int mode,int log)10930 int ObjectMoleculeMoveAtom(ObjectMolecule * I, int state, int index, const float *v, int mode,
10931 int log)
10932 {
10933 int result = 0;
10934 PyMOLGlobals *G = I->G;
10935 CoordSet *cs;
10936 if(!(I->AtomInfo[index].protekted == 1)) {
10937 if(state < 0)
10938 state = 0;
10939 if(I->NCSet == 1)
10940 state = 0;
10941 state = state % I->NCSet;
10942 if((!I->CSet[state]) && (SettingGet_b(G, I->Setting, NULL, cSetting_all_states)))
10943 state = 0;
10944 cs = I->CSet[state];
10945 if(cs) {
10946 result = CoordSetMoveAtom(I->CSet[state], index, v, mode);
10947 cs->invalidateRep(cRepAll, cRepInvCoord);
10948 ExecutiveUpdateCoordDepends(G, I);
10949 }
10950 }
10951 if(log) {
10952 OrthoLineType line, buffer;
10953 if(SettingGetGlobal_i(G, cSetting_logging)) {
10954 ObjectMoleculeGetAtomSele(I, index, buffer);
10955 sprintf(line, "cmd.translate_atom(\"%s\",%15.9f,%15.9f,%15.9f,%d,%d,%d)\n",
10956 buffer, v[0], v[1], v[2], state + 1, mode, 0);
10957 PLog(G, line, cPLog_no_flush);
10958 }
10959 }
10960 return (result);
10961 }
10962
10963
10964 /*========================================================================*/
ObjectMoleculeMoveAtomLabel(ObjectMolecule * I,int state,int index,float * v,int log,float * diff)10965 int ObjectMoleculeMoveAtomLabel(ObjectMolecule * I, int state, int index, float *v, int log, float *diff)
10966 {
10967 int result = 0;
10968 CoordSet *cs;
10969 if(!(I->AtomInfo[index].protekted == 1)) {
10970 if(state < 0)
10971 state = 0;
10972 if(I->NCSet == 1)
10973 state = 0;
10974 state = state % I->NCSet;
10975 if((!I->CSet[state])
10976 && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states)))
10977 state = 0;
10978 cs = I->CSet[state];
10979 if(cs) {
10980 result = CoordSetMoveAtomLabel(I->CSet[state], index, v, diff);
10981 cs->invalidateRep(cRepLabel, cRepInvCoord);
10982 }
10983 }
10984 return (result);
10985 }
10986
10987
10988 /*========================================================================*/
ObjectMoleculeInitBondPath(ObjectMolecule * I,ObjectMoleculeBPRec * bp)10989 int ObjectMoleculeInitBondPath(ObjectMolecule * I, ObjectMoleculeBPRec * bp)
10990 {
10991 int a;
10992 bp->dist = pymol::malloc<int>(I->NAtom);
10993 bp->list = pymol::malloc<int>(I->NAtom);
10994 for(a = 0; a < I->NAtom; a++)
10995 bp->dist[a] = -1;
10996 bp->n_atom = 0;
10997 return 1;
10998 }
10999
11000
11001 /*========================================================================*/
ObjectMoleculePurgeBondPath(ObjectMolecule * I,ObjectMoleculeBPRec * bp)11002 int ObjectMoleculePurgeBondPath(ObjectMolecule * I, ObjectMoleculeBPRec * bp)
11003 {
11004 FreeP(bp->dist);
11005 FreeP(bp->list);
11006 return 1;
11007 }
11008
11009
11010 /*========================================================================*/
ObjectMoleculeGetBondPaths(ObjectMolecule * I,int atom,int max,ObjectMoleculeBPRec * bp)11011 int ObjectMoleculeGetBondPaths(ObjectMolecule * I, int atom,
11012 int max, ObjectMoleculeBPRec * bp)
11013 {
11014 /* returns list of bond counts from atom to all others
11015 dist and list must be vla array pointers or NULL */
11016
11017 int a, a1, a2, n;
11018 int cur;
11019 int n_cur;
11020 int b_cnt = 0;
11021
11022 ObjectMoleculeUpdateNeighbors(I);
11023
11024 /* reinitialize dist array (if we've done at least one pass) */
11025
11026 for(a = 0; a < bp->n_atom; a++)
11027 bp->dist[bp->list[a]] = -1;
11028
11029 bp->n_atom = 0;
11030 bp->dist[atom] = 0;
11031 bp->list[bp->n_atom] = atom;
11032 bp->n_atom++;
11033
11034 cur = 0;
11035 while(1) {
11036 b_cnt++;
11037 if(b_cnt > max)
11038 break;
11039
11040 n_cur = bp->n_atom - cur;
11041
11042 /* iterate through all current atoms */
11043
11044 if(!n_cur)
11045 break;
11046 while(n_cur--) {
11047 a1 = bp->list[cur++];
11048 n = I->Neighbor[a1];
11049 n++; /* skip cnt */
11050 while(1) {
11051 a2 = I->Neighbor[n];
11052 n += 2;
11053 if(a2 < 0)
11054 break;
11055 if(bp->dist[a2] < 0) { /* for each atom not yet sampled... */
11056 bp->dist[a2] = b_cnt;
11057 bp->list[bp->n_atom] = a2;
11058 bp->n_atom++;
11059 }
11060 }
11061 }
11062 }
11063 return (bp->n_atom);
11064 }
11065
11066
11067 /*========================================================================*/
ObjectMoleculeGetBondPrint(ObjectMolecule * I,int max_bond,int max_type,int * dim)11068 int ***ObjectMoleculeGetBondPrint(ObjectMolecule * I, int max_bond, int max_type,
11069 int *dim)
11070 {
11071 int a, b, i, c;
11072 int at1, at2;
11073 int ***result = NULL;
11074 ObjectMoleculeBPRec bp;
11075
11076 dim[0] = max_type + 1;
11077 dim[1] = max_type + 1;
11078 dim[2] = max_bond + 1;
11079
11080 result = (int ***) UtilArrayCalloc((unsigned int *) dim, 3, sizeof(int));
11081
11082 ObjectMoleculeInitBondPath(I, &bp);
11083 for(a = 0; a < I->NAtom; a++) {
11084 at1 = I->AtomInfo[a].customType;
11085 if((at1 >= 0) && (at1 <= max_type)) {
11086 ObjectMoleculeGetBondPaths(I, a, max_bond, &bp);
11087 for(b = 0; b < bp.n_atom; b++) {
11088 i = bp.list[b];
11089 at2 = I->AtomInfo[i].customType;
11090 if((at2 >= 0) && (at2 <= max_type)) {
11091 c = bp.dist[i];
11092 result[at1][at2][c]++;
11093 }
11094 }
11095 }
11096 }
11097 ObjectMoleculePurgeBondPath(I, &bp);
11098 return (result);
11099 }
11100
11101
11102 /*========================================================================*/
ObjectMoleculeGetAvgHBondVector(ObjectMolecule * I,int atom,int state,float * v,float * incoming)11103 float ObjectMoleculeGetAvgHBondVector(ObjectMolecule * I, int atom,
11104 int state, float *v, float *incoming)
11105
11106
11107 /* computes average / optima hydrogen bonding vector for an atom */
11108 {
11109 float result = 0.0;
11110 int a1, a2, n;
11111 int vec_cnt = 0;
11112 float v_atom[3], v_neigh[3], v_diff[3], v_acc[3] = { 0.0, 0.0, 0.0 };
11113 int sp2_flag = false;
11114 int order;
11115
11116 CoordSet *cs;
11117
11118 ObjectMoleculeUpdateNeighbors(I);
11119
11120 a1 = atom;
11121 if(state < 0)
11122 state = 0;
11123 if(I->NCSet == 1)
11124 state = 0;
11125 state = state % I->NCSet;
11126 cs = I->CSet[state];
11127 if(cs) {
11128 if(CoordSetGetAtomVertex(cs, a1, v_atom)) { /* atom exists in this C-set */
11129 n = I->Neighbor[atom];
11130 n++;
11131 while(1) {
11132 a2 = I->Neighbor[n];
11133 if(a2 < 0)
11134 break;
11135 order = I->Bond[I->Neighbor[n + 1]].order;
11136 if((order == 2) || (order == 4)) {
11137 sp2_flag = true;
11138 }
11139 n += 2;
11140
11141 if(I->AtomInfo[a2].protons != 1) { /* ignore hydrogens */
11142 if(CoordSetGetAtomVertex(cs, a2, v_neigh)) {
11143 subtract3f(v_atom, v_neigh, v_diff);
11144 normalize3f(v_diff);
11145 add3f(v_diff, v_acc, v_acc);
11146 vec_cnt++;
11147 }
11148 }
11149 }
11150 if(vec_cnt) {
11151 result = (float) length3f(v_acc);
11152 result = result / vec_cnt;
11153 normalize23f(v_acc, v);
11154 } else {
11155 copy3f(v_acc, v);
11156 }
11157
11158 if(incoming && (vec_cnt == 1) && (fabs(dot_product3f(v, incoming)) < 0.99F)) {
11159 /* if we know where the donor is, and the acceptor can
11160 potentially rotate the lone pair, then we should optimally
11161 orient the acceptor, if possible */
11162 AtomInfoType *ai = I->AtomInfo + atom;
11163 float v_perp[3];
11164 float v_tmp1[3], v_tmp2[3];
11165 if(((ai->protons == cAN_O) && (!sp2_flag)) || /* C-O-H */
11166 ((ai->protons == cAN_N) && (sp2_flag))) { /* C=N-H */
11167
11168 remove_component3f(incoming, v, v_perp);
11169 normalize3f(v_perp);
11170 scale3f(v, 0.333644F, v_tmp1);
11171 scale3f(v_perp, 0.942699F, v_tmp2);
11172 add3f(v_tmp1, v_tmp2, v_tmp2);
11173 subtract3f(v, v_tmp2, v);
11174 normalize3f(v);
11175 }
11176 }
11177 }
11178 }
11179 return (result);
11180 }
11181
11182
11183 /*========================================================================*/
ObjectMoleculeGetAtomVertex(ObjectMolecule * I,int state,int index,float * v)11184 int ObjectMoleculeGetAtomVertex(ObjectMolecule * I, int state, int index, float *v)
11185 {
11186 int result = 0;
11187 if(state < 0)
11188 state = SettingGet_i(I->G, NULL, I->Setting, cSetting_state) - 1;
11189 if(state < 0)
11190 state = SceneGetState(I->G);
11191 if(I->NCSet == 1)
11192 state = 0; /* static singletons always active here it seems */
11193 state = state % I->NCSet;
11194 if((!I->CSet[state])
11195 && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states)))
11196 state = 0;
11197 if(I->CSet[state])
11198 result = CoordSetGetAtomVertex(I->CSet[state], index, v);
11199
11200 return (result);
11201 }
11202
11203
11204 /*========================================================================*/
ObjectMoleculeGetAtomTxfVertex(ObjectMolecule * I,int state,int index,float * v)11205 int ObjectMoleculeGetAtomTxfVertex(ObjectMolecule * I, int state, int index, float *v)
11206 {
11207 int result = 0;
11208 CoordSet *cs = NULL;
11209 if (I->DiscreteFlag){
11210 cs = I->DiscreteCSet[index];
11211 }
11212 if(state < 0){
11213 state = SettingGet_i(I->G, NULL, I->Setting, cSetting_state) - 1;
11214 }
11215 if(state < 0)
11216 state = SceneGetState(I->G);
11217 if(I->NCSet == 1)
11218 state = 0; /* static singletons always active here it seems */
11219 state = state % I->NCSet;
11220 {
11221 if (!cs)
11222 cs = I->CSet[state];
11223 if((!cs) && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states))) {
11224 state = 0;
11225 cs = I->CSet[state];
11226 }
11227 if(cs) {
11228 result = CoordSetGetAtomTxfVertex(cs, index, v);
11229 }
11230 }
11231 return (result);
11232 }
11233
11234 /*========================================================================*/
ObjectMoleculeSetAtomVertex(ObjectMolecule * I,int state,int index,float * v)11235 int ObjectMoleculeSetAtomVertex(ObjectMolecule * I, int state, int index, float *v)
11236 {
11237 int result = 0;
11238 if(state < 0)
11239 state = SettingGet_i(I->G, NULL, I->Setting, cSetting_state) - 1;
11240 if(state < 0)
11241 state = SceneGetState(I->G);
11242 if(I->NCSet == 1)
11243 state = 0;
11244 state = state % I->NCSet;
11245 if((!I->CSet[state])
11246 && (SettingGet_b(I->G, I->Setting, NULL, cSetting_all_states)))
11247 state = 0;
11248 if(I->CSet[state])
11249 result = CoordSetSetAtomVertex(I->CSet[state], index, v);
11250 return (result);
11251 }
11252
11253
11254 /*========================================================================*/
render(RenderInfo * info)11255 void ObjectMolecule::render(RenderInfo * info)
11256 {
11257 auto I = this;
11258 int state = info->state;
11259 CRay *ray = info->ray;
11260 auto pick = info->pick;
11261 int pass = info->pass;
11262 CoordSet *cs;
11263 int pop_matrix = false;
11264 int use_matrices = SettingGet_i(I->G, I->Setting, NULL, cSetting_matrix_mode);
11265 if(use_matrices<0) use_matrices = 0;
11266 PRINTFD(I->G, FB_ObjectMolecule)
11267 " ObjectMolecule: rendering %s pass %d...\n", I->Name, pass ENDFD;
11268
11269 ObjectPrepareContext(I, info);
11270
11271 if(I->UnitCellCGO && (I->visRep & cRepCellBit)) {
11272 if(ray) {
11273 /* need to apply object state matrix here */
11274 int ok = CGORenderRay(I->UnitCellCGO, ray, info, ColorGet(I->G, I->Color),
11275 NULL, I->Setting, NULL);
11276 if (!ok){
11277 CGOFree(I->UnitCellCGO);
11278 }
11279 } else if(G->HaveGUI && G->ValidContext) {
11280 if(pick) {
11281 } else {
11282 /* need to apply object state matrix here */
11283 ObjectUseColor(I);
11284 CGORenderGL(I->UnitCellCGO, ColorGet(I->G, I->Color),
11285 I->Setting, NULL, info, NULL);
11286 }
11287 }
11288 }
11289
11290 for(StateIterator iter(G, I->Setting, state, I->NCSet); iter.next();) {
11291 cs = I->CSet[iter.state];
11292 if(cs) {
11293 if(use_matrices)
11294 pop_matrix = ObjectStatePushAndApplyMatrix(cs, info);
11295 cs->render(info);
11296 if(pop_matrix)
11297 ObjectStatePopMatrix(cs, info);
11298 }
11299 }
11300 PRINTFD(I->G, FB_ObjectMolecule)
11301 " ObjectMolecule: rendering complete for object %s.\n", I->Name ENDFD;
11302 }
11303
11304
11305 /*========================================================================*/
ObjectMoleculeDummyUpdate(ObjectMolecule * I,int mode)11306 void ObjectMoleculeDummyUpdate(ObjectMolecule * I, int mode)
11307 {
11308 switch (mode) {
11309 case cObjectMoleculeDummyOrigin:
11310 SceneOriginGet(I->G, I->CSet[0]->Coord.data());
11311 break;
11312 case cObjectMoleculeDummyCenter:
11313 SceneGetCenter(I->G, I->CSet[0]->Coord.data());
11314 break;
11315 }
11316 }
11317
11318
11319 /*========================================================================*/
ObjectMoleculeDummyNew(PyMOLGlobals * G,int type)11320 ObjectMolecule *ObjectMoleculeDummyNew(PyMOLGlobals * G, int type)
11321 {
11322 ObjectMolecule *I = NULL;
11323
11324 int nAtom;
11325 int frame = -1;
11326 int ok = true;
11327
11328 I = new ObjectMolecule(G, false);
11329 CHECKOK(ok, I);
11330 if (!ok)
11331 return NULL;
11332
11333 nAtom = 1;
11334 float *coord = VLAlloc(float, 3 * nAtom);
11335 CHECKOK(ok, coord);
11336 if (!ok){
11337 DeleteP(I);
11338 return NULL;
11339 }
11340 zero3f(coord);
11341
11342 CoordSet *cset = CoordSetNew(G);
11343 CHECKOK(ok, cset);
11344 if (!ok){
11345 VLAFreeP(coord);
11346 DeleteP(I);
11347 return NULL;
11348 }
11349 cset->NIndex = nAtom;
11350 cset->Coord = pymol::vla_take_ownership(coord);
11351 cset->TmpBond = NULL;
11352 cset->NTmpBond = 0;
11353 strcpy(cset->Name, "_origin");
11354
11355 cset->Obj = I;
11356 cset->enumIndices();
11357
11358 pymol::vla<AtomInfoType> atInfo(nAtom);
11359 ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_IDMask, true);
11360 if (ok){
11361 if(frame < 0)
11362 frame = I->NCSet;
11363 VLACheck(I->CSet, CoordSet *, frame);
11364 CHECKOK(ok, I->CSet);
11365 if (ok){
11366 if(I->NCSet <= frame)
11367 I->NCSet = frame + 1;
11368 if(I->CSet[frame])
11369 I->CSet[frame]->fFree();
11370 I->CSet[frame] = cset;
11371
11372 I->NBond = 0;
11373 I->Bond = NULL;
11374 }
11375 }
11376 if (ok)
11377 ok &= ObjectMoleculeExtendIndices(I, frame);
11378 if (ok)
11379 ok &= ObjectMoleculeSort(I);
11380 if (ok){
11381 ObjectMoleculeUpdateIDNumbers(I);
11382 ObjectMoleculeUpdateNonbonded(I);
11383 }
11384 if (!ok){
11385 DeleteP(I);
11386 }
11387 return (I);
11388 }
11389
11390
11391 /*========================================================================*/
11392
ObjectMolecule(PyMOLGlobals * G,int discreteFlag)11393 ObjectMolecule::ObjectMolecule(PyMOLGlobals * G, int discreteFlag) : CObject(G)
11394 {
11395 auto I = this;
11396 int a;
11397 I->type = cObjectMolecule;
11398 I->CSet = pymol::vla<CoordSet*>(10); /* auto-zero */
11399 I->AtomCounter = -1;
11400 I->BondCounter = -1;
11401 I->DiscreteFlag = discreteFlag;
11402 if(I->DiscreteFlag) { /* discrete objects don't share atoms between states */
11403 I->DiscreteAtmToIdx = pymol::vla<int>(0);
11404 I->DiscreteCSet = pymol::vla<CoordSet*>(0);
11405 } else {
11406 I->DiscreteAtmToIdx = NULL;
11407 I->DiscreteCSet = NULL;
11408 }
11409 I->AtomInfo = pymol::vla<AtomInfoType>(10);
11410 for(a = 0; a <= cUndoMask; a++) {
11411 I->UndoCoord[a] = NULL;
11412 I->UndoState[a] = -1;
11413 }
11414 I->UndoIter = 0;
11415
11416 #ifndef _PYMOL_NO_UNDO
11417 #endif
11418 }
11419
11420
11421 /*========================================================================*/
ObjectMoleculeCopyNoAlloc(const ObjectMolecule * obj,ObjectMolecule * I)11422 void ObjectMoleculeCopyNoAlloc(const ObjectMolecule* obj, ObjectMolecule* I)
11423 {
11424 PyMOLGlobals * G = const_cast<PyMOLGlobals*>(obj->G);
11425
11426 int a;
11427 BondType *i0;
11428 const BondType *i1;
11429 (*I) = (*obj);
11430 if (I->Symmetry != nullptr) {
11431 I->Symmetry = new CSymmetry(*I->Symmetry);
11432 }
11433 I->UnitCellCGO = NULL;
11434 I->Neighbor = NULL;
11435 I->Sculpt = NULL;
11436 I->Setting = NULL; /* TODO - make a copy */
11437
11438 I->ViewElem = NULL;
11439 I->gridSlotSelIndicatorsCGO = NULL;
11440
11441 for(a = 0; a <= cUndoMask; a++)
11442 I->UndoCoord[a] = NULL;
11443 I->CSet = pymol::vla<CoordSet*>(I->NCSet); /* auto-zero */
11444 for(a = 0; a < I->NCSet; a++) {
11445 I->CSet[a] = CoordSetCopy(obj->CSet[a]);
11446 if (I->CSet[a])
11447 I->CSet[a]->Obj = I;
11448 }
11449
11450 if(obj->CSTmpl)
11451 I->CSTmpl = CoordSetCopy(obj->CSTmpl);
11452
11453 if (obj->DiscreteFlag){
11454 int sz = VLAGetSize(obj->DiscreteAtmToIdx);
11455 I->DiscreteAtmToIdx = VLACopy2(obj->DiscreteAtmToIdx);
11456 I->DiscreteCSet = pymol::vla<CoordSet*>(sz);
11457 I->updateAtmToIdx();
11458 }
11459 I->Bond = pymol::vla<BondType>(I->NBond);
11460 i0 = I->Bond.data();
11461 i1 = obj->Bond.data();
11462 for(a = 0; a < I->NBond; a++) {
11463 AtomInfoBondCopy(G, i1++, i0++);
11464 }
11465
11466 // unfortunately, the AtomInfoType is not copy-constructable yet
11467 // assert copy done correct
11468 if (I->AtomInfo.size() != obj->AtomInfo.size()) {
11469 throw "AtomInfo copy failed";
11470 }
11471 AtomInfoType *a0 = I->AtomInfo.data();
11472 const AtomInfoType* a1 = obj->AtomInfo.data();
11473 memset(a0, 0, sizeof(AtomInfoType) * I->NAtom);
11474 for(a = 0; a < I->NAtom; a++)
11475 AtomInfoCopy(G, a1++, a0++);
11476 }
11477
ObjectMoleculeCopy(const ObjectMolecule * obj)11478 ObjectMolecule *ObjectMoleculeCopy(const ObjectMolecule * obj)
11479 {
11480 PyMOLGlobals * G = const_cast<PyMOLGlobals*>(obj->G);
11481 auto I = new ObjectMolecule(G, obj->DiscreteFlag);
11482 ObjectMoleculeCopyNoAlloc(obj, I);
11483 return (I);
11484
11485 }
11486
11487 /*========================================================================*/
11488 /*
11489 * Set the order of coordinate sets with an index array
11490 */
ObjectMoleculeSetStateOrder(ObjectMolecule * I,int * order,int len)11491 int ObjectMoleculeSetStateOrder(ObjectMolecule * I, int * order, int len) {
11492 int a;
11493 CoordSet ** csets = VLAlloc(CoordSet *, I->NCSet);
11494
11495 ok_assert(1, len == I->NCSet);
11496
11497 // invalidate
11498 I->invalidate(cRepAll, cRepInvAll, -1);
11499
11500 // new coord set array
11501 for(a = 0; a < I->NCSet; a++) {
11502 int i = order[a];
11503 ok_assert(1, 0 <= i && i < I->NCSet);
11504 csets[a] = I->CSet[i];
11505 }
11506
11507 VLAFreeP(I->CSet);
11508 I->CSet = pymol::vla_take_ownership(csets);
11509
11510 return true;
11511 ok_except1:
11512 ErrMessage(I->G, "ObjectMoleculeSetStateOrder", "failed");
11513 VLAFreeP(csets);
11514 return false;
11515 }
11516
11517 /*========================================================================*/
~ObjectMolecule()11518 ObjectMolecule::~ObjectMolecule()
11519 {
11520 auto I = this;
11521 int a;
11522 SelectorPurgeObjectMembers(I->G, I);
11523 for(a = 0; a < I->NCSet; a++){
11524 if(I->CSet[a]) {
11525 I->CSet[a]->fFree();
11526 I->CSet[a] = NULL;
11527 }
11528 }
11529 if(I->Symmetry)
11530 SymmetryFree(I->Symmetry);
11531 VLAFreeP(I->Neighbor);
11532 VLAFreeP(I->DiscreteAtmToIdx);
11533 VLAFreeP(I->DiscreteCSet);
11534 VLAFreeP(I->CSet);
11535 I->m_ciffile.reset(); // free data
11536
11537 {
11538 int nAtom = I->NAtom;
11539 AtomInfoType *ai = I->AtomInfo.data();
11540
11541 for(a = 0; a < nAtom; a++) {
11542 AtomInfoPurge(I->G, ai);
11543 ai++;
11544 }
11545 VLAFreeP(I->AtomInfo);
11546 }
11547 {
11548 int nBond = I->NBond;
11549 BondType *bi = I->Bond.data();
11550
11551 for(a = 0; a < nBond; a++) {
11552 AtomInfoPurgeBond(I->G, bi);
11553 bi++;
11554 }
11555 VLAFreeP(I->Bond);
11556 }
11557 CGOFree(I->UnitCellCGO);
11558 for(a = 0; a <= cUndoMask; a++)
11559 FreeP(I->UndoCoord[a]);
11560 if(I->Sculpt)
11561 DeleteP(I->Sculpt);
11562 if(I->CSTmpl)
11563 I->CSTmpl->fFree();
11564 }
11565
11566 /*========================================================================*/
ObjectMoleculeReadPDBStr(PyMOLGlobals * G,ObjectMolecule * I,const char * PDBStr,int state,int discrete,char * pdb_name,const char ** next_pdb,PDBInfoRec * pdb_info,int quiet,int * model_number)11567 ObjectMolecule *ObjectMoleculeReadPDBStr(PyMOLGlobals * G, ObjectMolecule * I,
11568 const char *PDBStr, int state, int discrete,
11569 char *pdb_name,
11570 const char **next_pdb, PDBInfoRec * pdb_info,
11571 int quiet, int *model_number)
11572 {
11573 CoordSet *cset = NULL;
11574 pymol::vla<AtomInfoType> atInfo;
11575 int ok = true;
11576 int isNew = true;
11577 unsigned int nAtom = 0;
11578 const char *start, *restart = NULL;
11579 int repeatFlag = true;
11580 int successCnt = 0;
11581 unsigned int aic_mask = cAIC_PDBMask;
11582
11583 SegIdent segi_override = ""; /* saved segi for corrupted NMR pdb files */
11584
11585 start = PDBStr;
11586 while(repeatFlag) {
11587 repeatFlag = false;
11588
11589 if(!I)
11590 isNew = true;
11591 else
11592 isNew = false;
11593
11594 if(ok) {
11595 if(isNew) {
11596 I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
11597 CHECKOK(ok, I);
11598 if (ok)
11599 std::swap(atInfo, I->AtomInfo);
11600 isNew = true;
11601 } else {
11602 atInfo = pymol::vla<AtomInfoType>(10);
11603 CHECKOK(ok, atInfo);
11604 isNew = false;
11605 }
11606 if(ok && isNew) {
11607 I->Color = AtomInfoUpdateAutoColor(G);
11608
11609 if (pdb_info->variant == PDB_VARIANT_VDB ||
11610 pdb_info->variant == PDB_VARIANT_PQR) {
11611 // pqr files have no chain identifier by default
11612 // vdb files have same chain identifiers in all symmetry copies
11613 SettingSet(cSetting_retain_order, 1, (CObject *) I);
11614 }
11615 }
11616 if (ok)
11617 cset = ObjectMoleculePDBStr2CoordSet(G, start, &atInfo, &restart,
11618 segi_override, pdb_name,
11619 next_pdb, pdb_info, quiet, model_number);
11620 CHECKOK(ok, cset);
11621 if (ok){
11622 nAtom = cset->NIndex;
11623 }
11624 }
11625 if(ok && pdb_name && (*next_pdb)) {
11626 /* problematic scenario? */
11627 }
11628
11629 /* include coordinate set */
11630 if(ok) {
11631 if(I->DiscreteFlag && atInfo) {
11632 unsigned int a;
11633 int fp1 = state + 1;
11634 AtomInfoType *ai = atInfo.data();
11635 for(a = 0; a < nAtom; a++) {
11636 (ai++)->discrete_state = fp1;
11637 }
11638 }
11639
11640 cset->Obj = I;
11641 cset->enumIndices();
11642 cset->invalidateRep(cRepAll, cRepInvRep);
11643 if(isNew) {
11644 std::swap(I->AtomInfo, atInfo);
11645 } else {
11646 ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, true, aic_mask, true);
11647 /* NOTE: will release atInfo */
11648 }
11649 if(isNew)
11650 I->NAtom = nAtom;
11651 if(state < 0)
11652 state = I->NCSet;
11653 if(*model_number > 0) {
11654 if(SettingGetGlobal_b(G, cSetting_pdb_honor_model_number))
11655 state = *model_number - 1;
11656 }
11657 VLACheck(I->CSet, CoordSet *, state);
11658 CHECKOK(ok, I->CSet);
11659 if(ok){
11660 if(I->NCSet <= state)
11661 I->NCSet = state + 1;
11662 if(I->CSet[state])
11663 I->CSet[state]->fFree();
11664 I->CSet[state] = cset;
11665 }
11666 if(ok && isNew)
11667 ok &= ObjectMoleculeConnect(I, cset);
11668 if(ok && cset->Symmetry) {
11669 SymmetryFree(I->Symmetry);
11670 I->Symmetry = new CSymmetry(*cset->Symmetry);
11671 SymmetryUpdate(I->Symmetry);
11672 }
11673 if (I->Symmetry) {
11674 /* check scale records */
11675 if(pdb_info &&
11676 pdb_info->scale.flag[0] &&
11677 pdb_info->scale.flag[1] && pdb_info->scale.flag[2]) {
11678
11679 float *sca = pdb_info->scale.matrix;
11680 sca[15] = 1.0F;
11681
11682 CoordSetInsureOrthogonal(G, cset, sca, &I->Symmetry->Crystal, quiet);
11683 }
11684 }
11685 SceneCountFrames(G);
11686 if (ok)
11687 ok &= ObjectMoleculeExtendIndices(I, state);
11688 if (ok)
11689 ok &= ObjectMoleculeSort(I);
11690 if (ok){
11691 ObjectMoleculeUpdateIDNumbers(I);
11692 ObjectMoleculeUpdateNonbonded(I);
11693 ObjectMoleculeAutoDisableAtomNameWildcard(I);
11694 }
11695 if(SettingGetGlobal_b(G, cSetting_pdb_hetatm_guess_valences)) {
11696 ObjectMoleculeGuessValences(I, state, NULL, NULL, false);
11697 }
11698
11699 successCnt++;
11700 if(!quiet) {
11701 if(successCnt > 1) {
11702 if(successCnt == 2) {
11703 PRINTFB(G, FB_ObjectMolecule, FB_Actions)
11704 " %s: read MODEL %d\n", __func__, 1 ENDFB(G);
11705 }
11706 PRINTFB(G, FB_ObjectMolecule, FB_Actions)
11707 " %s: read MODEL %d\n", __func__, successCnt ENDFB(G);
11708 }
11709 }
11710 }
11711 if(restart) {
11712 repeatFlag = true;
11713 start = restart;
11714 state = state + 1;
11715 }
11716 }
11717 if (!ok && isNew){
11718 DeleteP(I);
11719 }
11720 return (I);
11721 }
11722
11723
11724 /*========================================================================*/
11725 static
ObjectMoleculeMMDStr2CoordSet(PyMOLGlobals * G,const char * buffer,AtomInfoType ** atInfoPtr,const char ** restart)11726 CoordSet *ObjectMoleculeMMDStr2CoordSet(PyMOLGlobals * G, const char *buffer,
11727 AtomInfoType ** atInfoPtr, const char **restart)
11728 {
11729 const char *p;
11730 int nAtom, nBond;
11731 int a, c, bPart, bOrder;
11732 float *coord = NULL;
11733 CoordSet *cset = NULL;
11734 AtomInfoType *atInfo = NULL, *ai;
11735 char cc[MAXLINELEN];
11736 WordType title;
11737
11738 float *f;
11739 BondType *ii, *bond = NULL;
11740 int ok = true;
11741 int auto_show = RepGetAutoShowMask(G);
11742
11743 p = buffer;
11744 nAtom = 0;
11745 if(atInfoPtr)
11746 atInfo = *atInfoPtr;
11747
11748 if(ok) {
11749 p = ncopy(cc, p, 6); // could this be too small for large molecules with #atoms > 999999 ?
11750 if(sscanf(cc, "%d", &nAtom) != 1)
11751 ok = ErrMessage(G, "ReadMMDFile", "bad atom count");
11752 }
11753
11754 if(ok) {
11755 coord = VLAlloc(float, 3 * nAtom);
11756 if(atInfo)
11757 VLACheck(atInfo, AtomInfoType, nAtom);
11758 }
11759
11760 if(!atInfo) {
11761 ErrFatal(G, "MMDStr2CoordSet", "need atom information record!");
11762 /* failsafe for old version.. */
11763 }
11764
11765 nBond = 0;
11766 if(ok) {
11767 bond = VLACalloc(BondType, 6 * nAtom);
11768 }
11769
11770 p = ParseWordCopy(title, p, sizeof(WordType) - 1);
11771 UtilCleanStr(title);
11772
11773 p = nextline(p);
11774
11775 /* read coordinates and atom names */
11776
11777 if(ok) {
11778 f = coord;
11779 ii = bond;
11780 for(a = 0; a < nAtom; a++) {
11781 ai = atInfo + a;
11782
11783 ai->id = a + 1;
11784 ai->rank = a;
11785 if(ok) {
11786 p = ncopy(cc, p, 4);
11787 if(sscanf(cc, "%d", &ai->customType) != 1)
11788 ok = ErrMessage(G, "ReadMMDFile", "bad atom type");
11789 }
11790 if(ok) {
11791 if(ai->customType <= 14)
11792 strcpy(ai->elem, "C");
11793 else if(ai->customType <= 23)
11794 strcpy(ai->elem, "O");
11795 else if(ai->customType <= 40)
11796 strcpy(ai->elem, "N");
11797 else if(ai->customType <= 48)
11798 strcpy(ai->elem, "H");
11799 else if(ai->customType <= 52)
11800 strcpy(ai->elem, "S");
11801 else if(ai->customType <= 53)
11802 strcpy(ai->elem, "P");
11803 else if(ai->customType <= 55)
11804 strcpy(ai->elem, "B");
11805 else if(ai->customType <= 56)
11806 strcpy(ai->elem, "F");
11807 else if(ai->customType <= 57)
11808 strcpy(ai->elem, "Cl");
11809 else if(ai->customType <= 58)
11810 strcpy(ai->elem, "Br");
11811 else if(ai->customType <= 59)
11812 strcpy(ai->elem, "I");
11813 else if(ai->customType <= 60)
11814 strcpy(ai->elem, "Si");
11815 else if(ai->customType <= 61)
11816 strcpy(ai->elem, "Du");
11817 else if(ai->customType <= 62)
11818 strcpy(ai->elem, "Z0");
11819 else if(ai->customType <= 63)
11820 strcpy(ai->elem, "Lp");
11821 else
11822 ai->elem[0] = 0;
11823 /* else strcpy(ai->elem,"?"); WLD 090305 -- guess instead. */
11824 }
11825 for(c = 0; c < 6; c++) {
11826 if(ok) {
11827 p = ncopy(cc, p, 8);
11828 if(sscanf(cc, "%d%d", &bPart, &bOrder) != 2)
11829 ok = ErrMessage(G, "ReadMMDFile", "bad bond record");
11830 else {
11831 if(bPart && bOrder && (a < (bPart - 1))) {
11832 nBond++;
11833 ii->index[0] = a;
11834 ii->index[1] = bPart - 1;
11835 ii->order = bOrder;
11836 ii->stereo = 0;
11837 ii->id = -1;
11838 ii++;
11839 }
11840 }
11841 }
11842 }
11843 if(ok) {
11844 p = ncopy(cc, p, 12);
11845 if(sscanf(cc, "%f", f++) != 1)
11846 ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
11847 }
11848 if(ok) {
11849 p = ncopy(cc, p, 12);
11850 if(sscanf(cc, "%f", f++) != 1)
11851 ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
11852 }
11853 if(ok) {
11854 p = ncopy(cc, p, 12);
11855 if(sscanf(cc, "%f", f++) != 1)
11856 ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
11857 }
11858 if(ok) {
11859 p = nskip(p, 1);
11860 p = ncopy(cc, p, 5);
11861 ai->setResi(cc);
11862
11863 // single-letter code
11864 p = nskip(p, 1);
11865
11866 // chain
11867 p = ncopy(cc, p, 1);
11868 LexAssign(G, ai->chain, cc);
11869 }
11870 if(ok) {
11871 p = nskip(p, 4);
11872 p = ncopy(cc, p, 9);
11873 if(sscanf(cc, "%f", &ai->partialCharge) != 1)
11874 ok = ErrMessage(G, "ReadMMDFile", "bad charge");
11875 }
11876 if(ok) {
11877 p = nskip(p, 10);
11878 p = ncopy(cc, p, 3);
11879 UtilCleanStr(cc);
11880 LexAssign(G, ai->resn, cc);
11881 ai->hetatm = true;
11882 }
11883
11884 ai->segi = 0;
11885 ai->alt[0] = 0;
11886
11887 if(ok) {
11888 p = nskip(p, 2);
11889 p = ntrim(cc, p, 4);
11890 if(!cc[0]) {
11891 sprintf(cc, "%s%02d", ai->elem, a + 1);
11892 }
11893 ai->name = LexIdx(G, cc);
11894
11895 ai->visRep = auto_show;
11896 }
11897 if(ok) {
11898 AtomInfoAssignParameters(G, ai);
11899 AtomInfoAssignColors(G, ai);
11900 }
11901 if(!ok)
11902 break;
11903 p = nextline(p);
11904 }
11905 }
11906 if(ok)
11907 VLASize(bond, BondType, nBond);
11908 if(ok) {
11909 cset = CoordSetNew(G);
11910 cset->NIndex = nAtom;
11911 cset->Coord = pymol::vla_take_ownership(coord);
11912 cset->NTmpBond = nBond;
11913 cset->TmpBond = pymol::vla_take_ownership(bond);
11914 strcpy(cset->Name, title);
11915 } else {
11916 VLAFreeP(bond);
11917 VLAFreeP(coord);
11918 }
11919 if(atInfoPtr)
11920 *atInfoPtr = atInfo;
11921 *restart = *p ? p : NULL;
11922 return (cset);
11923 }
11924
11925 #ifdef _PYMOL_IP_EXTRAS
11926 #endif
11927
11928 #ifndef _PYMOL_NO_UNDO
11929 #endif
11930
ObjectMoleculeAdjustDiscreteAtmIdx(ObjectMolecule * I,int * lookup,int nAtom)11931 void ObjectMoleculeAdjustDiscreteAtmIdx(ObjectMolecule *I, int *lookup, int nAtom){
11932 int a, a0;
11933 if (I->DiscreteAtmToIdx){
11934 for(a=0; a<nAtom; a++){
11935 a0 = lookup[a];
11936 if (a0!=a && a0>=0){
11937 I->DiscreteAtmToIdx[a0] = I->DiscreteAtmToIdx[a];
11938 I->DiscreteCSet[a0] = I->DiscreteCSet[a];
11939 }
11940 }
11941 }
11942 }
11943
AtomInfoSettingGenerateSideEffects(PyMOLGlobals * G,ObjectMolecule * obj,int index,int id)11944 void AtomInfoSettingGenerateSideEffects(PyMOLGlobals * G, ObjectMolecule *obj, int index, int id){
11945 switch(index){
11946 case cSetting_label_position:
11947 case cSetting_label_placement_offset:
11948 case cSetting_label_screen_point:
11949 case cSetting_label_relative_mode:
11950 obj->invalidate(cRepLabel, cRepInvCoord, -1);
11951 }
11952 }
11953
AtomInfoInOrder(PyMOLGlobals * G,const AtomInfoType * atom,int atom1,int atom2)11954 static int AtomInfoInOrder(PyMOLGlobals * G, const AtomInfoType * atom, int atom1, int atom2)
11955 {
11956 return (AtomInfoCompare(G, atom + atom1, atom + atom2) <= 0);
11957 }
11958
AtomInfoInOrderIgnoreHet(PyMOLGlobals * G,const AtomInfoType * atom,int atom1,int atom2)11959 static int AtomInfoInOrderIgnoreHet(PyMOLGlobals * G, const AtomInfoType * atom,
11960 int atom1, int atom2)
11961 {
11962 return (AtomInfoCompareIgnoreHet(G, atom + atom1, atom + atom2) <= 0);
11963 }
11964
AtomInfoInOrigOrder(PyMOLGlobals * G,const AtomInfoType * atom,int atom1,int atom2)11965 static int AtomInfoInOrigOrder(PyMOLGlobals * G, const AtomInfoType * atom,
11966 int atom1, int atom2)
11967 {
11968 if(atom[atom1].rank == atom[atom2].rank)
11969 return (AtomInfoCompare(G, atom + atom1, atom + atom2) <= 0);
11970 return (atom[atom1].rank < atom[atom2].rank);
11971 }
11972
AtomInfoGetSortedIndex(PyMOLGlobals * G,const ObjectMolecule * obj,const AtomInfoType * rec,int n,int ** outdex)11973 int *AtomInfoGetSortedIndex(PyMOLGlobals * G,
11974 const ObjectMolecule* obj,
11975 const AtomInfoType* rec, int n, int **outdex)
11976 {
11977 int *index;
11978 int a;
11979 const CSetting* setting = nullptr;
11980
11981 ok_assert(1, index = pymol::malloc<int>(n + 1));
11982 ok_assert(1, (*outdex) = pymol::malloc<int>(n + 1));
11983
11984 if(obj && obj->DiscreteFlag) {
11985 for(a = 0; a < n; a++)
11986 index[a] = a;
11987 } else {
11988 if(obj)
11989 setting = obj->Setting;
11990
11991 UtilSortIndexGlobals(G, n, rec, index, (UtilOrderFnGlobals *) (
11992 SettingGet_b(G, setting, NULL, cSetting_retain_order) ?
11993 AtomInfoInOrigOrder :
11994 SettingGet_b(G, setting, NULL, cSetting_pdb_hetatm_sort) ?
11995 AtomInfoInOrder :
11996 AtomInfoInOrderIgnoreHet));
11997 }
11998
11999 for(a = 0; a < n; a++)
12000 (*outdex)[index[a]] = a;
12001
12002 return index;
12003
12004 ok_except1:
12005 FreeP(index);
12006 return NULL;
12007 }
12008
12009 /*
12010 * Set the size of the DiscreteAtmToIdx and DiscreteCSet VLAs and pad
12011 * them with -1/NULL if necessary.
12012 */
setNDiscrete(int natom)12013 bool ObjectMolecule::setNDiscrete(int natom) {
12014 int n = VLAGetSize(DiscreteAtmToIdx);
12015
12016 if (n == natom)
12017 return true;
12018
12019 VLASize(DiscreteAtmToIdx, int, natom);
12020 VLASize(DiscreteCSet, CoordSet*, natom);
12021
12022 if (!DiscreteAtmToIdx || !DiscreteCSet)
12023 return false;
12024
12025 for (int i = n; i < natom; ++i) {
12026 DiscreteAtmToIdx[i] = -1;
12027 DiscreteCSet[i] = NULL;
12028 }
12029
12030 return true;
12031 }
12032
12033 /*
12034 * Update the AtmToIdx or DiscreteAtmToIdx/DiscreteCSet VLAs from the
12035 * IdxToAtm arrays
12036 */
updateAtmToIdx()12037 bool ObjectMolecule::updateAtmToIdx() {
12038 if (DiscreteFlag) {
12039 ok_assert(1, setNDiscrete(NAtom));
12040 }
12041
12042 for (int i = -1; i < NCSet; ++i) {
12043 CoordSet * cset = (i < 0) ? CSTmpl : CSet[i];
12044
12045 if (!cset)
12046 continue;
12047
12048 if (!DiscreteFlag) {
12049 if (!cset->AtmToIdx) {
12050 cset->AtmToIdx = pymol::vla<int>(NAtom);
12051 } else {
12052 VLASize(cset->AtmToIdx, int, NAtom);
12053 }
12054
12055 ok_assert(1, cset->AtmToIdx);
12056
12057 std::fill_n(cset->AtmToIdx.data(), NAtom, -1);
12058 }
12059
12060 for (int idx = 0; idx < cset->NIndex; ++idx) {
12061 int atm = cset->IdxToAtm[idx];
12062
12063 if (DiscreteFlag) {
12064 DiscreteAtmToIdx[atm] = idx;
12065 DiscreteCSet[atm] = cset;
12066 AtomInfo[atm].discrete_state = i + 1;
12067 } else {
12068 cset->AtmToIdx[atm] = idx;
12069 }
12070 }
12071
12072 cset->NAtIndex = NAtom;
12073 }
12074
12075 return true;
12076 ok_except1:
12077 return false;
12078 }
12079
12080 /**
12081 * Check if this atom has coordinates in any state
12082 */
atomHasAnyCoordinates(size_t atm) const12083 bool ObjectMolecule::atomHasAnyCoordinates(size_t atm) const
12084 {
12085 for (size_t i = 0; i < NCSet; ++i) {
12086 auto cset = CSet[i];
12087 if (cset && cset->atmToIdx(atm) != -1) {
12088 return true;
12089 }
12090 }
12091
12092 return false;
12093 }
12094
clone() const12095 CObject* ObjectMolecule::clone() const
12096 {
12097 return ObjectMoleculeCopy(this);
12098 }
12099
12100