layer2/AtomInfo.cpp (2,624 lines of code) (raw):

/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include <utility> #include <algorithm> #include"os_python.h" #include"os_predef.h" #include"os_std.h" #include"AtomInfo.h" #include"Word.h" #include"MemoryDebug.h" #include"Err.h" #include"Feedback.h" #include"Util.h" #include"Util2.h" #include"Color.h" #include"PConv.h" #include"Ortho.h" #include"OVOneToAny.h" #include"OVContext.h" #include"PyMOLObject.h" #include"Setting.h" #include"Executive.h" #include "Lex.h" #include <map> struct _CAtomInfo { int NColor, CColor, DColor, HColor, OColor, SColor; int BrColor, ClColor, FColor, IColor; int PColor, MgColor, MnColor, NaColor, KColor, CaColor; int CuColor, FeColor, ZnColor; int SeColor; int DefaultColor; int NextUniqueID; OVOneToAny *ActiveIDs; }; void AtomInfoCleanAtomName(char *name) { char *p = name, *q = name; while(*p) { if((((*p) >= '0') && ((*p) <= '9')) || (((*p) >= 'a') && ((*p) <= 'z')) || (((*p) >= 'A') && ((*p) <= 'Z')) || ((*p) == '.') || ((*p) == '_') || ((*p) == '+') || ((*p) == '\'') || ((*p) == '*')) { *q++ = *p; } p++; } *q = 0; } #ifndef _PYMOL_NOPY int AtomInfoSetSettingFromPyObject(PyMOLGlobals * G, AtomInfoType *ai, int setting_id, PyObject * val){ if (val == Py_None) val = NULL; if (!val) { if (!ai->has_setting) return true; } AtomInfoCheckUniqueID(G, ai); ai->has_setting = true; return SettingUniqueSetPyObject(G, ai->unique_id, setting_id, val); } #endif PyObject *SettingGetIfDefinedPyObject(PyMOLGlobals * G, AtomInfoType * ai, int setting_id) { if(ai->has_setting) { return SettingUniqueGetPyObject(G, ai->unique_id, setting_id); } return NULL; } static int AtomInfoPrimeUniqueIDs(PyMOLGlobals * G) { CAtomInfo *I = G->AtomInfo; if(!I->ActiveIDs) { OVContext *C = G->Context; I->ActiveIDs = OVOneToAny_New(C->heap); } return (I->ActiveIDs != NULL); } int AtomInfoReserveUniqueID(PyMOLGlobals * G, int unique_id) { CAtomInfo *I = G->AtomInfo; if(!I->ActiveIDs) AtomInfoPrimeUniqueIDs(G); if(I->ActiveIDs) return (OVreturn_IS_OK(OVOneToAny_SetKey(I->ActiveIDs, unique_id, 1))); return 0; } int AtomInfoIsUniqueIDActive(PyMOLGlobals * G, int unique_id) { CAtomInfo *I = G->AtomInfo; if(!I->ActiveIDs) return 0; else return (OVreturn_IS_OK(OVOneToAny_GetKey(I->ActiveIDs, unique_id))); return 0; } int AtomInfoGetNewUniqueID(PyMOLGlobals * G) { CAtomInfo *I = G->AtomInfo; int result = 0; AtomInfoPrimeUniqueIDs(G); if(I->ActiveIDs) { while(1) { result = I->NextUniqueID++; if(result) { /* skip zero */ if(OVOneToAny_GetKey(I->ActiveIDs, result).status == OVstatus_NOT_FOUND) { if(OVreturn_IS_ERROR(OVOneToAny_SetKey(I->ActiveIDs, result, 1))) result = 0; break; } } } } ExecutiveUniqueIDAtomDictInvalidate(G); return result; } int AtomInfoCheckUniqueID(PyMOLGlobals * G, AtomInfoType * ai) { if(!ai->unique_id) ai->unique_id = AtomInfoGetNewUniqueID(G); return ai->unique_id; } int AtomInfoCheckUniqueBondID(PyMOLGlobals * G, BondType * bi) { if(!bi->unique_id) bi->unique_id = AtomInfoGetNewUniqueID(G); return bi->unique_id; } void BondTypeInit(BondType *bt){ #ifdef _PYMOL_IP_EXTRAS bt->oldid = -1; #endif bt->unique_id = 0; bt->has_setting = 0; } /* * Set atom indices and bond order, and invalidate all other fields. */ void BondTypeInit2(BondType *bond, int i1, int i2, int order) { BondTypeInit(bond); bond->index[0] = i1; bond->index[1] = i2; bond->order = order; bond->id = -1; } int AtomInfoInit(PyMOLGlobals * G) { CAtomInfo *I = NULL; if((I = (G->AtomInfo = Calloc(CAtomInfo, 1)))) { AtomInfoPrimeColors(G); I->NextUniqueID = 1; return 1; } else return 0; } void AtomInfoFree(PyMOLGlobals * G) { CAtomInfo *I = G->AtomInfo; OVOneToAny_DEL_AUTO_NULL(I->ActiveIDs); FreeP(G->AtomInfo); } /*========================================================================*/ void AtomInfoGetPDB3LetHydroName(PyMOLGlobals * G, const char *resn, const char *iname, char *oname) { oname[0] = ' '; strcpy(oname + 1, iname); switch (resn[0]) { case 'A': switch (resn[1]) { case 'L': if(resn[2] == 'A') if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; case 'R': if(resn[2] == 'G') if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; case 'S': switch (resn[2]) { case 'P': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; case 'N': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'C': switch (resn[1]) { case 'Y': switch (resn[2]) { case 'S': case 'X': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'G': switch (resn[1]) { case 'L': switch (resn[2]) { case 'N': if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; case 'U': if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; case 'Y': if((iname[0] == 'H') && (iname[1] == 'A') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } } break; case 'H': switch (resn[1]) { case 'I': switch (resn[2]) { case 'S': case 'D': case 'E': case 'P': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; case 'O': switch (resn[2]) { case 'H': break; } break; case '2': switch (resn[2]) { case 'O': break; } break; } case 'I': switch (resn[1]) { case 'L': switch (resn[2]) { case 'E': break; } } break; case 'L': switch (resn[1]) { case 'E': switch (resn[2]) { case 'U': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; case 'Y': switch (resn[2]) { case 'S': if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D') || (iname[1] == 'E') || (iname[1] == 'Z')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'M': switch (resn[1]) { case 'E': switch (resn[2]) { case 'T': if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'E')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } } break; case 'P': switch (resn[1]) { case 'H': switch (resn[2]) { case 'E': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; case 'R': switch (resn[2]) { case 'O': if((iname[0] == 'H') && ((iname[1] == 'B') || (iname[1] == 'G') || (iname[1] == 'D')) && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'S': switch (resn[1]) { case 'E': switch (resn[2]) { case 'R': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'T': switch (resn[1]) { case 'H': switch (resn[2]) { case 'R': break; } break; case 'I': switch (resn[2]) { case 'P': break; } break; case 'R': switch (resn[2]) { case 'P': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; case 'Y': switch (resn[2]) { case 'R': if((iname[0] == 'H') && (iname[1] == 'B') && ((iname[2] >= '0') && (iname[2] <= '9'))) { oname[0] = iname[2]; oname[1] = iname[0]; oname[2] = iname[1]; oname[3] = 0; } break; } break; } break; case 'V': switch (resn[1]) { case 'A': switch (resn[2]) { case 'L': break; } break; } break; case 'W': switch (resn[1]) { case 'A': switch (resn[2]) { case 'T': break; } break; } break; } } int AtomInfoKnownWaterResName(PyMOLGlobals * G, const char *resn) { switch (resn[0]) { case 'H': switch (resn[1]) { case 'O': switch (resn[2]) { case 'H': return true; break; } break; case '2': switch (resn[2]) { case 'O': return true; break; } break; } case 'D': switch (resn[1]) { case 'O': switch (resn[2]) { case 'D': return true; break; } break; } break; case 'T': switch (resn[1]) { case 'I': case '3': // T3P case '4': // T4P switch (resn[2]) { case 'P': return true; break; } break; } break; case 'W': switch (resn[1]) { case 'A': switch (resn[2]) { case 'T': return true; break; } break; } break; case 'S': switch (resn[1]) { case 'O': switch (resn[2]) { case 'L': return true; break; } break; case 'P': switch (resn[2]) { case 'C': return true; break; } break; } break; } return false; } int AtomInfoKnownPolymerResName(const char *resn) { return AtomInfoKnownProteinResName(resn) || AtomInfoKnownNucleicResName(resn); } int AtomInfoKnownNucleicResName(const char *resn) { if (resn[0] == 'D') { // Deoxy ribonucleotide ++resn; } switch (resn[0]) { case 'A': case 'C': case 'G': case 'I': case 'T': case 'U': if (!resn[1]) return true; } return false; } int AtomInfoKnownProteinResName(const char *resn) { switch (resn[0]) { case 'A': switch (resn[1]) { case 'L': switch (resn[2]) { case 'A': /* ALA */ return true; break; } break; case 'R': switch (resn[2]) { case 'G': /* ARG */ return true; break; } break; case 'S': switch (resn[2]) { case 'P': /* ASP */ return true; break; case 'N': /* ASN */ return true; break; } break; } break; case 'C': switch (resn[1]) { case 'Y': switch (resn[2]) { case 'S': /* CYS */ case 'X': /* CYX */ return true; break; } break; } break; case 'G': switch (resn[1]) { case 'L': switch (resn[2]) { case 'N': /* GLN */ return true; break; case 'U': /* GLU */ return true; break; case 'Y': /* GLY */ return true; break; } } break; case 'H': switch (resn[1]) { case 'I': switch (resn[2]) { case 'S': /* HIS */ case 'D': /* HID */ case 'E': /* HIE */ case 'P': /* HIP */ return true; break; } break; } break; case 'I': switch (resn[1]) { case 'L': switch (resn[2]) { case 'E': /* ILE */ return true; break; } } break; case 'L': switch (resn[1]) { case 'E': switch (resn[2]) { case 'U': /* LEU */ return true; break; } break; case 'Y': switch (resn[2]) { case 'S': return true; break; /* LYS */ } break; } break; case 'M': switch (resn[1]) { case 'E': switch (resn[2]) { case 'T': /* MET */ return true; break; } case 'S': switch (resn[2]) { case 'E': /* MSE */ return true; break; } } break; case 'P': switch (resn[1]) { case 'H': switch (resn[2]) { case 'E': /* PHE */ return true; break; } break; case 'R': switch (resn[2]) { case 'O': /* PRO */ return true; break; } break; case 'T': switch (resn[2]) { case 'R': /* PTR */ return true; break; } break; } break; case 'S': switch (resn[1]) { case 'E': switch (resn[2]) { case 'R': /* SER */ return true; break; } break; } break; case 'T': switch (resn[1]) { case 'H': switch (resn[2]) { case 'R': /* THR */ return true; break; } break; case 'R': switch (resn[2]) { case 'P': /* TRP */ return true; break; } break; case 'Y': switch (resn[2]) { case 'R': /* TYR */ return true; break; } break; } break; case 'V': switch (resn[1]) { case 'A': switch (resn[2]) { case 'L': /* VAL */ return true; break; } break; } break; } return false; } /*========================================================================*/ static char getInscodeUpper(const AtomInfoType * ai) { char c = ai->inscode; if ('a' <= c && c <= 'z') return c - ('a' - 'A'); // 97 - 65 return c; } // return false if buffer too small bool AtomResiFromResv(char *resi, size_t size, int resv, char inscode) { if (inscode > ' ') return snprintf(resi, size, "%d%c", resv, inscode) < size; return snprintf(resi, size, "%d", resv) < size; } /*========================================================================*/ PyObject *AtomInfoAsPyList(PyMOLGlobals * G, const AtomInfoType * I) { PyObject *result = NULL; result = PyList_New(48); int version = SettingGetGlobal_f(G, cSetting_pse_export_version) * 1000; char resi[8]; // at some point, change this to !version || ... if (version >= 1810) { resi[0] = I->inscode; resi[1] = '\0'; } else { AtomResiFromResv(resi, sizeof(resi), I); } PyList_SetItem(result, 0, PyInt_FromLong(I->resv)); PyList_SetItem(result, 1, PyString_FromString(LexStr(G, I->chain))); PyList_SetItem(result, 2, PyString_FromString(I->alt)); PyList_SetItem(result, 3, PyString_FromString(resi)); PyList_SetItem(result, 4, PyString_FromString(LexStr(G, I->segi))); PyList_SetItem(result, 5, PyString_FromString(LexStr(G, I->resn))); PyList_SetItem(result, 6, PyString_FromString(LexStr(G, I->name))); PyList_SetItem(result, 7, PyString_FromString(I->elem)); PyList_SetItem(result, 8, PyString_FromString(LexStr(G, I->textType))); PyList_SetItem(result, 9, PyString_FromString(LexStr(G, I->label))); PyList_SetItem(result, 10, PyString_FromString(I->ssType)); PyList_SetItem(result, 11, PyInt_FromLong((int) I->isHydrogen())); // TODO redundant PyList_SetItem(result, 12, PyInt_FromLong(I->customType)); PyList_SetItem(result, 13, PyInt_FromLong(I->priority)); PyList_SetItem(result, 14, PyFloat_FromDouble(I->b)); PyList_SetItem(result, 15, PyFloat_FromDouble(I->q)); PyList_SetItem(result, 16, PyFloat_FromDouble(I->vdw)); PyList_SetItem(result, 17, PyFloat_FromDouble(I->partialCharge)); PyList_SetItem(result, 18, PyInt_FromLong(I->formalCharge)); PyList_SetItem(result, 19, PyInt_FromLong((int) I->hetatm)); PyList_SetItem(result, 20, PyInt_FromLong((int) I->visRep)); PyList_SetItem(result, 21, PyInt_FromLong(I->color)); PyList_SetItem(result, 22, PyInt_FromLong(I->id)); PyList_SetItem(result, 23, PyInt_FromLong((char) I->cartoon)); PyList_SetItem(result, 24, PyInt_FromLong(I->flags)); PyList_SetItem(result, 25, PyInt_FromLong((int) I->bonded)); PyList_SetItem(result, 26, PyInt_FromLong((int) I->chemFlag)); PyList_SetItem(result, 27, PyInt_FromLong((int) I->geom)); PyList_SetItem(result, 28, PyInt_FromLong((int) I->valence)); PyList_SetItem(result, 29, PyInt_FromLong((int) I->masked)); PyList_SetItem(result, 30, PyInt_FromLong((int) I->protekted)); PyList_SetItem(result, 31, PyInt_FromLong((int) I->protons)); PyList_SetItem(result, 32, PyInt_FromLong(I->unique_id)); PyList_SetItem(result, 33, PyInt_FromLong((char) I->stereo)); PyList_SetItem(result, 34, PyInt_FromLong(I->discrete_state)); PyList_SetItem(result, 35, PyFloat_FromDouble(I->elec_radius)); PyList_SetItem(result, 36, PyInt_FromLong(I->rank)); PyList_SetItem(result, 37, PyInt_FromLong((int) I->hb_donor)); PyList_SetItem(result, 38, PyInt_FromLong((int) I->hb_acceptor)); PyList_SetItem(result, 39, PyInt_FromLong(0 /* atomic_color */)); PyList_SetItem(result, 40, PyInt_FromLong((int) I->has_setting)); const float anisou_stack[] {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; const float * anisou = I->anisou ? I->anisou : anisou_stack; for (int i = 0; i < 6; ++i) { PyList_SetItem(result, 41 + i, PyFloat_FromDouble(anisou[i])); } PyList_SetItem(result, 47, PyString_FromString(LexStr(G, I->custom))); return (PConvAutoNone(result)); } int AtomInfoFromPyList(PyMOLGlobals * G, AtomInfoType * I, PyObject * list) { int ok = true; int tmp_int; ov_size ll = 0; OrthoLineType temp = ""; #define PCONVPYSTRTOLEXIDX(i, n) { \ ok = CPythonVal_PConvPyStrToStr_From_List(G, list, i, temp, sizeof(OrthoLineType)); \ n = LexIdx(G, temp); } if(ok) ok = PyList_Check(list); if(ok) ll = PyList_Size(list); if(ok) ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 0, &I->resv); if(ok) PCONVPYSTRTOLEXIDX(1, I->chain); if(ok) ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 2, I->alt, sizeof(Chain)); if(ok) { // get inscode from resi ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 3, temp, sizeof(temp)); int i = strlen(temp) - 1; if (i >= 0 && !isdigit(temp[i])) { I->setInscode(temp[i]); } } if(ok) PCONVPYSTRTOLEXIDX(4, I->segi); if(ok) PCONVPYSTRTOLEXIDX(5, I->resn); if(ok) PCONVPYSTRTOLEXIDX(6, I->name); if(ok) ok = CPythonVal_PConvPyStrToStr_From_List(G, list, 7, I->elem, sizeof(ElemName)); if(ok) PCONVPYSTRTOLEXIDX(8, I->textType); if(ok) PCONVPYSTRTOLEXIDX(9, I->label); if(ok) ok = PConvPyStrToStr(PyList_GetItem(list, 10), I->ssType, sizeof(SSType)); if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 12), &I->customType); if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 13), &I->priority); if(ok) ok = PConvPyFloatToFloat(PyList_GetItem(list, 14), &I->b); if(ok) ok = PConvPyFloatToFloat(PyList_GetItem(list, 15), &I->q); if(ok) ok = PConvPyFloatToFloat(PyList_GetItem(list, 16), &I->vdw); if(ok) ok = PConvPyFloatToFloat(PyList_GetItem(list, 17), &I->partialCharge); if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 18, &tmp_int))) I->formalCharge = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 19, &tmp_int))) I->hetatm = tmp_int; if(ok){ PyObject *val = PyList_GetItem(list, 20); if (PyList_Check(val)){ ok = PConvPyListToBitmask(val, &I->visRep, cRepCnt); } else { ok = PConvPyIntToInt(val, &I->visRep); } } if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 21), &I->color); if(ok) I->color = ColorConvertOldSessionIndex(G, I->color); if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 22), &I->id); if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 23, &tmp_int))) I->cartoon = tmp_int; if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 24), (int *) &I->flags); if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 25, &tmp_int))) I->bonded = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 26, &tmp_int))) I->chemFlag = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 27, &tmp_int))) I->geom = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 28, &tmp_int))) I->valence = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 29, &tmp_int))) I->masked = tmp_int; if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 30, &tmp_int))) I->protekted = tmp_int; if(ok) ok = PConvPyIntToChar(PyList_GetItem(list, 31), (char *) &I->protons); if(ok) ok = PConvPyIntToInt(PyList_GetItem(list, 32), &I->unique_id); if(ok && I->unique_id) { /* reserve existing IDs */ I->unique_id = SettingUniqueConvertOldSessionID(G, I->unique_id); } if(ok) if((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 33, &tmp_int))) I->stereo = tmp_int; if(ok && (ll > 34)) ok = PConvPyIntToInt(PyList_GetItem(list, 34), &I->discrete_state); if(ok && (ll > 35)) ok = PConvPyFloatToFloat(PyList_GetItem(list, 35), &I->elec_radius); if(ok && (ll > 36)) ok = PConvPyIntToInt(PyList_GetItem(list, 36), &I->rank); if(ok && (ll > 37)) if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 37, &tmp_int))) I->hb_donor = tmp_int; if(ok && (ll > 38)) if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 38, &tmp_int))) I->hb_acceptor = tmp_int; if(ok && (ll > 40)) if ((ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 40, &tmp_int))) I->has_setting = tmp_int; if(ok && (ll > 46)) { // only allocate if not all zero float u[6]; for (int i = 0; ok && i < 6; ++i) ok = CPythonVal_PConvPyFloatToFloat_From_List(G, list, 41 + i, u + i); if(ok && (u[0] || u[1] || u[2] || u[3] || u[4] || u[5])) memcpy(I->get_anisou(), u, 6 * sizeof(float)); } if(ok && (ll > 47)) { PCONVPYSTRTOLEXIDX(47, I->custom); } return (ok); } void AtomInfoCopy(PyMOLGlobals * G, const AtomInfoType * src, AtomInfoType * dst, int copy_properties) { /* copy, handling resource management issues... */ *dst = *src; dst->selEntry = 0; if(src->unique_id && src->has_setting) { dst->unique_id = AtomInfoGetNewUniqueID(G); if(!SettingUniqueCopyAll(G, src->unique_id, dst->unique_id)) dst->has_setting = 0; } else { dst->unique_id = 0; dst->has_setting = 0; } LexInc(G, dst->label); LexInc(G, dst->textType); LexInc(G, dst->custom); LexInc(G, dst->chain); LexInc(G, dst->segi); LexInc(G, dst->resn); LexInc(G, dst->name); #ifdef _PYMOL_IP_EXTRAS #endif if (src->anisou) { dst->anisou = NULL; memcpy(dst->get_anisou(), src->anisou, 6 * sizeof(float)); } } void AtomInfoBondCopy(PyMOLGlobals * G, const BondType * src, BondType * dst) { *(dst) = *(src); if(src->unique_id && src->has_setting) { dst->unique_id = AtomInfoGetNewUniqueID(G); if(!SettingUniqueCopyAll(G, src->unique_id, dst->unique_id)) dst->has_setting = 0; } else { dst->unique_id = 0; dst->has_setting = 0; } } void AtomInfoPurgeBond(PyMOLGlobals * G, BondType * bi) { CAtomInfo *I = G->AtomInfo; if(bi->has_setting && bi->unique_id) { SettingUniqueDetachChain(G, bi->unique_id); } if(bi->unique_id && I->ActiveIDs) { OVOneToAny_DelKey(I->ActiveIDs, bi->unique_id); bi->unique_id = 0; } } void AtomInfoPurge(PyMOLGlobals * G, AtomInfoType * ai) { CAtomInfo *I = G->AtomInfo; LexDec(G, ai->textType); LexDec(G, ai->custom); LexDec(G, ai->label); LexDec(G, ai->chain); ai->textType = 0; ai->custom = 0; ai->label = 0; ai->chain = 0; if(ai->has_setting && ai->unique_id) { SettingUniqueDetachChain(G, ai->unique_id); } if(ai->unique_id) { ExecutiveUniqueIDAtomDictInvalidate(G); if (I->ActiveIDs) OVOneToAny_DelKey(I->ActiveIDs, ai->unique_id); } #ifdef _PYMOL_IP_EXTRAS #endif DeleteAP(ai->anisou); } /*========================================================================*/ /* * Transfer `mask` selected atomic properties (such as b, q, text_type, etc.) * from `src` to `dst`, while keeping all atomic identifiers untouched. * Purges `src`. */ void AtomInfoCombine(PyMOLGlobals * G, AtomInfoType * dst, AtomInfoType * src, int mask) { if(mask & cAIC_tt) { std::swap(dst->textType, src->textType); } if(mask & cAIC_ct) dst->customType = src->customType; if(mask & cAIC_pc) dst->partialCharge = src->partialCharge; if(mask & cAIC_fc) dst->formalCharge = src->formalCharge; if(mask & cAIC_flags) dst->flags = src->flags; if(mask & cAIC_b) dst->b = src->b; if(mask & cAIC_q) dst->q = src->q; if(mask & cAIC_id) dst->id = src->id; if(mask & cAIC_state) dst->discrete_state = src->discrete_state; if(mask & cAIC_rank) dst->rank = src->rank; dst->temp1 = src->temp1; SWAP_NOREF(dst->has_setting, src->has_setting); std::swap(dst->unique_id, src->unique_id); #ifdef _PYMOL_IP_EXTRAS std::swap(dst->prop_id, src->prop_id); #endif /* keep all existing names, identifiers, etc. */ /* also keep all existing selections, colors, masks, and visible representations */ /* leaves dst->label untouched */ AtomInfoPurge(G, src); } /*========================================================================*/ int AtomInfoUniquefyNames(PyMOLGlobals * G, const AtomInfoType * atInfo0, int n0, AtomInfoType * atInfo1, int *flag1, int n1) { /* makes sure all names in atInfo1 are unique WRT 0 and 1 */ /* tricky optimizations to avoid n^2 dependence in this operation */ int result = 0; int a, b, c; const AtomInfoType *ai0, *lai1, *lai0; AtomInfoType *ai1; int st1, nd1, st0, nd0; /* starts and ends */ int matchFlag; int bracketFlag; WordType name; ai1 = atInfo1; lai0 = NULL; /* last atom compared against in each object */ lai1 = NULL; st0 = 0; nd0 = 0; st1 = 0; nd1 = 0; c = 1; /* ai1->name is the atom we're currently on */ b = 0; while(b < n1) { matchFlag = false; if(!ai1->name) matchFlag = true; if(!matchFlag) { /* check within object 1 */ if(!lai1) bracketFlag = true; else if(!AtomInfoSameResidue(G, lai1, ai1)) bracketFlag = true; else bracketFlag = false; if(bracketFlag) { c = 1; AtomInfoBracketResidue(G, atInfo1, n1, ai1, &st1, &nd1); lai1 = ai1; } ai0 = atInfo1 + st1; for(a = st1; a <= nd1; a++) { if(!WordMatchExact(G, ai1->name, ai0->name, true)) ai0++; else if(!AtomInfoSameResidue(G, ai1, ai0)) ai0++; else if(ai1 != ai0) { matchFlag = true; break; } else ai0++; } } if(!matchFlag) { if(atInfo0) { /* check within object 2 */ if(!lai0) bracketFlag = true; else if(!AtomInfoSameResidue(G, lai0, ai1)) bracketFlag = true; else bracketFlag = false; if(bracketFlag) { AtomInfoBracketResidue(G, atInfo0, n0, ai1, &st0, &nd0); lai0 = ai1; } ai0 = atInfo0 + st0; for(a = st0; a <= nd0; a++) { if(!WordMatchExact(G, ai1->name, ai0->name, true)) ai0++; else if(!AtomInfoSameResidue(G, ai1, ai0)) ai0++; else if(ai1 != ai0) { matchFlag = true; break; } else ai0++; } } } if(matchFlag && ((!flag1) || flag1[ai1 - atInfo1])) { if(c < 100) { if((c < 10) && ai1->elem[1]) /* try to keep halogens 3 or under */ sprintf(name, "%2s%1d", ai1->elem, c); else sprintf(name, "%1s%02d", ai1->elem, c); } else { sprintf(name, "%1d%1s%02d", c / 100, ai1->elem, c % 100); } LexAssign(G, ai1->name, name); result++; c = c + 1; } else { ai1++; b++; } } return result; } /*========================================================================*/ void AtomInfoBracketResidue(PyMOLGlobals * G, const AtomInfoType * ai0, int n0, const AtomInfoType * ai, int *st, int *nd) { /* inefficient but reliable way to find where residue atoms are located in an object * for purpose of residue-based operations */ int a; const AtomInfoType *ai1; *st = 0; *nd = n0 - 1; ai1 = ai0; for(a = 0; a < n0; a++) { if(!AtomInfoSameResidue(G, ai, ai1++)) *st = a; else break; } ai1 = ai0 + n0 - 1; for(a = n0 - 1; a >= 0; a--) { if(!AtomInfoSameResidue(G, ai, ai1--)) { *nd = a; } else break; } } /*========================================================================*/ void AtomInfoBracketResidueFast(PyMOLGlobals * G, const AtomInfoType * ai0, int n0, int cur, int *st, int *nd) { /* efficient but unreliable way to find where residue atoms are located in an object * for purpose of residue-based operations. * This function finds all atoms in the AtomInfoType array in both directions that * belong to the same residue. It starts the search from the "cur" offset, and returns * the beginning offset (st) and the ending offset (nd).*/ int a; const AtomInfoType *ai1; *st = cur; *nd = cur; ai0 = ai0 + cur; ai1 = ai0 - 1; for(a = cur - 1; a >= 0; a--) { if(!AtomInfoSameResidue(G, ai0, ai1--)) break; *st = a; } ai1 = ai0 + 1; for(a = cur + 1; a < n0; a++) { if(!AtomInfoSameResidue(G, ai0, ai1++)) break; *nd = a; } } /*========================================================================*/ int AtomInfoUpdateAutoColor(PyMOLGlobals * G) { CAtomInfo *I = G->AtomInfo; if(SettingGetGlobal_b(G, cSetting_auto_color)) I->CColor = ColorGetNext(G); else I->CColor = ColorGetIndex(G, "carbon"); return I->CColor; } /*========================================================================*/ void AtomInfoPrimeColors(PyMOLGlobals * G) { CAtomInfo *I = G->AtomInfo; I->NColor = ColorGetIndex(G, "nitrogen"); I->CColor = ColorGetIndex(G, "carbon"); I->HColor = ColorGetIndex(G, "hydrogen"); I->OColor = ColorGetIndex(G, "oxygen"); I->SColor = ColorGetIndex(G, "sulfur"); I->ClColor = ColorGetIndex(G, "chlorine"); I->BrColor = ColorGetIndex(G, "bromine"); I->FColor = ColorGetIndex(G, "fluorine"); I->IColor = ColorGetIndex(G, "iodine"); I->PColor = ColorGetIndex(G, "phosphorus"); I->MgColor = ColorGetIndex(G, "magnesium"); I->MnColor = ColorGetIndex(G, "manganese"); I->NaColor = ColorGetIndex(G, "sodium"); I->KColor = ColorGetIndex(G, "potassium"); I->CaColor = ColorGetIndex(G, "calcium"); I->CuColor = ColorGetIndex(G, "copper"); I->FeColor = ColorGetIndex(G, "iron"); I->ZnColor = ColorGetIndex(G, "zinc"); I->SeColor = ColorGetIndex(G, "selenium"); I->DColor = ColorGetIndex(G, "deuterium"); } /*========================================================================*/ float AtomInfoGetBondLength(PyMOLGlobals * G, const AtomInfoType * ai1, const AtomInfoType * ai2) { float result = 1.6F; const AtomInfoType *a1, *a2; /* very simple for now ... flush this out with pattern-based parameters later on */ if(ai1->protons > ai2->protons) { a1 = ai2; a2 = ai1; } else { a1 = ai1; a2 = ai2; } switch (a1->protons) { /* hydrogen =========================== */ case cAN_H: switch (a2->protons) { case cAN_H: result = 0.74F; break; case cAN_C: result = 1.09F; break; case cAN_N: result = 1.01F; break; case cAN_S: result = 1.34F; break; case cAN_O: result = 0.96F; break; default: result = 1.09F; break; } break; /* carbon =========================== */ case cAN_C: /* carbon */ switch (a1->geom) { case cAtomInfoLinear: /* linear carbon ============= */ switch (a2->geom) { case cAtomInfoLinear: switch (a2->protons) { case cAN_C: result = 1.20F; break; /* C#^C */ case cAN_N: result = 1.16F; break; /* C#^N */ default: result = 1.20F; break; } break; case cAtomInfoPlanar: switch (a2->protons) { case cAN_I: result = 2.14F; break; /* normal single bond lengths */ case cAN_Cl: result = 1.77F; break; case cAN_Br: result = 1.94F; break; case cAN_F: result = 1.35F; break; case cAN_S: result = 1.82F; break; case cAN_N: result = 1.47F; break; case cAN_O: result = 1.43F; break; case cAN_P: result = 1.84F; break; case cAN_C: result = 1.54F; break; default: result = 1.54F; break; } break; default: /* ?#C-^? */ switch (a2->protons) { case cAN_I: result = 2.14F; break; /* normal single bond lengths */ case cAN_Cl: result = 1.77F; break; case cAN_Br: result = 1.94F; break; case cAN_F: result = 1.35F; break; case cAN_S: result = 1.82F; break; case cAN_N: result = 1.47F; break; case cAN_O: result = 1.43F; break; case cAN_P: result = 1.84F; break; case cAN_C: result = 1.54F; break; default: result = 1.54F; break; } break; } break; case cAtomInfoPlanar: /* planer carbon ============= */ switch (a2->geom) { case cAtomInfoLinear: switch (a2->protons) { case cAN_I: result = 2.14F; break; /* normal single bond lengths */ case cAN_Cl: result = 1.77F; break; case cAN_Br: result = 1.94F; break; case cAN_F: result = 1.35F; break; case cAN_S: result = 1.82F; break; case cAN_N: result = 1.47F; break; case cAN_O: result = 1.43F; break; case cAN_P: result = 1.84F; break; case cAN_C: result = 1.54F; break; default: result = 1.54F; break; } break; case cAtomInfoPlanar: switch (a2->protons) { case cAN_C: result = 1.34F; break; /* C=^C or ?=C-^C=? */ case cAN_O: result = 1.20F; break; /* carbonyl */ case cAN_N: result = 1.29F; break; /* C=^N or ?=C-^N=? */ case cAN_S: result = 1.60F; break; /* C=^S or ?=C-^S-?=? */ default: result = 1.34F; break; } break; default: /* ?#C-^? */ switch (a2->protons) { case cAN_I: result = 2.14F; break; /* normal single bond lengths */ case cAN_Cl: result = 1.77F; break; case cAN_Br: result = 1.94F; break; case cAN_F: result = 1.35F; break; case cAN_S: result = 1.71F; break; /* mmod */ case cAN_N: result = 1.47F; break; case cAN_O: result = 1.43F; break; case cAN_P: result = 1.84F; break; case cAN_C: result = 1.54F; break; default: result = 1.54F; break; } break; } break; default: /* tetrahedral carbon */ switch (a2->protons) { case cAN_I: result = 2.14F; break; /* normal single bond lengths */ case cAN_Cl: result = 1.77F; break; case cAN_Br: result = 1.94F; break; case cAN_F: result = 1.35F; break; case cAN_S: result = 1.82F; break; case cAN_N: result = 1.47F; break; case cAN_O: result = 1.43F; break; case cAN_P: result = 1.84F; break; case cAN_C: result = 1.54F; break; default: result = 1.54F; break; } break; } break; /* nitrogen ========================= */ case cAN_N: if((a1->geom == cAtomInfoPlanar) && (a2->geom == cAtomInfoPlanar)) switch (a2->protons) { case cAN_N: result = 1.25F; break; case cAN_O: result = 1.21F; break; case cAN_S: result = 1.53F; break; /* interpolated */ default: result = 1.25F; break; } else { switch (a2->protons) { case cAN_N: result = 1.45F; break; case cAN_O: result = 1.40F; break; case cAN_S: result = 1.75F; break; /* interpolated */ default: result = 1.45F; break; } } break; /* oxygen =========================== */ case cAN_O: if((a1->geom == cAtomInfoPlanar) && (a2->geom == cAtomInfoPlanar)) switch (a2->protons) { case cAN_O: result = 1.35F; break; /* guess */ case cAN_S: result = 1.44F; break; /* macromodel */ default: result = 1.35F; break; } else if(a1->geom == cAtomInfoPlanar) { switch (a2->protons) { case cAN_O: result = 1.35F; break; /* guess */ case cAN_S: result = 1.44F; break; /* macromodel */ default: result = 1.35F; break; } } else { switch (a2->protons) { case cAN_O: result = 1.40F; break; case cAN_S: result = 1.75F; break; /* interpolated */ default: result = 1.45F; break; } } break; /* sulfur =========================== */ case cAN_S: switch (a2->protons) { case cAN_S: result = 2.05F; break; /* interpolated */ default: result = 1.82F; break; } break; /* fall-back to old method */ default: result = 0.0; switch (a1->geom) { case cAtomInfoLinear: result += 1.20F; break; case cAtomInfoPlanar: result += 1.34F; break; default: result += 1.54F; break; } switch (a2->geom) { case cAtomInfoLinear: result += 1.20F; break; case cAtomInfoPlanar: result += 1.34F; break; default: result += 1.54F; break; } result /= 2.0F; break; } return (result); } /* * Periodic table of elements * * Note: Default VDW radius is 1.80 */ const ElementTableItemType ElementTable[] = { {"lonepair", "LP", 0.50, 0.000000}, {"hydrogen", "H", 1.20, 1.007940}, {"helium", "He", 1.40, 4.002602}, {"lithium", "Li", 1.82, 6.941000}, {"beryllium", "Be", 1.80, 9.012182}, {"boron", "B", 1.85, 10.811000}, {"carbon", "C", 1.70, 12.010700}, {"nitrogen", "N", 1.55, 14.006700}, {"oxygen", "O", 1.52, 15.999400}, {"fluorine", "F", 1.47, 18.998403}, {"neon", "Ne", 1.54, 20.179700}, {"sodium", "Na", 2.27, 22.989770}, {"magnesium", "Mg", 1.73, 24.305000}, {"aluminum", "Al", 2.00, 26.981538}, {"silicon", "Si", 2.10, 28.085500}, {"phosphorus", "P", 1.80, 30.973761}, {"sulfur", "S", 1.80, 32.065000}, {"chlorine", "Cl", 1.75, 35.453000}, {"argon", "Ar", 1.88, 39.948000}, {"potassium", "K", 2.75, 39.098300}, {"calcium", "Ca", 1.80, 40.078000}, {"scandium", "Sc", 1.80, 44.955910}, {"titanium", "Ti", 1.80, 47.867000}, {"vanadium", "V", 1.80, 50.941500}, {"chromium", "Cr", 1.80, 51.996100}, {"manganese", "Mn", 1.73, 54.938049}, {"iron", "Fe", 1.80, 55.845000}, {"cobalt", "Co", 1.80, 58.933200}, {"nickel", "Ni", 1.63, 58.693400}, {"copper", "Cu", 1.40, 63.546000}, {"zinc", "Zn", 1.39, 65.390000}, {"gallium", "Ga", 1.87, 69.723000}, {"germanium", "Ge", 1.80, 72.640000}, {"arsenic", "As", 1.85, 74.921600}, {"selenium", "Se", 1.90, 78.960000}, {"bromine", "Br", 1.85, 79.904000}, {"krypton", "Kr", 2.02, 83.800000}, {"rubidium", "Rb", 1.80, 85.467800}, {"strontium", "Sr", 1.80, 87.620000}, {"yttrium", "Y", 1.80, 88.905850}, {"zirconium", "Zr", 1.80, 91.224000}, {"niobium", "Nb", 1.80, 92.906380}, {"molybdenum", "Mo", 1.80, 95.940000}, {"technetium", "Tc", 1.80, 98.000000}, {"ruthenium", "Ru", 1.80, 101.070000}, {"rhodium", "Rh", 1.80, 102.905500}, {"palladium", "Pd", 1.63, 106.420000}, {"silver", "Ag", 1.72, 107.868200}, {"cadmium", "Cd", 1.58, 112.411000}, {"indium", "In", 1.93, 114.818000}, {"tin", "Sn", 2.17, 118.710000}, {"antimony", "Sb", 1.80, 121.760000}, {"tellurium", "Te", 2.06, 127.600000}, {"iodine", "I", 1.98, 126.904470}, {"xenon", "Xe", 2.16, 131.293000}, {"cesium", "Cs", 1.80, 132.905450}, {"barium", "Ba", 1.80, 137.327000}, {"lanthanum", "La", 1.80, 138.905500}, {"cerium", "Ce", 1.80, 140.116000}, {"praseodymium", "Pr", 1.80, 140.907650}, {"neodymium", "Nd", 1.80, 144.240000}, {"promethium", "Pm", 1.80, 145.000000}, {"samarium", "Sm", 1.80, 150.360000}, {"europium", "Eu", 1.80, 151.964000}, {"gadolinium", "Gd", 1.80, 157.250000}, {"terbium", "Tb", 1.80, 158.925340}, {"dysprosium", "Dy", 1.80, 162.500000}, {"holmium", "Ho", 1.80, 164.930320}, {"erbium", "Er", 1.80, 167.259000}, {"thulium", "Tm", 1.80, 168.934210}, {"ytterbium", "Yb", 1.80, 173.040000}, {"lutetium", "Lu", 1.80, 174.967000}, {"hafnium", "Hf", 1.80, 178.490000}, {"tantalum", "Ta", 1.80, 180.947900}, {"tungsten", "W", 1.80, 183.840000}, {"rhenium", "Re", 1.80, 186.207000}, {"osmium", "Os", 1.80, 190.230000}, {"iridium", "Ir", 1.80, 192.217000}, {"platinum", "Pt", 1.75, 195.078000}, {"gold", "Au", 1.66, 196.966550}, {"mercury", "Hg", 1.55, 200.590000}, {"thallium", "Tl", 1.96, 204.383300}, {"lead", "Pb", 2.02, 207.200000}, {"bismuth", "Bi", 1.80, 208.980380}, {"polonium", "Po", 1.80, 208.980000}, {"astatine", "At", 1.80, 209.990000}, {"radon", "Rn", 1.80, 222.020000}, {"francium", "Fr", 1.80, 223.020000}, {"radium", "Ra", 1.80, 226.030000}, {"actinium", "Ac", 1.80, 227.030000}, {"thorium", "Th", 1.80, 232.038100}, {"protactinium", "Pa", 1.80, 231.035880}, {"uranium", "U", 1.86, 238.028910}, {"neptunium", "Np", 1.80, 237.050000}, {"plutonium", "Pu", 1.80, 244.060000}, {"americium", "Am", 1.80, 243.060000}, {"curium", "Cm", 1.80, 247.070000}, {"berkelium", "Bk", 1.80, 247.070000}, {"californium", "Cf", 1.80, 251.080000}, {"einsteinium", "Es", 1.80, 252.080000}, {"fermium", "Fm", 1.80, 257.100000}, {"mendelevium", "Md", 1.80, 258.100000}, {"nobelium", "No", 1.80, 259.100000}, {"lawrencium", "Lr", 1.80, 262.110000}, {"rutherfordium", "Rf", 1.80, 261.110000}, {"dubnium", "Db", 1.80, 262.110000}, {"seaborgium", "Sg", 1.80, 266.120000}, {"bohrium", "Bh", 1.80, 264.120000}, {"hassium", "Hs", 1.80, 269.130000}, {"meitnerium", "Mt", 1.80, 268.140000}, {"darmstadtium", "Ds", 1.80, 281.000000}, {"roentgenium", "Rg", 1.80, 281.000000}, {"copernicium", "Cn", 1.80, 285.000000}, {"nihonium", "Nh", 1.80, 286.000000}, {"flerovium", "Fl", 1.80, 289.000000}, {"moscovium", "Mc", 1.80, 290.000000}, {"livermorium", "Lv", 1.80, 293.000000}, {"tennessine", "Ts", 1.80, 294.000000}, {"oganesson", "Og", 1.80, 294.000000}, {NULL, NULL, 0.00, 0.000000} }; const int ElementTableSize = sizeof(ElementTable) / sizeof(ElementTable[0]) - 1; /* * Assign atomic color, or G->AtomInfo->CColor in case of carbon. */ void AtomInfoAssignColors(PyMOLGlobals * G, AtomInfoType * at1) { at1->color = AtomInfoGetColor(G, at1); } /* * Get atomic color, based on protons and elem */ int AtomInfoGetColor(PyMOLGlobals * G, const AtomInfoType * at1) { // fast lookup for most common elements switch (at1->protons) { case cAN_H: if (at1->elem[0] == 'D') return G->AtomInfo->DColor; return G->AtomInfo->HColor; case cAN_N: return G->AtomInfo->NColor; case cAN_C: return G->AtomInfo->CColor; case cAN_O: return G->AtomInfo->OColor; case cAN_P: return G->AtomInfo->PColor; } // general by-name lookup (exclude LP, PS) if (at1->protons > 0 && at1->protons < ElementTableSize) return ColorGetIndex(G, ElementTable[at1->protons].name); // special cases if (strcmp(at1->elem, "PS") == 0) return ColorGetIndex(G, "pseudoatom"); if (strcmp(at1->elem, "LP") == 0) return ColorGetIndex(G, "lonepair"); return G->AtomInfo->DefaultColor; } void AtomInfoFreeSortedIndexes(PyMOLGlobals * G, int **index, int **outdex) { FreeP(*index); FreeP(*outdex); } static int AtomInfoNameCompare(PyMOLGlobals * G, const char *name1, const char *name2) { const char *n1, *n2; int cmp; if((name1[0] >= '0') && (name1[0] <= '9')) n1 = name1 + 1; else n1 = name1; if((name2[0] >= '0') && (name2[0] <= '9')) n2 = name2 + 1; else n2 = name2; cmp = WordCompare(G, n1, n2, true); if(cmp) return cmp; return WordCompare(G, name1, name2, true); } static int AtomInfoNameCompare(PyMOLGlobals * G, const lexidx_t& name1, const lexidx_t& name2) { if (name1 == name2) return 0; return AtomInfoNameCompare(G, LexStr(G, name1), LexStr(G, name2)); } int AtomInfoCompareAll(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return (at1->resv != at2->resv || at1->customType != at2->customType || at1->priority != at2->priority || at1->b != at2->b || at1->q != at2->q || at1->vdw != at2->vdw || at1->partialCharge != at2->partialCharge || at1->formalCharge != at2->formalCharge || // at1->selEntry != at2->selEntry || at1->color != at2->color || at1->id != at2->id || at1->flags != at2->flags || // at1->temp1 != at2->temp1 || at1->unique_id != at2->unique_id || at1->discrete_state != at2->discrete_state || at1->elec_radius != at2->elec_radius || at1->rank != at2->rank || at1->textType != at2->textType || at1->custom != at2->custom || at1->label != at2->label || // !memcmp(at1->visRep, at2->visRep, sizeof(signed char)*cRepCnt) || // should this be in here? at1->stereo != at2->stereo || at1->cartoon != at2->cartoon || at1->hetatm != at2->hetatm || at1->bonded != at2->bonded || // at1->chemFlag != at2->chemFlag || // at1->geom != at2->geom || // at1->valence != at2->valence || // Valence should not be in, since it is not initially computed? at1->deleteFlag != at2->deleteFlag || at1->masked != at2->masked || at1->protekted != at2->protekted || at1->protons != at2->protons || at1->hb_donor != at2->hb_donor || at1->hb_acceptor != at2->hb_acceptor || at1->has_setting != at2->has_setting || at1->chain != at2->chain || at1->segi != at2->segi || at1->resn != at2->resn || at1->name != at2->name || strcmp(at1->alt, at2->alt) || at1->inscode != at2->inscode || strcmp(at1->elem, at2->elem) || strcmp(at1->ssType, at2->ssType)); // should these variables be in here? // float U11, U22, U33, U12, U13, U23; } /* * Compares atoms based on all atom identifiers, discrete state, priority, * hetatm (optional) and rank (optional) * * Insertion code sorting depends on settings: * - pdb_insertions_go_first * - rank_assisted_sorts * * Returns: * 0: at1 == at2 * -1: at1 < at2 * 1: at1 > at2 */ static int AtomInfoCompare(PyMOLGlobals *G, const AtomInfoType *at1, const AtomInfoType *at2, bool ignore_hetatm, bool ignore_rank) { int wc; if ((wc = WordCompare(G, at1->segi, at2->segi, false))) return wc; if ((wc = WordCompare(G, at1->chain, at2->chain, false))) return wc; if (!ignore_hetatm && at1->hetatm != at2->hetatm) return (at2->hetatm) ? -1 : 1; if (at1->resv != at2->resv) return (at1->resv < at2->resv) ? -1 : 1; if ((wc = getInscodeUpper(at1) - getInscodeUpper(at2))) { if (SettingGetGlobal_b(G, cSetting_pdb_insertions_go_first)) return (!at1->inscode) ? 1 : (!at2->inscode) ? -1 : wc; if ((at1->rank != at2->rank) && SettingGetGlobal_b(G, cSetting_rank_assisted_sorts)) return (at1->rank < at2->rank) ? -1 : 1; return wc; } if ((wc = WordCompare(G, at1->resn, at2->resn, true))) return wc; if (at1->discrete_state != at2->discrete_state) return (at1->discrete_state < at2->discrete_state) ? -1 : 1; // if this looks like a "bulk" het group with no residue number, then don't // compare by name/alt/priority (affects mmCIF with cif_use_auth=0) if (!ignore_rank && !at1->resv && at1->hetatm) goto rank_compare; if (at1->priority != at2->priority) return (at1->priority < at2->priority) ? -1 : 1; // Changed (PyMOL 2.1): name before alt if ((wc = AtomInfoNameCompare(G, at1->name, at2->name))) return wc; // Changed (PyMOL 2.1): empty alt goes first: '' < 'A' < 'B' if (at1->alt[0] != at2->alt[0]) { return (at1->alt[0] < at2->alt[0]) ? -1 : 1; } rank_compare: if (!ignore_rank && at1->rank != at2->rank) return (at1->rank < at2->rank) ? -1 : 1; return 0; } int AtomInfoCompare(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return AtomInfoCompare(G, at1, at2, false, false); } int AtomInfoCompareIgnoreRankHet(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return AtomInfoCompare(G, at1, at2, true, true); } int AtomInfoCompareIgnoreRank(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return AtomInfoCompare(G, at1, at2, false, true); } int AtomInfoCompareIgnoreHet(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return AtomInfoCompare(G, at1, at2, true, false); } /* * Function only used for matching atoms of two aligned residues. * * Changed (PyMOL 2.1): * Consider alt codes to be equal if at least one is empty. * Otherwise, alt-code (C-alpha) atoms can't be aligned to non-alt atoms * (also affects morphing). */ int AtomInfoNameOrder(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { int result; if(at1->alt[0] == at2->alt[0] || !at1->alt[0] || !at2->alt[0]) { if(at1->priority == at2->priority) { result = AtomInfoNameCompare(G, at1->name, at2->name); } else if(at1->priority < at2->priority) { result = -1; } else { result = 1; } } else if(at1->alt[0] < at2->alt[0]) { result = -1; } else { result = 1; } return (result); } int AtomInfoSameResidue(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { return ( at1->resv == at2->resv && at1->chain == at2->chain && at1->hetatm == at2->hetatm && at1->discrete_state == at2->discrete_state && at1->inscode == at2->inscode && at1->segi == at2->segi && WordMatchExact(G, at1->resn, at2->resn, true)); } int AtomInfoSameResidueP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { if(at1 && at2) return AtomInfoSameResidue(G, at1, at2); return 0; } int AtomInfoSameChainP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { if(at1 && at2) if(at1->chain == at2->chain) if(at1->segi == at2->segi) return 1; return 0; } int AtomInfoSameSegmentP(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2) { if(at1 && at2) if(at1->segi == at2->segi) return 1; return 0; } int AtomInfoSequential(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2, int mode) { if(mode > 0) { if(at1->hetatm == at2->hetatm) { if(mode > 1) { if(at1->segi == at2->segi) { if(mode > 2) { if(at1->chain == at2->chain) { if(mode > 3) { if(at1->resv == at2->resv) { if(mode > 4) { if(at1->inscode == at2->inscode) return 1; if(at1->inscode + 1 == at2->inscode) return 1; } else { return 1; /* no resi check */ } } else if((at1->resv + 1) == at2->resv) return 1; } else { return 1; /* no resv check */ } } } else { return 1; /* no chain check */ } } } else { return 1; /* no segi check */ } } } else { return 1; /* no hetatm check */ } return 0; } /* * Used in "rms" and "update" for matching two selections */ int AtomInfoMatch(PyMOLGlobals * G, const AtomInfoType * at1, const AtomInfoType * at2, bool ignore_case, bool ignore_case_chain) { if(at1->resv == at2->resv) if(WordMatchExact(G, at1->chain, at2->chain, ignore_case_chain)) if(WordMatchExact(G, at1->name, at2->name, ignore_case)) if(WordMatchExact(G, at1->inscode, at2->inscode, ignore_case)) if(WordMatchExact(G, at1->resn, at2->resn, ignore_case)) if(WordMatchExact(G, at1->segi, at2->segi, ignore_case_chain)) if(WordMatchExact(G, at1->alt[0], at2->alt[0], ignore_case)) return 1; return 0; } int AtomInfoIsFreeCation(PyMOLGlobals * G, const AtomInfoType * I) { switch (I->protons) { case cAN_Na: case cAN_K: case cAN_Ca: case cAN_Mg: case cAN_Mn: case cAN_Sr: return true; } return false; } int AtomInfoGetExpectedValence(PyMOLGlobals * G, const AtomInfoType * I) { int result = -1; /* negative indicates minimum expected valence (abs) but it could be higher */ if(I->formalCharge == 0) { switch (I->protons) { case cAN_H: result = 1; break; case cAN_C: result = 4; break; case cAN_N: result = 3; break; case cAN_O: result = 2; break; case cAN_F: result = 1; break; case cAN_Cl: result = 1; break; case cAN_Br: result = 1; break; case cAN_I: result = 1; break; case cAN_Na: result = 1; break; case cAN_Ca: result = 1; break; case cAN_K: result = 1; break; case cAN_Mg: result = 2; break; case cAN_Zn: result = -1; break; case cAN_S: result = -2; break; case cAN_P: result = -3; break; } } else if(I->formalCharge == 1) { switch (I->protons) { case cAN_N: result = 4; break; case cAN_O: result = 3; break; case cAN_Na: result = 0; break; case cAN_Ca: result = 0; break; case cAN_K: result = 0; break; case cAN_Mg: result = 1; break; case cAN_Zn: result = -1; break; case cAN_S: result = -2; break; case cAN_P: result = -3; break; } } else if(I->formalCharge == -1) { switch (I->protons) { case cAN_N: result = 2; break; case cAN_O: result = 1; break; case cAN_C: result = 3; break; case cAN_Zn: result = -1; break; case cAN_S: result = -2; break; case cAN_P: result = -3; break; } } else if(I->formalCharge == 2) { switch (I->protons) { case cAN_Mg: result = 0; break; case cAN_Zn: result = -1; break; case cAN_S: result = -2; break; case cAN_P: result = -3; break; } } return (result); } /* * Get number of protons for element symbol */ static int get_protons(const char * symbol) { char titleized[4]; static std::map<const char *, int, cstrless_t> lookup; if (lookup.empty()) { for (int i = 0; i < ElementTableSize; i++) lookup[ElementTable[i].symbol] = i; lookup["Q"] = cAN_H; lookup["D"] = cAN_H; } // check second letter for lower case if (symbol[0] && isupper(symbol[1]) && strcmp(symbol, "LP") != 0) { UtilNCopy(titleized, symbol, 4); titleized[1] = tolower(symbol[1]); symbol = titleized; } // find in lookup dictionary auto it = lookup.find(symbol); if (it != lookup.end()) { return it->second; } else { // allow wacky names for C and H switch(symbol[0]) { case 'C': return cAN_C; case 'H': return cAN_H; } } return -1; } /* * Assign "protons" from "elem" or "name" property */ static void set_protons(PyMOLGlobals * G, AtomInfoType * I) { int protons = get_protons(I->elem); if (protons < 0) { // try again with atom name, skip numbers const char * name = LexStr(G, I->name); while((*name >= '0') && (*name <= '9') && (*(name + 1))) name++; protons = get_protons(name); } I->protons = protons; } /* * Assign (based on name, elem, protons): * - elem (if empty string) * - protons (if < 1) * - vdw (if 0.0) * - priority */ void AtomInfoAssignParameters(PyMOLGlobals * G, AtomInfoType * I) { const char *n = NULL; char *e = NULL; int pri; e = I->elem; // elem from protons (except for LP, assume protons=0 if uninitialized) if(!*e && I->protons > 0) { atomicnumber2elem(e, I->protons); } // elem from name if(!*e) { /* try to guess atomic type from name */ n = LexStr(G, I->name); while(((*n) >= '0') && ((*n) <= '9') && (*(n + 1))) n++; strncpy(e, n, cElemNameLen); switch (*e) { case '\0': break; case 'C': if(*(e + 1) == 'A') { if(!WordMatchExact(G, G->lex_const.CA, I->resn, true) && (!WordMatchExact(G, "CA+", LexStr(G, I->resn), true))) /* CA intpreted as carbon, not calcium */ *(e + 1) = 0; } else if(!((*(e + 1) == 'a') || /* CA intpreted as carbon, not calcium */ (*(e + 1) == 'l') || (*(e + 1) == 'L') || (*(e + 1) == 'u') || (*(e + 1) == 'U') || (*(e + 1) == 'o') || (*(e + 1) == 'O') || (*(e + 1) == 's') || (*(e + 1) == 'S') || (*(e + 1) == 'r') || (*(e + 1) == 'R') )) *(e + 1) = 0; break; case 'H': if(!((*(e + 1) == 'e') )) *(e + 1) = 0; break; case 'D': /* take deuterium to hydrogen */ *(e + 1) = 0; break; case 'N': if(!((*(e + 1) == 'i') || (*(e + 1) == 'I') || (*(e + 1) == 'a') || (*(e + 1) == 'A') || (*(e + 1) == 'b') || (*(e + 1) == 'B') )) *(e + 1) = 0; break; case 'S': if(!((*(e + 1) == 'e') || (*(e + 1) == 'E') || (*(e + 1) == 'r') || (*(e + 1) == 'R') || (*(e + 1) == 'c') || (*(e + 1) == 'C') || (*(e + 1) == 'b') || (*(e + 1) == 'B') )) *(e + 1) = 0; break; case 'O': if(!((*(e + 1) == 's'))) *(e + 1) = 0; break; case 'Q': *(e + 1) = 0; break; } if(*(e + 1) && (e[1] != 'P' || e[0] != 'L')) *(e + 1) = tolower(*(e + 1)); } // priority n = LexStr(G, I->name); while((*n >= '0') && (*n <= '9') && (*(n + 1))) n++; if(toupper(*n) != I->elem[0]) { pri = 1000; /* unconventional atom name -- make no assignments */ } else if(SettingGetGlobal_b(G, cSetting_pdb_standard_order)) { switch (*n) { case 'N': case 'C': case 'O': case 'S': switch (*(n + 1)) { case 0: switch (*n) { case 'N': pri = 1; break; case 'C': pri = 3; break; case 'O': pri = 4; break; default: pri = 1000; break; } break; case 'A': switch (*n) { case 'C': pri = 2; break; default: pri = 5; break; /* generic alpha atom */ } break; case 'B': pri = 6; break; /* generic beta atom, etc. */ case 'G': pri = 7; break; case 'D': pri = 8; break; case 'E': pri = 9; break; case 'Z': pri = 10; break; case 'H': pri = 11; break; case 'I': pri = 12; break; case 'J': pri = 13; break; case 'K': pri = 14; break; case 'L': pri = 15; break; case 'M': pri = 16; break; case 'N': pri = 17; break; case 'X': switch (*(n + 2)) { case 'T': pri = 999; break; default: case 0: pri = 16; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pri = 0; n++; while(*n) { if(*n == 'P') { pri -= 200; break; } else if((*n == '*') || (*n == '\'')) { pri = (-100) - pri; break; } else if((*n < '0') || (*n > '9')) break; pri *= 10; pri += (*n - '0'); n++; } pri += 300; break; default: pri = 500; break; } break; case 'P': /* this will place the phosphate before CNO numbered atoms */ pri = 20; break; case 'D': case 'H': switch (*(n + 1)) { case 0: pri = 1001; break; case 'A': case 'B': pri = 1003; break; case 'G': pri = 1004; break; case 'D': pri = 1005; break; case 'E': pri = 1006; break; case 'Z': pri = 1007; break; case 'H': pri = 1008; break; case 'I': pri = 1009; break; case 'J': pri = 1010; break; case 'K': pri = 1011; break; case 'L': pri = 1012; break; case 'M': pri = 1013; break; case 'N': pri = 1002; break; case 'X': pri = 1999; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pri = 1020; n++; while(*n) { pri *= 10; pri += (*n - '0'); n++; } pri += 25; break; default: pri = 1500; break; } break; default: pri = 1000; break; } } else { switch (*n) { case 'N': case 'C': case 'O': case 'S': switch (*(n + 1)) { case 0: switch (*n) { case 'N': pri = 1; break; case 'C': pri = 997; break; case 'O': pri = 998; break; default: pri = 1000; break; } break; case 'A': pri = 3; break; /* generic alpha */ case 'B': pri = 4; break; case 'G': pri = 5; break; case 'D': pri = 6; break; case 'E': pri = 7; break; case 'Z': pri = 8; break; case 'H': pri = 9; break; case 'I': pri = 10; break; case 'J': pri = 11; break; case 'K': pri = 12; break; case 'L': pri = 13; break; case 'M': pri = 14; break; case 'N': pri = 15; break; case 'X': switch (*(n + 2)) { case 'T': pri = 999; break; default: case 0: pri = 16; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pri = 0; n++; while(*n) { pri *= 10; pri += (*n - '0'); n++; } pri += 25; break; default: pri = 500; break; } break; default: pri = 1000; break; } } I->priority = pri; // protons from elem or name if (I->protons < 1) set_protons(G, I); // vdw from protons if(I->vdw == 0.0) { if (I->protons > -1 && I->protons < ElementTableSize) { I->vdw = ElementTable[I->protons].vdw; } else { I->vdw = 1.80F; } } } int BondTypeCompare(PyMOLGlobals * G, const BondType * bt1, const BondType * bt2){ return (bt1->index[0] != bt2->index[0] || bt1->index[1] != bt2->index[1] || bt1->order != bt2->order || bt1->id != bt2->id || bt1->unique_id != bt2->unique_id || bt1->stereo != bt2->stereo || bt1->has_setting != bt2->has_setting); } void atomicnumber2elem(char * dst, int protons) { if (protons > -1 && protons < ElementTableSize) strncpy(dst, ElementTable[protons].symbol, cElemNameLen); } /* * Get a string representation of the stereo configuration. Either * R/S chirality, if set, or the SDF odd/even parity, if set. */ const char * AtomInfoGetStereoAsStr(const AtomInfoType * ai) { switch (ai->mmstereo) { case 1 /* MMSTEREO_CHIRALITY_R */: return "R"; case 2 /* MMSTEREO_CHIRALITY_S */: return "S"; } switch (ai->stereo) { case SDF_CHIRALITY_ODD: return "odd"; case SDF_CHIRALITY_EVEN: return "even"; } if (ai->mmstereo || ai->stereo) { return "?"; } return ""; } /* * Set stereochemistry. Valid are: R, S, N[one], E[ven], O[dd] */ void AtomInfoSetStereo(AtomInfoType * ai, const char * stereo) { switch (toupper(stereo[0])) { case 'R': ai->mmstereo = 1; ai->stereo = 0; break; // MMSTEREO_CHIRALITY_R case 'S': ai->mmstereo = 2; ai->stereo = 0; break; // MMSTEREO_CHIRALITY_S case 'E': ai->mmstereo = 0; ai->stereo = SDF_CHIRALITY_EVEN; break; case 'O': ai->mmstereo = 0; ai->stereo = SDF_CHIRALITY_ODD; break; case 'A': // ANS (s), ANR (r) pseudochirality case 'N': case 0: ai->mmstereo = ai->stereo = 0; break; default: ai->mmstereo = ai->stereo = 3; break; } } /* * Get column aligned (left space padded) PDB residue name * * resn: output buffer */ void AtomInfoGetAlignedPDBResidueName(PyMOLGlobals * G, const AtomInfoType * ai, ResName & resn) { sprintf(resn, "%3.4s", LexStr(G, ai->resn)); if(SettingGetGlobal_b(G, cSetting_pdb_truncate_residue_name)) { resn[3] = 0; /* enforce 3-letter residue name in PDB files */ } } /* * Get column aligned (left space padded) PDB atom name * * resn: space padded residue name * name: output buffer */ void AtomInfoGetAlignedPDBAtomName(PyMOLGlobals * G, const AtomInfoType * ai, const ResName & resn, AtomName & name) { int literal = SettingGetGlobal_b(G, cSetting_pdb_literal_names); int reformat = SettingGetGlobal_i(G, cSetting_pdb_reformat_names_mode); // default is "literal=0" and "reformat=0" (no reformatting) const char * ai_name = LexStr(G, ai->name); auto ai_name_len = strlen(ai_name); bool start_column_1 = false; UtilNCopy(name, ai_name, 5); if(!ai->name) { if(!ai->elem[1]) sprintf(name, " %s", ai->elem); else sprintf(name, "%s", ai->elem); } else if(!literal) { if(ai_name_len < 4) { /* atom name less than length 4 */ if(!isdigit(name[0])) { /* doesn't start with a number */ if((toupper(ai->elem[0]) == toupper(name[0])) && ((!ai->elem[1]) || /* symbol len = 1 */ (toupper(ai->elem[1]) == toupper(name[1])))) { /* matched len 2 */ /* starts with corrent atomic symbol, so */ if(!ai->elem[1]) { /* symbol len = 1 */ switch (reformat) { case 1: /* pdb with internal pdb */ case 3: /* pdb with internal iupac */ if((ai->elem[0] == 'H') && ai_name_len > 2) { AtomInfoGetPDB3LetHydroName(G, resn, ai_name, name); break; } default: /* otherwise, start in column 1 */ start_column_1 = true; break; } } } else { /* name doesn't start with atomic symbol */ /* then just place it in column 1 as usual */ start_column_1 = true; } } else { /* name starts with a number */ switch (reformat) { case 2: /* make Amber compliant */ if((ai->elem[0] == name[1]) && ((!ai->elem[1]) || (toupper(ai->elem[1]) == toupper(name[2])))) { /* rotate the name to place atom symbol in column 0 to comply with Amber PDB format */ name[3] = name[0]; name[0] = ' '; } break; } } /* just stick it in column 0 and hope for the best */ } else { /* if name is length 4 */ if((ai->elem[0] == name[0]) && ((!ai->elem[1]) || /* symbol len = 1 */ (toupper(ai->elem[1]) == toupper(name[1])))) { /* matched len 2 */ /* name starts with the atomic symbol */ if((!ai->elem[1]) && (ai->elem[0])) { /* but if element is one letter... */ switch (reformat) { case 1: /* retaining PDB compliance throughout, or */ case 3: /* saving as PDB compliant, but use IUPAC within PyMOL */ if(isdigit(name[3])) { /* and last character is a number */ /* rotate the name to place atom symbol in column 1 to comply with PDB format */ name[0] = ai_name[3]; name[1] = ai_name[0]; name[2] = ai_name[1]; name[3] = ai_name[2]; name[4] = 0; } break; } } } else { /* name does not start with the symbol... */ if(reformat == 2) { /* AMBER compliance mode */ if(isdigit(name[0])) { if((ai->elem[0] == name[1]) && ((!(ai->elem[1])) || (toupper(ai->elem[1]) == toupper(name[2])))) { /* rotate the name to place atom symbol in column 0 to comply with Amber PDB format */ name[0] = ai_name[1]; name[1] = ai_name[2]; name[2] = ai_name[3]; name[3] = ai_name[0]; name[4] = 0; } } } } } } else { /* LITERAL mode: preserve what was in the original PDB as best PyMOL can this should enable people to open and save amber pdb files without issues */ if (ai_name_len < 4 && !(ai->elem[1] && /* elem len = 2 */ toupper(ai->elem[0]) == toupper(name[0]) && toupper(ai->elem[1]) == toupper(name[1]))) { start_column_1 = true; } } if (start_column_1) { name[0] = ' '; UtilNCopy(name + 1, ai_name, 4); } name[4] = 0; }