1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include <utility>
18 #include <algorithm>
19 
20 #include"os_python.h"
21 
22 #include"os_predef.h"
23 #include"os_std.h"
24 
25 #include"AtomInfo.h"
26 #include"Word.h"
27 #include"MemoryDebug.h"
28 #include"Err.h"
29 #include"Feedback.h"
30 #include"Util.h"
31 #include"Util2.h"
32 #include"Color.h"
33 #include"PConv.h"
34 #include"Ortho.h"
35 #include"OVOneToAny.h"
36 #include"OVContext.h"
37 #include"PyMOLObject.h"
38 #include"Setting.h"
39 #include"Executive.h"
40 #include "Lex.h"
41 #include "pymol/zstring_view.h"
42 
43 #include <map>
44 
45 struct _CAtomInfo {
46   int NColor, CColor, DColor, HColor, OColor, SColor;
47   int BrColor, ClColor, FColor, IColor;
48   int PColor, MgColor, MnColor, NaColor, KColor, CaColor;
49   int CuColor, FeColor, ZnColor;
50   int SeColor;
51   int DefaultColor;
52   int NextUniqueID;
53   OVOneToAny *ActiveIDs;
54 };
55 
AtomInfoCleanAtomName(char * name)56 void AtomInfoCleanAtomName(char *name)
57 {
58   char *p = name, *q = name;
59   while(*p) {
60     if((((*p) >= '0') && ((*p) <= '9')) ||
61        (((*p) >= 'a') && ((*p) <= 'z')) ||
62        (((*p) >= 'A') && ((*p) <= 'Z')) ||
63        ((*p) == '.') ||
64        ((*p) == '_') || ((*p) == '+') || ((*p) == '\'') || ((*p) == '*')) {
65       *q++ = *p;
66     }
67     p++;
68   }
69   *q = 0;
70 }
71 
72 #ifndef _PYMOL_NOPY
AtomInfoSetSettingFromPyObject(PyMOLGlobals * G,AtomInfoType * ai,int setting_id,PyObject * val)73 int AtomInfoSetSettingFromPyObject(PyMOLGlobals * G, AtomInfoType *ai, int setting_id, PyObject * val){
74   if (val == Py_None)
75     val = NULL;
76 
77   if (!val) {
78     if (!ai->has_setting)
79       return true;
80   }
81 
82   AtomInfoCheckUniqueID(G, ai);
83   ai->has_setting = true;
84 
85   return SettingUniqueSetPyObject(G, ai->unique_id, setting_id, val);
86 }
87 #endif
88 
SettingGetIfDefinedPyObject(PyMOLGlobals * G,AtomInfoType * ai,int setting_id)89 PyObject *SettingGetIfDefinedPyObject(PyMOLGlobals * G, AtomInfoType * ai, int setting_id) {
90   if(ai->has_setting) {
91     return SettingUniqueGetPyObject(G, ai->unique_id, setting_id);
92   }
93   return NULL;
94 }
95 
AtomInfoPrimeUniqueIDs(PyMOLGlobals * G)96 static int AtomInfoPrimeUniqueIDs(PyMOLGlobals * G)
97 {
98   CAtomInfo *I = G->AtomInfo;
99   if(!I->ActiveIDs) {
100     OVContext *C = G->Context;
101     I->ActiveIDs = OVOneToAny_New(C->heap);
102   }
103   return (I->ActiveIDs != NULL);
104 }
105 
AtomInfoReserveUniqueID(PyMOLGlobals * G,int unique_id)106 int AtomInfoReserveUniqueID(PyMOLGlobals * G, int unique_id)
107 {
108   CAtomInfo *I = G->AtomInfo;
109   if(!I->ActiveIDs)
110     AtomInfoPrimeUniqueIDs(G);
111   if(I->ActiveIDs)
112     return (OVreturn_IS_OK(OVOneToAny_SetKey(I->ActiveIDs, unique_id, 1)));
113   return 0;
114 }
115 
AtomInfoIsUniqueIDActive(PyMOLGlobals * G,int unique_id)116 int AtomInfoIsUniqueIDActive(PyMOLGlobals * G, int unique_id)
117 {
118   CAtomInfo *I = G->AtomInfo;
119   if(!I->ActiveIDs)
120     return 0;
121   else
122     return (OVreturn_IS_OK(OVOneToAny_GetKey(I->ActiveIDs, unique_id)));
123   return 0;
124 }
125 
AtomInfoGetNewUniqueID(PyMOLGlobals * G)126 int AtomInfoGetNewUniqueID(PyMOLGlobals * G)
127 {
128   CAtomInfo *I = G->AtomInfo;
129   int result = 0;
130   AtomInfoPrimeUniqueIDs(G);
131   if(I->ActiveIDs) {
132     while(1) {
133       result = I->NextUniqueID++;
134       if(result) {              /* skip zero */
135         if(OVOneToAny_GetKey(I->ActiveIDs, result).status == OVstatus_NOT_FOUND) {
136           if(OVreturn_IS_ERROR(OVOneToAny_SetKey(I->ActiveIDs, result, 1)))
137             result = 0;
138           break;
139         }
140       }
141     }
142   }
143   ExecutiveUniqueIDAtomDictInvalidate(G);
144   return result;
145 }
146 
AtomInfoCheckUniqueID(PyMOLGlobals * G,AtomInfoType * ai)147 int AtomInfoCheckUniqueID(PyMOLGlobals * G, AtomInfoType * ai)
148 {
149   if(!ai->unique_id)
150     ai->unique_id = AtomInfoGetNewUniqueID(G);
151   return ai->unique_id;
152 }
153 
AtomInfoCheckUniqueBondID(PyMOLGlobals * G,BondType * bi)154 int AtomInfoCheckUniqueBondID(PyMOLGlobals * G, BondType * bi)
155 {
156   if(!bi->unique_id)
157     bi->unique_id = AtomInfoGetNewUniqueID(G);
158   return bi->unique_id;
159 }
160 
BondTypeInit(BondType * bt)161 void BondTypeInit(BondType *bt){
162 #ifdef _PYMOL_IP_EXTRAS
163   bt->oldid = -1;
164 #endif
165   bt->unique_id = 0;
166   bt->has_setting = 0;
167 }
168 
169 /**
170  * Set atom indices and bond order, and invalidate all other fields.
171  */
BondTypeInit2(BondType * bond,int i1,int i2,int order)172 void BondTypeInit2(BondType *bond, int i1, int i2, int order)
173 {
174   BondTypeInit(bond);
175   bond->index[0] = i1;
176   bond->index[1] = i2;
177   bond->order = order;
178   bond->id = -1;
179   bond->stereo = 0;
180 }
181 
AtomInfoInit(PyMOLGlobals * G)182 int AtomInfoInit(PyMOLGlobals * G)
183 {
184   CAtomInfo *I = NULL;
185   if((I = (G->AtomInfo = pymol::calloc<CAtomInfo>(1)))) {
186     AtomInfoPrimeColors(G);
187     I->NextUniqueID = 1;
188     return 1;
189   } else
190     return 0;
191 }
192 
AtomInfoFree(PyMOLGlobals * G)193 void AtomInfoFree(PyMOLGlobals * G)
194 {
195   CAtomInfo *I = G->AtomInfo;
196   OVOneToAny_DEL_AUTO_NULL(I->ActiveIDs);
197   FreeP(G->AtomInfo);
198 }
199 
200 
201 /*========================================================================*/
AtomInfoGetPDB3LetHydroName(PyMOLGlobals * G,const char * resn,const char * iname,char * oname)202 void AtomInfoGetPDB3LetHydroName(PyMOLGlobals * G, const char *resn, const char *iname, char *oname)
203 {
204   oname[0] = ' ';
205   strcpy(oname + 1, iname);
206 
207   switch (resn[0]) {
208   case 'A':
209     switch (resn[1]) {
210     case 'L':
211       if(resn[2] == 'A')
212         if((iname[0] == 'H') &&
213            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
214           oname[0] = iname[2];
215           oname[1] = iname[0];
216           oname[2] = iname[1];
217           oname[3] = 0;
218         }
219       break;
220     case 'R':
221       if(resn[2] == 'G')
222         if((iname[0] == 'H') &&
223            ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D')) &&
224            ((iname[2] >= '0') && (iname[2] <= '9'))) {
225           oname[0] = iname[2];
226           oname[1] = iname[0];
227           oname[2] = iname[1];
228           oname[3] = 0;
229         }
230       break;
231     case 'S':
232       switch (resn[2]) {
233       case 'P':
234         if((iname[0] == 'H') &&
235            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
236           oname[0] = iname[2];
237           oname[1] = iname[0];
238           oname[2] = iname[1];
239           oname[3] = 0;
240         }
241         break;
242       case 'N':
243         if((iname[0] == 'H') &&
244            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
245           oname[0] = iname[2];
246           oname[1] = iname[0];
247           oname[2] = iname[1];
248           oname[3] = 0;
249         }
250         break;
251       }
252       break;
253     }
254     break;
255   case 'C':
256     switch (resn[1]) {
257     case 'Y':
258       switch (resn[2]) {
259       case 'S':
260       case 'X':
261         if((iname[0] == 'H') &&
262            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
263           oname[0] = iname[2];
264           oname[1] = iname[0];
265           oname[2] = iname[1];
266           oname[3] = 0;
267         }
268         break;
269       }
270       break;
271     }
272     break;
273   case 'G':
274     switch (resn[1]) {
275     case 'L':
276       switch (resn[2]) {
277       case 'N':
278         if((iname[0] == 'H') &&
279            ((iname[1] == 'B') || (iname[1] == 'G')) &&
280            ((iname[2] >= '0') && (iname[2] <= '9'))) {
281           oname[0] = iname[2];
282           oname[1] = iname[0];
283           oname[2] = iname[1];
284           oname[3] = 0;
285         }
286         break;
287       case 'U':
288         if((iname[0] == 'H') &&
289            ((iname[1] == 'B') || (iname[1] == 'G')) &&
290            ((iname[2] >= '0') && (iname[2] <= '9'))) {
291           oname[0] = iname[2];
292           oname[1] = iname[0];
293           oname[2] = iname[1];
294           oname[3] = 0;
295         }
296         break;
297       case 'Y':
298         if((iname[0] == 'H') &&
299            (iname[1] == 'A') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
300           oname[0] = iname[2];
301           oname[1] = iname[0];
302           oname[2] = iname[1];
303           oname[3] = 0;
304         }
305         break;
306       }
307     }
308     break;
309   case 'H':
310     switch (resn[1]) {
311     case 'I':
312       switch (resn[2]) {
313       case 'S':
314       case 'D':
315       case 'E':
316       case 'P':
317         if((iname[0] == 'H') &&
318            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
319           oname[0] = iname[2];
320           oname[1] = iname[0];
321           oname[2] = iname[1];
322           oname[3] = 0;
323         }
324         break;
325       }
326       break;
327     case 'O':
328       switch (resn[2]) {
329       case 'H':
330         break;
331       }
332       break;
333     case '2':
334       switch (resn[2]) {
335       case 'O':
336         break;
337       }
338       break;
339     }
340   case 'I':
341     switch (resn[1]) {
342     case 'L':
343       switch (resn[2]) {
344       case 'E':
345         break;
346       }
347     }
348     break;
349   case 'L':
350     switch (resn[1]) {
351     case 'E':
352       switch (resn[2]) {
353       case 'U':
354         if((iname[0] == 'H') &&
355            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
356           oname[0] = iname[2];
357           oname[1] = iname[0];
358           oname[2] = iname[1];
359           oname[3] = 0;
360         }
361         break;
362       }
363       break;
364     case 'Y':
365       switch (resn[2]) {
366       case 'S':
367         if((iname[0] == 'H') &&
368            ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D')
369             || (iname[1] == 'E') || (iname[1] == 'Z')) && ((iname[2] >= '0')
370                                                            && (iname[2] <= '9'))) {
371           oname[0] = iname[2];
372           oname[1] = iname[0];
373           oname[2] = iname[1];
374           oname[3] = 0;
375         }
376         break;
377       }
378       break;
379     }
380     break;
381   case 'M':
382     switch (resn[1]) {
383     case 'E':
384       switch (resn[2]) {
385       case 'T':
386         if((iname[0] == 'H') &&
387            ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'E')) &&
388            ((iname[2] >= '0') && (iname[2] <= '9'))) {
389           oname[0] = iname[2];
390           oname[1] = iname[0];
391           oname[2] = iname[1];
392           oname[3] = 0;
393         }
394         break;
395       }
396     }
397     break;
398   case 'P':
399     switch (resn[1]) {
400     case 'H':
401       switch (resn[2]) {
402       case 'E':
403         if((iname[0] == 'H') &&
404            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
405           oname[0] = iname[2];
406           oname[1] = iname[0];
407           oname[2] = iname[1];
408           oname[3] = 0;
409         }
410         break;
411       }
412       break;
413     case 'R':
414       switch (resn[2]) {
415       case 'O':
416         if((iname[0] == 'H') &&
417            ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D')) &&
418            ((iname[2] >= '0') && (iname[2] <= '9'))) {
419           oname[0] = iname[2];
420           oname[1] = iname[0];
421           oname[2] = iname[1];
422           oname[3] = 0;
423         }
424         break;
425       }
426       break;
427     }
428     break;
429   case 'S':
430     switch (resn[1]) {
431     case 'E':
432       switch (resn[2]) {
433       case 'R':
434         if((iname[0] == 'H') &&
435            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
436           oname[0] = iname[2];
437           oname[1] = iname[0];
438           oname[2] = iname[1];
439           oname[3] = 0;
440         }
441         break;
442       }
443       break;
444     }
445     break;
446   case 'T':
447     switch (resn[1]) {
448     case 'H':
449       switch (resn[2]) {
450       case 'R':
451         break;
452       }
453       break;
454     case 'I':
455       switch (resn[2]) {
456       case 'P':
457         break;
458       }
459       break;
460     case 'R':
461       switch (resn[2]) {
462       case 'P':
463         if((iname[0] == 'H') &&
464            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
465           oname[0] = iname[2];
466           oname[1] = iname[0];
467           oname[2] = iname[1];
468           oname[3] = 0;
469         }
470         break;
471       }
472       break;
473     case 'Y':
474       switch (resn[2]) {
475       case 'R':
476         if((iname[0] == 'H') &&
477            (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) {
478           oname[0] = iname[2];
479           oname[1] = iname[0];
480           oname[2] = iname[1];
481           oname[3] = 0;
482         }
483         break;
484       }
485       break;
486     }
487     break;
488   case 'V':
489     switch (resn[1]) {
490     case 'A':
491       switch (resn[2]) {
492       case 'L':
493         break;
494       }
495       break;
496     }
497     break;
498   case 'W':
499     switch (resn[1]) {
500     case 'A':
501       switch (resn[2]) {
502       case 'T':
503         break;
504       }
505       break;
506     }
507     break;
508   }
509 }
510 
AtomInfoKnownWaterResName(PyMOLGlobals * G,const char * resn)511 int AtomInfoKnownWaterResName(PyMOLGlobals * G, const char *resn)
512 {
513   switch (resn[0]) {
514   case 'H':
515     switch (resn[1]) {
516     case 'O':
517       switch (resn[2]) {
518       case 'H':
519         return true;
520         break;
521       }
522       break;
523     case '2':
524       switch (resn[2]) {
525       case 'O':
526         return true;
527         break;
528       }
529       break;
530     }
531   case 'D':
532     switch (resn[1]) {
533     case 'O':
534       switch (resn[2]) {
535       case 'D':
536         return true;
537         break;
538       }
539       break;
540     }
541     break;
542   case 'T':
543     switch (resn[1]) {
544     case 'I':
545     case '3': // T3P
546     case '4': // T4P
547       switch (resn[2]) {
548       case 'P':
549         return true;
550         break;
551       }
552       break;
553     }
554     break;
555   case 'W':
556     switch (resn[1]) {
557     case 'A':
558       switch (resn[2]) {
559       case 'T':
560         return true;
561         break;
562       }
563       break;
564     }
565     break;
566   case 'S':
567     switch (resn[1]) {
568     case 'O':
569       switch (resn[2]) {
570       case 'L':
571         return true;
572         break;
573       }
574       break;
575     case 'P':
576       switch (resn[2]) {
577       case 'C':
578         return true;
579         break;
580       }
581       break;
582     }
583     break;
584   }
585   return false;
586 }
587 
AtomInfoKnownPolymerResName(const char * resn)588 int AtomInfoKnownPolymerResName(const char *resn)
589 {
590   return AtomInfoKnownProteinResName(resn) ||
591          AtomInfoKnownNucleicResName(resn);
592 }
593 
AtomInfoKnownNucleicResName(const char * resn)594 int AtomInfoKnownNucleicResName(const char *resn)
595 {
596   if (resn[0] == 'D') {
597     // Deoxy ribonucleotide
598     ++resn;
599   }
600 
601   switch (resn[0]) {
602     case 'A':
603     case 'C':
604     case 'G':
605     case 'I':
606     case 'T':
607     case 'U':
608       if (!resn[1])
609         return true;
610   }
611 
612   return false;
613 }
614 
AtomInfoKnownProteinResName(const char * resn)615 int AtomInfoKnownProteinResName(const char *resn)
616 {
617   switch (resn[0]) {
618   case 'A':
619     switch (resn[1]) {
620     case 'L':
621       switch (resn[2]) {
622       case 'A': /* ALA */
623         return true;
624         break;
625       }
626       break;
627     case 'R':
628       switch (resn[2]) {
629       case 'G': /* ARG */
630         return true;
631         break;
632       }
633       break;
634     case 'S':
635       switch (resn[2]) {
636       case 'P': /* ASP */
637         return true;
638         break;
639       case 'N': /* ASN */
640         return true;
641         break;
642       }
643       break;
644     }
645     break;
646   case 'C':
647     switch (resn[1]) {
648     case 'Y':
649       switch (resn[2]) {
650       case 'S': /* CYS */
651       case 'X': /* CYX */
652         return true;
653         break;
654       }
655       break;
656     }
657     break;
658   case 'G':
659     switch (resn[1]) {
660     case 'L':
661       switch (resn[2]) {
662       case 'N': /* GLN */
663         return true;
664         break;
665       case 'U': /* GLU */
666         return true;
667         break;
668       case 'Y': /* GLY */
669         return true;
670         break;
671       }
672     }
673     break;
674   case 'H':
675     switch (resn[1]) {
676     case 'I':
677       switch (resn[2]) {
678       case 'S': /* HIS */
679       case 'D': /* HID */
680       case 'E': /* HIE */
681       case 'P': /* HIP */
682         return true;
683         break;
684       }
685       break;
686     }
687     break;
688   case 'I':
689     switch (resn[1]) {
690     case 'L':
691       switch (resn[2]) {
692       case 'E': /* ILE */
693         return true;
694         break;
695       }
696     }
697     break;
698   case 'L':
699     switch (resn[1]) {
700     case 'E':
701       switch (resn[2]) {
702       case 'U': /* LEU */
703         return true;
704         break;
705       }
706       break;
707     case 'Y':
708       switch (resn[2]) {
709       case 'S':
710         return true;
711         break; /* LYS */
712       }
713       break;
714     }
715     break;
716   case 'M':
717     switch (resn[1]) {
718     case 'E':
719       switch (resn[2]) {
720       case 'T': /* MET */
721         return true;
722         break;
723       }
724     case 'S':
725       switch (resn[2]) {
726       case 'E': /* MSE */
727         return true;
728         break;
729       }
730     }
731     break;
732   case 'P':
733     switch (resn[1]) {
734     case 'H':
735       switch (resn[2]) {
736       case 'E': /* PHE */
737         return true;
738         break;
739       }
740       break;
741     case 'R':
742       switch (resn[2]) {
743       case 'O': /* PRO */
744         return true;
745         break;
746       }
747       break;
748     case 'T':
749       switch (resn[2]) {
750       case 'R': /* PTR */
751         return true;
752         break;
753       }
754       break;
755     }
756     break;
757   case 'S':
758     switch (resn[1]) {
759     case 'E':
760       switch (resn[2]) {
761       case 'R': /* SER */
762         return true;
763         break;
764       }
765       break;
766     }
767     break;
768   case 'T':
769     switch (resn[1]) {
770     case 'H':
771       switch (resn[2]) {
772       case 'R': /* THR */
773         return true;
774         break;
775       }
776       break;
777     case 'R':
778       switch (resn[2]) {
779       case 'P': /* TRP */
780         return true;
781         break;
782       }
783       break;
784     case 'Y':
785       switch (resn[2]) {
786       case 'R': /* TYR */
787         return true;
788         break;
789       }
790       break;
791     }
792     break;
793   case 'V':
794     switch (resn[1]) {
795     case 'A':
796       switch (resn[2]) {
797       case 'L': /* VAL */
798         return true;
799         break;
800       }
801       break;
802     }
803     break;
804   }
805   return false;
806 }
807 
808 
809 /*========================================================================*/
810 
getInscodeUpper(const AtomInfoType * ai)811 static char getInscodeUpper(const AtomInfoType * ai) {
812   char c = ai->inscode;
813   if ('a' <= c && c <= 'z')
814     return c - ('a' - 'A'); // 97 - 65
815   return c;
816 }
817 
818 // return false if buffer too small
AtomResiFromResv(char * resi,size_t size,int resv,char inscode)819 bool AtomResiFromResv(char *resi, size_t size, int resv, char inscode) {
820   if (inscode > ' ')
821     return snprintf(resi, size, "%d%c", resv, inscode) < size;
822   return snprintf(resi, size, "%d", resv) < size;
823 }
824 
825 /*========================================================================*/
AtomInfoAsPyList(PyMOLGlobals * G,const AtomInfoType * I)826 PyObject *AtomInfoAsPyList(PyMOLGlobals * G, const AtomInfoType * I)
827 {
828   PyObject *result = NULL;
829 
830   result = PyList_New(48);
831 
832   int version = SettingGetGlobal_f(G, cSetting_pse_export_version) * 1000;
833   char resi[8];
834 
835   // at some point, change this to !version || ...
836   if (version >= 1810) {
837     resi[0] = I->inscode;
838     resi[1] = '\0';
839   } else {
840     AtomResiFromResv(resi, sizeof(resi), I);
841   }
842 
843   PyList_SetItem(result, 0, PyInt_FromLong(I->resv));
844   PyList_SetItem(result, 1, PyString_FromString(LexStr(G, I->chain)));
845   PyList_SetItem(result, 2, PyString_FromString(I->alt));
846   PyList_SetItem(result, 3, PyString_FromString(resi));
847   PyList_SetItem(result, 4, PyString_FromString(LexStr(G, I->segi)));
848   PyList_SetItem(result, 5, PyString_FromString(LexStr(G, I->resn)));
849   PyList_SetItem(result, 6, PyString_FromString(LexStr(G, I->name)));
850   PyList_SetItem(result, 7, PyString_FromString(I->elem));
851   PyList_SetItem(result, 8, PyString_FromString(LexStr(G, I->textType)));
852   PyList_SetItem(result, 9, PyString_FromString(LexStr(G, I->label)));
853   PyList_SetItem(result, 10, PyString_FromString(I->ssType));
854   PyList_SetItem(result, 11, PyInt_FromLong((int) I->isHydrogen())); // TODO redundant
855   PyList_SetItem(result, 12, PyInt_FromLong(I->customType));
856   PyList_SetItem(result, 13, PyInt_FromLong(I->priority));
857   PyList_SetItem(result, 14, PyFloat_FromDouble(I->b));
858   PyList_SetItem(result, 15, PyFloat_FromDouble(I->q));
859   PyList_SetItem(result, 16, PyFloat_FromDouble(I->vdw));
860   PyList_SetItem(result, 17, PyFloat_FromDouble(I->partialCharge));
861   PyList_SetItem(result, 18, PyInt_FromLong(I->formalCharge));
862   PyList_SetItem(result, 19, PyInt_FromLong((int) I->hetatm));
863   PyList_SetItem(result, 20, PyInt_FromLong((int) I->visRep));
864   PyList_SetItem(result, 21, PyInt_FromLong(I->color));
865   PyList_SetItem(result, 22, PyInt_FromLong(I->id));
866   PyList_SetItem(result, 23, PyInt_FromLong((char) I->cartoon));
867   PyList_SetItem(result, 24, PyInt_FromLong(I->flags));
868   PyList_SetItem(result, 25, PyInt_FromLong((int) I->bonded));
869   PyList_SetItem(result, 26, PyInt_FromLong((int) I->chemFlag));
870   PyList_SetItem(result, 27, PyInt_FromLong((int) I->geom));
871   PyList_SetItem(result, 28, PyInt_FromLong((int) I->valence));
872   PyList_SetItem(result, 29, PyInt_FromLong((int) I->masked));
873   PyList_SetItem(result, 30, PyInt_FromLong((int) I->protekted));
874   PyList_SetItem(result, 31, PyInt_FromLong((int) I->protons));
875   PyList_SetItem(result, 32, PyInt_FromLong(I->unique_id));
876   PyList_SetItem(result, 33, PyInt_FromLong((char) I->stereo));
877   PyList_SetItem(result, 34, PyInt_FromLong(I->discrete_state));
878   PyList_SetItem(result, 35, PyFloat_FromDouble(I->elec_radius));
879   PyList_SetItem(result, 36, PyInt_FromLong(I->rank));
880   PyList_SetItem(result, 37, PyInt_FromLong((int) I->hb_donor));
881   PyList_SetItem(result, 38, PyInt_FromLong((int) I->hb_acceptor));
882   PyList_SetItem(result, 39, PyInt_FromLong(0 /* atomic_color */));
883   PyList_SetItem(result, 40, PyInt_FromLong((int) I->has_setting));
884 
885   const float anisou_stack[] {0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
886   const float * anisou = I->anisou ? I->anisou : anisou_stack;
887   for (int i = 0; i < 6; ++i) {
888     PyList_SetItem(result, 41 + i, PyFloat_FromDouble(anisou[i]));
889   }
890 
891   PyList_SetItem(result, 47, PyString_FromString(LexStr(G, I->custom)));
892 
893   return (PConvAutoNone(result));
894 }
895 
AtomInfoFromPyList(PyMOLGlobals * G,AtomInfoType * I,PyObject * list)896 int AtomInfoFromPyList(PyMOLGlobals * G, AtomInfoType * I, PyObject * list)
897 {
898   int ok = true;
899   int tmp_int;
900   ov_size ll = 0;
901   OrthoLineType temp = "";
902 
903 #define PCONVPYSTRTOLEXIDX(i, n) { \
904   ok = CPythonVal_PConvPyStrToStr_From_List(G, list, i, temp, sizeof(OrthoLineType)); \
905   n = LexIdx(G, temp); }
906 
907   if(ok)
908     ok = PyList_Check(list);
909   if(ok)
910     ll = PyList_Size(list);
911   if(ok)
912     ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 0, &I->resv);
913   if(ok)
914     PCONVPYSTRTOLEXIDX(1, I->chain);
915   if(ok)
916     ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 2, I->alt, sizeof(Chain));
917   if(ok)
918   {
919     // get inscode from resi
920     ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 3, temp, sizeof(temp));
921     int i = strlen(temp) - 1;
922     if (i >= 0 && !isdigit(temp[i])) {
923       I->setInscode(temp[i]);
924     }
925   }
926 
927   if(ok) PCONVPYSTRTOLEXIDX(4, I->segi);
928   if(ok) PCONVPYSTRTOLEXIDX(5, I->resn);
929   if(ok) PCONVPYSTRTOLEXIDX(6, I->name);
930   if(ok)
931     ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 7, I->elem, sizeof(ElemName));
932   if(ok) PCONVPYSTRTOLEXIDX(8, I->textType);
933   if(ok) PCONVPYSTRTOLEXIDX(9, I->label);
934   if(ok)
935     ok = PConvPyStrToStr(PyList_GetItem(list, 10), I->ssType, sizeof(SSType));
936   if(ok)
937     ok = PConvPyIntToInt(PyList_GetItem(list, 12), &I->customType);
938   if(ok)
939     ok = PConvPyIntToInt(PyList_GetItem(list, 13), &I->priority);
940   if(ok)
941     ok = PConvPyFloatToFloat(PyList_GetItem(list, 14), &I->b);
942   if(ok)
943     ok = PConvPyFloatToFloat(PyList_GetItem(list, 15), &I->q);
944   if(ok)
945     ok = PConvPyFloatToFloat(PyList_GetItem(list, 16), &I->vdw);
946   if(ok)
947     ok = PConvPyFloatToFloat(PyList_GetItem(list, 17), &I->partialCharge);
948   if(ok)
949     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 18, &tmp_int)))
950       I->formalCharge = tmp_int;
951   if(ok)
952     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 19, &tmp_int)))
953       I->hetatm = tmp_int;
954   if(ok){
955     PyObject *val = PyList_GetItem(list, 20);
956     if (PyList_Check(val)){
957       ok = PConvPyListToBitmask(val, &I->visRep, cRepCnt);
958     } else {
959       ok = PConvPyIntToInt(val, &I->visRep);
960     }
961   }
962   if(ok)
963     ok = PConvPyIntToInt(PyList_GetItem(list, 21), &I->color);
964   if(ok)
965     I->color = ColorConvertOldSessionIndex(G, I->color);
966   if(ok)
967     ok = PConvPyIntToInt(PyList_GetItem(list, 22), &I->id);
968   if(ok)
969     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 23, &tmp_int)))
970       I->cartoon = tmp_int;
971   if(ok)
972     ok = PConvPyIntToInt(PyList_GetItem(list, 24), (int *) &I->flags);
973   if(ok)
974     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 25, &tmp_int)))
975       I->bonded = tmp_int;
976   if(ok)
977     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 26, &tmp_int)))
978       I->chemFlag = tmp_int;
979   if(ok)
980     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 27, &tmp_int)))
981       I->geom = tmp_int;
982   if(ok)
983     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 28, &tmp_int)))
984       I->valence = tmp_int;
985   if(ok)
986     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 29, &tmp_int)))
987       I->masked = tmp_int;
988   if(ok)
989     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 30, &tmp_int)))
990       I->protekted = tmp_int;
991   if(ok)
992     ok = PConvPyIntToChar(PyList_GetItem(list, 31), (char *) &I->protons);
993   if(ok)
994     ok = PConvPyIntToInt(PyList_GetItem(list, 32), &I->unique_id);
995   if(ok && I->unique_id) {      /* reserve existing IDs */
996     I->unique_id = SettingUniqueConvertOldSessionID(G, I->unique_id);
997   }
998   if(ok)
999     if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 33, &tmp_int)))
1000       I->stereo = tmp_int;
1001   if(ok && (ll > 34))
1002     ok = PConvPyIntToInt(PyList_GetItem(list, 34), &I->discrete_state);
1003   if(ok && (ll > 35))
1004     ok = PConvPyFloatToFloat(PyList_GetItem(list, 35), &I->elec_radius);
1005   if(ok && (ll > 36))
1006     ok = PConvPyIntToInt(PyList_GetItem(list, 36), &I->rank);
1007   if(ok && (ll > 37))
1008     if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 37, &tmp_int)))
1009       I->hb_donor = tmp_int;
1010   if(ok && (ll > 38))
1011     if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 38, &tmp_int)))
1012       I->hb_acceptor = tmp_int;
1013   if(ok && (ll > 40))
1014     if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 40, &tmp_int)))
1015       I->has_setting = tmp_int;
1016   if(ok && (ll > 46)) {
1017     // only allocate if not all zero
1018     float u[6];
1019     for (int i = 0; ok && i < 6; ++i)
1020       ok = CPythonVal_PConvPyFloatToFloat_From_List(G, list, 41 + i, u + i);
1021     if(ok && (u[0] || u[1] || u[2] || u[3] || u[4] || u[5]))
1022       memcpy(I->get_anisou(), u, 6 * sizeof(float));
1023   }
1024   if(ok && (ll > 47)) {
1025     PCONVPYSTRTOLEXIDX(47, I->custom);
1026   }
1027   return (ok);
1028 }
1029 
AtomInfoCopy(PyMOLGlobals * G,const AtomInfoType * src,AtomInfoType * dst,int copy_properties)1030 void AtomInfoCopy(PyMOLGlobals * G, const AtomInfoType * src, AtomInfoType * dst, int copy_properties)
1031 {
1032   /* copy, handling resource management issues... */
1033 
1034   *dst = *src;
1035   dst->selEntry = 0;
1036   if(src->unique_id && src->has_setting) {
1037     dst->unique_id = AtomInfoGetNewUniqueID(G);
1038     if(!SettingUniqueCopyAll(G, src->unique_id, dst->unique_id))
1039       dst->has_setting = 0;
1040   } else {
1041     dst->unique_id = 0;
1042     dst->has_setting = 0;
1043   }
1044   LexInc(G, dst->label);
1045   LexInc(G, dst->textType);
1046   LexInc(G, dst->custom);
1047   LexInc(G, dst->chain);
1048   LexInc(G, dst->segi);
1049   LexInc(G, dst->resn);
1050   LexInc(G, dst->name);
1051 #ifdef _PYMOL_IP_EXTRAS
1052 #endif
1053   if (src->anisou) {
1054     dst->anisou = NULL;
1055     memcpy(dst->get_anisou(), src->anisou, 6 * sizeof(float));
1056   }
1057 }
1058 
AtomInfoBondCopy(PyMOLGlobals * G,const BondType * src,BondType * dst)1059 void AtomInfoBondCopy(PyMOLGlobals * G, const BondType * src, BondType * dst)
1060 {
1061   *(dst) = *(src);
1062 
1063   if(src->unique_id && src->has_setting) {
1064     dst->unique_id = AtomInfoGetNewUniqueID(G);
1065     if(!SettingUniqueCopyAll(G, src->unique_id, dst->unique_id))
1066       dst->has_setting = 0;
1067   } else {
1068     dst->unique_id = 0;
1069     dst->has_setting = 0;
1070   }
1071 }
1072 
AtomInfoPurgeBond(PyMOLGlobals * G,BondType * bi)1073 void AtomInfoPurgeBond(PyMOLGlobals * G, BondType * bi)
1074 {
1075   CAtomInfo *I = G->AtomInfo;
1076   if(bi->has_setting && bi->unique_id) {
1077     SettingUniqueDetachChain(G, bi->unique_id);
1078   }
1079   if(bi->unique_id && I->ActiveIDs) {
1080     OVOneToAny_DelKey(I->ActiveIDs, bi->unique_id);
1081     bi->unique_id = 0;
1082   }
1083 }
1084 
AtomInfoPurge(PyMOLGlobals * G,AtomInfoType * ai)1085 void AtomInfoPurge(PyMOLGlobals * G, AtomInfoType * ai)
1086 {
1087   CAtomInfo *I = G->AtomInfo;
1088   LexDec(G, ai->textType);
1089   LexDec(G, ai->custom);
1090   LexDec(G, ai->label);
1091   LexDec(G, ai->chain);
1092   ai->textType = 0;
1093   ai->custom = 0;
1094   ai->label = 0;
1095   ai->chain = 0;
1096   if(ai->has_setting && ai->unique_id) {
1097     SettingUniqueDetachChain(G, ai->unique_id);
1098   }
1099   if(ai->unique_id) {
1100     ExecutiveUniqueIDAtomDictInvalidate(G);
1101 
1102     if (I->ActiveIDs)
1103       OVOneToAny_DelKey(I->ActiveIDs, ai->unique_id);
1104   }
1105 #ifdef _PYMOL_IP_EXTRAS
1106 #endif
1107   DeleteAP(ai->anisou);
1108 }
1109 
1110 
1111 /*========================================================================*/
1112 /**
1113  * Transfer `mask` selected atomic properties (such as b, q, text_type, etc.)
1114  * from `src` to `dst`, while keeping all atomic identifiers untouched.
1115  * Purges `src`.
1116  *
1117  * @param mask bitmask of `cAIC_*` bits
1118  */
AtomInfoCombine(PyMOLGlobals * G,AtomInfoType * dst,AtomInfoType && src_,int mask)1119 void AtomInfoCombine(PyMOLGlobals * G, AtomInfoType * dst, AtomInfoType&& src_, int mask)
1120 {
1121   AtomInfoType* src = &src_;
1122   if(mask & cAIC_tt) {
1123     std::swap(dst->textType, src->textType);
1124   }
1125   if(mask & cAIC_ct)
1126     dst->customType = src->customType;
1127   if(mask & cAIC_pc)
1128     dst->partialCharge = src->partialCharge;
1129   if(mask & cAIC_fc)
1130     dst->formalCharge = src->formalCharge;
1131   if(mask & cAIC_flags)
1132     dst->flags = src->flags;
1133   if(mask & cAIC_b)
1134     dst->b = src->b;
1135   if(mask & cAIC_q)
1136     dst->q = src->q;
1137   if(mask & cAIC_id)
1138     dst->id = src->id;
1139   if(mask & cAIC_state)
1140     dst->discrete_state = src->discrete_state;
1141   if(mask & cAIC_rank)
1142     dst->rank = src->rank;
1143   dst->temp1 = src->temp1;
1144 
1145   SWAP_NOREF(dst->has_setting, src->has_setting);
1146   std::swap(dst->unique_id, src->unique_id);
1147 #ifdef _PYMOL_IP_EXTRAS
1148   std::swap(dst->prop_id, src->prop_id);
1149 #endif
1150 
1151   /* keep all existing names, identifiers, etc. */
1152   /* also keep all existing selections,
1153      colors, masks, and visible representations */
1154     /* leaves dst->label untouched */
1155 
1156   AtomInfoPurge(G, src);
1157 }
1158 
1159 
1160 /*========================================================================*/
1161 /**
1162  * Make atom names in `atInfo1` unique w.r.t.\ `atInfo0` (and to `atInfo1` itself).
1163  * @param atInfo0 List of reference atoms
1164  * @param n0 Size of atInfo0 list
1165  * @param atInfo1 List of atoms which need to be made unique
1166  * @param flag1 Optional whitelist mask for `atInfo1` or NULL
1167  * @param n1 Size of atInfo1 list
1168  * @param mol Optional reference molecule to limit to atoms with coordinates
1169  * @return Number of renamed atoms
1170  */
AtomInfoUniquefyNames(PyMOLGlobals * G,const AtomInfoType * atInfo0,int n0,AtomInfoType * atInfo1,int * flag1,int n1,const ObjectMolecule * mol)1171 int AtomInfoUniquefyNames(PyMOLGlobals * G, const AtomInfoType * atInfo0, int n0,
1172                           AtomInfoType * atInfo1, int *flag1, int n1,
1173                           const ObjectMolecule * mol)
1174 {
1175   /* makes sure all names in atInfo1 are unique WRT 0 and 1 */
1176   auto ignore_case = SettingGet<bool>(G, cSetting_ignore_case);
1177 
1178   /* tricky optimizations to avoid n^2 dependence in this operation */
1179   int result = 0;
1180   int a, b, c;
1181   const AtomInfoType *ai0, *lai1, *lai0;
1182   AtomInfoType *ai1;
1183   int st1, nd1, st0, nd0;       /* starts and ends */
1184   int matchFlag;
1185   int bracketFlag;
1186   WordType name;
1187 
1188   ai1 = atInfo1;
1189   lai0 = NULL;                  /* last atom compared against in each object */
1190   lai1 = NULL;
1191   st0 = 0;
1192   nd0 = 0;
1193   st1 = 0;
1194   nd1 = 0;
1195   c = 1;
1196   /* ai1->name is the atom we're currently on */
1197 
1198   b = 0;
1199   while(b < n1) {
1200     matchFlag = false;
1201 
1202     if(!ai1->name)
1203       matchFlag = true;
1204 
1205     if(!matchFlag) {
1206       /* check within object 1 */
1207 
1208       if(!lai1)
1209         bracketFlag = true;
1210       else if(!AtomInfoSameResidue(G, lai1, ai1))
1211         bracketFlag = true;
1212       else
1213         bracketFlag = false;
1214       if(bracketFlag) {
1215         c = 1;
1216         AtomInfoBracketResidue(G, atInfo1, n1, ai1, &st1, &nd1);
1217         lai1 = ai1;
1218       }
1219 
1220       ai0 = atInfo1 + st1;
1221       for(a = st1; a <= nd1; a++) {
1222         if(!WordMatchExact(G, ai1->name, ai0->name, ignore_case))
1223           ai0++;
1224         else if(!AtomInfoSameResidue(G, ai1, ai0))
1225           ai0++;
1226         else if(ai1 != ai0) {
1227           matchFlag = true;
1228           break;
1229         } else
1230           ai0++;
1231       }
1232     }
1233 
1234     if(!matchFlag) {
1235       if(atInfo0) {
1236         /* check within object 2 */
1237 
1238         if (!lai0 || !AtomInfoSameResidue(G, lai0, ai1)) {
1239           AtomInfoBracketResidue(G, atInfo0, n0, ai1, &st0, &nd0);
1240           lai0 = ai1;
1241         }
1242 
1243         for (a = st0; a <= nd0; ++a) {
1244           ai0 = atInfo0 + a;
1245 
1246           if (WordMatchExact(G, ai1->name, ai0->name, ignore_case) &&
1247               AtomInfoSameResidue(G, ai1, ai0) && ai1 != ai0 &&
1248               (!mol || mol->atomHasAnyCoordinates(a))) {
1249             matchFlag = true;
1250             break;
1251           }
1252         }
1253       }
1254     }
1255 
1256     if(matchFlag && ((!flag1) || flag1[ai1 - atInfo1])) {
1257       if(c < 100) {
1258         if((c < 10) && ai1->elem[1])    /* try to keep halogens 3 or under */
1259           sprintf(name, "%2s%1d", ai1->elem, c);
1260         else
1261           sprintf(name, "%1s%02d", ai1->elem, c);
1262       } else {
1263         sprintf(name, "%1d%1s%02d", c / 100, ai1->elem, c % 100);
1264       }
1265       LexAssign(G, ai1->name, name);
1266       result++;
1267       c = c + 1;
1268     } else {
1269       ai1++;
1270       b++;
1271     }
1272   }
1273   return result;
1274 }
1275 
1276 /**
1277  * Make atom names in `atoms` unique w.r.t.\ atoms-with-coordinates in `mol`.
1278  * @param mol Reference molecule
1279  * @param atoms List of atoms which need to be made unique
1280  * @param natoms Size of atoms list
1281  * @return Number of renamed atoms
1282  */
AtomInfoUniquefyNames(const ObjectMolecule * mol,AtomInfoType * atoms,size_t natoms)1283 int AtomInfoUniquefyNames(
1284     const ObjectMolecule* mol, AtomInfoType* atoms, size_t natoms)
1285 {
1286   return AtomInfoUniquefyNames(
1287       mol->G, mol->AtomInfo, mol->NAtom, atoms, nullptr, natoms, mol);
1288 }
1289 
1290 /*========================================================================*/
AtomInfoBracketResidue(PyMOLGlobals * G,const AtomInfoType * ai0,int n0,const AtomInfoType * ai,int * st,int * nd)1291 void AtomInfoBracketResidue(PyMOLGlobals * G, const AtomInfoType * ai0, int n0,
1292                             const AtomInfoType * ai, int *st, int *nd)
1293 {
1294   /* inefficient but reliable way to find where residue atoms are located in an object
1295    * for purpose of residue-based operations */
1296   int a;
1297   const AtomInfoType *ai1;
1298 
1299   *st = 0;
1300   *nd = n0 - 1;
1301   ai1 = ai0;
1302   for(a = 0; a < n0; a++) {
1303     if(!AtomInfoSameResidue(G, ai, ai1++))
1304       *st = a;
1305     else
1306       break;
1307   }
1308   ai1 = ai0 + n0 - 1;
1309   for(a = n0 - 1; a >= 0; a--) {
1310     if(!AtomInfoSameResidue(G, ai, ai1--)) {
1311       *nd = a;
1312     } else
1313       break;
1314   }
1315 }
1316 
1317 
1318 /*========================================================================*/
AtomInfoBracketResidueFast(PyMOLGlobals * G,const AtomInfoType * ai0,int n0,int cur,int * st,int * nd)1319 void AtomInfoBracketResidueFast(PyMOLGlobals * G, const AtomInfoType * ai0, int n0, int cur,
1320                                 int *st, int *nd)
1321 {
1322   /* efficient but unreliable way to find where residue atoms are located in an object
1323    * for purpose of residue-based operations.
1324    * This function finds all atoms in the AtomInfoType array in both directions that
1325    * belong to the same residue. It starts the search from the "cur" offset, and returns
1326    * the beginning offset (st) and the ending offset (nd).*/
1327   int a;
1328   const AtomInfoType *ai1;
1329 
1330   *st = cur;
1331   *nd = cur;
1332   ai0 = ai0 + cur;
1333   ai1 = ai0 - 1;
1334   for(a = cur - 1; a >= 0; a--) {
1335     if(!AtomInfoSameResidue(G, ai0, ai1--))
1336       break;
1337     *st = a;
1338   }
1339   ai1 = ai0 + 1;
1340   for(a = cur + 1; a < n0; a++) {
1341     if(!AtomInfoSameResidue(G, ai0, ai1++))
1342       break;
1343     *nd = a;
1344   }
1345 }
1346 
1347 
1348 /*========================================================================*/
AtomInfoUpdateAutoColor(PyMOLGlobals * G)1349 int AtomInfoUpdateAutoColor(PyMOLGlobals * G)
1350 {
1351   CAtomInfo *I = G->AtomInfo;
1352   if(SettingGetGlobal_b(G, cSetting_auto_color))
1353     I->CColor = ColorGetNext(G);
1354   else
1355     I->CColor = ColorGetIndex(G, "carbon");
1356   return I->CColor;
1357 }
1358 
1359 
1360 /*========================================================================*/
AtomInfoPrimeColors(PyMOLGlobals * G)1361 void AtomInfoPrimeColors(PyMOLGlobals * G)
1362 {
1363   CAtomInfo *I = G->AtomInfo;
1364 
1365   I->NColor = ColorGetIndex(G, "nitrogen");
1366   I->CColor = ColorGetIndex(G, "carbon");
1367 
1368   I->HColor = ColorGetIndex(G, "hydrogen");
1369   I->OColor = ColorGetIndex(G, "oxygen");
1370   I->SColor = ColorGetIndex(G, "sulfur");
1371 
1372   I->ClColor = ColorGetIndex(G, "chlorine");
1373   I->BrColor = ColorGetIndex(G, "bromine");
1374   I->FColor = ColorGetIndex(G, "fluorine");
1375   I->IColor = ColorGetIndex(G, "iodine");
1376 
1377   I->PColor = ColorGetIndex(G, "phosphorus");
1378 
1379   I->MgColor = ColorGetIndex(G, "magnesium");
1380   I->MnColor = ColorGetIndex(G, "manganese");
1381 
1382   I->NaColor = ColorGetIndex(G, "sodium");
1383   I->KColor = ColorGetIndex(G, "potassium");
1384   I->CaColor = ColorGetIndex(G, "calcium");
1385 
1386   I->CuColor = ColorGetIndex(G, "copper");
1387   I->FeColor = ColorGetIndex(G, "iron");
1388   I->ZnColor = ColorGetIndex(G, "zinc");
1389 
1390   I->SeColor = ColorGetIndex(G, "selenium");
1391   I->DColor = ColorGetIndex(G, "deuterium");
1392 }
1393 
1394 
1395 /*========================================================================*/
AtomInfoGetBondLength(PyMOLGlobals * G,const AtomInfoType * ai1,const AtomInfoType * ai2)1396 float AtomInfoGetBondLength(PyMOLGlobals * G, const AtomInfoType * ai1, const AtomInfoType * ai2)
1397 {
1398   float result = 1.6F;
1399   const AtomInfoType *a1, *a2;
1400 
1401   /* very simple for now ...
1402      flush this out with pattern-based parameters later on */
1403 
1404   if(ai1->protons > ai2->protons) {
1405     a1 = ai2;
1406     a2 = ai1;
1407   } else {
1408     a1 = ai1;
1409     a2 = ai2;
1410   }
1411   switch (a1->protons) {
1412 
1413 
1414 /* hydrogen =========================== */
1415   case cAN_H:
1416     switch (a2->protons) {
1417     case cAN_H:
1418       result = 0.74F;
1419       break;
1420     case cAN_C:
1421       result = 1.09F;
1422       break;
1423     case cAN_N:
1424       result = 1.01F;
1425       break;
1426     case cAN_S:
1427       result = 1.34F;
1428       break;
1429     case cAN_O:
1430       result = 0.96F;
1431       break;
1432     default:
1433       result = 1.09F;
1434       break;
1435     }
1436     break;
1437 
1438 /* carbon =========================== */
1439 
1440   case cAN_C:                  /* carbon */
1441     switch (a1->geom) {
1442 
1443     case cAtomInfoLinear:      /* linear carbon ============= */
1444       switch (a2->geom) {
1445       case cAtomInfoLinear:
1446         switch (a2->protons) {
1447         case cAN_C:
1448           result = 1.20F;
1449           break;                /* C#^C */
1450         case cAN_N:
1451           result = 1.16F;
1452           break;                /* C#^N */
1453         default:
1454           result = 1.20F;
1455           break;
1456         }
1457         break;
1458       case cAtomInfoPlanar:
1459         switch (a2->protons) {
1460         case cAN_I:
1461           result = 2.14F;
1462           break;                /* normal single bond lengths */
1463         case cAN_Cl:
1464           result = 1.77F;
1465           break;
1466         case cAN_Br:
1467           result = 1.94F;
1468           break;
1469         case cAN_F:
1470           result = 1.35F;
1471           break;
1472         case cAN_S:
1473           result = 1.82F;
1474           break;
1475         case cAN_N:
1476           result = 1.47F;
1477           break;
1478         case cAN_O:
1479           result = 1.43F;
1480           break;
1481         case cAN_P:
1482           result = 1.84F;
1483           break;
1484         case cAN_C:
1485           result = 1.54F;
1486           break;
1487         default:
1488           result = 1.54F;
1489           break;
1490         }
1491         break;
1492       default:                 /* ?#C-^? */
1493         switch (a2->protons) {
1494         case cAN_I:
1495           result = 2.14F;
1496           break;                /* normal single bond lengths */
1497         case cAN_Cl:
1498           result = 1.77F;
1499           break;
1500         case cAN_Br:
1501           result = 1.94F;
1502           break;
1503         case cAN_F:
1504           result = 1.35F;
1505           break;
1506         case cAN_S:
1507           result = 1.82F;
1508           break;
1509         case cAN_N:
1510           result = 1.47F;
1511           break;
1512         case cAN_O:
1513           result = 1.43F;
1514           break;
1515         case cAN_P:
1516           result = 1.84F;
1517           break;
1518         case cAN_C:
1519           result = 1.54F;
1520           break;
1521         default:
1522           result = 1.54F;
1523           break;
1524         }
1525         break;
1526       }
1527       break;
1528 
1529     case cAtomInfoPlanar:      /* planer carbon ============= */
1530       switch (a2->geom) {
1531       case cAtomInfoLinear:
1532         switch (a2->protons) {
1533         case cAN_I:
1534           result = 2.14F;
1535           break;                /* normal single bond lengths */
1536         case cAN_Cl:
1537           result = 1.77F;
1538           break;
1539         case cAN_Br:
1540           result = 1.94F;
1541           break;
1542         case cAN_F:
1543           result = 1.35F;
1544           break;
1545         case cAN_S:
1546           result = 1.82F;
1547           break;
1548         case cAN_N:
1549           result = 1.47F;
1550           break;
1551         case cAN_O:
1552           result = 1.43F;
1553           break;
1554         case cAN_P:
1555           result = 1.84F;
1556           break;
1557         case cAN_C:
1558           result = 1.54F;
1559           break;
1560         default:
1561           result = 1.54F;
1562           break;
1563         }
1564         break;
1565       case cAtomInfoPlanar:
1566         switch (a2->protons) {
1567         case cAN_C:
1568           result = 1.34F;
1569           break;                /* C=^C or ?=C-^C=? */
1570         case cAN_O:
1571           result = 1.20F;
1572           break;                /* carbonyl */
1573         case cAN_N:
1574           result = 1.29F;
1575           break;                /* C=^N or ?=C-^N=? */
1576         case cAN_S:
1577           result = 1.60F;
1578           break;                /* C=^S or ?=C-^S-?=? */
1579         default:
1580           result = 1.34F;
1581           break;
1582         }
1583         break;
1584       default:                 /* ?#C-^? */
1585         switch (a2->protons) {
1586         case cAN_I:
1587           result = 2.14F;
1588           break;                /* normal single bond lengths */
1589         case cAN_Cl:
1590           result = 1.77F;
1591           break;
1592         case cAN_Br:
1593           result = 1.94F;
1594           break;
1595         case cAN_F:
1596           result = 1.35F;
1597           break;
1598         case cAN_S:
1599           result = 1.71F;
1600           break;                /* mmod */
1601         case cAN_N:
1602           result = 1.47F;
1603           break;
1604         case cAN_O:
1605           result = 1.43F;
1606           break;
1607         case cAN_P:
1608           result = 1.84F;
1609           break;
1610         case cAN_C:
1611           result = 1.54F;
1612           break;
1613         default:
1614           result = 1.54F;
1615           break;
1616         }
1617         break;
1618       }
1619       break;
1620 
1621     default:                   /* tetrahedral carbon */
1622       switch (a2->protons) {
1623       case cAN_I:
1624         result = 2.14F;
1625         break;                  /* normal single bond lengths */
1626       case cAN_Cl:
1627         result = 1.77F;
1628         break;
1629       case cAN_Br:
1630         result = 1.94F;
1631         break;
1632       case cAN_F:
1633         result = 1.35F;
1634         break;
1635       case cAN_S:
1636         result = 1.82F;
1637         break;
1638       case cAN_N:
1639         result = 1.47F;
1640         break;
1641       case cAN_O:
1642         result = 1.43F;
1643         break;
1644       case cAN_P:
1645         result = 1.84F;
1646         break;
1647       case cAN_C:
1648         result = 1.54F;
1649         break;
1650       default:
1651         result = 1.54F;
1652         break;
1653       }
1654       break;
1655     }
1656     break;
1657 
1658 
1659 /* nitrogen ========================= */
1660 
1661   case cAN_N:
1662     if((a1->geom == cAtomInfoPlanar) && (a2->geom == cAtomInfoPlanar))
1663       switch (a2->protons) {
1664       case cAN_N:
1665         result = 1.25F;
1666         break;
1667       case cAN_O:
1668         result = 1.21F;
1669         break;
1670       case cAN_S:
1671         result = 1.53F;
1672         break;                  /* interpolated */
1673       default:
1674         result = 1.25F;
1675         break;
1676     } else {
1677       switch (a2->protons) {
1678       case cAN_N:
1679         result = 1.45F;
1680         break;
1681       case cAN_O:
1682         result = 1.40F;
1683         break;
1684       case cAN_S:
1685         result = 1.75F;
1686         break;                  /* interpolated */
1687       default:
1688         result = 1.45F;
1689         break;
1690       }
1691     }
1692     break;
1693 
1694 
1695 /* oxygen =========================== */
1696 
1697   case cAN_O:
1698     if((a1->geom == cAtomInfoPlanar) && (a2->geom == cAtomInfoPlanar))
1699       switch (a2->protons) {
1700       case cAN_O:
1701         result = 1.35F;
1702         break;                  /* guess */
1703       case cAN_S:
1704         result = 1.44F;
1705         break;                  /* macromodel */
1706       default:
1707         result = 1.35F;
1708         break;
1709     } else if(a1->geom == cAtomInfoPlanar) {
1710       switch (a2->protons) {
1711       case cAN_O:
1712         result = 1.35F;
1713         break;                  /* guess */
1714       case cAN_S:
1715         result = 1.44F;
1716         break;                  /* macromodel */
1717       default:
1718         result = 1.35F;
1719         break;
1720       }
1721     } else {
1722       switch (a2->protons) {
1723       case cAN_O:
1724         result = 1.40F;
1725         break;
1726       case cAN_S:
1727         result = 1.75F;
1728         break;                  /* interpolated */
1729       default:
1730         result = 1.45F;
1731         break;
1732       }
1733     }
1734     break;
1735 
1736 
1737 /* sulfur =========================== */
1738 
1739   case cAN_S:
1740     switch (a2->protons) {
1741     case cAN_S:
1742       result = 2.05F;
1743       break;                    /* interpolated */
1744     default:
1745       result = 1.82F;
1746       break;
1747     }
1748     break;
1749 
1750     /* fall-back to old method */
1751   default:
1752 
1753     result = 0.0;
1754     switch (a1->geom) {
1755     case cAtomInfoLinear:
1756       result += 1.20F;
1757       break;
1758     case cAtomInfoPlanar:
1759       result += 1.34F;
1760       break;
1761     default:
1762       result += 1.54F;
1763       break;
1764     }
1765     switch (a2->geom) {
1766     case cAtomInfoLinear:
1767       result += 1.20F;
1768       break;
1769     case cAtomInfoPlanar:
1770       result += 1.34F;
1771       break;
1772     default:
1773       result += 1.54F;
1774       break;
1775     }
1776     result /= 2.0F;
1777     break;
1778   }
1779   return (result);
1780 }
1781 
1782 /**
1783  * Periodic table of elements
1784  *
1785  * Note: Default VDW radius is 1.80
1786  */
1787 const ElementTableItemType ElementTable[] = {
1788   {"lonepair",          "LP",   0.50,   0.000000},
1789   {"hydrogen",          "H",    1.20,   1.007940},
1790   {"helium",            "He",   1.40,   4.002602},
1791   {"lithium",           "Li",   1.82,   6.941000},
1792   {"beryllium",         "Be",   1.80,   9.012182},
1793   {"boron",             "B",    1.85,  10.811000},
1794   {"carbon",            "C",    1.70,  12.010700},
1795   {"nitrogen",          "N",    1.55,  14.006700},
1796   {"oxygen",            "O",    1.52,  15.999400},
1797   {"fluorine",          "F",    1.47,  18.998403},
1798   {"neon",              "Ne",   1.54,  20.179700},
1799   {"sodium",            "Na",   2.27,  22.989770},
1800   {"magnesium",         "Mg",   1.73,  24.305000},
1801   {"aluminum",          "Al",   2.00,  26.981538},
1802   {"silicon",           "Si",   2.10,  28.085500},
1803   {"phosphorus",        "P",    1.80,  30.973761},
1804   {"sulfur",            "S",    1.80,  32.065000},
1805   {"chlorine",          "Cl",   1.75,  35.453000},
1806   {"argon",             "Ar",   1.88,  39.948000},
1807   {"potassium",         "K",    2.75,  39.098300},
1808   {"calcium",           "Ca",   1.80,  40.078000},
1809   {"scandium",          "Sc",   1.80,  44.955910},
1810   {"titanium",          "Ti",   1.80,  47.867000},
1811   {"vanadium",          "V",    1.80,  50.941500},
1812   {"chromium",          "Cr",   1.80,  51.996100},
1813   {"manganese",         "Mn",   1.73,  54.938049},
1814   {"iron",              "Fe",   1.80,  55.845000},
1815   {"cobalt",            "Co",   1.80,  58.933200},
1816   {"nickel",            "Ni",   1.63,  58.693400},
1817   {"copper",            "Cu",   1.40,  63.546000},
1818   {"zinc",              "Zn",   1.39,  65.390000},
1819   {"gallium",           "Ga",   1.87,  69.723000},
1820   {"germanium",         "Ge",   1.80,  72.640000},
1821   {"arsenic",           "As",   1.85,  74.921600},
1822   {"selenium",          "Se",   1.90,  78.960000},
1823   {"bromine",           "Br",   1.85,  79.904000},
1824   {"krypton",           "Kr",   2.02,  83.800000},
1825   {"rubidium",          "Rb",   1.80,  85.467800},
1826   {"strontium",         "Sr",   1.80,  87.620000},
1827   {"yttrium",           "Y",    1.80,  88.905850},
1828   {"zirconium",         "Zr",   1.80,  91.224000},
1829   {"niobium",           "Nb",   1.80,  92.906380},
1830   {"molybdenum",        "Mo",   1.80,  95.940000},
1831   {"technetium",        "Tc",   1.80,  98.000000},
1832   {"ruthenium",         "Ru",   1.80, 101.070000},
1833   {"rhodium",           "Rh",   1.80, 102.905500},
1834   {"palladium",         "Pd",   1.63, 106.420000},
1835   {"silver",            "Ag",   1.72, 107.868200},
1836   {"cadmium",           "Cd",   1.58, 112.411000},
1837   {"indium",            "In",   1.93, 114.818000},
1838   {"tin",               "Sn",   2.17, 118.710000},
1839   {"antimony",          "Sb",   1.80, 121.760000},
1840   {"tellurium",         "Te",   2.06, 127.600000},
1841   {"iodine",            "I",    1.98, 126.904470},
1842   {"xenon",             "Xe",   2.16, 131.293000},
1843   {"cesium",            "Cs",   1.80, 132.905450},
1844   {"barium",            "Ba",   1.80, 137.327000},
1845   {"lanthanum",         "La",   1.80, 138.905500},
1846   {"cerium",            "Ce",   1.80, 140.116000},
1847   {"praseodymium",      "Pr",   1.80, 140.907650},
1848   {"neodymium",         "Nd",   1.80, 144.240000},
1849   {"promethium",        "Pm",   1.80, 145.000000},
1850   {"samarium",          "Sm",   1.80, 150.360000},
1851   {"europium",          "Eu",   1.80, 151.964000},
1852   {"gadolinium",        "Gd",   1.80, 157.250000},
1853   {"terbium",           "Tb",   1.80, 158.925340},
1854   {"dysprosium",        "Dy",   1.80, 162.500000},
1855   {"holmium",           "Ho",   1.80, 164.930320},
1856   {"erbium",            "Er",   1.80, 167.259000},
1857   {"thulium",           "Tm",   1.80, 168.934210},
1858   {"ytterbium",         "Yb",   1.80, 173.040000},
1859   {"lutetium",          "Lu",   1.80, 174.967000},
1860   {"hafnium",           "Hf",   1.80, 178.490000},
1861   {"tantalum",          "Ta",   1.80, 180.947900},
1862   {"tungsten",          "W",    1.80, 183.840000},
1863   {"rhenium",           "Re",   1.80, 186.207000},
1864   {"osmium",            "Os",   1.80, 190.230000},
1865   {"iridium",           "Ir",   1.80, 192.217000},
1866   {"platinum",          "Pt",   1.75, 195.078000},
1867   {"gold",              "Au",   1.66, 196.966550},
1868   {"mercury",           "Hg",   1.55, 200.590000},
1869   {"thallium",          "Tl",   1.96, 204.383300},
1870   {"lead",              "Pb",   2.02, 207.200000},
1871   {"bismuth",           "Bi",   1.80, 208.980380},
1872   {"polonium",          "Po",   1.80, 208.980000},
1873   {"astatine",          "At",   1.80, 209.990000},
1874   {"radon",             "Rn",   1.80, 222.020000},
1875   {"francium",          "Fr",   1.80, 223.020000},
1876   {"radium",            "Ra",   1.80, 226.030000},
1877   {"actinium",          "Ac",   1.80, 227.030000},
1878   {"thorium",           "Th",   1.80, 232.038100},
1879   {"protactinium",      "Pa",   1.80, 231.035880},
1880   {"uranium",           "U",    1.86, 238.028910},
1881   {"neptunium",         "Np",   1.80, 237.050000},
1882   {"plutonium",         "Pu",   1.80, 244.060000},
1883   {"americium",         "Am",   1.80, 243.060000},
1884   {"curium",            "Cm",   1.80, 247.070000},
1885   {"berkelium",         "Bk",   1.80, 247.070000},
1886   {"californium",       "Cf",   1.80, 251.080000},
1887   {"einsteinium",       "Es",   1.80, 252.080000},
1888   {"fermium",           "Fm",   1.80, 257.100000},
1889   {"mendelevium",       "Md",   1.80, 258.100000},
1890   {"nobelium",          "No",   1.80, 259.100000},
1891   {"lawrencium",        "Lr",   1.80, 262.110000},
1892   {"rutherfordium",     "Rf",   1.80, 261.110000},
1893   {"dubnium",           "Db",   1.80, 262.110000},
1894   {"seaborgium",        "Sg",   1.80, 266.120000},
1895   {"bohrium",           "Bh",   1.80, 264.120000},
1896   {"hassium",           "Hs",   1.80, 269.130000},
1897   {"meitnerium",        "Mt",   1.80, 268.140000},
1898   {"darmstadtium",      "Ds",   1.80, 281.000000},
1899   {"roentgenium",       "Rg",   1.80, 281.000000},
1900   {"copernicium",       "Cn",   1.80, 285.000000},
1901   {"nihonium",          "Nh",   1.80, 286.000000},
1902   {"flerovium",         "Fl",   1.80, 289.000000},
1903   {"moscovium",         "Mc",   1.80, 290.000000},
1904   {"livermorium",       "Lv",   1.80, 293.000000},
1905   {"tennessine",        "Ts",   1.80, 294.000000},
1906   {"oganesson",         "Og",   1.80, 294.000000},
1907   {NULL,                NULL,   0.00,   0.000000}
1908 };
1909 
1910 const int ElementTableSize = sizeof(ElementTable) / sizeof(ElementTable[0]) - 1;
1911 
1912 /**
1913  * Assign atomic color, or G->AtomInfo->CColor in case of carbon.
1914  */
AtomInfoAssignColors(PyMOLGlobals * G,AtomInfoType * at1)1915 void AtomInfoAssignColors(PyMOLGlobals * G, AtomInfoType * at1)
1916 {
1917   at1->color = AtomInfoGetColor(G, at1);
1918 }
1919 
1920 /**
1921  * Get atomic color, based on protons and elem
1922  * @return color index
1923  */
AtomInfoGetColor(PyMOLGlobals * G,const AtomInfoType * at1)1924 int AtomInfoGetColor(PyMOLGlobals * G, const AtomInfoType * at1)
1925 {
1926   // fast lookup for most common elements
1927   switch (at1->protons) {
1928     case cAN_H:
1929       if (at1->elem[0] == 'D')
1930         return G->AtomInfo->DColor;
1931       return G->AtomInfo->HColor;
1932     case cAN_N: return G->AtomInfo->NColor;
1933     case cAN_C: return G->AtomInfo->CColor;
1934     case cAN_O: return G->AtomInfo->OColor;
1935     case cAN_P: return G->AtomInfo->PColor;
1936   }
1937 
1938   // general by-name lookup (exclude LP, PS)
1939   if (at1->protons > 0 && at1->protons < ElementTableSize)
1940     return ColorGetIndex(G, ElementTable[at1->protons].name);
1941 
1942   // special cases
1943   if (strcmp(at1->elem, "PS") == 0)
1944     return ColorGetIndex(G, "pseudoatom");
1945   if (strcmp(at1->elem, "LP") == 0)
1946     return ColorGetIndex(G, "lonepair");
1947 
1948   return G->AtomInfo->DefaultColor;
1949 }
1950 
AtomInfoFreeSortedIndexes(PyMOLGlobals * G,int ** index,int ** outdex)1951 void AtomInfoFreeSortedIndexes(PyMOLGlobals * G, int **index, int **outdex)
1952 {
1953   FreeP(*index);
1954   FreeP(*outdex);
1955 }
1956 
AtomInfoNameCompare(PyMOLGlobals * G,const char * name1,const char * name2)1957 static int AtomInfoNameCompare(PyMOLGlobals * G, const char *name1, const char *name2)
1958 {
1959   const char *n1, *n2;
1960   int cmp;
1961 
1962   if((name1[0] >= '0') && (name1[0] <= '9'))
1963     n1 = name1 + 1;
1964   else
1965     n1 = name1;
1966   if((name2[0] >= '0') && (name2[0] <= '9'))
1967     n2 = name2 + 1;
1968   else
1969     n2 = name2;
1970   cmp = WordCompare(G, n1, n2, true);
1971 
1972   if(cmp)
1973     return cmp;
1974   return WordCompare(G, name1, name2, true);
1975 
1976 }
1977 
AtomInfoNameCompare(PyMOLGlobals * G,const lexidx_t & name1,const lexidx_t & name2)1978 static int AtomInfoNameCompare(PyMOLGlobals * G, const lexidx_t& name1, const lexidx_t& name2)
1979 {
1980   if (name1 == name2)
1981     return 0;
1982   return AtomInfoNameCompare(G, LexStr(G, name1), LexStr(G, name2));
1983 }
1984 
AtomInfoCompareAll(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)1985 int AtomInfoCompareAll(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
1986 {
1987   return (at1->resv != at2->resv ||
1988 	  at1->customType != at2->customType ||
1989 	  at1->priority != at2->priority ||
1990 	  at1->b != at2->b ||
1991 	  at1->q != at2->q ||
1992 	  at1->vdw != at2->vdw ||
1993 	  at1->partialCharge != at2->partialCharge ||
1994 	  at1->formalCharge != at2->formalCharge ||
1995 	  //	  at1->selEntry != at2->selEntry ||
1996 	  at1->color != at2->color ||
1997 	  at1->id != at2->id ||
1998 	  at1->flags != at2->flags ||
1999 	  //	  at1->temp1 != at2->temp1 ||
2000 	  at1->unique_id != at2->unique_id ||
2001 	  at1->discrete_state != at2->discrete_state ||
2002 	  at1->elec_radius != at2->elec_radius ||
2003 	  at1->rank != at2->rank ||
2004 	  at1->textType != at2->textType ||
2005 	  at1->custom != at2->custom ||
2006 	  at1->label != at2->label ||
2007 	  //	  !memcmp(at1->visRep, at2->visRep, sizeof(signed char)*cRepCnt) || // should this be in here?
2008 	  at1->stereo != at2->stereo ||
2009 	  at1->cartoon != at2->cartoon ||
2010 	  at1->hetatm != at2->hetatm ||
2011 	  at1->bonded != at2->bonded ||
2012 	  //	  at1->chemFlag != at2->chemFlag ||
2013 	  //	  at1->geom != at2->geom ||
2014 	  //	  at1->valence != at2->valence ||  // Valence should not be in, since it is not initially computed?
2015 	  at1->deleteFlag != at2->deleteFlag ||
2016 	  at1->masked != at2->masked ||
2017 	  at1->protekted != at2->protekted ||
2018 	  at1->protons != at2->protons ||
2019 	  at1->hb_donor != at2->hb_donor ||
2020 	  at1->hb_acceptor != at2->hb_acceptor ||
2021 	  at1->has_setting != at2->has_setting ||
2022 	  at1->chain != at2->chain ||
2023 	  at1->segi != at2->segi ||
2024 	  at1->resn != at2->resn ||
2025 	  at1->name != at2->name ||
2026 	  strcmp(at1->alt, at2->alt) ||
2027 	  at1->inscode != at2->inscode ||
2028 	  strcmp(at1->elem, at2->elem) ||
2029 	  strcmp(at1->ssType, at2->ssType));
2030 	  // should these variables be in here?
2031 	  //  float U11, U22, U33, U12, U13, U23;
2032 }
2033 
2034 /**
2035  * Compares atoms based on all atom identifiers, discrete state, priority,
2036  * hetatm (optional) and rank (optional)
2037  *
2038  * Insertion code sorting depends on settings:
2039  * - pdb_insertions_go_first
2040  * - rank_assisted_sorts
2041  *
2042  * Returns:
2043  *   0: at1 == at2
2044  *  -1: at1 < at2
2045  *   1: at1 > at2
2046  */
AtomInfoCompare(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2,bool ignore_hetatm,bool ignore_rank)2047 static int AtomInfoCompare(PyMOLGlobals *G, const AtomInfoType *at1, const AtomInfoType *at2,
2048     bool ignore_hetatm, bool ignore_rank)
2049 {
2050   int wc;
2051 
2052   if ((wc = WordCompare(G, at1->segi, at2->segi, false))) return wc;
2053   if ((wc = WordCompare(G, at1->chain, at2->chain, false))) return wc;
2054   if (!ignore_hetatm && at1->hetatm != at2->hetatm) return (at2->hetatm) ? -1 : 1;
2055   if (at1->resv != at2->resv) return (at1->resv < at2->resv) ? -1 : 1;
2056 
2057   if ((wc = getInscodeUpper(at1) - getInscodeUpper(at2))) {
2058     if (SettingGetGlobal_b(G, cSetting_pdb_insertions_go_first))
2059       return (!at1->inscode) ? 1 : (!at2->inscode) ? -1 : wc;
2060 
2061     if ((at1->rank != at2->rank) && SettingGetGlobal_b(G, cSetting_rank_assisted_sorts))
2062       return (at1->rank < at2->rank) ? -1 : 1;
2063 
2064     return wc;
2065   }
2066 
2067   if ((wc = WordCompare(G, at1->resn, at2->resn, true))) return wc;
2068   if (at1->discrete_state != at2->discrete_state) return (at1->discrete_state < at2->discrete_state) ? -1 : 1;
2069 
2070   // if this looks like a "bulk" het group with no residue number, then don't
2071   // compare by name/alt/priority (affects mmCIF with cif_use_auth=0)
2072   if (!ignore_rank && !at1->resv && at1->hetatm)
2073     goto rank_compare;
2074 
2075   if (at1->priority != at2->priority) return (at1->priority < at2->priority) ? -1 : 1;
2076 
2077   // Changed (PyMOL 2.1): name before alt
2078   if ((wc = AtomInfoNameCompare(G, at1->name, at2->name))) return wc;
2079 
2080   // Changed (PyMOL 2.1): empty alt goes first: '' < 'A' < 'B'
2081   if (at1->alt[0] != at2->alt[0]) {
2082     return (at1->alt[0] < at2->alt[0]) ? -1 : 1;
2083   }
2084 
2085 rank_compare:
2086   if (!ignore_rank && at1->rank != at2->rank) return (at1->rank < at2->rank) ? -1 : 1;
2087 
2088   return 0;
2089 }
2090 
AtomInfoCompare(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2091 int AtomInfoCompare(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2092 {
2093   return AtomInfoCompare(G, at1, at2, false, false);
2094 }
2095 
AtomInfoCompareIgnoreRankHet(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2096 int AtomInfoCompareIgnoreRankHet(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2097 {
2098   return AtomInfoCompare(G, at1, at2, true, true);
2099 }
2100 
AtomInfoCompareIgnoreRank(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2101 int AtomInfoCompareIgnoreRank(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2102 {
2103   return AtomInfoCompare(G, at1, at2, false, true);
2104 }
2105 
AtomInfoCompareIgnoreHet(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2106 int AtomInfoCompareIgnoreHet(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2107 {
2108   return AtomInfoCompare(G, at1, at2, true, false);
2109 }
2110 
2111 /**
2112  * Function only used for matching atoms of two aligned residues.
2113  *
2114  * Changed (PyMOL 2.1):
2115  * Consider alt codes to be equal if at least one is empty.
2116  * Otherwise, alt-code (C-alpha) atoms can't be aligned to non-alt atoms
2117  * (also affects morphing).
2118  */
AtomInfoNameOrder(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2119 int AtomInfoNameOrder(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2120 {
2121   int result;
2122   if(at1->alt[0] == at2->alt[0] || !at1->alt[0] || !at2->alt[0]) {
2123     if(at1->priority == at2->priority) {
2124       result = AtomInfoNameCompare(G, at1->name, at2->name);
2125     } else if(at1->priority < at2->priority) {
2126       result = -1;
2127     } else {
2128       result = 1;
2129     }
2130   } else if(at1->alt[0] < at2->alt[0]) {
2131     result = -1;
2132   } else {
2133     result = 1;
2134   }
2135   return (result);
2136 }
2137 
AtomInfoSameResidue(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2138 int AtomInfoSameResidue(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2139 {
2140   return (
2141       at1->resv == at2->resv &&
2142       at1->chain == at2->chain &&
2143       at1->hetatm == at2->hetatm &&
2144       at1->discrete_state == at2->discrete_state &&
2145       at1->inscode == at2->inscode &&
2146       at1->segi == at2->segi &&
2147       WordMatchExact(G, at1->resn, at2->resn, SettingGet<bool>(G, cSetting_ignore_case)));
2148 }
2149 
AtomInfoSameResidueP(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2150 int AtomInfoSameResidueP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2151 {
2152   if(at1 && at2)
2153     return AtomInfoSameResidue(G, at1, at2);
2154   return 0;
2155 }
2156 
AtomInfoSameChainP(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2157 int AtomInfoSameChainP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2158 {
2159   if(at1 && at2)
2160     if(at1->chain == at2->chain)
2161       if(at1->segi == at2->segi)
2162         return 1;
2163   return 0;
2164 }
2165 
AtomInfoSameSegmentP(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2)2166 int AtomInfoSameSegmentP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2)
2167 {
2168   if(at1 && at2)
2169     if(at1->segi == at2->segi)
2170       return 1;
2171   return 0;
2172 }
2173 
AtomInfoSequential(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2,int mode)2174 int AtomInfoSequential(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2, int mode)
2175 {
2176   if(mode > 0) {
2177     if(at1->hetatm == at2->hetatm) {
2178       if(mode > 1) {
2179         if(at1->segi == at2->segi) {
2180           if(mode > 2) {
2181             if(at1->chain == at2->chain) {
2182               if(mode > 3) {
2183                 if(at1->resv == at2->resv) {
2184                   if(mode > 4) {
2185                     if(at1->inscode == at2->inscode)
2186                       return 1;
2187                     if(at1->inscode + 1 == at2->inscode)
2188                       return 1;
2189                   } else {
2190                     return 1;   /* no resi check */
2191                   }
2192                 } else if((at1->resv + 1) == at2->resv)
2193                   return 1;
2194               } else {
2195                 return 1;       /* no resv check */
2196               }
2197             }
2198           } else {
2199             return 1;           /* no chain check */
2200           }
2201         }
2202       } else {
2203         return 1;               /* no segi check */
2204       }
2205     }
2206   } else {
2207     return 1;                   /* no hetatm check */
2208   }
2209   return 0;
2210 }
2211 
2212 /**
2213  * Used in "rms" and "update" for matching two selections
2214  * @return 1 if atoms match, 0 otherwise
2215  */
AtomInfoMatch(PyMOLGlobals * G,const AtomInfoType * at1,const AtomInfoType * at2,bool ignore_case,bool ignore_case_chain)2216 int AtomInfoMatch(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2,
2217     bool ignore_case, bool ignore_case_chain)
2218 {
2219   if(at1->resv == at2->resv)
2220   if(WordMatchExact(G, at1->chain, at2->chain, ignore_case_chain))
2221     if(WordMatchExact(G, at1->name, at2->name, ignore_case))
2222       if(WordMatchExact(G, at1->inscode, at2->inscode, ignore_case))
2223         if(WordMatchExact(G, at1->resn, at2->resn, ignore_case))
2224           if(WordMatchExact(G, at1->segi, at2->segi, ignore_case_chain))
2225             if(WordMatchExact(G, at1->alt[0], at2->alt[0], ignore_case))
2226               return 1;
2227   return 0;
2228 }
2229 
AtomInfoIsFreeCation(PyMOLGlobals * G,const AtomInfoType * I)2230 int AtomInfoIsFreeCation(PyMOLGlobals * G, const AtomInfoType * I)
2231 {
2232   switch (I->protons) {
2233   case cAN_Na:
2234   case cAN_K:
2235   case cAN_Ca:
2236   case cAN_Mg:
2237   case cAN_Mn:
2238   case cAN_Sr:
2239     return true;
2240   }
2241   return false;
2242 }
2243 
AtomInfoGetExpectedValence(PyMOLGlobals * G,const AtomInfoType * I)2244 int AtomInfoGetExpectedValence(PyMOLGlobals * G, const AtomInfoType * I)
2245 {
2246   int result = -1;              /* negative indicates minimum expected valence (abs)
2247                                    but it could be higher  */
2248 
2249   if(I->formalCharge == 0) {
2250     switch (I->protons) {
2251     case cAN_H:
2252       result = 1;
2253       break;
2254     case cAN_C:
2255       result = 4;
2256       break;
2257     case cAN_N:
2258       result = 3;
2259       break;
2260     case cAN_O:
2261       result = 2;
2262       break;
2263     case cAN_F:
2264       result = 1;
2265       break;
2266     case cAN_Cl:
2267       result = 1;
2268       break;
2269     case cAN_Br:
2270       result = 1;
2271       break;
2272     case cAN_I:
2273       result = 1;
2274       break;
2275     case cAN_Na:
2276       result = 1;
2277       break;
2278     case cAN_Ca:
2279       result = 1;
2280       break;
2281     case cAN_K:
2282       result = 1;
2283       break;
2284     case cAN_Mg:
2285       result = 2;
2286       break;
2287     case cAN_Zn:
2288       result = -1;
2289       break;
2290     case cAN_S:
2291       result = -2;
2292       break;
2293     case cAN_P:
2294       result = -3;
2295       break;
2296     }
2297   } else if(I->formalCharge == 1) {
2298     switch (I->protons) {
2299     case cAN_N:
2300       result = 4;
2301       break;
2302     case cAN_O:
2303       result = 3;
2304       break;
2305     case cAN_Na:
2306       result = 0;
2307       break;
2308     case cAN_Ca:
2309       result = 0;
2310       break;
2311     case cAN_K:
2312       result = 0;
2313       break;
2314     case cAN_Mg:
2315       result = 1;
2316       break;
2317     case cAN_Zn:
2318       result = -1;
2319       break;
2320     case cAN_S:
2321       result = -2;
2322       break;
2323     case cAN_P:
2324       result = -3;
2325       break;
2326     }
2327   } else if(I->formalCharge == -1) {
2328     switch (I->protons) {
2329     case cAN_N:
2330       result = 2;
2331       break;
2332     case cAN_O:
2333       result = 1;
2334       break;
2335     case cAN_C:
2336       result = 3;
2337       break;
2338     case cAN_Zn:
2339       result = -1;
2340       break;
2341     case cAN_S:
2342       result = -2;
2343       break;
2344     case cAN_P:
2345       result = -3;
2346       break;
2347     }
2348   } else if(I->formalCharge == 2) {
2349     switch (I->protons) {
2350     case cAN_Mg:
2351       result = 0;
2352       break;
2353     case cAN_Zn:
2354       result = -1;
2355       break;
2356     case cAN_S:
2357       result = -2;
2358       break;
2359     case cAN_P:
2360       result = -3;
2361       break;
2362     }
2363   }
2364   return (result);
2365 }
2366 
2367 /**
2368  * Get number of protons for element symbol
2369  */
get_protons(const char * symbol)2370 static int get_protons(const char * symbol)
2371 {
2372   char titleized[4];
2373   static std::map<pymol::zstring_view, int> lookup;
2374 
2375   if (lookup.empty()) {
2376     for (int i = 0; i < ElementTableSize; i++)
2377       lookup[ElementTable[i].symbol] = i;
2378 
2379     lookup["Q"] = cAN_H;
2380     lookup["D"] = cAN_H;
2381   }
2382 
2383   // check second letter for lower case
2384   if (symbol[0] && isupper(symbol[1]) && strcmp(symbol, "LP") != 0) {
2385     UtilNCopy(titleized, symbol, 4);
2386     titleized[1] = tolower(symbol[1]);
2387     symbol = titleized;
2388   }
2389 
2390   // find in lookup dictionary
2391   auto it = lookup.find(symbol);
2392 
2393   if (it != lookup.end()) {
2394     return it->second;
2395   } else {
2396     // allow wacky names for C and H
2397     switch(symbol[0]) {
2398     case 'C':
2399       return cAN_C;
2400     case 'H':
2401       return cAN_H;
2402     }
2403   }
2404 
2405   return -1;
2406 }
2407 
2408 /**
2409  * Assign "protons" from "elem" or "name" property
2410  */
set_protons(PyMOLGlobals * G,AtomInfoType * I)2411 static void set_protons(PyMOLGlobals * G, AtomInfoType * I)
2412 {
2413   int protons = get_protons(I->elem);
2414 
2415   if (protons < 0) {
2416     // try again with atom name, skip numbers
2417     const char * name = LexStr(G, I->name);
2418     while((*name >= '0') && (*name <= '9') && (*(name + 1)))
2419       name++;
2420 
2421     protons = get_protons(name);
2422   }
2423 
2424   I->protons = protons;
2425 }
2426 
2427 /**
2428  * Assign (based on name, elem, protons):
2429  *  - elem      (if empty string)
2430  *  - protons   (if < 1)
2431  *  - vdw       (if 0.0)
2432  *  - priority
2433  */
AtomInfoAssignParameters(PyMOLGlobals * G,AtomInfoType * I)2434 void AtomInfoAssignParameters(PyMOLGlobals * G, AtomInfoType * I)
2435 {
2436   const char *n = NULL;
2437   char *e = NULL;
2438   int pri;
2439 
2440   e = I->elem;
2441 
2442   // elem from protons (except for LP, assume protons=0 if uninitialized)
2443   if(!*e && I->protons > 0) {
2444     atomicnumber2elem(e, I->protons);
2445   }
2446 
2447   // elem from name
2448   if(!*e) {                     /* try to guess atomic type from name */
2449     n = LexStr(G, I->name);
2450     while(((*n) >= '0') && ((*n) <= '9') && (*(n + 1)))
2451       n++;
2452     strncpy(e, n, cElemNameLen);
2453     switch (*e) {
2454     case '\0':
2455       break;
2456     case 'C':
2457       if(*(e + 1) == 'A') {
2458         if(!WordMatchExact(G, G->lex_const.CA, I->resn, true)
2459            && (!WordMatchExact(G, "CA+", LexStr(G, I->resn), true)))
2460           /* CA intpreted as carbon, not calcium */
2461           *(e + 1) = 0;
2462       } else if(!((*(e + 1) == 'a') ||  /* CA intpreted as carbon, not calcium */
2463                   (*(e + 1) == 'l') || (*(e + 1) == 'L') ||
2464                   (*(e + 1) == 'u') || (*(e + 1) == 'U') ||
2465                   (*(e + 1) == 'o') || (*(e + 1) == 'O') ||
2466                   (*(e + 1) == 's') || (*(e + 1) == 'S') ||
2467                   (*(e + 1) == 'r') || (*(e + 1) == 'R')
2468                 ))
2469         *(e + 1) = 0;
2470       break;
2471     case 'H':
2472       if(!((*(e + 1) == 'e')
2473          ))
2474         *(e + 1) = 0;
2475       break;
2476     case 'D':                  /* take deuterium to hydrogen */
2477       *(e + 1) = 0;
2478       break;
2479     case 'N':
2480       if(!((*(e + 1) == 'i') || (*(e + 1) == 'I') ||
2481            (*(e + 1) == 'a') || (*(e + 1) == 'A') ||
2482            (*(e + 1) == 'b') || (*(e + 1) == 'B')
2483          ))
2484         *(e + 1) = 0;
2485       break;
2486     case 'S':
2487       if(!((*(e + 1) == 'e') || (*(e + 1) == 'E') ||
2488            (*(e + 1) == 'r') || (*(e + 1) == 'R') ||
2489            (*(e + 1) == 'c') || (*(e + 1) == 'C') ||
2490            (*(e + 1) == 'b') || (*(e + 1) == 'B')
2491          ))
2492         *(e + 1) = 0;
2493 
2494       break;
2495     case 'O':
2496       if(!((*(e + 1) == 's')))
2497         *(e + 1) = 0;
2498       break;
2499     case 'Q':
2500       *(e + 1) = 0;
2501       break;
2502     case 'p':
2503       if (p_strstartswith(n, "pseudo")) {
2504         strcpy(e, "PS");
2505       }
2506     }
2507     if(*(e + 1) &&
2508         (/* e != "LP" */ e[1] != 'P' || e[0] != 'L') &&
2509         (/* e != "PS" */ e[1] != 'S' || e[0] != 'P'))
2510       *(e + 1) = tolower(*(e + 1));
2511   }
2512 
2513   // priority
2514   n = LexStr(G, I->name);
2515   while((*n >= '0') && (*n <= '9') && (*(n + 1)))
2516     n++;
2517   if(toupper(*n) != I->elem[0]) {
2518     pri = 1000;                 /* unconventional atom name -- make no assignments */
2519   } else if(SettingGetGlobal_b(G, cSetting_pdb_standard_order)) {
2520     switch (*n) {
2521 
2522     case 'N':
2523     case 'C':
2524     case 'O':
2525     case 'S':
2526       switch (*(n + 1)) {
2527       case 0:
2528         switch (*n) {
2529         case 'N':
2530           pri = 1;
2531           break;
2532         case 'C':
2533           pri = 3;
2534           break;
2535         case 'O':
2536           pri = 4;
2537           break;
2538         default:
2539           pri = 1000;
2540           break;
2541         }
2542         break;
2543       case 'A':
2544         switch (*n) {
2545         case 'C':
2546           pri = 2;
2547           break;
2548         default:
2549           pri = 5;
2550           break;                /* generic alpha atom */
2551         }
2552         break;
2553       case 'B':
2554         pri = 6;
2555         break;                  /* generic beta atom, etc. */
2556       case 'G':
2557         pri = 7;
2558         break;
2559       case 'D':
2560         pri = 8;
2561         break;
2562       case 'E':
2563         pri = 9;
2564         break;
2565       case 'Z':
2566         pri = 10;
2567         break;
2568       case 'H':
2569         pri = 11;
2570         break;
2571       case 'I':
2572         pri = 12;
2573         break;
2574       case 'J':
2575         pri = 13;
2576         break;
2577       case 'K':
2578         pri = 14;
2579         break;
2580       case 'L':
2581         pri = 15;
2582         break;
2583       case 'M':
2584         pri = 16;
2585         break;
2586       case 'N':
2587         pri = 17;
2588         break;
2589       case 'X':
2590         switch (*(n + 2)) {
2591         case 'T':
2592           pri = 999;
2593           break;
2594         default:
2595         case 0:
2596           pri = 16;
2597           break;
2598         }
2599       case '0':
2600       case '1':
2601       case '2':
2602       case '3':
2603       case '4':
2604       case '5':
2605       case '6':
2606       case '7':
2607       case '8':
2608       case '9':
2609         pri = 0;
2610         n++;
2611         while(*n) {
2612           if(*n == 'P') {
2613             pri -= 200;
2614             break;
2615           } else if((*n == '*') || (*n == '\'')) {
2616             pri = (-100) - pri;
2617             break;
2618           } else if((*n < '0') || (*n > '9'))
2619             break;
2620           pri *= 10;
2621           pri += (*n - '0');
2622           n++;
2623         }
2624         pri += 300;
2625         break;
2626       default:
2627         pri = 500;
2628         break;
2629       }
2630       break;
2631     case 'P':                  /* this will place the phosphate before CNO numbered atoms */
2632       pri = 20;
2633       break;
2634     case 'D':
2635     case 'H':
2636       switch (*(n + 1)) {
2637       case 0:
2638         pri = 1001;
2639         break;
2640       case 'A':
2641       case 'B':
2642         pri = 1003;
2643         break;
2644       case 'G':
2645         pri = 1004;
2646         break;
2647       case 'D':
2648         pri = 1005;
2649         break;
2650       case 'E':
2651         pri = 1006;
2652         break;
2653       case 'Z':
2654         pri = 1007;
2655         break;
2656       case 'H':
2657         pri = 1008;
2658         break;
2659       case 'I':
2660         pri = 1009;
2661         break;
2662       case 'J':
2663         pri = 1010;
2664         break;
2665       case 'K':
2666         pri = 1011;
2667         break;
2668       case 'L':
2669         pri = 1012;
2670         break;
2671       case 'M':
2672         pri = 1013;
2673         break;
2674       case 'N':
2675         pri = 1002;
2676         break;
2677       case 'X':
2678         pri = 1999;
2679         break;
2680       case '0':
2681       case '1':
2682       case '2':
2683       case '3':
2684       case '4':
2685       case '5':
2686       case '6':
2687       case '7':
2688       case '8':
2689       case '9':
2690         pri = 1020;
2691         n++;
2692         while(*n) {
2693           pri *= 10;
2694           pri += (*n - '0');
2695           n++;
2696         }
2697         pri += 25;
2698         break;
2699 
2700       default:
2701         pri = 1500;
2702         break;
2703       }
2704       break;
2705     default:
2706       pri = 1000;
2707       break;
2708     }
2709   } else {
2710     switch (*n) {
2711     case 'N':
2712     case 'C':
2713     case 'O':
2714     case 'S':
2715       switch (*(n + 1)) {
2716       case 0:
2717         switch (*n) {
2718         case 'N':
2719           pri = 1;
2720           break;
2721         case 'C':
2722           pri = 997;
2723           break;
2724         case 'O':
2725           pri = 998;
2726           break;
2727         default:
2728           pri = 1000;
2729           break;
2730         }
2731         break;
2732       case 'A':
2733         pri = 3;
2734         break;                  /* generic alpha */
2735       case 'B':
2736         pri = 4;
2737         break;
2738       case 'G':
2739         pri = 5;
2740         break;
2741       case 'D':
2742         pri = 6;
2743         break;
2744       case 'E':
2745         pri = 7;
2746         break;
2747       case 'Z':
2748         pri = 8;
2749         break;
2750       case 'H':
2751         pri = 9;
2752         break;
2753       case 'I':
2754         pri = 10;
2755         break;
2756       case 'J':
2757         pri = 11;
2758         break;
2759       case 'K':
2760         pri = 12;
2761         break;
2762       case 'L':
2763         pri = 13;
2764         break;
2765       case 'M':
2766         pri = 14;
2767         break;
2768       case 'N':
2769         pri = 15;
2770         break;
2771       case 'X':
2772         switch (*(n + 2)) {
2773         case 'T':
2774           pri = 999;
2775           break;
2776         default:
2777         case 0:
2778           pri = 16;
2779           break;
2780         }
2781       case '0':
2782       case '1':
2783       case '2':
2784       case '3':
2785       case '4':
2786       case '5':
2787       case '6':
2788       case '7':
2789       case '8':
2790       case '9':
2791         pri = 0;
2792         n++;
2793         while(*n) {
2794           pri *= 10;
2795           pri += (*n - '0');
2796           n++;
2797         }
2798         pri += 25;
2799         break;
2800       default:
2801         pri = 500;
2802         break;
2803       }
2804       break;
2805     default:
2806       pri = 1000;
2807       break;
2808     }
2809   }
2810 
2811   I->priority = pri;
2812 
2813   // protons from elem or name
2814   if (I->protons < 1)
2815     set_protons(G, I);
2816 
2817   // vdw from protons
2818   if(I->vdw == 0.0) {
2819     if (I->protons > -1 && I->protons < ElementTableSize) {
2820       I->vdw = ElementTable[I->protons].vdw;
2821     } else {
2822       I->vdw = 1.80F;
2823     }
2824   }
2825 }
2826 
BondTypeCompare(PyMOLGlobals * G,const BondType * bt1,const BondType * bt2)2827 int BondTypeCompare(PyMOLGlobals * G, const BondType * bt1, const BondType * bt2){
2828   return (bt1->index[0] != bt2->index[0] ||
2829 	  bt1->index[1] != bt2->index[1] ||
2830 	  bt1->order != bt2->order ||
2831 	  bt1->id != bt2->id ||
2832 	  bt1->unique_id != bt2->unique_id ||
2833 	  bt1->stereo != bt2->stereo ||
2834 	  bt1->has_setting != bt2->has_setting);
2835 }
2836 
2837 /**
2838  * Get the element symbol. E.g. "He" for Helium.
2839  * @param[out] dst output buffer of length cElemNameLen
2840  * @param protons atomic number, e.g. 2 for Helium
2841  */
atomicnumber2elem(char * dst,int protons)2842 void atomicnumber2elem(char * dst, int protons) {
2843   if (protons > -1 && protons < ElementTableSize)
2844     strncpy(dst, ElementTable[protons].symbol, cElemNameLen);
2845 }
2846 
2847 /**
2848  * Get a string representation of the stereo configuration. Either
2849  * R/S chirality, if set, or the SDF odd/even parity, if set.
2850  */
AtomInfoGetStereoAsStr(const AtomInfoType * ai)2851 const char * AtomInfoGetStereoAsStr(const AtomInfoType * ai) {
2852   switch (ai->mmstereo) {
2853     case 1 /* MMSTEREO_CHIRALITY_R */: return "R";
2854     case 2 /* MMSTEREO_CHIRALITY_S */: return "S";
2855   }
2856 
2857   switch (ai->stereo) {
2858     case SDF_CHIRALITY_ODD:  return "odd";
2859     case SDF_CHIRALITY_EVEN: return "even";
2860   }
2861 
2862   if (ai->mmstereo || ai->stereo) {
2863     return "?";
2864   }
2865 
2866   return "";
2867 }
2868 
2869 /**
2870  * Set stereochemistry. Valid are: R, S, N[one], E[ven], O[dd]
2871  */
AtomInfoSetStereo(AtomInfoType * ai,const char * stereo)2872 void AtomInfoSetStereo(AtomInfoType * ai, const char * stereo) {
2873   switch (toupper(stereo[0])) {
2874     case 'R':  ai->mmstereo = 1; ai->stereo = 0; break; // MMSTEREO_CHIRALITY_R
2875     case 'S':  ai->mmstereo = 2; ai->stereo = 0; break; // MMSTEREO_CHIRALITY_S
2876     case 'E':  ai->mmstereo = 0; ai->stereo = SDF_CHIRALITY_EVEN;       break;
2877     case 'O':  ai->mmstereo = 0; ai->stereo = SDF_CHIRALITY_ODD;        break;
2878     case 'A': // ANS (s), ANR (r) pseudochirality
2879     case 'N': case 0: ai->mmstereo = ai->stereo = 0;    break;
2880     default:   ai->mmstereo = ai->stereo = 3;           break;
2881   }
2882 }
2883 
2884 
2885 /**
2886  * Get column aligned (left space padded) PDB residue name
2887  *
2888  * @param[out] resn output buffer
2889  */
AtomInfoGetAlignedPDBResidueName(PyMOLGlobals * G,const AtomInfoType * ai,ResName & resn)2890 void AtomInfoGetAlignedPDBResidueName(PyMOLGlobals * G,
2891     const AtomInfoType * ai,
2892     ResName & resn)
2893 {
2894   sprintf(resn, "%3.4s", LexStr(G, ai->resn));
2895   if(SettingGetGlobal_b(G, cSetting_pdb_truncate_residue_name)) {
2896     resn[3] = 0;                /* enforce 3-letter residue name in PDB files */
2897   }
2898 }
2899 
2900 
2901 /**
2902  * Get column aligned (left space padded) PDB atom name
2903  *
2904  * @param resn space padded residue name
2905  * @param[out] name output buffer
2906  */
AtomInfoGetAlignedPDBAtomName(PyMOLGlobals * G,const AtomInfoType * ai,const ResName & resn,AtomName & name)2907 void AtomInfoGetAlignedPDBAtomName(PyMOLGlobals * G,
2908     const AtomInfoType * ai,
2909     const ResName & resn,
2910     AtomName & name)
2911 {
2912   int literal = SettingGetGlobal_b(G, cSetting_pdb_literal_names);
2913   int reformat = SettingGetGlobal_i(G, cSetting_pdb_reformat_names_mode);
2914 
2915   // default is "literal=0" and "reformat=0" (no reformatting)
2916 
2917   const char * ai_name = LexStr(G, ai->name);
2918   auto ai_name_len = strlen(ai_name);
2919   bool start_column_1 = false;
2920 
2921   UtilNCopy(name, ai_name, 5);
2922 
2923   if(!ai->name) {
2924     if(!ai->elem[1])
2925       sprintf(name, " %s", ai->elem);
2926     else
2927       sprintf(name, "%s", ai->elem);
2928   } else if(!literal) {
2929     if(ai_name_len < 4) {  /* atom name less than length 4 */
2930       if(!isdigit(name[0])) {     /* doesn't start with a number */
2931         if((toupper(ai->elem[0]) == toupper(name[0])) && ((!ai->elem[1]) || /* symbol len = 1 */
2932               (toupper(ai->elem[1]) == toupper(name[1])))) {        /* matched len 2 */
2933           /* starts with corrent atomic symbol, so */
2934           if(!ai->elem[1]) { /* symbol len = 1 */
2935             switch (reformat) {
2936             case 1:            /* pdb with internal pdb */
2937             case 3:            /* pdb with internal iupac */
2938               if((ai->elem[0] == 'H') && ai_name_len > 2) {
2939                 AtomInfoGetPDB3LetHydroName(G, resn, ai_name, name);
2940                 break;
2941               }
2942             default:           /* otherwise, start in column 1 */
2943               start_column_1 = true;
2944               break;
2945             }
2946           }
2947         } else {                /* name doesn't start with atomic symbol */
2948           /* then just place it in column 1 as usual */
2949           start_column_1 = true;
2950         }
2951       } else {                  /* name starts with a number */
2952         switch (reformat) {
2953         case 2:                /* make Amber compliant */
2954           if((ai->elem[0] == name[1]) &&
2955              ((!ai->elem[1]) || (toupper(ai->elem[1]) == toupper(name[2])))) {
2956             /* rotate the name to place atom symbol in column 0 to comply with Amber PDB format */
2957             name[3] = name[0];
2958             name[0] = ' ';
2959           }
2960           break;
2961         }
2962       }                         /* just stick it in column 0 and hope for the best */
2963     } else {                    /* if name is length 4 */
2964       if((ai->elem[0] == name[0]) && ((!ai->elem[1]) ||     /* symbol len = 1 */
2965                                           (toupper(ai->elem[1]) == toupper(name[1])))) {    /* matched len 2 */
2966         /* name starts with the atomic symbol */
2967         if((!ai->elem[1]) && (ai->elem[0])) {   /* but if element is one letter... */
2968           switch (reformat) {
2969           case 1:              /* retaining PDB compliance throughout, or */
2970           case 3:              /* saving as PDB compliant, but use IUPAC within PyMOL */
2971             if(isdigit(name[3])) {  /* and last character is a number */
2972               /* rotate the name to place atom symbol in column 1 to comply with PDB format */
2973               name[0] = ai_name[3];
2974               name[1] = ai_name[0];
2975               name[2] = ai_name[1];
2976               name[3] = ai_name[2];
2977               name[4] = 0;
2978             }
2979             break;
2980           }
2981         }
2982       } else {                  /* name does not start with the symbol... */
2983         if(reformat == 2) {     /* AMBER compliance mode */
2984           if(isdigit(name[0])) {
2985             if((ai->elem[0] == name[1]) &&
2986                ((!(ai->elem[1])) || (toupper(ai->elem[1]) == toupper(name[2])))) {
2987               /* rotate the name to place atom symbol in column 0 to comply with Amber PDB format */
2988               name[0] = ai_name[1];
2989               name[1] = ai_name[2];
2990               name[2] = ai_name[3];
2991               name[3] = ai_name[0];
2992               name[4] = 0;
2993             }
2994           }
2995         }
2996       }
2997     }
2998   } else {                      /* LITERAL mode: preserve what was in the original PDB as best PyMOL can
2999                                    this should enable people to open and save amber pdb files without issues */
3000     if (ai_name_len < 4 && !(ai->elem[1] && /* elem len = 2 */
3001           toupper(ai->elem[0]) == toupper(name[0]) &&
3002           toupper(ai->elem[1]) == toupper(name[1]))) {
3003       start_column_1 = true;
3004     }
3005   }
3006 
3007   if (start_column_1) {
3008     name[0] = ' ';
3009     UtilNCopy(name + 1, ai_name, 4);
3010   }
3011 
3012   name[4] = 0;
3013 }
3014