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