layer2/ObjectMolecule2.cpp (3,861 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"Version.h"
#include"os_python.h"
#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"
#include"Base.h"
#include"Debug.h"
#include"Parse.h"
#include"OOMac.h"
#include"Vector.h"
#include"PConv.h"
#include"ObjectMolecule.h"
#include"Feedback.h"
#include"Util.h"
#include"Util2.h"
#include"AtomInfo.h"
#include"Selector.h"
#include"ObjectDist.h"
#include"Executive.h"
#include"P.h"
#include"ObjectCGO.h"
#include"Scene.h"
#include "Lex.h"
#include"AtomInfoHistory.h"
#include"BondTypeHistory.h"
#include <iostream>
#include <map>
#define ntrim ParseNTrim
#define nextline ParseNextLine
#define ncopy ParseNCopy
#define nskip ParseNSkip
#define cResvMask 0x7FFF
#define cMaxOther 6
#define strstartswith p_strstartswith
static int strstartswithword(const char * s, const char * word) {
while(*word)
if(*s++ != *word++)
return false;
switch(*s) {
case ' ':
case '\t':
case '\r':
case '\n':
case '\0':
return true;
}
return false;
}
static void AddOrthoOutputIfMatchesTags(PyMOLGlobals * G, int n_tags,
int nAtom, const char* const* tag_start, const char *p, char *cc, int quiet)
{
if(n_tags && !quiet && !(nAtom > 0 && strstartswith(p, "HEADER"))) {
// HEADER is the mark for a new object, this is a new object, and
// gets processed on the next pass, when nAtom=0
int tc = 0;
for(; tc < n_tags; tc++) {
if(!strstartswithword(p, tag_start[tc]))
continue;
ParseNTrimRight(cc, p, MAXLINELEN - 1);
OrthoAddOutput(G, cc);
OrthoNewLine(G, NULL, true);
break;
}
}
}
typedef struct {
int n_cyclic_arom, cyclic_arom[cMaxOther];
int n_arom, arom[cMaxOther];
int n_high_val, high_val[cMaxOther];
int n_cyclic, cyclic[cMaxOther];
int n_planer, planer[cMaxOther];
int n_rest, rest[cMaxOther];
int score;
} OtherRec;
static int populate_other(OtherRec * other, int at, AtomInfoType * ai, BondType * bd,
int *neighbor)
{
int five_cycle = false;
int six_cycle = false;
{
int mem[9], nbr[7];
const int ESCAPE_MAX = 500;
int escape_count;
escape_count = ESCAPE_MAX; /* don't get bogged down with structures
that have unreasonable connectivity */
mem[0] = bd->index[0];
mem[1] = bd->index[1];
nbr[1] = neighbor[mem[1]] + 1;
while(((mem[2] = neighbor[nbr[1]]) >= 0)) {
if(mem[2] != mem[0]) {
nbr[2] = neighbor[mem[2]] + 1;
while(((mem[3] = neighbor[nbr[2]]) >= 0)) {
if(mem[3] != mem[1]) {
nbr[3] = neighbor[mem[3]] + 1;
while(((mem[4] = neighbor[nbr[3]]) >= 0)) {
if((mem[4] != mem[2]) && (mem[4] != mem[1]) && (mem[4] != mem[0])) {
nbr[4] = neighbor[mem[4]] + 1;
while(((mem[5] = neighbor[nbr[4]]) >= 0)) {
if(!(escape_count--))
goto escape;
if((mem[5] != mem[3]) && (mem[5] != mem[2]) && (mem[5] != mem[1])) {
if(mem[5] == mem[0]) { /* five-cycle */
five_cycle = true;
}
nbr[5] = neighbor[mem[5]] + 1;
while(((mem[6] = neighbor[nbr[5]]) >= 0)) {
if((mem[6] != mem[4]) && (mem[6] != mem[3]) && (mem[6] != mem[2])
&& (mem[6] != mem[1])) {
if(mem[6] == mem[0]) { /* six-cycle */
six_cycle = true;
}
}
nbr[5] += 2;
}
}
nbr[4] += 2;
}
}
nbr[3] += 2;
}
}
nbr[2] += 2;
}
}
nbr[1] += 2;
}
}
escape:
if(bd->order == 4) { /* aromatic */
if((five_cycle || six_cycle) && (other->n_cyclic_arom < cMaxOther)) {
other->cyclic_arom[other->n_cyclic_arom++] = at;
if(five_cycle && six_cycle)
other->score += 34;
else if(five_cycle)
other->score += 33;
else
other->score += 32;
return 1;
} else if(other->n_arom < cMaxOther) {
other->arom[other->n_arom++] = at;
other->score += 64;
return 1;
}
}
if(bd->order > 1) {
if(other->n_high_val < cMaxOther) {
other->high_val[other->n_high_val++] = at;
other->score += 16;
return 1;
}
}
if(five_cycle || six_cycle) {
if(other->n_cyclic < cMaxOther) {
other->cyclic[other->n_cyclic++] = at;
other->score += 8;
return 1;
}
}
if(ai->geom == cAtomInfoPlanar) {
if(other->n_planer < cMaxOther) {
other->planer[other->n_planer++] = at;
other->score += 4;
return 1;
}
}
if(other->n_rest < cMaxOther) {
other->rest[other->n_rest++] = at;
other->score += 1;
return 1;
}
return 0;
}
static int append_index(int *result, int offset, int a1, int a2, int score, int ar_count)
{
int c;
c = result[a1];
while(c < offset) {
if(result[c] == a2) { /* already entered */
if(result[c + 1] < score) {
result[c + 1] = score;
result[c + 2] = ar_count;
}
return offset;
}
c += 3;
}
result[offset++] = a2;
result[offset++] = score;
result[offset++] = ar_count;
return offset;
}
int ObjectMoleculeAddPseudoatom(ObjectMolecule * I, int sele_index, const char *name,
const char *resn, const char *resi, const char *chain,
const char *segi, const char *elem, float vdw,
int hetatm, float b, float q, const char *label,
float *pos, int color, int state, int mode, int quiet)
{
PyMOLGlobals *G = I->Obj.G;
int start_state = 0, stop_state = 0;
int extant_only = false;
int ai_merged = false;
float pos_array[3] = { 0.0F, 0.0F, 0.0F };
int ok = true;
AtomInfoType *atInfo = VLACalloc(AtomInfoType, 1);
#ifdef _PYMOL_IP_EXTRAS
atInfo->oldid = -1;
#endif
if(state >= 0) { /* specific state */
start_state = state;
stop_state = state + 1;
} else if(state == -1) { /* current state */
start_state = ObjectGetCurrentState(&I->Obj, true);
stop_state = start_state + 1;
} else { /* all states */
if(sele_index >= 0) {
start_state = 0;
stop_state = SelectorCountStates(G, sele_index);
if(state == -3)
extant_only = true;
} else {
start_state = 0;
stop_state = ExecutiveCountStates(G, cKeywordAll);
if(stop_state < 1)
stop_state = 1;
}
}
{
/* match existing properties of the old atom */
AtomInfoType *ai = atInfo;
ai->setResi(resi);
ai->hetatm = hetatm;
ai->geom = cAtomInfoNone;
ai->q = q;
ai->b = b;
ai->chain = LexIdx(G, chain);
ai->segi = LexIdx(G, segi);
ai->resn = LexIdx(G, resn);
ai->name = LexIdx(G, name);
strcpy(ai->elem, elem);
ai->id = -1;
ai->rank = -1;
if(vdw >= 0.0F)
ai->vdw = vdw;
else
ai->vdw = 1.0F;
if(label[0]) {
ai->label = LexIdx(G, label);
ai->visRep = cRepLabelBit;
} else {
ai->visRep = RepGetAutoShowMask(G);
}
ai->flags |= cAtomFlag_inorganic; // suppress auto_show_classified
if(color < 0) {
AtomInfoAssignColors(I->Obj.G, ai);
if((ai->elem[0] == 'C') && (ai->elem[1] == 0))
/* carbons are always colored according to the object color */
ai->color = I->Obj.Color;
} else {
ai->color = color;
}
AtomInfoAssignParameters(I->Obj.G, ai);
AtomInfoUniquefyNames(I->Obj.G, I->AtomInfo, I->NAtom, ai, NULL, 1);
if(!quiet) {
PRINTFB(G, FB_ObjectMolecule, FB_Actions)
" ObjMol: created %s/%s/%s/%s`%d%c/%s\n",
I->Obj.Name, LexStr(G, ai->segi), LexStr(G, ai->chain),
LexStr(G, ai->resn), ai->resv, ai->getInscode(true),
LexStr(G, ai->name) ENDFB(G);
}
}
CoordSet *cset = CoordSetNew(G);
cset->NIndex = 1;
cset->Coord = VLAlloc(float, 3);
cset->Obj = I;
cset->enumIndices();
for(state = start_state; state < stop_state; state++) {
if((extant_only && (state < I->NCSet) && I->CSet[state]) || !extant_only) {
if(sele_index >= 0) {
ObjectMoleculeOpRec op;
ObjectMoleculeOpRecInit(&op);
op.code = OMOP_CSetSumVertices;
op.cs1 = state;
ExecutiveObjMolSeleOp(I->Obj.G, sele_index, &op);
if(op.i1) {
float factor = 1.0F / op.i1;
scale3f(op.v1, factor, pos_array);
pos = pos_array;
if(vdw < 0.0F) {
switch (mode) {
case 1:
ObjectMoleculeOpRecInit(&op);
op.code = OMOP_CSetMaxDistToPt;
copy3f(pos_array, op.v1);
op.cs1 = state;
ExecutiveObjMolSeleOp(I->Obj.G, sele_index, &op);
vdw = op.f1;
break;
case 2:
ObjectMoleculeOpRecInit(&op);
op.code = OMOP_CSetSumSqDistToPt;
copy3f(pos_array, op.v1);
op.cs1 = state;
ExecutiveObjMolSeleOp(I->Obj.G, sele_index, &op);
vdw = sqrt1f(op.d1 / op.i1);
break;
case 0:
default:
vdw = 0.5F;
break;
}
if(vdw >= 0.0F)
atInfo->vdw = vdw; /* NOTE: only uses vdw from first state selection... */
}
} else {
pos = NULL; /* skip this state */
}
} else if(!pos) {
pos = pos_array;
SceneGetCenter(I->Obj.G, pos);
}
if(pos) { /* only add coordinate to state if we have position for it */
float *coord = cset->Coord;
copy3f(pos, coord);
if(!ai_merged) {
if (ok)
ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_AllMask, true); /* NOTE: will release atInfo */
if (ok)
ok &= ObjectMoleculeExtendIndices(I, -1);
if (ok)
ok &= ObjectMoleculeUpdateNeighbors(I);
ai_merged = true;
}
if(state >= I->NCSet) {
VLACheck(I->CSet, CoordSet *, state);
I->NCSet = state + 1;
}
if(!I->CSet[state]) {
/* new coordinate set */
I->CSet[state] = CoordSetCopy(cset);
} else {
/* merge coordinate set */
if (ok)
ok &= CoordSetMerge(I, I->CSet[state], cset);
}
}
}
}
cset->fFree();
if(ai_merged) {
if (ok)
ok &= ObjectMoleculeSort(I);
ObjectMoleculeUpdateIDNumbers(I);
ObjectMoleculeUpdateNonbonded(I);
ObjectMoleculeInvalidate(I, cRepAll, cRepInvAtoms, -1);
} else {
VLAFreeP(atInfo);
}
return (ok);
}
int *ObjectMoleculeGetPrioritizedOtherIndexList(ObjectMolecule * I, CoordSet * cs)
{
int a, b;
int b1, b2, a1, a2, a3;
OtherRec *o;
OtherRec *other = Calloc(OtherRec, cs->NIndex);
int *result = NULL;
int offset;
int n_alloc = 0;
BondType *bd;
int ok = true;
CHECKOK(ok, other);
if (ok){
ok &= ObjectMoleculeUpdateNeighbors(I);
}
bd = I->Bond;
for(a = 0; ok && a < I->NBond; a++) {
b1 = bd->index[0];
b2 = bd->index[1];
if(I->DiscreteFlag) {
if((cs == I->DiscreteCSet[b1]) && (cs == I->DiscreteCSet[b2])) {
a1 = I->DiscreteAtmToIdx[b1];
a2 = I->DiscreteAtmToIdx[b2];
} else {
a1 = -1;
a2 = -1;
}
} else {
a1 = cs->AtmToIdx[b1];
a2 = cs->AtmToIdx[b2];
}
if((a1 >= 0) && (a2 >= 0)) {
n_alloc += populate_other(other + a1, a2, I->AtomInfo + b2, bd, I->Neighbor);
n_alloc += populate_other(other + a2, a1, I->AtomInfo + b1, bd, I->Neighbor);
}
bd++;
ok &= !I->Obj.G->Interrupt;
}
if (ok){
n_alloc = 3 * (n_alloc + cs->NIndex);
o = other;
result = Alloc(int, n_alloc);
CHECKOK(ok, result);
}
if (ok){
for(a = 0; a < cs->NIndex; a++) {
result[a] = -1;
}
offset = cs->NIndex;
bd = I->Bond;
}
for(a = 0; ok && a < I->NBond; a++) {
b1 = bd->index[0];
b2 = bd->index[1];
if(I->DiscreteFlag) {
if((cs == I->DiscreteCSet[b1]) && (cs == I->DiscreteCSet[b2])) {
a1 = I->DiscreteAtmToIdx[b1];
a2 = I->DiscreteAtmToIdx[b2];
} else {
a1 = -1;
a2 = -1;
}
} else {
a1 = cs->AtmToIdx[b1];
a2 = cs->AtmToIdx[b2];
}
if((a1 >= 0) && (a2 >= 0)) {
if(result[a1] < 0) {
o = other + a1;
result[a1] = offset;
for(b = 0; b < o->n_cyclic_arom; b++) {
a3 = o->cyclic_arom[b];
offset = append_index(result, offset, a1, a3, 128 + other[a3].score, 1);
}
for(b = 0; b < o->n_arom; b++) {
a3 = o->arom[b];
offset = append_index(result, offset, a1, a3, 64 + other[a3].score, 1);
}
for(b = 0; b < o->n_high_val; b++) {
a3 = o->high_val[b];
offset = append_index(result, offset, a1, a3, 16 + other[a3].score, 0);
}
for(b = 0; b < o->n_cyclic; b++) {
a3 = o->cyclic[b];
offset = append_index(result, offset, a1, a3, 8 + other[a3].score, 0);
}
for(b = 0; b < o->n_planer; b++) {
a3 = o->planer[b];
offset = append_index(result, offset, a1, a3, 2 + other[a3].score, 0);
}
for(b = 0; b < o->n_rest; b++) {
a3 = o->rest[b];
offset = append_index(result, offset, a1, a3, 1 + other[a3].score, 0);
}
result[offset++] = -1;
}
if(result[a2] < 0) {
o = other + a2;
result[a2] = offset;
for(b = 0; b < o->n_cyclic_arom; b++) {
a3 = o->cyclic_arom[b];
offset = append_index(result, offset, a2, a3, 128 + other[a3].score, 1);
}
for(b = 0; b < o->n_arom; b++) {
a3 = o->arom[b];
offset = append_index(result, offset, a2, a3, 64 + other[a3].score, 1);
}
for(b = 0; b < o->n_high_val; b++) {
a3 = o->high_val[b];
offset = append_index(result, offset, a2, a3, 16 + other[a3].score, 0);
}
for(b = 0; b < o->n_cyclic; b++) {
a3 = o->cyclic[b];
offset = append_index(result, offset, a2, a3, 8 + other[a3].score, 0);
}
for(b = 0; b < o->n_planer; b++) {
a3 = o->planer[b];
offset = append_index(result, offset, a2, a3, 2 + other[a3].score, 0);
}
for(b = 0; b < o->n_rest; b++) {
a3 = o->rest[b];
offset = append_index(result, offset, a2, a3, 1 + other[a3].score, 0);
}
result[offset++] = -1;
}
}
bd++;
ok &= !I->Obj.G->Interrupt;
}
FreeP(other);
return result;
}
int ObjectMoleculeGetNearestBlendedColor(ObjectMolecule * I, const float *point,
float cutoff, int state, float *dist,
float *color, int sub_vdw)
{
int result = -1;
float tot_weight = 0.0F;
float cutoff2 = cutoff * cutoff;
float nearest = -1.0F;
color[0] = 0.0F;
color[1] = 0.0F;
color[2] = 0.0F;
if(state < 0)
state = ObjectGetCurrentState(&I->Obj, true);
if((state >= 0) && (state < I->NCSet)) {
CoordSet *cs = I->CSet[state];
if(cs) {
MapType *map;
CoordSetUpdateCoord2IdxMap(cs, cutoff);
if(sub_vdw) {
cutoff -= MAX_VDW;
cutoff2 = cutoff * cutoff;
}
nearest = cutoff2;
if((map = cs->Coord2Idx)) {
int a, b, c, d, e, f, j;
float test;
float *v;
MapLocus(map, point, &a, &b, &c);
for(d = a - 1; d <= a + 1; d++)
for(e = b - 1; e <= b + 1; e++)
for(f = c - 1; f <= c + 1; f++) {
j = *(MapFirst(map, d, e, f));
while(j >= 0) {
v = cs->Coord + (3 * j);
test = diffsq3f(v, point);
if(sub_vdw) {
test = sqrt1f(test);
test -= I->AtomInfo[cs->IdxToAtm[j]].vdw;
if(test < 0.0F)
test = 0.0F;
test = test * test;
}
if(test < cutoff2) {
float weight = cutoff - sqrt1f(test);
const float *at_col = ColorGet(I->Obj.G, I->AtomInfo[cs->IdxToAtm[j]].color);
color[0] += at_col[0] * weight;
color[1] += at_col[1] * weight;
color[2] += at_col[2] * weight;
tot_weight += weight;
}
if(test <= nearest) {
result = j;
nearest = test;
}
j = MapNext(map, j);
}
}
} else {
int j;
float test, *v = cs->Coord;
for(j = 0; j < cs->NIndex; j++) {
test = diffsq3f(v, point);
if(sub_vdw) {
test = sqrt1f(test);
test -= I->AtomInfo[cs->IdxToAtm[j]].vdw;
if(test < 0.0F)
test = 0.0F;
test = test * test;
}
if(test < cutoff2) {
float weight = cutoff - sqrt1f(test);
const float *at_col = ColorGet(I->Obj.G, I->AtomInfo[cs->IdxToAtm[j]].color);
color[0] += at_col[0] * weight;
color[1] += at_col[1] * weight;
color[2] += at_col[2] * weight;
tot_weight += weight;
}
if(test <= nearest) {
result = j;
nearest = test;
}
v += 3;
}
}
if(result >= 0)
result = cs->IdxToAtm[result];
}
}
if(dist) {
if(result >= 0) {
*dist = sqrt1f(nearest);
if(tot_weight > 0.0F) {
color[0] /= tot_weight;
color[1] /= tot_weight;
color[2] /= tot_weight;
}
} else {
*dist = -1.0F;
}
}
return result;
}
int ObjectMoleculeGetNearestAtomIndex(ObjectMolecule * I, const float *point, float cutoff,
int state, float *dist)
{
int result = -1;
float nearest = -1.0F;
if(state < 0)
state = ObjectGetCurrentState(&I->Obj, true);
if((state >= 0) && (state < I->NCSet)) {
CoordSet *cs = I->CSet[state];
if(cs) {
MapType *map;
CoordSetUpdateCoord2IdxMap(cs, cutoff);
nearest = cutoff * cutoff;
if((map = cs->Coord2Idx)) {
int a, b, c, d, e, f, j;
float test;
float *v;
MapLocus(map, point, &a, &b, &c);
for(d = a - 1; d <= a + 1; d++)
for(e = b - 1; e <= b + 1; e++)
for(f = c - 1; f <= c + 1; f++) {
j = *(MapFirst(map, d, e, f));
while(j >= 0) {
v = cs->Coord + (3 * j);
test = diffsq3f(v, point);
if(test <= nearest) {
result = j;
nearest = test;
}
j = MapNext(map, j);
}
}
} else {
int j;
float test, *v = cs->Coord;
for(j = 0; j < cs->NIndex; j++) {
test = diffsq3f(v, point);
if(test <= nearest) {
result = j;
nearest = test;
}
v += 3;
}
}
if(result >= 0)
result = cs->IdxToAtm[result];
}
}
if(dist) {
if(result >= 0) {
*dist = sqrt1f(nearest);
} else {
*dist = -1.0F;
}
}
return result;
}
int ObjectMoleculeGetPrioritizedOther(int *other, int a1, int a2, int *double_sided)
{
int a3 = -1;
int lvl = -1, ck, ck_lvl;
int offset;
int ar_count = 0;
a3 = -1;
lvl = -1;
if(a1 >= 0) {
offset = other[a1];
if(offset >= 0) {
while(1) {
ck = other[offset];
if(ck != a2) {
if(ck >= 0) {
ck_lvl = other[offset + 1];
if(ck_lvl > lvl) {
a3 = ck;
lvl = ck_lvl;
}
ar_count += other[offset + 2];
} else
break;
}
offset += 3;
}
}
}
if(a2 >= 0) {
offset = other[a2];
if(offset >= 0) {
while(1) {
ck = other[offset];
if(ck != a1) {
if(ck >= 0) {
ck_lvl = other[offset + 1];
if(ck_lvl > lvl) {
a3 = ck;
lvl = ck_lvl;
}
ar_count += other[offset + 2];
} else
break;
}
offset += 3;
}
}
}
if(double_sided) {
if(ar_count == 4)
*double_sided = true;
else
*double_sided = false;
}
return a3;
}
/* Check if atom is bonded to an atom with given name
*
* param same_res:
* 0 = must not be in same residue
* 1 = must be in same residue
* -1 = don't check residue
*/
int ObjectMoleculeIsAtomBondedToName(ObjectMolecule * obj, int a0, const char *name, int same_res)
{
int a2, s;
PyMOLGlobals * G = obj->Obj.G;
AtomInfoType *ai2, *ai0 = obj->AtomInfo + a0;
if(a0 >= 0) {
ITERNEIGHBORATOMS(obj->Neighbor, a0, a2, s) {
ai2 = obj->AtomInfo + a2;
if(WordMatchExact(G, LexStr(G, ai2->name), name, true) &&
(same_res < 0 || (same_res == AtomInfoSameResidue(G, ai0, ai2))))
return true;
}
}
return false;
}
int ObjectMoleculeAreAtomsBonded2(ObjectMolecule * obj0, int a0, ObjectMolecule * obj1,
int a1)
{
/* assumes neighbor list is current */
if(obj0 != obj1)
return false;
else {
int a2, s;
if(a0 >= 0) {
s = obj0->Neighbor[a0];
s++; /* skip count */
while(1) {
a2 = obj0->Neighbor[s];
if(a2 < 0)
break;
if(a1 == a2)
return true;
s += 2;
}
}
}
return false;
}
int ObjectMoleculeDoesAtomNeighborSele(ObjectMolecule * I, int index, int sele)
{
int result = false;
ObjectMoleculeUpdateNeighbors(I);
if(index < I->NAtom) {
int a1;
int n;
AtomInfoType *ai;
n = I->Neighbor[index] + 1;
while(1) { /* look for an attached non-hydrogen as a base */
a1 = I->Neighbor[n];
n += 2;
if(a1 < 0)
break;
ai = I->AtomInfo + a1;
if(SelectorIsMember(I->Obj.G, ai->selEntry, sele)) {
result = true;
break;
}
}
}
return result;
}
/*
* Based on PDB nomenclature (resn, name), do:
*
* 1) If `ai1` or `ai2` is a known charged PDB atom, assign `formalCharge`
* and seth `chemFlag` to false
*
* 2) If `ai1` and `ai2` are connected by a double bond, set (*bond_order) = 2
*/
static void assign_pdb_known_residue(PyMOLGlobals * G, AtomInfoType * ai1,
AtomInfoType * ai2, int *bond_order)
{
int order = *(bond_order);
const char *name1 = LexStr(G, ai1->name);
const char *name2 = LexStr(G, ai2->name);
const char *resn1 = LexStr(G, ai1->resn);
/* nasty high-speed hack to get bond valences and formal charges
for standard residues */
if(((!name1[1]) && (!name2[1])) &&
(((name1[0] == 'C') && (name2[0] == 'O')) ||
((name1[0] == 'O') && (name2[0] == 'C')))) {
order = 2;
} else if((!name2[1]) && (name2[0] == 'C') && (!strcmp(name1, "OXT"))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if((!name1[1]) && (name1[0] == 'C') && (!strcmp(name2, "OXT"))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
} else {
switch (resn1[0]) {
case 'A':
switch (resn1[1]) {
case 'R':
switch (resn1[2]) {
case 'G': /* ARG... */
switch (resn1[3]) {
case 0:
case 'P': /* ARG, ARGP */
if(!strcmp(name1, "NH1")) {
ai1->formalCharge = 1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "NH1")) {
ai2->formalCharge = 1;
ai2->chemFlag = false;
}
break;
}
if(((!strcmp(name1, "CZ")) && (!strcmp(name2, "NH1"))) ||
((!strcmp(name2, "CZ")) && (!strcmp(name1, "NH1"))))
order = 2;
break;
}
break;
case 'S':
switch (resn1[2]) {
case 'P': /* ASP... */
switch (resn1[3]) {
case 0:
case 'M': /* ASP, ASPM minus assumption */
if(!strcmp(name1, "OD2")) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "OD2")) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
break;
}
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "OD1"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "OD1"))))
order = 2;
break;
case 'N': /* ASN */
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "OD1"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "OD1"))))
order = 2;
break;
}
break;
case 0:
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C8")) && (!strcmp(name2, "N7"))) ||
((!strcmp(name2, "C8")) && (!strcmp(name1, "N7"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "C5"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "C5"))))
order = 2;
else if(((!strcmp(name1, "C6")) && (!strcmp(name2, "N1"))) ||
((!strcmp(name2, "C6")) && (!strcmp(name1, "N1"))))
order = 2;
else if(((!strcmp(name1, "C2")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "N3"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
break;
}
break;
case 'C':
if(resn1[1] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "N3"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'D': /* deoxy nucleic acids */
switch (resn1[1]) {
case 'A':
if(resn1[2] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C8")) && (!strcmp(name2, "N7"))) ||
((!strcmp(name2, "C8")) && (!strcmp(name1, "N7"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "C5"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "C5"))))
order = 2;
else if(((!strcmp(name1, "C6")) && (!strcmp(name2, "N1"))) ||
((!strcmp(name2, "C6")) && (!strcmp(name1, "N1"))))
order = 2;
else if(((!strcmp(name1, "C2")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "N3"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'C':
if(resn1[2] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "N3"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'T':
if(resn1[2] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2"))))
ai1->formalCharge = -1;
else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2"))))
ai2->formalCharge = -1;
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "O4"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "O4"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'G':
if(resn1[2] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C6")) && (!strcmp(name2, "O6"))) ||
((!strcmp(name2, "C6")) && (!strcmp(name1, "O6"))))
order = 2;
else if(((!strcmp(name1, "C2")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "N3"))))
order = 2;
else if(((!strcmp(name1, "C8")) && (!strcmp(name2, "N7"))) ||
((!strcmp(name2, "C8")) && (!strcmp(name1, "N7"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "C5"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "C5"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'U':
if(resn1[2] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "O4"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "O4"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
}
break;
case 'G':
switch (resn1[1]) {
case 'L':
switch (resn1[2]) {
case 'U': /* GLU missing GLUN, GLUH, GLH handling */
switch (resn1[3]) {
case 0:
case 'M': /* minus */
if(!strcmp(name1, "OE2")) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "OE2")) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
break;
}
if(((!strcmp(name1, "CD")) && (!strcmp(name2, "OE1"))) ||
((!strcmp(name2, "CD")) && (!strcmp(name1, "OE1"))))
order = 2;
break;
case 'N': /* GLN or GLU */
if(((!strcmp(name1, "CD")) && (!strcmp(name2, "OE1"))) ||
((!strcmp(name2, "CD")) && (!strcmp(name1, "OE1"))))
order = 2;
break;
}
break;
case 0:
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C6")) && (!strcmp(name2, "O6"))) ||
((!strcmp(name2, "C6")) && (!strcmp(name1, "O6"))))
order = 2;
else if(((!strcmp(name1, "C2")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "N3"))))
order = 2;
else if(((!strcmp(name1, "C8")) && (!strcmp(name2, "N7"))) ||
((!strcmp(name2, "C8")) && (!strcmp(name1, "N7"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "C5"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "C5"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
break;
}
break;
case 'H':
switch (resn1[1]) {
case 'I':
switch (resn1[2]) {
case 'P':
if(!strcmp(name1, "ND1")) {
ai1->formalCharge = 1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "ND1")) {
ai2->formalCharge = 1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "ND1"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "ND1"))))
order = 2;
break;
case 'S':
switch (resn1[3]) {
case 'A': /* HISA Gromacs */
case 'D': /* HISD Quanta */
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "NE2"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "NE2"))))
order = 2;
break;
case 0: /* plain HIS */
case 'B': /* HISB Gromacs */
case 'E': /* HISE Quanta */
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "ND1"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "ND1"))))
order = 2;
break;
case 'H': /* HISH Gromacs */
case 'P': /* HISP Quanta */
if(!strcmp(name1, "ND1")) {
ai1->formalCharge = 1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "ND1")) {
ai2->formalCharge = 1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "ND1"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "ND1"))))
order = 2;
break;
}
break;
case 'E': /* HIE */
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "ND1"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "ND1"))))
order = 2;
break;
case 'D': /* HID */
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD2"))))
order = 2;
else if(((!strcmp(name1, "CE1")) && (!strcmp(name2, "NE2"))) ||
((!strcmp(name2, "CE1")) && (!strcmp(name1, "NE2"))))
order = 2;
break;
}
break;
}
break;
case 'I':
if(resn1[1] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C8")) && (!strcmp(name2, "N7"))) ||
((!strcmp(name2, "C8")) && (!strcmp(name1, "N7"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "C5"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "C5"))))
order = 2;
else if(((!strcmp(name1, "C6")) && (!strcmp(name2, "N1"))) ||
((!strcmp(name2, "C6")) && (!strcmp(name1, "N1"))))
order = 2;
else if(((!strcmp(name1, "C2")) && (!strcmp(name2, "N3"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "N3"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
case 'P':
switch (resn1[1]) {
case 'H': /* PHE */
if(resn1[2] == 'E') {
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD1"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD1"))))
order = 2;
else if(((!strcmp(name1, "CZ")) && (!strcmp(name2, "CE1"))) ||
((!strcmp(name2, "CZ")) && (!strcmp(name1, "CE1"))))
order = 2;
else if(((!strcmp(name1, "CE2")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CE2")) && (!strcmp(name1, "CD2"))))
order = 2;
break;
}
}
break;
case 'L':
switch (resn1[1]) {
case 'Y':
switch (resn1[2]) {
case 'S': /* LYS. */
switch (resn1[3]) {
case 0:
case 'P': /* LYS, LYSP */
if(!strcmp(name1, "NZ")) {
ai1->formalCharge = 1;
ai1->chemFlag = false;
} else if(!strcmp(name2, "NZ")) {
ai2->formalCharge = 1;
ai2->chemFlag = false;
}
break;
}
break;
}
break;
}
break;
case 'T':
switch (resn1[1]) {
case 'Y': /* TYR */
if(resn1[2] == 'R') {
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD1"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD1"))))
order = 2;
else if(((!strcmp(name1, "CZ")) && (!strcmp(name2, "CE1"))) ||
((!strcmp(name2, "CZ")) && (!strcmp(name1, "CE1"))))
order = 2;
else if(((!strcmp(name1, "CE2")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CE2")) && (!strcmp(name1, "CD2"))))
order = 2;
break;
}
break;
case 'R':
if(resn1[2] == 'P') {
if(((!strcmp(name1, "CG")) && (!strcmp(name2, "CD1"))) ||
((!strcmp(name2, "CG")) && (!strcmp(name1, "CD1"))))
order = 2;
else if(((!strcmp(name1, "CZ3")) && (!strcmp(name2, "CE3"))) ||
((!strcmp(name2, "CZ3")) && (!strcmp(name1, "CE3"))))
order = 2;
else if(((!strcmp(name1, "CZ2")) && (!strcmp(name2, "CH2"))) ||
((!strcmp(name2, "CZ2")) && (!strcmp(name1, "CH2"))))
order = 2;
else if(((!strcmp(name1, "CE2")) && (!strcmp(name2, "CD2"))) ||
((!strcmp(name2, "CE2")) && (!strcmp(name1, "CD2"))))
order = 2;
break;
}
break;
case 0:
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "O4"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "O4"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
break;
}
break;
case 'U':
if(resn1[1] == 0) {
if(((!strcmp(name1, "O2P")) || (!strcmp(name1, "OP2")))) {
ai1->formalCharge = -1;
ai1->chemFlag = false;
} else if(((!strcmp(name2, "O2P")) || (!strcmp(name2, "OP2")))) {
ai2->formalCharge = -1;
ai2->chemFlag = false;
}
if(((!strcmp(name1, "C2")) && (!strcmp(name2, "O2"))) ||
((!strcmp(name2, "C2")) && (!strcmp(name1, "O2"))))
order = 2;
else if(((!strcmp(name1, "C4")) && (!strcmp(name2, "O4"))) ||
((!strcmp(name2, "C4")) && (!strcmp(name1, "O4"))))
order = 2;
else if(((!strcmp(name1, "C5")) && (!strcmp(name2, "C6"))) ||
((!strcmp(name2, "C5")) && (!strcmp(name1, "C6"))))
order = 2;
else
if(((!strcmp(name1, "P"))
&& (((!strcmp(name2, "O1P")) || (!strcmp(name2, "OP1")))))
|| ((!strcmp(name2, "P"))
&& (((!strcmp(name1, "O1P")) || (!strcmp(name1, "OP1"))))))
order = 2;
}
break;
}
}
*(bond_order) = order;
}
void ObjectMoleculeFixChemistry(ObjectMolecule * I, int sele1, int sele2, int invalidate)
{
int b;
int flag = false;
int s1, s2;
AtomInfoType *ai1, *ai2;
int order;
BondType *bond;
bond = I->Bond;
for(b = 0; b < I->NBond; b++) {
flag = false;
ai1 = I->AtomInfo + bond->index[0];
ai2 = I->AtomInfo + bond->index[1];
s1 = ai1->selEntry;
s2 = ai2->selEntry;
if((SelectorIsMember(I->Obj.G, s1, sele1) &&
SelectorIsMember(I->Obj.G, s2, sele2)) ||
(SelectorIsMember(I->Obj.G, s2, sele1) && SelectorIsMember(I->Obj.G, s1, sele2))) {
order = -1;
if(strlen(LexStr(I->Obj.G, ai1->resn)) < 4) { /* Standard disconnected PDB residue */
if(AtomInfoSameResidue(I->Obj.G, ai1, ai2)) {
assign_pdb_known_residue(I->Obj.G, ai1, ai2, &order);
}
}
if(order > 0) {
bond->order = order;
ai1->chemFlag = false;
ai2->chemFlag = false;
flag = true;
} else if(invalidate) {
ai1->chemFlag = false;
ai2->chemFlag = false;
flag = true;
}
}
bond++;
}
if(flag) {
ObjectMoleculeInvalidate(I, cRepAll, cRepInvAll, -1);
SceneChanged(I->Obj.G);
}
}
void ObjMolPairwiseInit(ObjMolPairwise * pairwise)
{
UtilZeroMem((char *) pairwise, sizeof(ObjMolPairwise));
pairwise->trg_vla = VLAlloc(int, 10);
pairwise->mbl_vla = VLAlloc(int, 10);
}
void ObjMolPairwisePurge(ObjMolPairwise * pairwise)
{
VLAFreeP(pairwise->trg_vla);
VLAFreeP(pairwise->mbl_vla);
}
int ObjectMoleculeConvertIDsToIndices(ObjectMolecule * I, int *id, int n_id)
{
/* return true if all IDs are unique, false if otherwise */
int min_id, max_id, range, *lookup = NULL;
int unique = true;
/* this routine only works if IDs cover a reasonable range --
should rewrite using a hash table */
if(I->NAtom) {
/* determine range */
{
int a, cur_id;
cur_id = I->AtomInfo[0].id;
min_id = cur_id;
max_id = cur_id;
for(a = 1; a < I->NAtom; a++) {
cur_id = I->AtomInfo[a].id;
if(min_id > cur_id)
min_id = cur_id;
if(max_id < cur_id)
max_id = cur_id;
}
}
/* create cross-reference table */
{
int a, offset;
range = max_id - min_id + 1;
lookup = Calloc(int, range);
for(a = 0; a < I->NAtom; a++) {
offset = I->AtomInfo[a].id - min_id;
if(!lookup[offset])
lookup[offset] = a + 1;
else
unique = false;
}
}
/* iterate through IDs and replace with indices or -1 */
{
int i, offset, lkup;
for(i = 0; i < n_id; i++) {
offset = id[i] - min_id;
if((offset >= 0) && (offset < range)) {
lkup = lookup[offset];
if(lkup > 0) {
id[i] = lkup - 1;
} else {
id[i] = -1; /* negative means no match */
}
} else
id[i] = -1;
}
}
}
FreeP(lookup);
return unique;
}
static const char *check_next_pdb_object(const char *p, int skip_to_next)
{
const char *start = p;
while(*p) {
if(strstartswith(p, "HEADER")) {
if(skip_to_next)
return p;
return start;
} else if(strstartswith(p, "ATOM ") || strstartswith(p, "HETATM")) {
return start;
} else if(skip_to_next && strcmp("END", p) == 0) {
/* if we pass over the END of the current PDB file, then reset start */
start = p;
}
p = nextline(p);
}
return NULL;
}
static int get_multi_object_status(const char *p)
{ /* expensive -- only call
this if there is no
other way to determine
this information */
int seen_header = 0;
while(*p) {
if(strstartswith(p, "HEADER")) {
if(seen_header) {
return 1;
} else {
seen_header = true;
}
}
p = nextline(p);
}
return -1;
}
/*
* If any atom in I->AtomInfo contains the wildcard character (from
* "atom_name_wildcard" or "wildcard" setting), then set the object-level
* "atom_name_wildcard" setting to " " (disables wildcard matching).
*/
int ObjectMoleculeAutoDisableAtomNameWildcard(ObjectMolecule * I)
{
PyMOLGlobals *G = I->Obj.G;
char wildcard = 0;
int found_wildcard = false;
{
const char *tmp = SettingGet_s(G, NULL, I->Obj.Setting, cSetting_atom_name_wildcard);
if(tmp && tmp[0]) {
wildcard = *tmp;
} else {
tmp = SettingGet_s(G, NULL, I->Obj.Setting, cSetting_wildcard);
if(tmp) {
wildcard = *tmp;
}
}
if(wildcard == 32)
wildcard = 0;
}
if(wildcard) {
int a;
const char *p;
char ch;
AtomInfoType *ai = I->AtomInfo;
for(a = 0; a < I->NAtom; a++) {
p = LexStr(G, ai->name);
while((ch = *(p++))) {
if(ch == wildcard) {
found_wildcard = true;
break;
}
}
ai++;
}
if(found_wildcard) {
ExecutiveSetObjSettingFromString(G, cSetting_atom_name_wildcard, " ",
&I->Obj, -1, true, true);
}
}
return found_wildcard;
}
/*========================================================================*/
#define PDB_MAX_TAGS 64
static void ObjectMoleculePDBStr2CoordSetPASS1(PyMOLGlobals * G, int *ok,
const char **restart_model, const char *p, int n_tags, const char* const* tag_start,
int *nAtom, char cc[], int quiet, int *bogus_name_alignment,
int *ssFlag, const char **next_pdb, PDBInfoRec *info, int only_read_one_model,
int *ignore_conect, int *bondFlag, M4XAnnoType * m4x, int *have_bond_order) {
int seen_end_of_atoms = false;
*restart_model = NULL;
while(*ok && *p) {
AddOrthoOutputIfMatchesTags(G, n_tags, *nAtom, tag_start, p, cc, quiet);
if((strstartswith(p, "ATOM ") ||
strstartswith(p, "HETATM")) && (!*restart_model)) {
if(!seen_end_of_atoms)
(*nAtom)++;
if(*bogus_name_alignment) {
ncopy(cc, nskip(p, 12), 4); /* copy the atom field */
if((cc[0] == 32) && (cc[1] != 32)) { /* check to see if indentation was followed correctly, 32 - space */
*bogus_name_alignment = false;
}
}
} else if(strstartswith(p, "HELIX ")){
*ssFlag = true;
}else if(strstartswith(p, "SHEET ")){
*ssFlag = true;
}else if(strstartswith(p, "HEADER")) {
if(*nAtom > 0) { /* if we've already found atom records, then this must be a new pdb */
(*next_pdb) = p;
break;
}
} else if(strstartswith(p, "REMARK")) {
// char * start = p; // USED TO SAVE REMARKS (TODO)
do {
if(info && strncmp(" GENERATED BY TRJCONV", p + 6, 24) == 0)
info->ignore_header_names = true;
p = nextline(p);
AddOrthoOutputIfMatchesTags(G, n_tags, *nAtom, tag_start, p, cc, quiet);
} while(strstartswith(p, "REMARK"));
// REMARKS are string from start to p, but not saved currently (TODO)
continue;
} else if(strstartswith(p, "ENDMDL") && (!*restart_model)) {
*restart_model = nextline(p);
seen_end_of_atoms = true;
if(only_read_one_model)
break;
} else if(strstartswith(p, "END")) { /* stop parsing after END */
ntrim(cc, p, 6);
if(strcmp("END", cc) == 0) { /* END */
seen_end_of_atoms = true;
if(next_pdb) {
p = nextline(p);
ncopy(cc, p, 6);
if(strcmp("HEADER", cc) == 0) {
(*next_pdb) = p; /* found another PDB file after this one... */
} else if(strcmp("ENDMDL", cc) == 0) {
seen_end_of_atoms = false;
}
}
break;
}
} else if(strstartswith(p, "CONECT")) {
if(!*ignore_conect)
*bondFlag = true;
} else if(strstartswith(p, "USER") && (!*restart_model)) {
/* Metaphorics key now 'USER M4X ', changed from 'USER ' */
if(strstartswith(p + 4, " M4X ") && m4x) {
p = nskip(p, 10);
p = ntrim(cc, p, 6);
m4x->annotated_flag = true;
switch (cc[0]) {
case 'H':
if(WordMatchExact(G, "HINT", cc, true)) {
p = nskip(p, 1);
p = ntrim(cc, p, 6); /* get context name */
if(WordMatchExact(G, "ALIGN", cc, true)) { /* ALIGN is special */
if(!m4x->align) {
m4x->align = Calloc(M4XAlignType, 1);
CHECKOK(*ok, m4x->align);
if (*ok){
M4XAlignInit(m4x->align);
p = nskip(p, 8);
p = ntrim(cc, p, 6); /* get visibility of this structure */
}
}
} else if(WordMatchExact(G, "HIDE", cc, true)) {
m4x->invisible = 1;
} else {
if(!m4x->context) {
m4x->context = VLACalloc(M4XContextType, 10);
CHECKOK(*ok, m4x->context);
}
if(*ok && m4x->context) {
int cn;
int found = false;
/* does context already exist ? */
for(cn = 0; cn < m4x->n_context; cn++) {
if(WordMatchExact(G, m4x->context[cn].name, cc, true)) {
found = true;
break;
}
}
/* if not, then create it */
if(!found) {
cn = m4x->n_context++;
VLACheck(m4x->context, M4XContextType, cn);
CHECKOK(*ok, m4x->context);
if (*ok){
UtilNCopy(m4x->context[cn].name, cc, sizeof(WordType));
}
}
while(*ok && *cc) {
p = nskip(p, 1);
p = ntrim(cc, p, 6);
switch (cc[0]) {
case 'B':
if(WordMatchExact(G, "BORDER", cc, true)) {
/* ignore PDB CONECT if BORDER present */
*ignore_conect = true;
*have_bond_order = true;
*bondFlag = true;
}
break;
case 'S':
if(WordMatchExact(G, "SITE", cc, true)) {
if(!m4x->context[cn].site) {
m4x->context[cn].site = VLAlloc(int, 50);
CHECKOK(*ok, m4x->context[cn].site);
}
}
break;
case 'L':
if(WordMatchExact(G, "LIGAND", cc, true)) {
if(!m4x->context[cn].ligand) {
m4x->context[cn].ligand = VLAlloc(int, 50);
CHECKOK(*ok, m4x->context[cn].ligand);
}
}
break;
case 'W':
if(WordMatchExact(G, "WATER", cc, true)) {
if(!m4x->context[cn].water) {
m4x->context[cn].water = VLAlloc(int, 50);
CHECKOK(*ok, m4x->context[cn].water);
}
}
break;
case 'H':
if(WordMatchExact(G, "HBOND", cc, true)) {
if(!m4x->context[cn].hbond) {
m4x->context[cn].hbond = VLAlloc(M4XBondType, 50);
CHECKOK(*ok, m4x->context[cn].hbond);
}
}
break;
case 'N':
if(WordMatchExact(G, "NBOND", cc, true)) {
if(!m4x->context[cn].nbond) {
m4x->context[cn].nbond = VLAlloc(M4XBondType, 50);
CHECKOK(*ok, m4x->context[cn].nbond);
}
}
break;
}
}
}
}
}
}
}
}
p = nextline(p);
}
}
/*
* Datastructure for efficient array-based secondary structure lookup.
*/
typedef struct {
int n_ss; // number of ss_list items
int* ss[256]; // one array for each chain identifier
SSEntry *ss_list; // VLA
} SSHash;
static void sshash_free(SSHash *hash) {
int a;
if(!hash)
return;
for(a = 0; a <= 255; a++)
FreeP(hash->ss[a]);
VLAFreeP(hash->ss_list);
FreeP(hash);
}
static SSHash * sshash_new() {
SSHash *hash = Calloc(SSHash, 1);
ok_assert(1, hash);
hash->n_ss = 1;
hash->ss_list = VLAlloc(SSEntry, 50);
ok_assert(1, hash->ss_list);
return hash;
ok_except1:
sshash_free(hash);
return NULL;
}
/*
* Insert a secondary structure record into the hash table.
*/
static int sshash_register_rec(SSHash * hash,
unsigned char ss_chain1, int ss_resv1, char ss_inscode1,
unsigned char ss_chain2, int ss_resv2, char ss_inscode2,
char SSCode) {
/* pretty confusing how this works... the following efficient (i.e. array-based)
secondary structure lookup even when there are multiple insertion codes
and where there may be multiple SS records for the residue using different
insertion codes */
unsigned char chain;
int ss_found = false, ssi = 0, a, b, index;
SSEntry *sst;
// up to two iterations:
// 1) assume chain1==chain2
// 2) chains are not the same (undefined in PDB spec?)
for (a = 0, chain = ss_chain1; a < 2; a++, chain = ss_chain2) {
// allocate new array for chain if necc.
if(!hash->ss[chain]) {
ok_assert(1, hash->ss[chain] = Calloc(int, cResvMask + 1));
}
sst = NULL;
// iterate over all residues indicated
for(b = ss_resv1; b <= ss_resv2; b++) {
index = b & cResvMask;
if(hash->ss[chain][index]) {
// make a unique copy in the event of multiple entries for one resv
sst = NULL;
}
if(!sst) {
VLACheck(hash->ss_list, SSEntry, hash->n_ss);
ok_assert(1, hash->ss_list);
ssi = (hash->n_ss)++;
sst = hash->ss_list + ssi;
sst->resv1 = ss_resv1;
sst->resv2 = ss_resv2;
sst->chain1 = ss_chain1;
sst->chain2 = ss_chain2;
sst->type = SSCode;
sst->inscode1 = ss_inscode1;
sst->inscode2 = ss_inscode2;
ss_found = true;
}
sst->next = hash->ss[chain][index];
hash->ss[chain][index] = ssi;
if(sst->next)
sst = NULL; /* force another unique copy */
}
}
return ss_found;
ok_except1:
return false;
}
/*
* Assign ai->ssType
*/
static void sshash_lookup(SSHash *hash, AtomInfoType *ai, unsigned char ss_chain1) {
int index, ssi;
SSEntry *sst = NULL;
index = ai->resv & cResvMask;
if(hash->ss[ss_chain1]) {
ssi = hash->ss[ss_chain1][index];
while(ssi) {
sst = hash->ss_list + ssi;
/* contains shared entry, or unique linked list for each residue */
if( ai->resv >= sst->resv1
&& ai->resv <= sst->resv2
&& (ai->resv != sst->resv1 || ai->inscode >= sst->inscode1)
&& (ai->resv != sst->resv2 || ai->inscode <= sst->inscode2))
{
ai->ssType[0] = sst->type;
return;
}
ssi = sst->next;
}
}
}
/*
* PQR atom line parsing
*
* Try to parse columns white space delimited (10 columns with optional
* chain, 9 without).
*
* Where PQR files come from:
*
* pdb2pqr -> writes PDB-like fixed colums. APBS will fail to read those files
* if columns are too wide.
*
* pdb2pqr --whitespace -> puts extra whitespace, starting at column 2, but
* not between chain and resi! PyMOL <= 2.1 fails to read those files.
*
* APBS Tools Plugin by Michael Lerner adds extra whitespace before negative
* coordinates (assuming -100.0 is the most likely source of error).
*
* @param[in,out] p Pointer to parse from, points after the ATOM field.
* Will move the pointer to the end of the line if
* parsing was successful.
* @param[out] ai Atom to populate with data
* @param[out] coord Coordinates to populate
* @return true on success, false otherwise.
*/
static bool parse_pqr_atom_line(PyMOLGlobals * G,
const char * &p,
AtomInfoType * ai,
float * coord)
{
auto p_eol = nskip(p, 999); // end of line pointer
std::string cc(p, p_eol); // line (starting after ATOM field)
// whitespace splitting
auto columns = strsplit(cc);
// insert chain column if missing
if (columns.size() == 9) {
columns.insert(columns.begin() + 3, "");
// check for concatenated chain + resi
if (columns[4].size() > 4 && !isdigit(columns[4][0])) {
columns[3] = columns[4].substr(0, 1);
columns[4] = columns[4].substr(1);
}
}
// for validation: if number parsing consumes the entire string, then dummy
// will never be populated (sscanf(...) == 1, not 2)
char dummy[2];
// validate numeric fields and populate atom info and coordinates
if (columns.size() == 10 &&
sscanf(columns[0].c_str(), "%d%1s", &ai->id, dummy) == 1 &&
sscanf(columns[5].c_str(), "%f%1s", coord + 0, dummy) == 1 &&
sscanf(columns[6].c_str(), "%f%1s", coord + 1, dummy) == 1 &&
sscanf(columns[7].c_str(), "%f%1s", coord + 2, dummy) == 1 &&
sscanf(columns[8].c_str(), "%f%1s", &ai->partialCharge, dummy) == 1 &&
sscanf(columns[9].c_str(), "%f%1s", &ai->elec_radius, dummy) == 1) {
LexAssign(G, ai->name, columns[1].c_str());
LexAssign(G, ai->resn, columns[2].c_str());
LexAssign(G, ai->chain, columns[3].c_str());
ai->setResi(columns[4].c_str());
// move parser to next line
p = p_eol;
return true;
}
return false;
}
CoordSet *ObjectMoleculePDBStr2CoordSet(PyMOLGlobals * G,
const char *buffer,
AtomInfoType ** atInfoPtr,
const char **restart_model,
char *segi_override,
M4XAnnoType * m4x,
char *pdb_name,
const char **next_pdb,
PDBInfoRec * info, int quiet, int *model_number)
{
const char *p;
int nAtom;
int a;
float *coord = NULL;
CoordSet *cset = NULL;
AtomInfoType *atInfo = NULL, *ai;
int AFlag;
char SSCode;
int atomCount;
int bondFlag = false;
BondType *bond = NULL, *ii1, *ii2;
int *idx;
int nBond = 0;
int b1, b2, nReal, maxAt;
CSymmetry *symmetry = NULL;
int auto_show = RepGetAutoShowMask(G);
int reformat_names = SettingGetGlobal_i(G, cSetting_pdb_reformat_names_mode);
int truncate_resn = SettingGetGlobal_b(G, cSetting_pdb_truncate_residue_name);
const char *tags_in = SettingGetGlobal_s(G, cSetting_pdb_echo_tags), *tag_start[PDB_MAX_TAGS];
int n_tags = 0;
int foundNextModelFlag = false;
int ssFlag = false;
int ss_resv1 = 0, ss_resv2 = 0;
char ss_inscode1 = '\0', ss_inscode2 = '\0';
unsigned char ss_chain1 = 0, ss_chain2 = 0;
SSHash *ss_hash = NULL;
char cc[MAXLINELEN], tags[MAXLINELEN];
int ignore_pdb_segi = 0;
int ss_valid, ss_found = false;
int only_read_one_model = false;
int ignore_conect = SettingGetGlobal_b(G, cSetting_pdb_ignore_conect);
int have_bond_order = false;
int seen_model, in_model = false;
int is_end_of_object = false;
int literal_names = SettingGetGlobal_b(G, cSetting_pdb_literal_names);
int bogus_name_alignment = true;
AtomName literal_name = "";
int ok = true;
lexidx_t segi_override_idx = LexIdx(G, segi_override);
if(tags_in && (!quiet) && (!*restart_model)) {
char *p = tags;
strcpy(tags, tags_in);
while(*p) {
while(*p == ' ') /* skip spaces */
p++;
if(*p) {
tag_start[n_tags] = p;
n_tags++;
while(*p) {
if(*p != ',') {
if(*p == ' ')
*p = 0;
p++;
} else
break;
}
if(*p) { /* terminate tag */
*p = 0;
p++;
}
}
}
}
if(literal_names)
reformat_names = 0;
ignore_pdb_segi = SettingGetGlobal_b(G, cSetting_ignore_pdb_segi);
p = buffer;
nAtom = 0;
if(atInfoPtr)
atInfo = *atInfoPtr;
if(!atInfo)
ErrFatal(G, "PDBStr2CoordSet", "need atom information record!"); /* failsafe for old version.. */
if(buffer == *restart_model)
only_read_one_model = true;
else if(info && (info->multiplex > 0)) {
only_read_one_model = true;
if(!info->multi_object_status) { /* figure out if this is a multi-object (multi-HEADER) pdb file */
info->multi_object_status = get_multi_object_status(p);
}
}
/* PASS 1 */
ObjectMoleculePDBStr2CoordSetPASS1(G, &ok, restart_model, p, n_tags,
tag_start, &nAtom, cc, quiet, &bogus_name_alignment, &ssFlag, next_pdb,
info, only_read_one_model, &ignore_conect, &bondFlag, m4x, &have_bond_order);
/* INPUT USED:
only_read_one_model - only reads one pdb model if set
n_tags, tag_start - tags defined to report in log
cc - just temporary char * buffer that is used, no input/output
OUTPUT USED:
bogus_name_alignment - whether all ATOM/HETATM has correct names in PDB (12-16, starting with space
ssFlag - if PDB has HELIX and/or SHEET records
m4x - M4XAnnoType information from Metaphorics (USER records)
both INPUT/OUTPUT:
nAtom - returns the number of atoms read in, also uses it to detect multiple PDBs
ignore_conect - do not set bondFlag if set, also set if Metaphorics has BORDER info (bondFlag will also be set in this case)
END PASS 1 */
*restart_model = NULL;
if (ok){
coord = VLAlloc(float, 3 * nAtom);
CHECKOK(ok, coord);
}
if(ok && atInfo){
VLACheck(atInfo, AtomInfoType, nAtom);
CHECKOK(ok, atInfo);
if (ok)
*atInfoPtr = atInfo;
}
if(ok && bondFlag) {
nBond = 0;
bond = VLACalloc(BondType, 6 * nAtom);
CHECKOK(ok, bond);
}
p = buffer;
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMoleculeReadPDB: Found %i atoms...\n", nAtom ENDFB(G);
if(ok && ssFlag) {
ss_hash = sshash_new();
}
a = 0; /* WATCHOUT */
atomCount = 0;
/* PASS 2 */
seen_model = false;
while(ok && *p) {
/* printf("%c%c%c%c%c%c\n",p[0],p[1],p[2],p[3],p[4],p[5]); */
AFlag = false;
SSCode = 0;
if(strstartswith(p, "ATOM "))
AFlag = 1;
else if(strstartswith(p, "HETATM"))
AFlag = 2;
else if(strstartswith(p, "HEADER")) {
if(pdb_name) {
if(atomCount > 0) {
/* have we already read atoms? then this is probably a new PDB file */
(*next_pdb) = p; /* found another PDB file after this one... */
break;
} else if((!info) || (!info->ignore_header_names)) {
/* if this isn't an MD trajectory... */
const char *pp;
pp = nskip(p, 62); /* is there a four-letter PDB code? */
pp = ntrim(pdb_name, pp, 4);
if(pdb_name[0] == 0) { /* if not, is there a plain name (for MERCK!) */
p = nskip(p, 6);
p = ntrim(cc, p, 44);
UtilNCopy(pdb_name, cc, WordLength);
} else {
p = pp;
}
}
}
} else if(strstartswith(p, "MODEL ")) {
if(model_number) {
int tmp;
p = nskip(p, 10);
p = ncopy(cc, p, 5);
if(sscanf(cc, "%d", &tmp) == 1)
*model_number = tmp;
}
seen_model = true;
in_model = true;
} else if(strstartswith(p, "HELIX ")) {
ss_valid = true;
p = nskip(p, 19);
ss_chain1 = (*p);
p = nskip(p, 2);
p = ncopy(cc, p, 4);
if(!sscanf(cc, "%d", &ss_resv1))
ss_valid = false;
ss_inscode1 = makeInscode(*p);
p = nskip(p, 6);
ss_chain2 = (*p);
p = nskip(p, 2);
p = ncopy(cc, p, 4);
if(!sscanf(cc, "%d", &ss_resv2))
ss_valid = false;
ss_inscode2 = makeInscode(*p);
if(ss_valid) {
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMolecule: read HELIX %c %d%.1s %c %d%.1s\n",
ss_chain1, ss_resv1, &ss_inscode1, ss_chain2, ss_resv2, &ss_inscode2 ENDFB(G);
SSCode = 'H';
}
if(ss_chain1 == ' ')
ss_chain1 = 0;
if(ss_chain2 == ' ')
ss_chain2 = 0;
} else if(strstartswith(p, "SHEET ")) {
ss_valid = true;
p = nskip(p, 21);
ss_chain1 = (*p);
p = nskip(p, 1);
p = ncopy(cc, p, 4);
if(!sscanf(cc, "%d", &ss_resv1))
ss_valid = false;
ss_inscode1 = makeInscode(*p);
p = nskip(p, 6);
ss_chain2 = (*p);
p = nskip(p, 1);
p = ncopy(cc, p, 4);
if(!sscanf(cc, "%d", &ss_resv2))
ss_valid = false;
ss_inscode2 = makeInscode(*p);
if(ss_valid) {
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMolecule: read SHEET %c %d%.1s %c %d%.1s\n",
ss_chain1, ss_resv1, &ss_inscode1, ss_chain2, ss_resv2, &ss_inscode2 ENDFB(G);
SSCode = 'S';
}
if(ss_chain1 == ' ')
ss_chain1 = 0;
if(ss_chain2 == ' ')
ss_chain2 = 0;
} else if(strstartswith(p, "ENDMDL")) {
if(*restart_model)
in_model = false;
else {
*restart_model = nextline(p);
in_model = false;
if(only_read_one_model) {
const char *pp;
pp = nextline(p);
if(strstartswith(pp, "END")) { /* END we're going to be starting a new object... */
(*next_pdb) = check_next_pdb_object(nextline(pp), true);
if(info && (info->multiplex == 0)) {
/* multiplex == 0: FORCED multimodel behavior with concatenated PDB files */
(*restart_model) = (*next_pdb);
(*next_pdb) = NULL;
foundNextModelFlag = true;
info->multi_object_status = -1;
} else {
is_end_of_object = true;
}
} else if(strstartswith(pp, "MODEL")) { /* not a new object...just a new state (model) */
if(info && (info->multiplex > 0)) { /* end object if we're multiplexing */
(*next_pdb) = check_next_pdb_object(pp, true);
(*restart_model) = NULL;
} else
is_end_of_object = false;
} else {
if(pp[0] > 32) /* more content follows... */
(*next_pdb) = check_next_pdb_object(pp, true);
else
(*next_pdb) = NULL; /* at end of file */
}
break;
}
}
} else if(strstartswith(p, "END")) {
ntrim(cc, p, 6);
if((strcmp("END", cc) == 0) && (!in_model)) { /* stop parsing here... */
const char *pp;
pp = nextline(p); /* ...unless we're in MODEL or next line is itself ENDMDL */
p = ncopy(cc, p, 6);
if(!((cc[0] == 'E') && (cc[1] == 'N') && (cc[2] == 'D') && (cc[3] == 'M') && (cc[4] == 'D') && (cc[5] == 'L'))) { /* NOTE: this test seems unnecessary given strcmp above... */
if(!*next_pdb) {
(*next_pdb) = check_next_pdb_object(pp, false);
}
if((*next_pdb) && info && (!info->multiplex) && !(*restart_model)) {
/* multiplex == 0: FORCED multimodel behavior with concatenated PDB files */
(*restart_model) = (*next_pdb);
(*next_pdb) = NULL;
foundNextModelFlag = true;
info->multi_object_status = -1;
is_end_of_object = false;
break;
}
if(*next_pdb) { /* we've found another object... */
if(*restart_model)
is_end_of_object = false; /* however, if we're parsing multi-models, then we're not yet at the end */
else
is_end_of_object = true;
break;
} else if(!seen_model)
break;
}
}
} else if(strstartswith(p, "CRYST1") && (!*restart_model)) {
if(!symmetry){
symmetry = SymmetryNew(G);
CHECKOK(ok, symmetry);
}
if(symmetry) {
int symFlag = true;
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" PDBStrToCoordSet: Attempting to read symmetry information\n" ENDFB(G);
p = nskip(p, 6);
symFlag = true;
p = ncopy(cc, p, 9);
if(sscanf(cc, "%f", &symmetry->Crystal->Dim[0]) != 1)
symFlag = false;
p = ncopy(cc, p, 9);
if(sscanf(cc, "%f", &symmetry->Crystal->Dim[1]) != 1)
symFlag = false;
p = ncopy(cc, p, 9);
if(sscanf(cc, "%f", &symmetry->Crystal->Dim[2]) != 1)
symFlag = false;
p = ncopy(cc, p, 7);
if(sscanf(cc, "%f", &symmetry->Crystal->Angle[0]) != 1)
symFlag = false;
p = ncopy(cc, p, 7);
if(sscanf(cc, "%f", &symmetry->Crystal->Angle[1]) != 1)
symFlag = false;
p = ncopy(cc, p, 7);
if(sscanf(cc, "%f", &symmetry->Crystal->Angle[2]) != 1)
symFlag = false;
p = nskip(p, 1);
p = ncopy(symmetry->SpaceGroup, p, 11);
UtilCleanStr(symmetry->SpaceGroup);
p = ncopy(cc, p, 3);
if(sscanf(cc, "%d", &symmetry->PDBZValue) != 1)
symmetry->PDBZValue = 1;
if(!symFlag) {
ErrMessage(G, "PDBStrToCoordSet", "Error reading CRYST1 record\n");
SymmetryFree(symmetry);
symmetry = NULL;
}
}
} else if(strstartswith(p, "SCALE") && (!*restart_model) && info) { /* SCALEn */
switch (p[5]) {
case '1':
case '2':
case '3':
{
int flag = (p[5] - '1');
int offset = flag * 4;
int scale_flag = true;
p = nskip(p, 10);
p = ncopy(cc, p, 10);
if(sscanf(cc, "%f", &info->scale.matrix[offset]) != 1)
scale_flag = false;
p = ncopy(cc, p, 10);
if(sscanf(cc, "%f", &info->scale.matrix[offset + 1]) != 1)
scale_flag = false;
p = ncopy(cc, p, 10);
if(sscanf(cc, "%f", &info->scale.matrix[offset + 2]) != 1)
scale_flag = false;
p = nskip(p, 5);
p = ncopy(cc, p, 10);
if(sscanf(cc, "%f", &info->scale.matrix[offset + 3]) != 1)
scale_flag = false;
if(scale_flag)
info->scale.flag[flag] = true;
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" PDBStrToCoordSet: SCALE%d %8.4f %8.4f %8.4f %8.4f\n", flag + 1,
info->scale.matrix[offset],
info->scale.matrix[offset + 1],
info->scale.matrix[offset + 2], info->scale.matrix[offset + 3]
ENDFB(G);
}
break;
}
} else if(strstartswith(p, "CONECT") &&
bondFlag && (!ignore_conect) && ((!*restart_model) || (!in_model))) {
p = nskip(p, 6);
p = ncopy(cc, p, 5);
if(sscanf(cc, "%d", &b1) == 1)
while(1) {
p = ncopy(cc, p, 5);
if(sscanf(cc, "%d", &b2) != 1)
break;
else {
if((b1 >= 0) && (b2 >= 0) && (b1 != b2)) { /* IDs must be positive and distinct */
VLACheck(bond, BondType, nBond);
CHECKOK(ok, bond);
if (ok){
if(b1 <= b2) {
bond[nBond].index[0] = b1; /* temporarily store the atom indexes */
bond[nBond].index[1] = b2;
bond[nBond].order = 1;
bond[nBond].stereo = 0;
} else {
bond[nBond].index[0] = b2;
bond[nBond].index[1] = b1;
bond[nBond].order = 1;
bond[nBond].stereo = 0;
}
nBond++;
}
}
}
}
} else if(strstartswith(p, "USER") && (!*restart_model)) {
/* Metaphorics key 'USER M4X ' was 'USER ' */
if(strstartswith(p + 4, " M4X ") && m4x) {
int parsed_flag = false;
p = nskip(p, 10);
p = ntrim(cc, p, 6);
/* is this a context name or a USER record? */
switch (cc[0]) {
case 'X':
if(WordMatchExact(G, "XNAME", cc, true)) { /* object name */
p = nskip(p, 1);
p = ntrim(m4x->xname, p, 10);
if(m4x->xname[0]) {
m4x->xname_flag = true;
parsed_flag = true;
}
}
break;
case 'A': /* alignment information */
if(WordMatchExact(G, "ALIGN", cc, true)) {
if(m4x->align && m4x->align->id_at_point) {
M4XAlignType *align = m4x->align;
char target[11];
int atom_id, point_id;
float fitness;
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &atom_id) == 1) {
p = nskip(p, 1);
p = ntrim(target, p, 10);
if(target[0]) {
if(!align->target[0])
UtilNCopy(align->target, target, WordLength);
if(WordMatchExact(G, align->target, target, true)) { /* must match the one target allowed */
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &point_id) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%f", &fitness) == 1) {
VLACheck(align->id_at_point, int, point_id);
CHECKOK(ok, align->id_at_point);
if (ok){
VLACheck(align->fitness, float, point_id);
CHECKOK(ok, align->id_at_point);
if (ok){
if(point_id >= align->n_point)
align->n_point = point_id + 1;
align->id_at_point[point_id] = atom_id;
align->fitness[point_id] = fitness;
}
}
/* printf("read alignment atom %d to target %s point %d fitness %8.3f\n",
atom_id,target,point_id,fitness); */
}
}
}
}
}
}
parsed_flag = true;
}
break;
}
if(ok && (!parsed_flag) && m4x->context) { /* named context of some sort... */
int cn;
int found = false;
/* does context already exist ? */
for(cn = 0; cn < m4x->n_context; cn++) {
if(WordMatchExact(G, m4x->context[cn].name, cc, true)) {
found = true;
break;
}
}
if(found) {
M4XContextType *cont = m4x->context + cn;
p = nskip(p, 1);
p = ntrim(cc, p, 6);
switch (cc[0]) {
case 'B':
if(WordMatchExact(G, "BORDER", cc, true) && bondFlag) {
int order;
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &b1) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &b2) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &order) == 1) {
if((b1 >= 0) && (b2 >= 0)) { /* IDs must be positive */
VLACheck(bond, BondType, nBond);
CHECKOK(ok, bond);
if (ok){
if(b1 <= b2) {
bond[nBond].index[0] = b1; /* temporarily store the atom indexes */
bond[nBond].index[1] = b2;
bond[nBond].order = order;
bond[nBond].stereo = 0;
} else {
bond[nBond].index[0] = b2;
bond[nBond].index[1] = b1;
bond[nBond].order = order;
bond[nBond].stereo = 0;
}
}
nBond++;
}
}
}
}
}
break;
case 'S':
if(WordMatchExact(G, "SITE", cc, true)) {
if(cont->site) {
int id;
while(ok && *cc) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id) == 1) {
VLACheck(cont->site, int, cont->n_site);
CHECKOK(ok, cont->site);
if (ok)
cont->site[cont->n_site++] = id;
}
}
}
}
break;
case 'L':
if(WordMatchExact(G, "LIGAND", cc, true)) {
if(cont->ligand) {
int id;
while(ok && *cc) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id) == 1) {
VLACheck(cont->ligand, int, cont->n_ligand);
CHECKOK(ok, cont->ligand);
if (ok)
cont->ligand[cont->n_ligand++] = id;
}
}
}
}
break;
case 'W':
if(WordMatchExact(G, "WATER", cc, true)) {
if(cont->water) {
int id;
while(ok && *cc) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id) == 1) {
VLACheck(cont->water, int, cont->n_water);
CHECKOK(ok, cont->water);
if (ok)
cont->water[cont->n_water++] = id;
}
}
}
}
break;
case 'H':
if(WordMatchExact(G, "HBOND", cc, true)) {
if(cont->hbond) {
int id1, id2;
float strength;
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id1) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id2) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%f", &strength) == 1) {
VLACheck(cont->hbond, M4XBondType, cont->n_hbond);
CHECKOK(ok, cont->hbond);
if (ok){
cont->hbond[cont->n_hbond].atom1 = id1;
cont->hbond[cont->n_hbond].atom2 = id2;
cont->hbond[cont->n_hbond].strength = strength;
cont->n_hbond++;
}
}
}
}
}
}
break;
case 'N':
if(WordMatchExact(G, "NBOND", cc, true)) {
if(cont->nbond) {
int id1, id2;
float strength;
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id1) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%d", &id2) == 1) {
p = nskip(p, 1);
p = ncopy(cc, p, 6);
if(sscanf(cc, "%f", &strength) == 1) {
VLACheck(cont->nbond, M4XBondType, cont->n_nbond);
CHECKOK(ok, cont->nbond);
if (ok){
cont->nbond[cont->n_nbond].atom1 = id1;
cont->nbond[cont->n_nbond].atom2 = id2;
cont->nbond[cont->n_nbond].strength = strength;
cont->n_nbond++;
}
}
}
}
}
}
break;
}
}
}
}
} else if(strstartswith(p, "ANISOU") && (!*restart_model) && (atomCount)) {
ai = atInfo + atomCount - 1;
/* TODO: check atom identifier match */
{
int dummy;
p = nskip(p, 6);
p = ncopy(cc, p, 5);
if(!sscanf(cc, "%d", &dummy))
dummy = 0;
if(dummy == ai->id) { /* ATOM ID must match */
int dummy;
float * anisou = ai->get_anisou();
p = nskip(p, 17);
for (int i = 0; i < 6; ++i) {
p = ncopy(cc, p, 7);
if(sscanf(cc, "%d", &dummy))
anisou[i] = dummy / 10000.0F;
}
}
}
}
/* END KEYWORDS */
/* Secondary structure records */
if(ok && SSCode) {
ss_found = sshash_register_rec(ss_hash,
ss_chain1, ss_resv1, ss_inscode1,
ss_chain2, ss_resv2, ss_inscode2, SSCode);
}
/* Atom records */
if(ok && AFlag && (!*restart_model)) {
ai = atInfo + atomCount;
p = nskip(p, 6);
ai->rank = atomCount;
if(info && info->is_pqr_file()) {
if (parse_pqr_atom_line(G, p, ai, coord + a)) {
goto pqr_done;
}
}
p = ncopy(cc, p, 5);
if(!sscanf(cc, "%d", &ai->id))
ai->id = 0;
p = nskip(p, 1); /* to 12 */
p = ncopy(literal_name, p, 4);
if(literal_names) {
LexAssign(G, ai->name, literal_name);
} else {
ParseNTrim(cc, literal_name, 4);
LexAssign(G, ai->name, cc);
}
p = ncopy(cc, p, 1);
if(*cc == 32)
ai->alt[0] = 0;
else {
ai->alt[0] = *cc;
ai->alt[1] = 0;
}
p = ntrim(cc, p, 4); /* now allowing for 4-letter residues */
if (truncate_resn) /* unless specifically disabled */
cc[3] = 0;
LexAssign(G, ai->resn, cc);
if(ai->name) {
const char * ai_name = LexStr(G, ai->name);
int name_len = strlen(ai_name);
char name[5];
switch (reformat_names) {
case 1: /* pdb compliant: HH12 becomes 2HH1, etc. */
if(name_len > 3) {
if((ai_name[0] >= 'A') && ((ai_name[0] <= 'Z')) &&
isdigit(ai_name[3])) {
if(!(((ai_name[1] >= 'a') && (ai_name[1] <= 'z')) ||
((ai_name[0] == 'C') && (ai_name[1] == 'L')) || /* try to be smart about */
((ai_name[0] == 'B') && (ai_name[1] == 'R')) || /* distinguishing common atoms */
((ai_name[0] == 'C') && (ai_name[1] == 'A')) || /* in all-caps from typical */
((ai_name[0] == 'F') && (ai_name[1] == 'E')) || /* nonatomic abbreviations */
((ai_name[0] == 'C') && (ai_name[1] == 'U')) ||
((ai_name[0] == 'N') && (ai_name[1] == 'A')) ||
((ai_name[0] == 'N') && (ai_name[1] == 'I')) ||
((ai_name[0] == 'M') && (ai_name[1] == 'G')) ||
((ai_name[0] == 'M') && (ai_name[1] == 'N')) ||
((ai_name[0] == 'H') && (ai_name[1] == 'G')) ||
((ai_name[0] == 'S') && (ai_name[1] == 'E')) ||
((ai_name[0] == 'S') && (ai_name[1] == 'I')) ||
((ai_name[0] == 'Z') && (ai_name[1] == 'N'))
)) {
strncpy(name + 1, ai_name, 3);
name[0] = ai_name[3];
name[4] = 0;
LexAssign(G, ai->name, name);
}
}
} else if(name_len == 3) {
if((ai_name[0] == 'H') &&
(ai_name[1] >= 'A') && ((ai_name[1] <= 'Z')) &&
isdigit(ai_name[2])) {
AtomInfoGetPDB3LetHydroName(G, LexStr(G, ai->resn), ai_name, name);
LexAssign(G, ai->name, (name[0] == ' ') ? (name + 1) : name);
}
}
break;
case 2: /* amber compliant: 2HH1 becomes HH12 */
case 3: /* pdb compliant, but use IUPAC within PyMOL */
if(ai_name[0]) {
if(isdigit(ai_name[0]) && ai_name[1] && (!isdigit(ai_name[1]))) {
if (1 < name_len && name_len < 5) {
strcpy(name, ai_name + 1);
name[name_len - 1] = ai_name[0];
name[name_len] = 0;
LexAssign(G, ai->name, name);
}
break;
default: /* AS IS */
break;
}
}
break;
case 4: /* simply read trim and write back out with 3-letter names starting from the
second column, and four-letter names starting in the first */
ntrim(cc, ai_name, 4);
LexAssign(G, ai->name, cc);
break;
}
}
p = ncopy(cc, p, 1);
if (ai->chain){
LexDec(G, ai->chain);
}
if(*cc == ' ') {
ss_chain1 = 0;
ai->chain = 0;
} else {
ss_chain1 = *cc;
ai->chain = LexIdx(G, cc);
}
p = ncopy(cc, p, 4);
if(!sscanf(cc, "%d", &ai->resv))
ai->resv = 0;
ai->setInscode(*p);
p = nskip(p, 1);
if(ssFlag) { /* get secondary structure information (if avail) */
sshash_lookup(ss_hash, ai, ss_chain1);
} else {
ai->cartoon = cCartoon_tube;
}
{
p = nskip(p, 3);
p = ncopy(cc, p, 8);
sscanf(cc, "%f", coord + a);
p = ncopy(cc, p, 8);
sscanf(cc, "%f", coord + (a + 1));
p = ncopy(cc, p, 8);
sscanf(cc, "%f", coord + (a + 2));
}
if((!info) || (!info->is_pqr_file())) { /* standard PDB file */
p = ncopy(cc, p, 6);
if(!sscanf(cc, "%f", &ai->q))
ai->q = 1.0;
p = ncopy(cc, p, 6);
if(!sscanf(cc, "%f", &ai->b))
ai->b = 0.0;
if (info->variant == PDB_VARIANT_PDBQT) {
ignore_pdb_segi = true;
p = nskip(p, 4);
p = ncopy(cc, p, 6);
if(!sscanf(cc, "%f", &ai->partialCharge))
ai->partialCharge = 0.0;
// type is 78-79 in pdbqt, 77-78 in pdb
p = nskip(p, 1);
} else {
p = nskip(p, 6);
p = ncopy(cc, p, 4);
}
if(!ignore_pdb_segi) {
if(!segi_override_idx) {
if(cc[3] == '1' && atomCount && strncmp(p, "0000", 4) == 0) {
/* atom ID overflow? (nonstandard use...)... */
LexAssign(G, segi_override_idx, (ai - 1)->segi);
LexAssign(G, ai->segi, (ai - 1)->segi);
} else {
UtilCleanStr(cc);
LexAssign(G, ai->segi, cc);
}
} else {
LexAssign(G, ai->segi, segi_override_idx);
}
} else {
LexAssign(G, ai->segi, 0);
}
p = ncopy(cc, p, 2);
if(!sscanf(cc, "%s", ai->elem))
ai->elem[0] = 0;
else if(!((((ai->elem[0] >= 'a') && (ai->elem[0] <= 'z')) || /* don't get confused by PDB misuse */
((ai->elem[0] >= 'A') && (ai->elem[0] <= 'Z'))) &&
(((ai->elem[1] == 0) ||
((ai->elem[1] >= 'a') && (ai->elem[1] <= 'z')) ||
((ai->elem[1] >= 'A') && (ai->elem[1] <= 'Z'))))))
ai->elem[0] = 0;
else if (info->variant == PDB_VARIANT_PDBQT) {
if (strcmp(ai->elem, "A") == 0) {
// aromatic carbon
ai->elem[0] = 'C';
} else if (isupper(ai->elem[1])) {
// h-bond donor or acceptor
ai->elem[1] = 0;
}
}
if(!ai->elem[0]) {
if(((literal_name[0] == ' ') || ((literal_name[0] >= '0') && (literal_name[0] <= '9'))) && (literal_name[1] >= 'A') && (literal_name[1] <= 'Z')) { /* infer element from name column */
ai->elem[0] = literal_name[1];
ai->elem[1] = 0;
} else if(((literal_name[0] >= 'A') && (literal_name[0] <= 'Z')) && (((literal_name[1] >= 'A') && (literal_name[1] <= 'Z')) || ((literal_name[1] >= 'a') && (literal_name[1] <= 'z')))) { /* infer element from name column */
ai->elem[0] = literal_name[0];
ai->elem[2] = 0;
if((literal_name[1] >= 'A') && (literal_name[1] <= 'Z')) { /* second letter is capitalized */
if(bogus_name_alignment) {
/* if other atom names aren't properly aligned */
ai->elem[1] = 0; /* kill 2nd letter */
} else if(literal_name[0] == 'H') {
/* or if this is an ultra-bogus PDB with inconsistent
indendentation, and this is likely a hydrogen */
ai->elem[1] = 0; /* kill 2nd letter */
} else {
ai->elem[1] = tolower(literal_name[1]);
}
} else
ai->elem[1] = literal_name[1];
}
}
p = ncopy(cc, p, 2);
if((cc[1] == '-') || (cc[1] == '+')) {
/* only read formal charge when sign is present */
char ctmp = cc[0];
cc[0] = cc[1];
cc[1] = ctmp;
if(!sscanf(cc, "%hhi", &ai->formalCharge))
ai->formalCharge = 0;
}
/* end normal PDB */
} else if(info && info->is_pqr_file()) {
p = ParseWordNumberCopy(cc, p, MAXLINELEN - 1);
if(!sscanf(cc, "%f", &ai->partialCharge))
ai->partialCharge = 0.0F;
p = ParseWordNumberCopy(cc, p, MAXLINELEN - 1);
if(sscanf(cc, "%f", &ai->elec_radius) != 1)
ai->elec_radius = 0.0F;
}
pqr_done:
ai->visRep = auto_show;
if(AFlag == 1)
ai->hetatm = 0;
else {
ai->hetatm = 1;
ai->flags = cAtomFlag_ignore;
}
AtomInfoAssignParameters(G, ai);
AtomInfoAssignColors(G, ai);
PRINTFD(G, FB_ObjectMolecule)
"%s %s %d%c %s %8.3f %8.3f %8.3f %6.2f %6.2f %s\n",
LexStr(G, ai->name), LexStr(G, ai->resn), ai->resv, ai->getInscode(true), LexStr(G, ai->chain),
*(coord + a), *(coord + a + 1), *(coord + a + 2), ai->b, ai->q, LexStr(G, ai->segi) ENDFD;
if(atomCount < nAtom) { /* safety */
a += 3;
atomCount++;
}
}
p = nextline(p);
}
/* END PASS 2 */
if(ok && bondFlag) {
UtilSortInPlace(G, bond, nBond, sizeof(BondType), (UtilOrderFn *) BondInOrder);
if(nBond) {
if(!have_bond_order) { /* handle PDB bond-order kludge */
ii1 = bond;
ii2 = bond + 1;
nReal = 1;
ii1->order = 1;
a = nBond - 1;
while(a) {
if((ii1->index[0] == ii2->index[0]) && (ii1->index[1] == ii2->index[1])) {
ii1->order++; /* count dup */
} else {
ii1++; /* non-dup, make copy */
ii1->index[0] = ii2->index[0];
ii1->index[1] = ii2->index[1];
ii1->order = ii2->order;
ii1->stereo = ii2->stereo;
nReal++;
}
ii2++;
a--;
}
nBond = nReal;
}
/* now, find atoms we're looking for */
/* determine ranges */
maxAt = nAtom;
ii1 = bond;
for(a = 0; a < nBond; a++) {
if(ii1->index[0] > maxAt)
maxAt = ii1->index[0];
if(ii1->index[1] > maxAt)
maxAt = ii1->index[1];
ii1++;
}
for(a = 0; a < nAtom; a++)
if(maxAt < atInfo[a].id)
maxAt = atInfo[a].id;
/* build index */
maxAt++;
idx = Alloc(int, maxAt + 1);
CHECKOK(ok, idx);
if (ok){
for(a = 0; a < maxAt; a++) {
idx[a] = -1;
}
for(a = 0; a < nAtom; a++)
idx[atInfo[a].id] = a;
/* convert indices to bonds */
ii1 = bond;
ii2 = bond;
nReal = 0;
}
if (ok) {
int unbond_cations = SettingGetGlobal_i(G, cSetting_pdb_unbond_cations);
int flag;
for(a = 0; a < nBond; a++) {
if((ii1->index[0] >= 0) && ((ii1->index[1]) >= 0)) {
if((idx[ii1->index[0]] >= 0) && (idx[ii1->index[1]] >= 0)) { /* in case PDB file has bad bonds */
ii2->index[0] = idx[ii1->index[0]];
ii2->index[1] = idx[ii1->index[1]];
ii2->order = ii1->order;
if((ii2->index[0] >= 0) && (ii2->index[1] >= 0)) {
if(!have_bond_order) { /* handle PDB bond order kludge */
if(ii1->order <= 2)
ii2->order = 1;
else if(ii1->order <= 4)
ii2->order = 2;
else if(ii1->order <= 6)
ii2->order = 3;
else
ii2->order = 4;
}
flag = true;
if(unbond_cations) {
if(AtomInfoIsFreeCation(G, atInfo + ii2->index[0]))
flag = false;
else if(AtomInfoIsFreeCation(G, atInfo + ii2->index[1]))
flag = false;
}
if(flag) {
atInfo[ii2->index[0]].bonded = true;
atInfo[ii2->index[1]].bonded = true;
nReal++;
ii2++;
}
}
}
}
ii1++;
}
}
nBond = nReal;
FreeP(idx);
}
}
if(ss_found && !quiet) {
PRINTFB(G, FB_ObjectMolecule, FB_Details)
" ObjectMolecule: Read secondary structure assignments.\n" ENDFB(G);
}
if(symmetry && !quiet && (!only_read_one_model)) {
PRINTFB(G, FB_ObjectMolecule, FB_Details)
" ObjectMolecule: Read crystal symmetry information.\n" ENDFB(G);
}
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" PDBStr2CoordSet: Read %d bonds from CONECT records (%p).\n", nBond,
(void *) bond ENDFB(G);
if (ok){
cset = CoordSetNew(G);
CHECKOK(ok, cset);
cset->NIndex = nAtom;
cset->Coord = coord;
cset->TmpBond = bond;
cset->NTmpBond = nBond;
if(symmetry)
cset->Symmetry = symmetry;
if(atInfoPtr)
*atInfoPtr = atInfo;
if((*restart_model) && (!foundNextModelFlag)) {
/* if plan on need to reading another model into this object,
make sure there is another model to read... */
p = *restart_model;
while(*p) {
if(strstartswith(p, "HEADER")) {
/* seeing HEADER means we're off the end of the existing file */
break;
} else if(strstartswith(p, "MODEL ") ||
strstartswith(p, "ENDMDL")) {
foundNextModelFlag = true;
break;
}
p = nextline(p);
}
if(!foundNextModelFlag) {
*restart_model = NULL;
}
}
}
if (!ok){
if (cset){
cset->fFree();
cset = NULL;
} else {
VLAFreeP(coord);
VLAFreeP(bond);
}
}
sshash_free(ss_hash);
if (ok){
if(!seen_model)
*model_number = 1;
if((*restart_model) && (*next_pdb)) { /* if we're mixing multistate objects and
trajectories, then enforce sanity by
reading the models first... */
if(is_end_of_object)
(*restart_model) = NULL;
else if((*next_pdb) < (*restart_model))
(*next_pdb) = NULL;
}
}
LexDec(G, segi_override_idx);
return (cset);
}
/*========================================================================*/
void ObjectMoleculeM4XAnnotate(ObjectMolecule * I, M4XAnnoType * m4x, const char *script_file,
int match_colors, int nbr_sele)
{
int a;
WordType name;
M4XContextType *cont;
if(m4x) {
for(a = 0; a < m4x->n_context; a++) {
cont = m4x->context + a;
if(cont->site) {
UtilNCopy(name, I->Obj.Name, sizeof(WordType));
UtilNConcat(name, "_", sizeof(WordType));
UtilNConcat(name, cont->name, sizeof(WordType));
UtilNConcat(name, "_site", sizeof(WordType));
SelectorSelectByID(I->Obj.G, name, I, cont->site, cont->n_site);
}
if(cont->ligand) {
UtilNCopy(name, I->Obj.Name, sizeof(WordType));
UtilNConcat(name, "_", sizeof(WordType));
UtilNConcat(name, cont->name, sizeof(WordType));
UtilNConcat(name, "_ligand", sizeof(WordType));
SelectorSelectByID(I->Obj.G, name, I, cont->ligand, cont->n_ligand);
}
if(cont->water) {
UtilNCopy(name, I->Obj.Name, sizeof(WordType));
UtilNConcat(name, "_", sizeof(WordType));
UtilNConcat(name, cont->name, sizeof(WordType));
UtilNConcat(name, "_water", sizeof(WordType));
SelectorSelectByID(I->Obj.G, name, I, cont->water, cont->n_water);
}
if(cont->hbond) {
ObjectDist *distObj;
UtilNCopy(name, I->Obj.Name, sizeof(WordType));
UtilNConcat(name, "_", sizeof(WordType));
UtilNConcat(name, cont->name, sizeof(WordType));
UtilNConcat(name, "_hbond", sizeof(WordType));
ExecutiveDelete(I->Obj.G, name);
distObj = ObjectDistNewFromM4XBond(I->Obj.G, NULL,
I, cont->hbond, cont->n_hbond, nbr_sele);
if(match_colors)
distObj->Obj.Color = I->Obj.Color;
else
distObj->Obj.Color = ColorGetIndex(I->Obj.G, "yellow");
ObjectSetName((CObject *) distObj, name);
if(distObj)
ExecutiveManageObject(I->Obj.G, (CObject *) distObj, false, true);
}
if(cont->nbond && 0) {
/* ObjectDist *distObj; */
UtilNCopy(name, I->Obj.Name, sizeof(WordType));
UtilNConcat(name, "_", sizeof(WordType));
UtilNConcat(name, cont->name, sizeof(WordType));
UtilNConcat(name, "_nbond", sizeof(WordType));
ExecutiveDelete(I->Obj.G, name);
/* distObj = ObjectDistNewFromM4XBond(I->Obj.G,NULL,
I,
cont->nbond,
cont->n_nbond);
if(distObj)
ExecutiveManageObject(I->Obj.G,(CObject*)distObj,false,true); */
{
CGO *cgo = NULL;
ObjectCGO *ocgo;
cgo = CGONew(I->Obj.G);
/*
CGOBegin(cgo,GL_LINES);
for(a=0;a<op1.nvv1;a++) {
CGOVertexv(cgo,op2.vv1+(a*3));
MatrixApplyTTTfn3f(1,v1,op2.ttt,op1.vv1+(a*3));
CGOVertexv(cgo,v1);
*/
CGOEnd(cgo);
CGOStop(cgo);
ocgo = ObjectCGOFromCGO(I->Obj.G, NULL, cgo, 0);
if(match_colors)
ocgo->Obj.Color = I->Obj.Color;
else
ocgo->Obj.Color = ColorGetIndex(I->Obj.G, "yellow");
ObjectSetName((CObject *) ocgo, name);
ExecutiveDelete(I->Obj.G, ocgo->Obj.Name);
ExecutiveManageObject(I->Obj.G, (CObject *) ocgo, false, true);
SceneInvalidate(I->Obj.G);
}
}
}
if(script_file)
PParse(I->Obj.G, script_file);
}
}
void ObjectMoleculeInitHBondCriteria(PyMOLGlobals * G, HBondCriteria * hbc)
{
hbc->maxAngle = SettingGet_f(G, NULL, NULL, cSetting_h_bond_max_angle);
hbc->maxDistAtMaxAngle = SettingGet_f(G, NULL, NULL, cSetting_h_bond_cutoff_edge);
hbc->maxDistAtZero = SettingGet_f(G, NULL, NULL, cSetting_h_bond_cutoff_center);
hbc->power_a = SettingGet_f(G, NULL, NULL, cSetting_h_bond_power_a);
hbc->power_b = SettingGet_f(G, NULL, NULL, cSetting_h_bond_power_b);
hbc->cone_dangle =
(float) cos(PI * 0.5 * SettingGet_f(G, NULL, NULL, cSetting_h_bond_cone) / 180.0F);
if(hbc->maxDistAtMaxAngle != 0.0F) {
hbc->factor_a = (float) (0.5 / pow(hbc->maxAngle, hbc->power_a));
hbc->factor_b = (float) (0.5 / pow(hbc->maxAngle, hbc->power_b));
}
}
static int ObjectMoleculeTestHBond(float *donToAcc, float *donToH, float *hToAcc,
float *accPlane, HBondCriteria * hbc)
{
float nDonToAcc[3], nDonToH[3], nAccPlane[3], nHToAcc[3];
double angle;
double cutoff;
double curve;
double dist;
float dangle;
/* A ~~ H - D */
normalize23f(donToAcc, nDonToAcc);
normalize23f(hToAcc, nHToAcc);
if(accPlane) { /* remember, plane need not exist if it's water... */
normalize23f(accPlane, nAccPlane);
if(dot_product3f(nHToAcc, nAccPlane) > (-hbc->cone_dangle)) /* don't allow H behind Acceptor plane */
return 0;
}
normalize23f(donToH, nDonToH);
normalize23f(donToAcc, nDonToAcc);
dangle = dot_product3f(nDonToH, nDonToAcc);
if((dangle < 1.0F) && (dangle > 0.0F))
angle = 180.0 * acos((double) dangle) / PI;
else if(dangle > 0.0F)
angle = 0.0;
else
angle = 90.0;
if(angle > hbc->maxAngle)
return 0;
/* interpolate cutoff based on ADH angle */
if(hbc->maxDistAtMaxAngle != 0.0F) {
curve = (pow(angle, (double) hbc->power_a) * hbc->factor_a +
pow(angle, (double) hbc->power_b) * hbc->factor_b);
cutoff = (hbc->maxDistAtMaxAngle * curve) + (hbc->maxDistAtZero * (1.0 - curve));
} else {
cutoff = hbc->maxDistAtZero;
}
/*
printf("angle %8.3f curve %8.3f %8.3f %8.3f %8.3f\n",angle,
curve,cutoff,hbc->maxDistAtMaxAngle,hbc->maxDistAtZero);
*/
dist = length3f(donToAcc);
if(dist > cutoff)
return 0;
else
return 1;
}
/*========================================================================*/
static int ObjectMoleculeFindBestDonorH(ObjectMolecule * I,
int atom,
int state, float *dir, float *best,
AtomInfoType **h_real)
{
int result = 0;
CoordSet *cs;
int n, nn;
int idx;
int a1;
float cand[3], cand_dir[3];
float best_dot = 0.0F, cand_dot;
float *orig;
ObjectMoleculeUpdateNeighbors(I);
if((state >= 0) && (state < I->NCSet) && (cs = I->CSet[state]) && (atom < I->NAtom)) {
if(I->DiscreteFlag) {
if(cs == I->DiscreteCSet[atom]) {
idx = I->DiscreteAtmToIdx[atom];
} else {
idx = -1;
}
} else {
idx = cs->AtmToIdx[atom];
}
if(idx >= 0) {
orig = cs->Coord + 3 * idx;
/* do we need to add any new hydrogens? */
n = I->Neighbor[atom];
nn = I->Neighbor[n++];
/* printf("nn %d valence %d %s\n",nn,
I->AtomInfo[atom].valence,I->AtomInfo[atom].name);
{
int i;
for(i=0;i<nn;i++) {
printf("%d \n",I->Neighbor[n+2*i]);
}
}
*/
if((nn < I->AtomInfo[atom].valence) || I->AtomInfo[atom].hb_donor) { /* is there an implicit hydrogen? */
if(ObjectMoleculeFindOpenValenceVector(I, state, atom, best, dir, -1)) {
result = true;
best_dot = dot_product3f(best, dir);
add3f(orig, best, best);
if(h_real)
*h_real = NULL;
}
}
/* iterate through real hydrogens looking for best match
with desired direction */
while(1) { /* look for an attached non-hydrogen as a base */
a1 = I->Neighbor[n];
n += 2;
if(a1 < 0)
break;
if(I->AtomInfo[a1].protons == 1) { /* hydrogen */
if(ObjectMoleculeGetAtomVertex(I, state, a1, cand)) { /* present */
subtract3f(cand, orig, cand_dir);
normalize3f(cand_dir);
cand_dot = dot_product3f(cand_dir, dir);
if(result) { /* improved */
if((best_dot < cand_dot) || (h_real && !*h_real)) {
best_dot = cand_dot;
copy3f(cand, best);
if(h_real)
*h_real = I->AtomInfo + a1;
}
} else { /* first */
result = true;
copy3f(cand, best);
best_dot = cand_dot;
if(h_real)
*h_real = I->AtomInfo + a1;
}
}
}
}
}
}
return result;
}
/*========================================================================*/
int ObjectMoleculeGetCheckHBond(AtomInfoType **h_real,
float *h_crd_ret,
ObjectMolecule * don_obj,
int don_atom,
int don_state,
ObjectMolecule * acc_obj,
int acc_atom,
int acc_state,
HBondCriteria * hbc)
{
int result = 0;
CoordSet *csD, *csA;
int idxD, idxA;
float *vAcc, *vDon;
float donToAcc[3];
float donToH[3];
float bestH[3];
float hToAcc[3];
float accPlane[3], *vAccPlane = NULL;
/* first, check for existence of coordinate sets */
if((don_state >= 0) &&
(don_state < don_obj->NCSet) &&
(csD = don_obj->CSet[don_state]) &&
(acc_state >= 0) &&
(acc_state < acc_obj->NCSet) &&
(csA = acc_obj->CSet[acc_state]) &&
(don_atom < don_obj->NAtom) && (acc_atom < acc_obj->NAtom)) {
/* now check for coordinates of these actual atoms */
if(don_obj->DiscreteFlag) {
if(csD == don_obj->DiscreteCSet[don_atom]) {
idxD = don_obj->DiscreteAtmToIdx[don_atom];
} else {
idxD = -1;
}
} else {
idxD = csD->AtmToIdx[don_atom];
}
if(acc_obj->DiscreteFlag) {
if(csA == acc_obj->DiscreteCSet[acc_atom]) {
idxA = acc_obj->DiscreteAtmToIdx[acc_atom];
} else {
idxA = -1;
}
} else {
idxA = csA->AtmToIdx[acc_atom];
}
if((idxA >= 0) && (idxD >= 0)) {
/* now get local geometries, including
real or virtual hydrogen atom positions */
vDon = csD->Coord + 3 * idxD;
vAcc = csA->Coord + 3 * idxA;
subtract3f(vAcc, vDon, donToAcc);
if(ObjectMoleculeFindBestDonorH(don_obj,
don_atom, don_state, donToAcc, bestH, h_real)) {
subtract3f(bestH, vDon, donToH);
subtract3f(vAcc, bestH, hToAcc);
if(ObjectMoleculeGetAvgHBondVector(acc_obj, acc_atom,
acc_state, accPlane, hToAcc) > 0.1) {
vAccPlane = &accPlane[0];
}
result = ObjectMoleculeTestHBond(donToAcc, donToH, hToAcc, vAccPlane, hbc);
if(result && h_crd_ret && h_real && *h_real)
copy3f(bestH, h_crd_ret);
}
}
}
return (result);
}
/*========================================================================*/
float ObjectMoleculeGetMaxVDW(ObjectMolecule * I)
{
float max_vdw = 0.0F;
int a;
AtomInfoType *ai;
if(I->NAtom) {
ai = I->AtomInfo;
for(a = 0; a < I->NAtom; a++) {
if(max_vdw < ai->vdw)
max_vdw = ai->vdw;
ai++;
}
}
return (max_vdw);
}
/*========================================================================*/
static PyObject *ObjectMoleculeCSetAsPyList(ObjectMolecule * I)
{
PyObject *result = NULL;
int a;
result = PyList_New(I->NCSet);
for(a = 0; a < I->NCSet; a++) {
if(I->CSet[a]) {
PyList_SetItem(result, a, CoordSetAsPyList(I->CSet[a]));
} else {
PyList_SetItem(result, a, PConvAutoNone(Py_None));
}
}
return (PConvAutoNone(result));
}
/*static PyObject *ObjectMoleculeDiscreteCSetAsPyList(ObjectMolecule *I)
{
PyObject *result = NULL;
return(PConvAutoNone(result));
}*/
static int ObjectMoleculeCSetFromPyList(ObjectMolecule * I, PyObject * list)
{
int ok = true;
int a;
if(ok)
ok = PyList_Check(list);
if(ok) {
VLACheck(I->CSet, CoordSet *, I->NCSet);
for(a = 0; a < I->NCSet; a++) {
if(ok)
ok = CoordSetFromPyList(I->Obj.G, PyList_GetItem(list, a), &I->CSet[a]);
PRINTFB(I->Obj.G, FB_ObjectMolecule, FB_Debugging)
" ObjectMoleculeCSetFromPyList: ok %d after CoordSet %d\n", ok, a ENDFB(I->Obj.G);
if(ok)
if(I->CSet[a]) /* WLD 030205 */
I->CSet[a]->Obj = I;
}
}
return (ok);
}
static PyObject *ObjectMoleculeBondAsPyList(ObjectMolecule * I)
{
PyObject *result = NULL;
PyObject *bond_list;
BondType *bond;
int a;
#ifndef PICKLETOOLS
PyMOLGlobals *G = I->Obj.G;
int pse_export_version = SettingGetGlobal_f(I->Obj.G, cSetting_pse_export_version) * 1000;
if (SettingGetGlobal_b(G, cSetting_pse_binary_dump) && (!pse_export_version || pse_export_version >= 1765)){
/* For the pse_binary_dump, save entire Bond array to a binary string array
*/
// supported versions
auto version = (!pse_export_version || pse_export_version >= 1810) ?
181 : (pse_export_version >= 1770) ? 177 : 176;
auto blob = Copy_To_BondType_Version(version, I->Bond, I->NBond);
auto blobsize = VLAGetByteSize(blob);
result = PyList_New(2);
PyList_SetItem(result, 0, PyInt_FromLong(version));
PyList_SetItem(result, 1, PyBytes_FromStringAndSize(reinterpret_cast<const char*>(blob), blobsize));
VLAFreeP(blob);
return result;
}
#endif
result = PyList_New(I->NBond);
bond = I->Bond;
for(a = 0; a < I->NBond; a++) {
bond_list = PyList_New(7);
PyList_SetItem(bond_list, 0, PyInt_FromLong(bond->index[0]));
PyList_SetItem(bond_list, 1, PyInt_FromLong(bond->index[1]));
PyList_SetItem(bond_list, 2, PyInt_FromLong(bond->order));
PyList_SetItem(bond_list, 3, PyInt_FromLong(bond->id));
PyList_SetItem(bond_list, 4, PyInt_FromLong(bond->stereo));
PyList_SetItem(bond_list, 5, PyInt_FromLong(bond->unique_id));
PyList_SetItem(bond_list, 6, PyInt_FromLong(bond->has_setting));
PyList_SetItem(result, a, bond_list);
bond++;
}
return (PConvAutoNone(result));
}
static int ObjectMoleculeBondFromPyList(ObjectMolecule * I, PyObject * list)
{
PyMOLGlobals *G = I->Obj.G;
int ok = true;
int a;
int stereo, ll = 0;
PyObject *bond_list = NULL;
BondType *bond;
if(ok)
ok = PyList_Check(list);
if(ok)
ll = PyList_Size(list);
bool pse_binary_dump = false;
if (ll == 2){
// checking if from pse_binary_dump
// pse_binary_dump saves 2 values: bondInfo_version, BondType binary
CPythonVal *val1 = CPythonVal_PyList_GetItem(G, list, 1);
pse_binary_dump = PyBytes_Check(val1);
CPythonVal_Free(val1);
}
if (pse_binary_dump){
CPythonVal *verobj = CPythonVal_PyList_GetItem(G, list, 0);
int bondInfo_version;
ok = PConvPyIntToInt(verobj, &bondInfo_version);
CPythonVal *strobj = CPythonVal_PyList_GetItem(G, list, 1);
auto strval = PyBytes_AsSomeString(strobj);
if(ok)
ok = ((I->Bond = VLAlloc(BondType, I->NBond)) != NULL);
Copy_Into_BondType_From_Version(strval.data(), bondInfo_version, I->Bond, I->NBond);
CPythonVal_Free(verobj);
CPythonVal_Free(strobj);
} else {
if(ok)
ok = ((I->Bond = VLAlloc(BondType, I->NBond)) != NULL);
bond = I->Bond;
for(a = 0; a < I->NBond; a++) {
bond_list = NULL;
if(ok)
bond_list = PyList_GetItem(list, a);
if(ok)
ok = PyList_Check(bond_list);
if(ok)
ll = PyList_Size(bond_list);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 0), &bond->index[0]);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 1), &bond->index[1]);
if(ok)
if((ok = CPythonVal_PConvPyIntToInt_From_List(I->Obj.G, bond_list, 2, &stereo)))
bond->order = stereo;
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 3), &bond->id);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 4), &stereo);
if(ok)
bond->stereo = (short int) stereo;
if(ok && (ll > 5)) {
int has_setting;
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 5), &bond->unique_id);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(bond_list, 6), &has_setting);
if(ok)
bond->has_setting = (short int) has_setting;
if(ok && bond->unique_id) { /* reserve existing IDs */
bond->unique_id = SettingUniqueConvertOldSessionID(G, bond->unique_id);
}
}
bond++;
}
}
PRINTFB(G, FB_ObjectMolecule, FB_Debugging)
" ObjectMoleculeBondFromPyList: ok %d after restore\n", ok ENDFB(G);
return (ok);
}
static PyObject *ObjectMoleculeAtomAsPyList(ObjectMolecule * I)
{
PyMOLGlobals *G = I->Obj.G;
PyObject *result = NULL;
AtomInfoType *ai;
int a;
#ifndef PICKLETOOLS
int pse_export_version = SettingGetGlobal_f(I->Obj.G, cSetting_pse_export_version) * 1000;
if (SettingGetGlobal_b(G, cSetting_pse_binary_dump) && (!pse_export_version || pse_export_version >= 1765)){
/* For the pse_binary_dump, record all strings in lex and
write them into separate binary string
*/
AtomInfoTypeConverter converter(G, I->NAtom);
std::set<lexidx_t> lexIDs;
int totalstlen = 0;
AtomInfoType *ai = I->AtomInfo;
for(a = 0; a < I->NAtom; a++) {
if (ai->textType) lexIDs.insert(ai->textType);
if (ai->chain) lexIDs.insert(ai->chain);
if (ai->label) lexIDs.insert(ai->label);
if (ai->custom) lexIDs.insert(ai->custom);
if (ai->segi) lexIDs.insert(ai->segi);
if (ai->resn) lexIDs.insert(ai->resn);
if (ai->name) lexIDs.insert(ai->name);
++ai;
}
for (const auto& lexID : lexIDs) { // need to calculate totalstlen so we can allocate
const char *lexstr = LexStr(G, lexID);
int lexlen = strlen(lexstr);
totalstlen += lexlen + 1;
}
int strinfolen = totalstlen + sizeof(int) * (lexIDs.size() + 1);
void *strinfo = Alloc(unsigned char, strinfolen);
int *strval = (int*)strinfo;
*(strval++) = lexIDs.size(); // first write number of strings into binary data string
char *strpl = (char*)((char*)strinfo + (1 + lexIDs.size()) * sizeof(int));
/* write map of lex ids and strings into binary data string as an array of ids
and null-terminated strings */
for (auto it = lexIDs.begin(); it != lexIDs.end(); ++it){
*(strval++) = converter.to_lexidx_int(*it);
const char *strptr = LexStr(G, *it);
strcpy(strpl, strptr);
strpl += strlen(strptr) + 1;
}
auto version = AtomInfoVERSION;
if (pse_export_version && pse_export_version < 1810) {
if (pse_export_version < 1770) {
version = 176;
} else {
version = 177;
}
}
auto blob = converter.allocCopy(version, I->AtomInfo);
auto blobsize = VLAGetByteSize(blob);
result = PyList_New(3);
PyList_SetItem(result, 0, PyInt_FromLong(version));
PyList_SetItem(result, 1, PyBytes_FromStringAndSize(reinterpret_cast<const char*>(blob), blobsize));
PyList_SetItem(result, 2, PyBytes_FromStringAndSize(reinterpret_cast<const char*>(strinfo), strinfolen));
VLAFreeP(blob);
FreeP(strinfo);
return result;
}
#endif
result = PyList_New(I->NAtom);
ai = I->AtomInfo;
for(a = 0; a < I->NAtom; a++) {
PyList_SetItem(result, a, AtomInfoAsPyList(I->Obj.G, ai));
ai++;
}
return (PConvAutoNone(result));
}
static int ObjectMoleculeAtomFromPyList(ObjectMolecule * I, PyObject * list)
{
PyMOLGlobals *G = I->Obj.G;
int ok = true;
int a, ll = 0;
AtomInfoType *ai;
if(ok)
ok = PyList_Check(list);
if (ok)
ll = PyList_Size(list);
bool pse_binary_dump = false;
if (ll == 3){
// checking if from pse_binary_dump
// pse_binary_dump saves 3 values: atomInfo_version, AtomInfo binary, and strings array
CPythonVal *val1 = CPythonVal_PyList_GetItem(G, list, 1);
CPythonVal *val2 = CPythonVal_PyList_GetItem(G, list, 2);
pse_binary_dump = PyBytes_Check(val1) && PyBytes_Check(val2);
CPythonVal_Free(val1);
CPythonVal_Free(val2);
}
if (pse_binary_dump){
CPythonVal *verobj = CPythonVal_PyList_GetItem(G, list, 0);
int atomInfo_version;
ok = PConvPyIntToInt(verobj, &atomInfo_version);
CPythonVal *strlookupobj = CPythonVal_PyList_GetItem(G, list, 2);
auto strval_1 = PyBytes_AsSomeString(strlookupobj);
int *strval = (int*)strval_1.data();
AtomInfoTypeConverter converter(G, I->NAtom);
auto& oldIDtoLexID = converter.lexidxmap;
int nstrings = *(strval++);
char *strpl = (char*)(strval + nstrings);
int strcnt = nstrings;
int stlen;
// populate oldIDtoLexID with nstrings from binary string data (3rd entry in list)
while (strcnt){
lexidx_t idx = LexIdx(G, strpl); // increments ref count, need to take into account
int oldidx = *(strval++);
oldIDtoLexID[oldidx] = idx;
stlen = strlen(strpl);
strpl += stlen + 1;
strcnt--;
}
CPythonVal *strobj = CPythonVal_PyList_GetItem(G, list, 1);
auto strval_2 = PyBytes_AsSomeString(strobj);
VLACheck(I->AtomInfo, AtomInfoType, I->NAtom + 1);
converter.copy(I->AtomInfo, strval_2.data(), atomInfo_version);
// go through AtomInfo array, swap new strings, convert colors, convert settings
// (everything that AtomInfoFromPyList does except set properties, which are currently
// not saved for pse_binary_dump)
AtomInfoType *ai = I->AtomInfo;
for(a = 0; a < I->NAtom; ++a, ++ai) {
ai->color = ColorConvertOldSessionIndex(G, ai->color);
if (ai->unique_id){
ai->unique_id = SettingUniqueConvertOldSessionID(G, ai->unique_id);
}
}
// need to decrement since we call LexIdx() above on each
for (auto it = oldIDtoLexID.begin(); it != oldIDtoLexID.end(); ++it){
LexDec(G, it->second);
}
CPythonVal_Free(verobj);
CPythonVal_Free(strobj);
CPythonVal_Free(strlookupobj);
} else {
// The old slow way of loading in AtomInfo, using python lists
if (ok)
VLACheck(I->AtomInfo, AtomInfoType, I->NAtom + 1);
CHECKOK(ok, I->AtomInfo);
ai = I->AtomInfo;
for(a = 0; ok && a < I->NAtom; a++) {
if(ok)
ok = AtomInfoFromPyList(I->Obj.G, ai, PyList_GetItem(list, a));
ai++;
}
}
PRINTFB(I->Obj.G, FB_ObjectMolecule, FB_Debugging)
" ObjectMoleculeAtomFromPyList: ok %d \n", ok ENDFB(I->Obj.G);
return (ok);
}
int ObjectMoleculeNewFromPyList(PyMOLGlobals * G, PyObject * list,
ObjectMolecule ** result)
{
int ok = true;
ObjectMolecule *I = NULL;
int discrete_flag = 0;
(*result) = NULL;
if(ok)
ok = PyList_Check(list);
/* TO SUPPORT BACKWARDS COMPATIBILITY...
Always check ll when adding new PyList_GetItem's */
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 8), &discrete_flag);
if (ok)
I = ObjectMoleculeNew(G, discrete_flag);
CHECKOK(ok, I);
if(ok)
ok = ObjectFromPyList(G, PyList_GetItem(list, 0), &I->Obj);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->NCSet);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->NBond);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 3), &I->NAtom);
if(ok)
ok = ObjectMoleculeCSetFromPyList(I, PyList_GetItem(list, 4));
if(ok){
ok = CoordSetFromPyList(G, PyList_GetItem(list, 5), &I->CSTmpl);
if(I->CSTmpl)
I->CSTmpl->Obj = I;
}
if(ok)
ok = ObjectMoleculeBondFromPyList(I, PyList_GetItem(list, 6));
if(ok)
ok = ObjectMoleculeAtomFromPyList(I, PyList_GetItem(list, 7));
if(ok)
I->Symmetry = SymmetryNewFromPyList(G, PyList_GetItem(list, 10));
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 11), &I->CurCSet);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 12), &I->BondCounter);
if(ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 13), &I->AtomCounter);
I->updateAtmToIdx();
if (ok)
ObjectMoleculeInvalidate(I, cRepAll, cRepInvAll, -1);
if(ok)
(*result) = I;
else {
/* cleanup */
if (I)
ObjectMoleculeFree(I);
(*result) = NULL;
}
return (ok);
}
/*========================================================================*/
PyObject *ObjectMoleculeAsPyList(ObjectMolecule * I)
{
PyObject *result = NULL;
/* first, dump the atoms */
result = PyList_New(16);
PyList_SetItem(result, 0, ObjectAsPyList(&I->Obj));
PyList_SetItem(result, 1, PyInt_FromLong(I->NCSet));
PyList_SetItem(result, 2, PyInt_FromLong(I->NBond));
PyList_SetItem(result, 3, PyInt_FromLong(I->NAtom));
PyList_SetItem(result, 4, ObjectMoleculeCSetAsPyList(I));
PyList_SetItem(result, 5, CoordSetAsPyList(I->CSTmpl));
PyList_SetItem(result, 6, ObjectMoleculeBondAsPyList(I));
PyList_SetItem(result, 7, ObjectMoleculeAtomAsPyList(I));
PyList_SetItem(result, 8, PyInt_FromLong(I->DiscreteFlag));
PyList_SetItem(result, 9, PyInt_FromLong(I->DiscreteFlag ? I->NAtom : 0 /* NDiscrete */));
PyList_SetItem(result, 10, SymmetryAsPyList(I->Symmetry));
PyList_SetItem(result, 11, PyInt_FromLong(I->CurCSet));
PyList_SetItem(result, 12, PyInt_FromLong(I->BondCounter));
PyList_SetItem(result, 13, PyInt_FromLong(I->AtomCounter));
float pse_export_version = SettingGetGlobal_f(I->Obj.G, cSetting_pse_export_version);
if(I->DiscreteFlag
&& (pse_export_version || !SettingGetGlobal_b(I->Obj.G, cSetting_pse_binary_dump))
&& pse_export_version < 1.7699) {
int *dcs;
int a;
CoordSet *cs;
/* get coordinate set indices */
for(a = 0; a < I->NCSet; a++) {
cs = I->CSet[a];
if(cs) {
cs->tmp_index = a;
}
}
dcs = Alloc(int, I->NAtom);
for(a = 0; a < I->NAtom; a++) {
cs = I->DiscreteCSet[a];
if(cs)
dcs[a] = cs->tmp_index;
else
dcs[a] = -1;
}
PyList_SetItem(result, 14, PConvIntArrayToPyList(I->DiscreteAtmToIdx, I->NAtom));
PyList_SetItem(result, 15, PConvIntArrayToPyList(dcs, I->NAtom));
FreeP(dcs);
} else {
PyList_SetItem(result, 14, PConvAutoNone(NULL));
PyList_SetItem(result, 15, PConvAutoNone(NULL));
}
return (PConvAutoNone(result));
}
/*========================================================================*/
static
float connect_cutoff_adjustment(
const AtomInfoType * ai1,
const AtomInfoType * ai2)
{
if (ai1->isHydrogen() || ai2->isHydrogen())
return -0.2f;
if (ai1->protons == cAN_S || ai2->protons == cAN_S)
return 0.2f;
return 0.f;
}
/*
* True if two atoms should be bonded
*/
static
bool is_distance_bonded(
PyMOLGlobals * G,
const CoordSet * cs,
const AtomInfoType * ai1,
const AtomInfoType * ai2,
const float * v1,
const float * v2,
float cutoff,
int connect_mode,
int discrete_chains,
bool connect_bonded,
bool unbond_cations)
{
auto dst = (float) diff3f(v1, v2);
dst -= (ai1->vdw + ai2->vdw) / 2;
cutoff += connect_cutoff_adjustment(ai1, ai2);
if (dst > cutoff)
return false;
if (ai1->isHydrogen() && ai2->isHydrogen())
return false;
if (discrete_chains > 0 && ai1->chain != ai2->chain)
return false;
if (!connect_bonded && ai1->bonded && ai2->bonded)
return false;
bool water_flag = (
AtomInfoKnownWaterResName(G, LexStr(G, ai1->resn)) ||
AtomInfoKnownWaterResName(G, LexStr(G, ai2->resn)));
if (connect_mode != 3 &&
cs->TmpBond && /* connectivity information present in file */
ai1->hetatm &&
ai2->hetatm &&
!water_flag &&
!(AtomInfoKnownPolymerResName(LexStr(G, ai1->resn)) &&
AtomInfoKnownPolymerResName(LexStr(G, ai2->resn))))
return false;
// don't connect water atoms in different residues
if (water_flag && !AtomInfoSameResidue(G, ai1, ai2))
return false;
// don't connect atoms with different, non-NULL alternate conformations
if (ai1->alt[0] != ai2->alt[0] && ai1->alt[0] && ai2->alt[0])
return false;
// if either is a cation, unbond is user wants
if (unbond_cations &&
(AtomInfoIsFreeCation(G, ai1) ||
AtomInfoIsFreeCation(G, ai2)))
return false;
return true;
}
/*========================================================================*/
int ObjectMoleculeConnect(ObjectMolecule * I, int *nbond, BondType ** bond, AtomInfoType * ai,
struct CoordSet *cs, int bondSearchMode,
int connectModeOverride)
{
#define cMULT 1
PyMOLGlobals *G = I->Obj.G;
int a, b, c, d, e, f, i, j;
int a1, a2;
float *v1, *v2;
int maxBond;
MapType *map;
int nBond;
BondType *ii1, *ii2;
int flag;
int order;
AtomInfoType *ai1, *ai2;
/* Sulfur cutoff */
float cutoff_s;
float cutoff_v;
float max_cutoff;
int repeat = true;
int discrete_chains = SettingGetGlobal_i(G, cSetting_pdb_discrete_chains);
int connect_bonded = SettingGetGlobal_b(G, cSetting_connect_bonded);
int connect_mode = SettingGetGlobal_i(G, cSetting_connect_mode);
int unbond_cations = SettingGetGlobal_i(G, cSetting_pdb_unbond_cations);
int ok = true;
cutoff_v = SettingGetGlobal_f(G, cSetting_connect_cutoff);
cutoff_s = cutoff_v + 0.2F;
max_cutoff = cutoff_s;
if(connectModeOverride >= 0)
connect_mode = connectModeOverride;
if(connect_mode == 2) { /* force use of distance-based connectivity,
ignoring that provided with file */
bondSearchMode = true;
cs->NTmpBond = 0;
VLAFreeP(cs->TmpBond);
} else if (connect_mode == 4) {
// mmCIF specific, fall back to default to get any bonds for PDB, XYZ, etc.
connect_mode = 0;
}
/* FeedbackMask[FB_ObjectMolecule]=0xFF; */
nBond = 0;
maxBond = cs->NIndex * 8;
(*bond) = VLACalloc(BondType, maxBond);
CHECKOK(ok, (*bond));
while(ok && repeat) {
repeat = false;
/*
* BOND SEARCH MODE
*/
if(cs->NIndex && bondSearchMode) { /* &&(!I->DiscreteFlag) WLD 010527 */
/* if there are atoms, and we need to search for bonds, instead of using
* (possibly) supplied CONECT records... */
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMoleculeConnect: Searching for bonds amongst %d coordinates.\n",
cs->NIndex ENDFB(G);
if(Feedback(G, FB_ObjectMolecule, FB_Debugging)) {
for(a = 0; a < cs->NIndex; a++)
printf(" ObjectMoleculeConnect: coord %d %8.3f %8.3f %8.3f\n",
a, cs->Coord[a * 3], cs->Coord[a * 3 + 1], cs->Coord[a * 3 + 2]);
}
switch (connect_mode) {
/* DISTANCE BASED CONNECTIONS */
case 0: /* distance-based and explicit (not HETATM to HETATM) */
case 3: /* distance-based and explicit (even HETATM to HETATM) */
case 2: /* distance-based only */ {
/* distance-based bond location */
int violations = 0;
int *cnt = Alloc(int, cs->NIndex);
int valcnt;
CHECKOK(ok, cnt);
if (ok){
/* initialize each atom's (max) expected valence */
for(i = 0; i < cs->NIndex; i++) {
valcnt = AtomInfoGetExpectedValence(G, ai + cs->IdxToAtm[i]);
if(valcnt < 0)
valcnt = 6;
cnt[i] = valcnt;
}
}
/* make a map of the local neighborhood in space */
if (ok)
map = MapNew(G, max_cutoff + MAX_VDW, cs->Coord, cs->NIndex, NULL);
CHECKOK(ok, map);
if(ok) {
int dim12 = map->D1D2;
int dim2 = map->Dim[2];
for(i = 0; ok && i < cs->NIndex; i++) {
if(nBond > maxBond)
break;
/* atom i's position in space */
v1 = cs->Coord + (3 * i);
a1 = cs->IdxToAtm[i];
ai1 = ai + a1;
MapLocus(map, v1, &a, &b, &c);
/* d = [a-1, a, a+1] */
for(d = a - 1; ok && d <= a + 1; d++) {
int *j_ptr1 = map->Head + d * dim12 + (b - 1) * dim2;
/* e = [b-1, b, b+1] */
for(e = b - 1; ok && e <= b + 1; e++) {
int *j_ptr2 = j_ptr1 + c - 1;
j_ptr1 += dim2;
/* f = [c-1, c, c+1] */
for(f = c - 1; ok && f <= c + 1; f++) {
j = *(j_ptr2++); /* *MapFirst(map,d,e,f)); */
while(ok && j >= 0) {
if(i < j) {
/* position in space for atom 2 */
v2 = cs->Coord + (3 * j);
a2 = cs->IdxToAtm[j];
ai2 = ai + a2;
flag = is_distance_bonded(G, cs, ai1, ai2, v1, v2,
cutoff_v, connect_mode, discrete_chains,
connect_bonded, unbond_cations);
{
/* we have a bond, now process it */
if(flag) {
VLACheck((*bond), BondType, nBond);
CHECKOK(ok, (*bond));
if (ok){
(*bond)[nBond].index[0] = a1;
(*bond)[nBond].index[1] = a2;
(*bond)[nBond].stereo = 0;
order = 1;
}
/* if we allow bonds between chains and it screws up the
* bonding, disallow inter-chain bonds */
if(ok && discrete_chains < 0) { /* if we're allowing bonds between chains,
then make sure things don't get out of hand */
if(cnt[i] == -1)
violations++;
if(cnt[j] == -1)
violations++;
/* decrement free valences, since we have a bond */
cnt[i]--;
cnt[j]--;
if(violations > (cs->NIndex >> 3)) {
/* if more than 12% of the structure has excessive #'s of bonds... */
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMoleculeConnect: Assuming chains are discrete...\n"
ENDFB(G);
discrete_chains = 1;
repeat = true;
goto do_it_again;
}
}
if(!ai1->hetatm || ai1->resn == G->lex_const.MSE) {
if(AtomInfoSameResidue(I->Obj.G, ai1, ai2)) {
/* hookup standard disconnected PDB residue */
assign_pdb_known_residue(G, ai1, ai2, &order);
}
}
(*bond)[nBond].order = -order; /* store tentative valence as negative */
nBond++;
}
}
}
j = MapNext(map, j);
}
}
}
}
}
do_it_again:
MapFree(map);
FreeP(cnt);
}
}
case 1: /* only use explicit connectivity from file (don't do anything) */
break;
case 4: /* dictionary-based connectivity */
/* TODO */
break;
}
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMoleculeConnect: Found %d bonds.\n", nBond ENDFB(G);
if(Feedback(G, FB_ObjectMolecule, FB_Debugging)) {
for(a = 0; a < nBond; a++)
printf(" ObjectMoleculeConnect: bond %d ind0 %d ind1 %d\n",
a, (*bond)[a].index[0], (*bond)[a].index[1]);
}
}
if(repeat) {
nBond = 0;
}
}
/* if we have explicit connectivity, determine if we need to set check_conect_all */
if(ok && cs->NTmpBond && cs->TmpBond) {
int check_conect_all = false;
int pdb_conect_all = false;
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
" ObjectMoleculeConnect: incorporating explicit bonds. %d %d\n",
nBond, cs->NTmpBond ENDFB(G);
if((nBond == 0) && (cs->NTmpBond > 0) &&
bondSearchMode && (connect_mode == 0) && cs->NIndex) {
/* if no bonds were found, and we have explicit connectivity,
* try to determine if we need to set pdb_conect_mode */
for(i = 0; i < cs->NIndex; i++) {
a1 = cs->IdxToAtm[i];
ai1 = ai + a1;
if(ai1->bonded && (!ai1->hetatm)) {
/* apparent PDB ATOM record with explicit bonding... */
check_conect_all = true;
break;
}
}
}
VLACheck((*bond), BondType, (nBond + cs->NTmpBond));
CHECKOK(ok, (*bond));
if (ok){
ii1 = (*bond) + nBond;
ii2 = cs->TmpBond;
}
if (ok) {
int n_atom = I->NAtom;
for(a = 0; a < cs->NTmpBond; a++) {
a1 = cs->IdxToAtm[ii2->index[0]]; /* convert bonds from index space */
a2 = cs->IdxToAtm[ii2->index[1]]; /* to atom space */
if((a1 >= 0) && (a2 >= 0) && (a1 < n_atom) && (a2 < n_atom)) {
if(check_conect_all) {
if((!ai[a1].hetatm) && (!ai[a2].hetatm)) {
/* found bond between non-HETATMs -- so tell PyMOL to CONECT all ATOMs
* when writing out a PDB file */
pdb_conect_all = true;
}
}
ai[a1].bonded = true;
ai[a2].bonded = true;
(*ii1) = (*ii2); /* note this copies owned ids and thus settings etc. */
ii1->index[0] = a1;
ii1->index[1] = a2;
ii1++;
ii2++;
}
}
}
if (ok){
nBond = nBond + cs->NTmpBond;
VLAFreeP(cs->TmpBond);
cs->NTmpBond = 0;
if(pdb_conect_all) {
int dummy;
if(!SettingGetIfDefined_b(G, I->Obj.Setting, cSetting_pdb_conect_all, &dummy)) {
CSetting **handle = NULL;
if(I->Obj.fGetSettingHandle) {
handle = I->Obj.fGetSettingHandle(&I->Obj, -1);
if(handle) {
SettingCheckHandle(G, handle);
SettingSet_b(*handle, cSetting_pdb_conect_all, true);
}
}
}
}
}
}
/* Link b/t ligand and residue? */
if(ok && cs->NTmpLinkBond && cs->TmpLinkBond) {
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
"ObjectMoleculeConnect: incorporating linkage bonds. %d %d\n",
nBond, cs->NTmpLinkBond ENDFB(G);
VLACheck((*bond), BondType, (nBond + cs->NTmpLinkBond));
CHECKOK(ok, (*bond));
if (ok){
ii1 = (*bond) + nBond;
ii2 = cs->TmpLinkBond;
for(a = 0; a < cs->NTmpLinkBond; a++) {
a1 = ii2->index[0]; /* first atom is in object */
a2 = cs->IdxToAtm[ii2->index[1]]; /* second is in the cset */
ai[a1].bonded = true;
ai[a2].bonded = true;
(*ii1) = (*ii2); /* note this copies owned ids and thus settings etc. */
ii1->index[0] = a1;
ii1->index[1] = a2;
ii1++;
ii2++;
}
nBond = nBond + cs->NTmpLinkBond;
}
VLAFreeP(cs->TmpLinkBond);
cs->NTmpLinkBond = 0;
}
PRINTFD(G, FB_ObjectMolecule)
" ObjectMoleculeConnect: elminating duplicates with %d bonds...\n", nBond ENDFD;
if(ok && !I->DiscreteFlag) {
UtilSortInPlace(G, (*bond), nBond, sizeof(BondType), (UtilOrderFn *) BondInOrder);
if(nBond) { /* eliminate duplicates */
ii1 = (*bond) + 1;
ii2 = (*bond) + 1;
a = nBond - 1;
nBond = 1;
if(a > 0)
while(a--) {
if((ii2->index[0] != (ii1 - 1)->index[0]) ||
(ii2->index[1] != (ii1 - 1)->index[1])) {
*(ii1++) = *(ii2++); /* copy bond */
nBond++;
} else {
if((ii2->order > 0) && ((ii1 - 1)->order < 0))
(ii1 - 1)->order = ii2->order; /* use most certain valence */
ii2++; /* skip bond */
}
}
VLASize((*bond), BondType, nBond);
CHECKOK(ok, (*bond));
}
}
/* restore bond oder positivity */
if (ok){
ii1 = *bond;
for(a = 0; a < nBond; a++) {
if(ii1->order < 0)
ii1->order = -ii1->order;
ii1++;
}
}
PRINTFD(G, FB_ObjectMolecule)
" ObjectMoleculeConnect: leaving with %d bonds...\n", nBond ENDFD;
if (ok)
*nbond = nBond;
return ok;
}
/*========================================================================*/
int ObjectMoleculeSort(ObjectMolecule * I)
{ /* sorts atoms and bonds */
int *index;
int *outdex = NULL;
int a, b;
CoordSet *cs, **dcs;
AtomInfoType *atInfo;
int *dAtmToIdx = NULL;
int ok = true;
if(!I->DiscreteFlag) { /* currently, discrete objects are never sorted */
int n_bytes = sizeof(int) * I->NAtom;
int already_in_order = true;
int i_NAtom = I->NAtom;
index = AtomInfoGetSortedIndex(I->Obj.G, I, I->AtomInfo, i_NAtom, &outdex);
CHECKOK(ok, index);
if (ok){
for(a = 0; a < i_NAtom; a++) {
if(index[a] != a) {
already_in_order = false;
break;
}
}
}
if(ok && !already_in_order) { /* if we aren't already in perfect order */
for(a = 0; a < I->NBond; a++) { /* bonds */
I->Bond[a].index[0] = outdex[I->Bond[a].index[0]];
I->Bond[a].index[1] = outdex[I->Bond[a].index[1]];
}
for(a = -1; a < I->NCSet; a++) { /* coordinate set mapping */
if(a < 0) {
cs = I->CSTmpl;
} else {
cs = I->CSet[a];
}
if(cs) {
int cs_NIndex = cs->NIndex;
int *cs_IdxToAtm = cs->IdxToAtm;
int *cs_AtmToIdx = cs->AtmToIdx;
for(b = 0; b < cs_NIndex; b++)
cs_IdxToAtm[b] = outdex[cs_IdxToAtm[b]];
if(cs_AtmToIdx) {
memset(cs_AtmToIdx, -1, n_bytes);
/* for(b=0;b<i_NAtom;b++)
cs_AtmToIdx[b]=-1; */
for(b = 0; b < cs_NIndex; b++) {
cs_AtmToIdx[cs_IdxToAtm[b]] = b;
}
}
}
}
ExecutiveUniqueIDAtomDictInvalidate(I->Obj.G);
atInfo = (AtomInfoType *) VLAMalloc(i_NAtom, sizeof(AtomInfoType), 5, true);
CHECKOK(ok, atInfo);
if (ok){
/* autozero here is important */
for(a = 0; a < i_NAtom; a++)
atInfo[a] = std::move(I->AtomInfo[index[a]]);
}
VLAFreeP(I->AtomInfo);
std::swap(I->AtomInfo, atInfo);
if(ok && I->DiscreteFlag) {
dcs = VLAlloc(CoordSet *, i_NAtom);
CHECKOK(ok, dcs);
if (ok)
dAtmToIdx = VLAlloc(int, i_NAtom);
CHECKOK(ok, dAtmToIdx);
if (ok){
for(a = 0; a < i_NAtom; a++) {
b = index[a];
dcs[a] = I->DiscreteCSet[b];
dAtmToIdx[a] = I->DiscreteAtmToIdx[b];
}
} else {
VLAFreeP(dcs);
VLAFreeP(dAtmToIdx);
dcs = NULL;
dAtmToIdx = NULL;
}
VLAFreeP(I->DiscreteCSet);
VLAFreeP(I->DiscreteAtmToIdx);
I->DiscreteCSet = dcs;
I->DiscreteAtmToIdx = dAtmToIdx;
}
}
AtomInfoFreeSortedIndexes(I->Obj.G, &index, &outdex);
if (ok){
UtilSortInPlace(I->Obj.G, I->Bond, I->NBond, sizeof(BondType),
(UtilOrderFn *) BondInOrder);
/* sort...important! */
ObjectMoleculeInvalidate(I, cRepAll, cRepInvAtoms, -1); /* important */
}
}
return ok;
}