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