mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-03 19:54:24 +08:00
12035 lines
348 KiB
C++
12035 lines
348 KiB
C++
/*
|
|
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"os_python.h"
|
|
|
|
#include"os_predef.h"
|
|
#include"os_std.h"
|
|
#include"os_gl.h"
|
|
|
|
#include"Base.h"
|
|
#include"Parse.h"
|
|
#include"Vector.h"
|
|
#include"MemoryDebug.h"
|
|
#include"Err.h"
|
|
#include"Map.h"
|
|
#include"Selector.h"
|
|
#include"ObjectMolecule.h"
|
|
#include"Ortho.h"
|
|
#include"Util.h"
|
|
#include"Matrix.h"
|
|
#include"Scene.h"
|
|
#include"P.h"
|
|
#include"PConv.h"
|
|
#include"Executive.h"
|
|
#include"Setting.h"
|
|
#include"Sphere.h"
|
|
#include"main.h"
|
|
#include"CGO.h"
|
|
#include"Editor.h"
|
|
#include"Sculpt.h"
|
|
#include"OVLexicon.h"
|
|
#include"ListMacros.h"
|
|
#include"File.h"
|
|
#include "Lex.h"
|
|
#include "MolV3000.h"
|
|
#include "HydrogenAdder.h"
|
|
#include "Feedback.h"
|
|
#include "Util2.h"
|
|
|
|
#include "Property.h"
|
|
|
|
#ifdef _WEBGL
|
|
#endif
|
|
|
|
#define cMaxNegResi 100
|
|
|
|
#define ntrim ParseNTrim
|
|
#define nextline ParseNextLine
|
|
#define ncopy ParseNCopy
|
|
#define nskip ParseNSkip
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <set>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#ifndef NO_MMLIBS
|
|
#include "mmpymolx.h"
|
|
#endif
|
|
|
|
static
|
|
CoordSet *ObjectMoleculeMMDStr2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr, const char **restart);
|
|
|
|
static
|
|
void ObjectMoleculeTransformTTTf(ObjectMolecule * I, float *ttt, int state);
|
|
|
|
static
|
|
int ObjectMoleculeGetAtomGeometry(const ObjectMolecule * I, int state, int at);
|
|
|
|
static
|
|
void ObjectMoleculeInferHBondFromChem(ObjectMolecule * I);
|
|
|
|
/**
|
|
* Order function for sorting bonds by atom indices (ignores other properties
|
|
* like bond order).
|
|
*
|
|
* Pre-condition: index pairs are in order (index[0] < index[1])
|
|
*/
|
|
static
|
|
int BondTypeInOrder(PyMOLGlobals * G, const BondType * bonds, int i1, int i2) {
|
|
auto lhs = bonds[i1].index;
|
|
auto rhs = bonds[i2].index;
|
|
return lhs[0] < rhs[0] || (lhs[0] == rhs[0] && lhs[1] < rhs[1]);
|
|
}
|
|
|
|
/**
|
|
* Remove duplicated bonds. Disregards if two atoms would be bonded by two
|
|
* bonds of different bond order (only one will be kept).
|
|
*/
|
|
static
|
|
void ObjectMoleculeRemoveDuplicateBonds(PyMOLGlobals * G, ObjectMolecule * I) {
|
|
// make sure index pairs are in order
|
|
for (int i = 0; i < I->NBond; ++i) {
|
|
auto& bond = I->Bond[i];
|
|
if (bond.index[0] > bond.index[1] && !bond.symop_2) {
|
|
std::swap(bond.index[0], bond.index[1]);
|
|
}
|
|
}
|
|
|
|
// get sorted indices
|
|
int * sorted = pymol::malloc<int>(I->NBond);
|
|
UtilSortIndexGlobals(G, I->NBond, I->Bond.data(), sorted,
|
|
(UtilOrderFnGlobals *) BondTypeInOrder);
|
|
|
|
// purge duplicated bonds and mark for removal
|
|
for (int i = 0, j = -1; i < I->NBond; ++i) {
|
|
int b = sorted[i];
|
|
auto ptr = I->Bond + sorted[i];
|
|
|
|
bool equal = false;
|
|
if (j != -1) {
|
|
const auto& other = I->Bond[j];
|
|
if (ptr->index[0] == other.index[0] &&
|
|
ptr->index[1] == other.index[1])
|
|
equal = true;
|
|
}
|
|
|
|
if (!equal) {
|
|
j = b;
|
|
} else {
|
|
AtomInfoPurgeBond(G, ptr);
|
|
|
|
// mark for removal
|
|
ptr->index[0] = ptr->index[1] = 0;
|
|
}
|
|
}
|
|
|
|
FreeP(sorted);
|
|
|
|
// remove purged bonds from array
|
|
int j = 0;
|
|
for (int i = 0; i < I->NBond; ++i) {
|
|
auto& bond = I->Bond[i];
|
|
if (bond.index[0] != bond.index[1]) {
|
|
if (j != i)
|
|
std::swap(I->Bond[j], bond);
|
|
++j;
|
|
}
|
|
}
|
|
I->NBond = j;
|
|
VLASize(I->Bond, BondType, I->NBond);
|
|
}
|
|
|
|
/**
|
|
* Convert a discrete object to non-discrete. Will merge atoms from states
|
|
* by identifiers. Atom level properties like color, b, q, etc. are lost
|
|
* on the merged atoms.
|
|
*/
|
|
static
|
|
bool ObjectMoleculeSetNotDiscrete(PyMOLGlobals * G, ObjectMolecule * I) {
|
|
if (!I->DiscreteFlag)
|
|
return true;
|
|
|
|
VLAFreeP(I->DiscreteAtmToIdx);
|
|
VLAFreeP(I->DiscreteCSet);
|
|
I->DiscreteFlag = false;
|
|
|
|
for (int atm = 0; atm < I->NAtom; ++atm) {
|
|
I->AtomInfo[atm].discrete_state = 0;
|
|
}
|
|
|
|
int * outdex;
|
|
int * aindex = AtomInfoGetSortedIndex(G, I, I->AtomInfo, I->NAtom, &outdex);
|
|
|
|
// merge duplicate atoms
|
|
for (int i = 0, j = -1; i < I->NAtom; ++i) {
|
|
int atm = aindex[i];
|
|
auto ai = I->AtomInfo + atm;
|
|
|
|
if (j == -1 || !AtomInfoMatch(G, ai, I->AtomInfo + j, false, false)) {
|
|
j = atm;
|
|
} else {
|
|
ai->deleteFlag = true;
|
|
}
|
|
|
|
// remapping of merged indices
|
|
outdex[atm] = j;
|
|
}
|
|
|
|
// point coordsets to merged atoms
|
|
for (int state = 0; state < I->NCSet; ++state) {
|
|
auto cs = I->CSet[state];
|
|
if (!cs)
|
|
continue;
|
|
|
|
for (int idx = 0; idx < cs->NIndex; ++idx) {
|
|
int atm = cs->IdxToAtm[idx];
|
|
cs->IdxToAtm[idx] = outdex[atm];
|
|
}
|
|
}
|
|
|
|
// point bonds to merged atoms
|
|
for (int i = 0; i < I->NBond; ++i) {
|
|
auto& bond = I->Bond[i];
|
|
bond.index[0] = outdex[bond.index[0]];
|
|
bond.index[1] = outdex[bond.index[1]];
|
|
}
|
|
|
|
AtomInfoFreeSortedIndexes(G, &aindex, &outdex);
|
|
|
|
ObjectMoleculeRemoveDuplicateBonds(G, I);
|
|
|
|
I->updateAtmToIdx();
|
|
ObjectMoleculePurge(I);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Make a non-discrete object discrete, or vice versa.
|
|
*/
|
|
int ObjectMoleculeSetDiscrete(PyMOLGlobals * G, ObjectMolecule * I, int discrete)
|
|
{
|
|
int state, idx, ao, an, ao1, ao2, an1, an2;
|
|
int maxnatom, natom = I->NAtom, nbond = I->NBond;
|
|
int *aostate2an = nullptr;
|
|
char *bondseen = nullptr;
|
|
BondType *bond;
|
|
CoordSet *cs;
|
|
|
|
if (!discrete) {
|
|
if (!I->DiscreteFlag)
|
|
return true;
|
|
|
|
return ObjectMoleculeSetNotDiscrete(G, I);
|
|
}
|
|
|
|
if (I->DiscreteFlag)
|
|
return true;
|
|
|
|
// upper bound for number of discrete atoms
|
|
maxnatom = I->NAtom * I->NCSet;
|
|
|
|
// mapping (for bonds): atom_old -> atom_new
|
|
ok_assert(1, aostate2an = pymol::malloc<int>(I->NAtom));
|
|
ok_assert(1, bondseen = pymol::calloc<char >(I->NBond));
|
|
|
|
// discrete setup
|
|
I->DiscreteFlag = discrete;
|
|
ok_assert(1, I->DiscreteAtmToIdx = pymol::vla<int>(maxnatom));
|
|
ok_assert(1, I->DiscreteCSet = pymol::vla<CoordSet*>(maxnatom));
|
|
|
|
// for all coordinate sets
|
|
for (state = 0; state < I->NCSet; state++) {
|
|
cs = I->CSet[state];
|
|
|
|
if (!cs)
|
|
continue;
|
|
|
|
// init the atom_old -> atom_new array
|
|
for (ao = 0; ao < I->NAtom; ao++)
|
|
aostate2an[ao] = -1;
|
|
|
|
// for all atoms in coordinate set
|
|
for (idx = 0; idx < cs->NIndex; idx++) {
|
|
ao = an = cs->IdxToAtm[idx];
|
|
|
|
if (I->DiscreteCSet[ao]) {
|
|
// seen before, have to copy
|
|
an = natom++;
|
|
|
|
VLACheck(I->AtomInfo, AtomInfoType, an);
|
|
ok_assert(1, I->AtomInfo);
|
|
|
|
AtomInfoCopy(G,
|
|
I->AtomInfo + ao,
|
|
I->AtomInfo + an);
|
|
cs->IdxToAtm[idx] = an;
|
|
}
|
|
|
|
I->AtomInfo[an].discrete_state = state + 1; // 1-based :-(
|
|
I->DiscreteCSet[an] = cs;
|
|
I->DiscreteAtmToIdx[an] = idx;
|
|
|
|
// mapping (for bonds): (atom_old, state) -> atom_new
|
|
aostate2an[ao] = an;
|
|
}
|
|
|
|
// unused in discrete coordsets
|
|
cs->AtmToIdx.clear();
|
|
|
|
// for all old bonds
|
|
for (idx = 0; idx < I->NBond; idx++) {
|
|
bond = I->Bond + idx;
|
|
|
|
// old atom indices
|
|
ao1 = bond->index[0];
|
|
ao2 = bond->index[1];
|
|
|
|
// new atom indices
|
|
an1 = aostate2an[ao1];
|
|
an2 = aostate2an[ao2];
|
|
|
|
// do both atoms exist in this coord set?
|
|
if (an1 == -1 || an2 == -1)
|
|
continue;
|
|
|
|
if (bondseen[idx]) {
|
|
// seen before, have to copy
|
|
VLACheck(I->Bond, BondType, nbond);
|
|
ok_assert(1, I->Bond);
|
|
|
|
bond = I->Bond + (nbond++);
|
|
AtomInfoBondCopy(G, I->Bond + idx, bond);
|
|
} else {
|
|
bondseen[idx] = true;
|
|
}
|
|
|
|
bond->index[0] = an1;
|
|
bond->index[1] = an2;
|
|
}
|
|
}
|
|
|
|
// not needed anymore
|
|
mfree(aostate2an);
|
|
mfree(bondseen);
|
|
|
|
// update N* count fields
|
|
I->NAtom = natom;
|
|
I->NBond = nbond;
|
|
|
|
// trim VLAs memory to actual size
|
|
if (I->NBond)
|
|
VLASize(I->Bond, BondType, I->NBond);
|
|
if (I->NAtom)
|
|
VLASize(I->AtomInfo, AtomInfoType, I->NAtom);
|
|
|
|
I->setNDiscrete(I->NAtom);
|
|
|
|
I->invalidate(cRepAll, cRepInvAll, -1);
|
|
|
|
return true;
|
|
|
|
ok_except1:
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" %s: memory allocation failed\n", __func__ ENDFB(G);
|
|
return false;
|
|
}
|
|
|
|
int ObjectMoleculeCheckFullStateSelection(ObjectMolecule * I, int sele, int state)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
int result = false;
|
|
if((state >= 0) && (state < I->NCSet)) {
|
|
const AtomInfoType *ai = I->AtomInfo.data();
|
|
CoordSet *cs = I->CSet[state];
|
|
if(cs) {
|
|
int a;
|
|
int at;
|
|
result = true;
|
|
for(a = 0; a < cs->NIndex; a++) {
|
|
at = cs->IdxToAtm[a];
|
|
if(!SelectorIsMember(G, ai[at].selEntry, sele)) {
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* PARAMS
|
|
* ch -- ptr to empty str of length 'len'
|
|
* len -- str len
|
|
* RETURNS
|
|
* ch, with the caption in place
|
|
* NOTES
|
|
* User owns the buffer so must clean up after it
|
|
*/
|
|
char *ObjectMolecule::getCaption(char * ch, int len) const
|
|
{
|
|
auto I = this;
|
|
int objState;
|
|
int n = 0;
|
|
int show_state = 0;
|
|
int show_as_fraction = 0;
|
|
const char *frozen_str = "";
|
|
|
|
int state = ObjectGetCurrentState(I, false);
|
|
int counter_mode = SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_state_counter_mode);
|
|
int frozen = SettingGetIfDefined_i(I->G, I->Setting.get(), cSetting_state, &objState);
|
|
|
|
/* if frozen print (blue) STATE / NSTATES
|
|
* if not frozen, print STATE/NSTATES
|
|
* if beyond NSTATES, print * /NSTATES.
|
|
*/
|
|
|
|
if(frozen) { /* frozen color */
|
|
frozen_str = "\\789";
|
|
} else {
|
|
if (I->DiscreteFlag) { /* discrete states */
|
|
frozen_str = "\\993";
|
|
} else { /* normal case */
|
|
frozen_str = "";
|
|
}
|
|
}
|
|
|
|
switch(counter_mode) {
|
|
case 0: /* off */
|
|
show_state = show_as_fraction = 0;
|
|
break;
|
|
case 2: /* just state */
|
|
show_state = 1;
|
|
show_as_fraction = 0;
|
|
break;
|
|
case -1: /* fraction, full-on */
|
|
case 1:
|
|
default:
|
|
show_state = show_as_fraction = 1;
|
|
break;
|
|
}
|
|
|
|
/* bail on null string or no room */
|
|
if (!ch || len==0)
|
|
return nullptr;
|
|
|
|
ch[0] = 0;
|
|
|
|
/* if the state is valid, setup the label */
|
|
if(state >= 0) {
|
|
if (state < I->NCSet) {
|
|
const CoordSet *cs = I->CSet[state];
|
|
if(cs) {
|
|
if(show_state) {
|
|
if (show_as_fraction) {
|
|
if (strlen(cs->Name)) { /* NAME */
|
|
n = snprintf(ch, len, "%s %s%d/%d", cs->Name, frozen_str, state+1, I->NCSet);
|
|
}
|
|
else { /* no name */
|
|
n = snprintf(ch, len, "%s%d/%d", frozen_str, state+1, I->NCSet);
|
|
}
|
|
} else { /* not fraction */
|
|
if (strlen(cs->Name)) {
|
|
n = snprintf(ch, len, "%s %s%d", cs->Name, frozen_str, state+1);
|
|
} else { /* no name */
|
|
n = snprintf(ch, len, "%s%d", frozen_str, state+1);
|
|
}
|
|
}
|
|
} else { /* no state */
|
|
n = snprintf(ch, len, "%s", cs->Name);
|
|
}
|
|
} else { /* no coord set, can be an N-state object missing a CSet */
|
|
}
|
|
} else { /* state > NCSet, out of range due to other object or global setting */
|
|
if(show_state) {
|
|
if(show_as_fraction) {
|
|
n = snprintf(ch, len, "%s--/%d", frozen_str, I->NCSet);
|
|
} else { /* no fraction */
|
|
n = snprintf(ch, len, "%s--", frozen_str);
|
|
}
|
|
}
|
|
}
|
|
} else if (state == -1) {
|
|
// all states
|
|
n = snprintf(ch, len, "%s*/%d", frozen_str, I->NCSet);
|
|
} else {
|
|
/* blank out the title if outside the valid # of states */
|
|
}
|
|
|
|
if (n > len)
|
|
return nullptr;
|
|
|
|
return ch;
|
|
}
|
|
|
|
|
|
#define MAX_BOND_DIST 50
|
|
|
|
/* find sets of atoms with identical skeletons */
|
|
|
|
struct match_info {
|
|
AtomInfoType *ai_a;
|
|
AtomInfoType *ai_b;
|
|
BondType *bi_a;
|
|
BondType *bi_b;
|
|
const int *nbr_a, *nbr_b;
|
|
int *matched;
|
|
|
|
// values: -1 .. 2
|
|
using mark_type = signed char;
|
|
|
|
std::vector<mark_type> atom_mark_a;
|
|
std::vector<mark_type> atom_mark_b;
|
|
std::vector<mark_type> bond_mark_a;
|
|
std::vector<mark_type> bond_mark_b;
|
|
};
|
|
|
|
#define recmat3(x,y,z) \
|
|
(recursive_match(u_a[0],u_b[x],x_a[0],x_b[x],mi) && \
|
|
recursive_match(u_a[1],u_b[y],x_a[1],x_b[y],mi) && \
|
|
recursive_match(u_a[2],u_b[z],x_a[2],x_b[z],mi))
|
|
|
|
#define recmat4(w,x,y,z) \
|
|
(recursive_match(u_a[0],u_b[w],x_a[0],x_b[w],mi) && \
|
|
recursive_match(u_a[1],u_b[x],x_a[1],x_b[x],mi) && \
|
|
recursive_match(u_a[2],u_b[y],x_a[2],x_b[y],mi) && \
|
|
recursive_match(u_a[3],u_b[z],x_a[3],x_b[z],mi))
|
|
|
|
static void undo_match(int *start, match_info * mi)
|
|
{
|
|
/* remove the matched atom set */
|
|
int *match = mi->matched;
|
|
while(match > start) {
|
|
int a = match[-4];
|
|
int b = match[-3];
|
|
int aa = match[-2];
|
|
int bb = match[-1];
|
|
mi->atom_mark_a[a] = false;
|
|
mi->atom_mark_b[b] = false;
|
|
mi->bond_mark_a[aa] = false;
|
|
mi->bond_mark_b[bb] = false;
|
|
match -= 4;
|
|
}
|
|
mi->matched = start;
|
|
}
|
|
|
|
static int recursive_match(int a, int b, int aa, int bb, match_info * mi)
|
|
{
|
|
if (mi->atom_mark_a[a] && mi->atom_mark_b[b]) {
|
|
if (aa >= 0 && bb >= 0 && !(mi->bond_mark_a[aa] || mi->bond_mark_b[bb])) {
|
|
/* note bond */
|
|
mi->atom_mark_a[a] = true;
|
|
mi->atom_mark_b[b] = true;
|
|
mi->bond_mark_a[aa] = true;
|
|
mi->bond_mark_b[bb] = true;
|
|
mi->matched[0] = a; /* atoms */
|
|
mi->matched[1] = b;
|
|
mi->matched[2] = aa; /* bonds */
|
|
mi->matched[3] = bb;
|
|
mi->matched += 4;
|
|
}
|
|
return true;
|
|
} else if (mi->atom_mark_a[a] != mi->atom_mark_b[b])
|
|
return false;
|
|
else if(mi->ai_a[a].protons != mi->ai_b[b].protons)
|
|
return false;
|
|
else {
|
|
int ni_a = mi->nbr_a[a];
|
|
int ni_b = mi->nbr_b[b];
|
|
int num_a = mi->nbr_a[ni_a++];
|
|
int num_b = mi->nbr_b[ni_b++];
|
|
|
|
if((num_a != num_b) || (num_a > 4)) /* must have the same number of neighbors (four or less) */
|
|
return false;
|
|
else {
|
|
int match_flag = false;
|
|
int u_a[4], u_b[4], x_a[4], x_b[4];
|
|
int n_u_a = 0;
|
|
int n_u_b = 0;
|
|
int *before = mi->matched;
|
|
mi->atom_mark_a[a] = true;
|
|
mi->atom_mark_b[b] = true;
|
|
mi->bond_mark_a[aa] = true;
|
|
mi->bond_mark_b[bb] = true;
|
|
mi->matched[0] = a; /* atoms */
|
|
mi->matched[1] = b;
|
|
mi->matched[2] = aa; /* bonds */
|
|
mi->matched[3] = bb;
|
|
mi->matched += 4;
|
|
|
|
/* collect unvisited bonds */
|
|
|
|
while(num_a--) {
|
|
int i_a = mi->nbr_a[ni_a + 1];
|
|
if(!mi->bond_mark_a[i_a]) { /* bond not yet visited */
|
|
u_a[n_u_a] = mi->nbr_a[ni_a]; /* atom index */
|
|
x_a[n_u_a++] = i_a;
|
|
}
|
|
ni_a += 2;
|
|
}
|
|
|
|
while(num_b--) {
|
|
int i_b = mi->nbr_b[ni_b + 1];
|
|
if(!mi->bond_mark_b[i_b]) {
|
|
u_b[n_u_b] = mi->nbr_b[ni_b];
|
|
x_b[n_u_b++] = i_b;
|
|
}
|
|
ni_b += 2;
|
|
}
|
|
/* NOTE: implicitly relying upon C short-circuit evaluation */
|
|
if(n_u_a == n_u_b) {
|
|
if(!n_u_a)
|
|
match_flag = true;
|
|
else if(n_u_a == 1) {
|
|
match_flag = recursive_match(u_a[0], u_b[0], x_a[0], x_b[0], mi);
|
|
} else if(n_u_a == 2) {
|
|
match_flag =
|
|
(recursive_match(u_a[0], u_b[0], x_a[0], x_b[0], mi) &&
|
|
recursive_match(u_a[1], u_b[1], x_a[1], x_b[1], mi)) ||
|
|
(recursive_match(u_a[0], u_b[1], x_a[0], x_b[1], mi) &&
|
|
recursive_match(u_a[1], u_b[0], x_a[1], x_b[0], mi));
|
|
} else if(n_u_a == 3) {
|
|
match_flag =
|
|
recmat3(0, 1, 2) ||
|
|
recmat3(0, 2, 1) ||
|
|
recmat3(1, 0, 2) || recmat3(1, 2, 0) || recmat3(2, 0, 1) || recmat3(2, 1, 0);
|
|
} else if(n_u_a == 4) {
|
|
match_flag =
|
|
recmat4(0, 1, 2, 3) ||
|
|
recmat4(0, 2, 1, 3) ||
|
|
recmat4(1, 0, 2, 3) ||
|
|
recmat4(1, 2, 0, 3) || recmat4(2, 0, 1, 3) || recmat4(2, 1, 0, 3);
|
|
if(!match_flag) {
|
|
match_flag =
|
|
recmat4(0, 1, 3, 2) ||
|
|
recmat4(0, 2, 3, 1) ||
|
|
recmat4(1, 0, 3, 2) ||
|
|
recmat4(1, 2, 3, 0) || recmat4(2, 0, 3, 1) || recmat4(2, 1, 3, 0);
|
|
if(!match_flag) {
|
|
match_flag =
|
|
recmat4(0, 3, 1, 2) ||
|
|
recmat4(0, 3, 2, 1) ||
|
|
recmat4(1, 3, 0, 2) ||
|
|
recmat4(1, 3, 2, 0) || recmat4(2, 3, 0, 1) || recmat4(2, 3, 1, 0);
|
|
if(!match_flag) {
|
|
match_flag =
|
|
recmat4(3, 0, 1, 2) ||
|
|
recmat4(3, 0, 2, 1) ||
|
|
recmat4(3, 1, 0, 2) ||
|
|
recmat4(3, 1, 2, 0) || recmat4(3, 2, 0, 1) || recmat4(3, 2, 1, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!match_flag) { /* clean the match tree */
|
|
undo_match(before, mi);
|
|
}
|
|
return match_flag;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ObjectMoleculeXferValences(ObjectMolecule * Ia, int sele1, int sele2,
|
|
int target_state, ObjectMolecule * Ib, int sele3,
|
|
int source_state, int quiet)
|
|
{
|
|
int *matched = nullptr;
|
|
int match_found = false;
|
|
PyMOLGlobals *G = Ia->G;
|
|
if(Ia == Ib)
|
|
return false;
|
|
|
|
{
|
|
int max_match = Ia->NAtom + Ia->NBond;
|
|
if(max_match < (Ib->NAtom + Ib->NBond))
|
|
max_match = (Ib->NAtom + Ib->NBond);
|
|
matched = pymol::calloc<int>(max_match * 4);
|
|
}
|
|
|
|
{
|
|
int a, b;
|
|
AtomInfoType *ai_a = Ia->AtomInfo.data();
|
|
AtomInfoType *ai_b = Ib->AtomInfo.data();
|
|
BondType *bi_a = Ia->Bond.data();
|
|
BondType *bi_b = Ib->Bond.data();
|
|
|
|
match_info mi;
|
|
|
|
mi.atom_mark_a.resize(Ia->NAtom);
|
|
mi.atom_mark_b.resize(Ib->NAtom);
|
|
mi.bond_mark_a.resize(Ia->NBond);
|
|
mi.bond_mark_b.resize(Ib->NBond);
|
|
|
|
// confirm zero-initialization
|
|
assert(std::none_of(mi.atom_mark_a.begin(), mi.atom_mark_a.end(),
|
|
[](bool m) { return m; }));
|
|
|
|
mi.ai_a = ai_a;
|
|
mi.ai_b = ai_b;
|
|
mi.bi_a = bi_a;
|
|
mi.bi_b = bi_b;
|
|
mi.nbr_a = Ia->getNeighborArray();
|
|
mi.nbr_b = Ib->getNeighborArray();
|
|
mi.matched = matched;
|
|
for(a = 0; a < Ia->NAtom; a++) {
|
|
if(!mi.atom_mark_a[a]) {
|
|
int a_entry = ai_a[a].selEntry;
|
|
if(SelectorIsMember(G, a_entry, sele1) || SelectorIsMember(G, a_entry, sele2)) {
|
|
for(b = 0; b < Ib->NAtom; b++) {
|
|
if(SelectorIsMember(G, ai_b[b].selEntry, sele3)) {
|
|
if(recursive_match(a, b, -1, -1, &mi)) {
|
|
int *match = mi.matched;
|
|
match_found = true;
|
|
/* now graft bond orders for bonds in matching atoms */
|
|
|
|
while(match > matched) {
|
|
int at_a = match[-4];
|
|
int at_b = match[-3];
|
|
int bd_a = match[-2];
|
|
int bd_b = match[-1];
|
|
|
|
if((bd_a >= 0) && (bd_a >= 0)) {
|
|
int a1 = bi_a[bd_a].index[0];
|
|
int a2 = bi_a[bd_a].index[1];
|
|
|
|
int a1_entry = ai_a[a1].selEntry;
|
|
int a2_entry = ai_a[a2].selEntry;
|
|
|
|
if((SelectorIsMember(G, a1_entry, sele1) &&
|
|
SelectorIsMember(G, a2_entry, sele2)) ||
|
|
(SelectorIsMember(G, a2_entry, sele1) &&
|
|
SelectorIsMember(G, a1_entry, sele2))) {
|
|
/* only update bonds which actually sit between the two selections */
|
|
bi_a[bd_a].order = bi_b[bd_b].order;
|
|
ai_a[at_a].chemFlag = false;
|
|
}
|
|
}
|
|
mi.atom_mark_b[at_b] = false; /* release source for future matching */
|
|
if(bd_b >= 0)
|
|
mi.bond_mark_b[bd_b] = false;
|
|
match -= 4;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FreeP(matched);
|
|
return match_found;
|
|
}
|
|
|
|
void ObjectMoleculeTransformState44f(ObjectMolecule * I, int state, const float *matrix,
|
|
int log_trans, int homogenous, int transformed)
|
|
{
|
|
int a;
|
|
float tmp_matrix[16];
|
|
CoordSet *cs;
|
|
int use_matrices = SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
if(!use_matrices) {
|
|
ObjectMoleculeTransformSelection(I, state, -1, matrix, log_trans, I->Name,
|
|
homogenous, true);
|
|
} else {
|
|
double dbl_matrix[16];
|
|
if(state == -2)
|
|
state = ObjectGetCurrentState(I, false);
|
|
/* ensure homogenous matrix to preserve programmer sanity */
|
|
if(!homogenous) {
|
|
convertTTTfR44d(matrix, dbl_matrix);
|
|
copy44d44f(dbl_matrix, tmp_matrix);
|
|
matrix = tmp_matrix;
|
|
} else {
|
|
copy44f44d(matrix, dbl_matrix);
|
|
}
|
|
|
|
if(state < 0) { /* all states */
|
|
for(a = 0; a < I->NCSet; a++) {
|
|
cs = I->CSet[a];
|
|
if(cs)
|
|
ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
|
|
}
|
|
} else if(state < I->NCSet) { /* single state */
|
|
cs = I->CSet[state];
|
|
if(cs)
|
|
ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
|
|
} else if(I->NCSet == 1) { /* static singleton state */
|
|
cs = I->CSet[0];
|
|
if(cs && SettingGet_b(I->G, I->Setting.get(), nullptr, cSetting_static_singletons)) {
|
|
ObjectStateLeftCombineMatrixR44d(cs, dbl_matrix);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
static int ObjectMoleculeFixSeleHydrogens(ObjectMolecule * I, int sele, int state)
|
|
{
|
|
int a;
|
|
int seleFlag = false;
|
|
const AtomInfoType *ai0;
|
|
int ok = true;
|
|
|
|
ai0 = I->AtomInfo;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(SelectorIsMember(I->G, ai0->selEntry, sele)) {
|
|
seleFlag = true;
|
|
break;
|
|
}
|
|
ai0++;
|
|
}
|
|
if(seleFlag) {
|
|
seleFlag = false;
|
|
if(!ObjectMoleculeVerifyChemistry(I, state)) {
|
|
ErrMessage(I->G, " AddHydrogens", "missing chemical geometry information.");
|
|
} else {
|
|
ai0 = I->AtomInfo;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!ai0->isHydrogen()) { /* only do heavies */
|
|
if(SelectorIsMember(I->G, ai0->selEntry, sele)) {
|
|
for(StateIterator iter(I->G, I->Setting.get(), state, I->NCSet);
|
|
iter.next();) {
|
|
auto cs = I->CSet[iter.state];
|
|
if (!cs)
|
|
continue;
|
|
|
|
seleFlag |= ObjectMoleculeSetMissingNeighborCoords(I, cs, a, true);
|
|
}
|
|
}
|
|
}
|
|
ai0++;
|
|
}
|
|
}
|
|
if(seleFlag)
|
|
I->invalidate(cRepAll, cRepInvAll, -1);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static const char *skip_fortran(int num, int per_line, const char *p)
|
|
{
|
|
int a, b;
|
|
b = 0;
|
|
for(a = 0; a < num; a++) {
|
|
if((++b) == per_line) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b || (!num))
|
|
p = nextline(p);
|
|
return (p);
|
|
}
|
|
|
|
void ObjectMoleculeOpRecInit(ObjectMoleculeOpRec * op)
|
|
{
|
|
UtilZeroMem((char *) op, sizeof(ObjectMoleculeOpRec));
|
|
}
|
|
|
|
bool ObjectMoleculeGetNeighborVector(
|
|
ObjectMolecule* I, int atom, int state, float* v_result)
|
|
{
|
|
float v_atom[3] = {0.0, 0.0, 0.0};
|
|
|
|
auto const* cs = I->getCoordSet(state);
|
|
if (cs == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
if (!CoordSetGetAtomVertex(cs, atom, v_atom)) {
|
|
return false;
|
|
}
|
|
|
|
for (auto const& neighbor : AtomNeighbors(I, atom)) {
|
|
auto const a2 = neighbor.atm;
|
|
|
|
// ignore hydrogens
|
|
if (I->AtomInfo[a2].protons != cAN_H &&
|
|
CoordSetGetAtomVertex(cs, a2, v_result)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the most proton-rich element with the lowest priority value (OG1
|
|
* before OG2, CG, HB1)
|
|
*/
|
|
int ObjectMoleculeGetTopNeighbor(PyMOLGlobals * G,
|
|
ObjectMolecule * I, int start, int excluded)
|
|
{
|
|
int highest_at = -1, highest_prot = 0, lowest_pri = 9999;
|
|
|
|
for (auto const& neighbor : AtomNeighbors(I, start)) {
|
|
auto const at = neighbor.atm;
|
|
auto const *ai = I->AtomInfo.data() + at;
|
|
if((highest_at < 0) && (at != excluded)) {
|
|
highest_prot = ai->protons;
|
|
lowest_pri = ai->priority;
|
|
highest_at = at;
|
|
} else if(((ai->protons > highest_prot) ||
|
|
((ai->protons == highest_prot) && (ai->priority < lowest_pri)))
|
|
&& (at != excluded)) {
|
|
highest_prot = ai->protons;
|
|
highest_at = at;
|
|
lowest_pri = ai->priority;
|
|
}
|
|
}
|
|
return highest_at;
|
|
}
|
|
|
|
/**
|
|
* Selection mapping for `load_traj` to only load coordinates for a subset of
|
|
* atoms. Returns a mapping of old to new coordinate indices and modifies the
|
|
* index-related members of `cs`.
|
|
*
|
|
* @param[in] obj Object Molecule for evaluation of the selection
|
|
* @param[in,out] cs Candidate coordinate set (with valid index arrays)
|
|
* @param[in] selection Named selection
|
|
* @return index mapping, or nullptr if `selection` is not valid
|
|
*/
|
|
std::unique_ptr<int[]> LoadTrajSeleHelper(
|
|
const ObjectMolecule* obj, CoordSet* cs, const char* selection)
|
|
{
|
|
auto G = obj->G;
|
|
int sele0 = SelectorIndexByName(G, selection);
|
|
|
|
// We can skip "all" (0) here since it's a trivial case
|
|
if (sele0 <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto xref = std::unique_ptr<int[]>(new int[cs->NIndex]);
|
|
|
|
int idx_new = 0;
|
|
|
|
for (int idx = 0; idx < cs->NIndex; ++idx) {
|
|
auto atm = cs->IdxToAtm[idx];
|
|
if (SelectorIsMember(G, obj->AtomInfo[atm].selEntry, sele0)) {
|
|
cs->IdxToAtm[idx_new] = atm;
|
|
cs->AtmToIdx[atm] = idx_new;
|
|
xref[idx] = idx_new;
|
|
++idx_new;
|
|
} else {
|
|
cs->AtmToIdx[atm] = -1;
|
|
xref[idx] = -1;
|
|
}
|
|
}
|
|
|
|
cs->NIndex = idx_new;
|
|
cs->IdxToAtm.resize(cs->NIndex);
|
|
cs->Coord.resize(cs->NIndex * 3);
|
|
|
|
return xref;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
ObjectMolecule *ObjectMoleculeLoadTRJFile(PyMOLGlobals * G, ObjectMolecule * I,
|
|
const char *fname, int frame, int interval,
|
|
int average, int start, int stop, int max,
|
|
const char *sele, int image, const float *shift, int quiet)
|
|
{
|
|
FILE *f;
|
|
char *buffer;
|
|
const char *p;
|
|
char cc[MAXLINELEN];
|
|
int n_read;
|
|
int to_go;
|
|
int skip_first_line = true;
|
|
int periodic = false;
|
|
int angles = true;
|
|
float f0, f1, f2, *fp;
|
|
float box[3], angle[3];
|
|
float r_cent[3], r_trans[3];
|
|
int r_act, r_val, r_cnt;
|
|
float *r_fp_start = nullptr, *r_fp_stop = nullptr;
|
|
int a, b, c, i;
|
|
int zoom_flag = false;
|
|
int cnt = 0;
|
|
int n_avg = 0;
|
|
int icnt;
|
|
int ncnt = 0;
|
|
float zerovector[3] = { 0.0, 0.0, 0.0 };
|
|
CoordSet *cs = nullptr;
|
|
|
|
if (I->DiscreteFlag) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" %s: Discrete objects not supported\n", __func__ ENDFB(G);
|
|
return I;
|
|
}
|
|
|
|
if(!shift)
|
|
shift = zerovector;
|
|
if(interval < 1)
|
|
interval = 1;
|
|
|
|
icnt = interval;
|
|
#define BUFSIZE 4194304
|
|
#define GETTING_LOW 10000
|
|
|
|
f = pymol_fopen(fname, "rb");
|
|
if(!f) {
|
|
ErrMessage(G, __func__, "Unable to open file!");
|
|
} else {
|
|
if(I->CSTmpl) {
|
|
cs = CoordSetCopy(I->CSTmpl);
|
|
} else if (I->NCSet > 0) {
|
|
cs = CoordSetCopy(I->CSet[0]);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjMolLoadTRJFile: Missing topology" ENDFB(G);
|
|
return (I);
|
|
}
|
|
|
|
auto xref = LoadTrajSeleHelper(I, cs, sele);
|
|
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" ObjMolLoadTRJFile: Loading from \"%s\".\n", fname ENDFB(G);
|
|
buffer = pymol::malloc<char>(BUFSIZE + 1); /* 1 MB read buffer */
|
|
p = buffer;
|
|
buffer[0] = 0;
|
|
n_read = 0;
|
|
to_go = 0;
|
|
a = 0;
|
|
b = 0;
|
|
c = 0;
|
|
f1 = 0.0;
|
|
f2 = 0.0;
|
|
while(1) {
|
|
to_go = n_read - (p - buffer);
|
|
if(to_go < GETTING_LOW)
|
|
if(!feof(f)) {
|
|
if(to_go)
|
|
memcpy(buffer, p, to_go);
|
|
n_read = fread(buffer + to_go, 1, BUFSIZE - to_go, f);
|
|
n_read = to_go + n_read;
|
|
buffer[n_read] = 0;
|
|
p = buffer;
|
|
if(skip_first_line) {
|
|
p = nextline(p);
|
|
skip_first_line = false;
|
|
}
|
|
to_go = n_read - (p - buffer);
|
|
}
|
|
if(!to_go)
|
|
break;
|
|
p = ncopy(cc, p, 8);
|
|
if((++b) == 10) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
f0 = f1;
|
|
f1 = f2;
|
|
if(sscanf(cc, "%f", &f2) == 1) {
|
|
if((++c) == 3) {
|
|
c = 0;
|
|
if((cnt + 1) >= start) {
|
|
if(icnt <= 1) {
|
|
if(xref) {
|
|
if(xref[a] >= 0)
|
|
fp = cs->coordPtr(xref[a]);
|
|
else
|
|
fp = nullptr;
|
|
} else {
|
|
fp = cs->coordPtr(a);
|
|
}
|
|
if(fp) {
|
|
if(n_avg) {
|
|
*(fp++) += f0;
|
|
*(fp++) += f1;
|
|
*(fp++) += f2;
|
|
} else {
|
|
*(fp++) = f0;
|
|
*(fp++) = f1;
|
|
*(fp++) = f2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if((++a) == I->NAtom) {
|
|
|
|
cnt++;
|
|
a = 0;
|
|
if(b)
|
|
p = nextline(p);
|
|
b = 0;
|
|
|
|
if(cs->PeriodicBoxType != CoordSet::NoPeriodicity) {
|
|
/* read periodic box */
|
|
|
|
c = 0;
|
|
periodic = true;
|
|
angles = true;
|
|
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &box[0]) != 1)
|
|
periodic = false;
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &box[1]) != 1)
|
|
periodic = false;
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &box[2]) != 1)
|
|
periodic = false;
|
|
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &angle[0]) != 1)
|
|
angles = false;
|
|
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &angle[1]) != 1)
|
|
angles = false;
|
|
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%f", &angle[2]) != 1)
|
|
angles = false;
|
|
if(periodic) {
|
|
cs->Symmetry = std::make_unique<CSymmetry>(G);
|
|
cs->Symmetry->Crystal.setDims(box);
|
|
if(angles) {
|
|
cs->Symmetry->Crystal.setAngles(angle);
|
|
}
|
|
p = nextline(p);
|
|
b = 0;
|
|
}
|
|
|
|
if(cs->PeriodicBoxType == CoordSet::Octahedral)
|
|
periodic = false; /* can't handle this yet... */
|
|
}
|
|
|
|
if((stop > 0) && (cnt >= stop))
|
|
break;
|
|
if(cnt >= start) {
|
|
icnt--;
|
|
if(icnt > 0) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
|
|
} else {
|
|
icnt = interval;
|
|
n_avg++;
|
|
}
|
|
|
|
if(icnt == interval) {
|
|
if(n_avg < average) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
|
|
} else {
|
|
|
|
/* compute average */
|
|
if(n_avg > 1) {
|
|
fp = cs->Coord.data();
|
|
for(i = 0; i < cs->NIndex; i++) {
|
|
*(fp++) /= n_avg;
|
|
*(fp++) /= n_avg;
|
|
*(fp++) /= n_avg;
|
|
}
|
|
}
|
|
if(periodic && image) {
|
|
/* Perform residue-based period image transformation */
|
|
i = 0;
|
|
r_cnt = 0;
|
|
r_act = 0; /* 0 unspec, 1=load, 2=image, 3=leave */
|
|
r_val = -1;
|
|
while(r_act != 3) {
|
|
if(i >= cs->NIndex) {
|
|
if(r_cnt)
|
|
r_act = 2;
|
|
else
|
|
r_act = 3;
|
|
}
|
|
if(r_act == 0) {
|
|
/* start new residue */
|
|
r_cnt = 0;
|
|
r_act = 1; /* now load */
|
|
}
|
|
if(r_act == 1) {
|
|
if(i < cs->NIndex) {
|
|
|
|
/* is there a coordinate for atom? */
|
|
if(xref) {
|
|
if(xref[i] >= 0)
|
|
fp = cs->coordPtr(xref[i]);
|
|
else
|
|
fp = nullptr;
|
|
} else {
|
|
fp = cs->coordPtr(i);
|
|
}
|
|
if(fp) { /* yes there is... */
|
|
if(r_cnt) {
|
|
if(r_val != I->AtomInfo[cs->IdxToAtm[i]].resv) {
|
|
r_act = 2; /* end of residue-> time to image */
|
|
} else {
|
|
r_cnt++;
|
|
r_cent[0] += *(fp++);
|
|
r_cent[1] += *(fp++);
|
|
r_cent[2] += *(fp++);
|
|
r_fp_stop = fp; /* stop here */
|
|
i++;
|
|
}
|
|
} else {
|
|
r_val = I->AtomInfo[cs->IdxToAtm[i]].resv;
|
|
r_cnt++;
|
|
r_fp_start = fp; /* start here */
|
|
r_cent[0] = *(fp++);
|
|
r_cent[1] = *(fp++);
|
|
r_cent[2] = *(fp++);
|
|
r_fp_stop = fp; /* stop here */
|
|
i++;
|
|
}
|
|
} else {
|
|
i++;
|
|
}
|
|
} else {
|
|
r_act = 2; /* image */
|
|
}
|
|
}
|
|
|
|
if(r_act == 2) { /* time to image */
|
|
if(r_cnt) {
|
|
r_cent[0] /= r_cnt;
|
|
r_cent[1] /= r_cnt;
|
|
r_cent[2] /= r_cnt;
|
|
const auto& periodicbox = cs->getSymmetry()->Crystal;
|
|
transform33f3f(periodicbox.realToFrac(), r_cent, r_cent);
|
|
r_trans[0] = fmodf(1000.0F + shift[0] + r_cent[0], 1.0F);
|
|
r_trans[1] = fmodf(1000.0F + shift[1] + r_cent[1], 1.0F);
|
|
r_trans[2] = fmodf(1000.0F + shift[2] + r_cent[2], 1.0F);
|
|
r_trans[0] -= r_cent[0];
|
|
r_trans[1] -= r_cent[1];
|
|
r_trans[2] -= r_cent[2];
|
|
transform33f3f(periodicbox.fracToReal(), r_trans, r_trans);
|
|
fp = r_fp_start;
|
|
while(fp < r_fp_stop) {
|
|
*(fp++) += r_trans[0];
|
|
*(fp++) += r_trans[1];
|
|
*(fp++) += r_trans[2];
|
|
}
|
|
}
|
|
r_act = 0; /* reset */
|
|
r_cnt = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* add new coord set */
|
|
cs->invalidateRep(cRepAll, cRepInvRep);
|
|
if(frame < 0)
|
|
frame = I->NCSet;
|
|
if(!I->NCSet) {
|
|
zoom_flag = true;
|
|
}
|
|
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
delete I->CSet[frame];
|
|
I->CSet[frame] = cs;
|
|
ncnt++;
|
|
|
|
if(average < 2) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: read set %d into state %d...\n", cnt, frame + 1
|
|
ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: averaging set %d...\n", cnt ENDFB(G);
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: average loaded into state %d...\n", frame + 1
|
|
ENDFB(G);
|
|
}
|
|
frame++;
|
|
cs = CoordSetCopy(cs);
|
|
n_avg = 0;
|
|
if((stop > 0) && (cnt >= stop))
|
|
break;
|
|
if((max > 0) && (ncnt >= max))
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: skipping set %d...\n", cnt ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjMolLoadTRJFile-Error: Failed to read expected coordinate value.\n This traj. does not match the loaded parameter/topology file.\n Likely cause: either the atom count or the periodic box settings\n are inconsistent between the two files.\n"
|
|
ENDFB(G);
|
|
break;
|
|
}
|
|
}
|
|
mfree(buffer);
|
|
}
|
|
delete cs;
|
|
SceneChanged(G);
|
|
SceneCountFrames(G);
|
|
if(zoom_flag)
|
|
if(SettingGetGlobal_i(G, cSetting_auto_zoom)) {
|
|
ExecutiveWindowZoom(G, I->Name, 0.0, -1, 0, 0, quiet); /* auto zoom (all states) */
|
|
}
|
|
|
|
return (I);
|
|
}
|
|
|
|
ObjectMolecule *ObjectMoleculeLoadRSTFile(PyMOLGlobals * G, ObjectMolecule * I,
|
|
const char *fname, int frame, int quiet, char mode)
|
|
{
|
|
/*
|
|
* mode = 0: AMBER coordinate/restart file (one frame only)
|
|
* mode = 1: AMBER trajectory
|
|
* mode = 2: AMBER trajectory with box
|
|
* http://ambermd.org/formats.html
|
|
*/
|
|
int ok = true;
|
|
char *buffer, *p;
|
|
char cc[MAXLINELEN];
|
|
float f0, f1, f2, *fp;
|
|
int a, b, c;
|
|
int zoom_flag = false;
|
|
CoordSet *cs = nullptr;
|
|
char ncolumn = 6; // number of coordinates per line
|
|
char nbyte = 12; // width of one coordinate
|
|
|
|
if(mode > 0) {
|
|
ncolumn = 10;
|
|
nbyte = 8;
|
|
}
|
|
|
|
#define BUFSIZE 4194304
|
|
#define GETTING_LOW 10000
|
|
|
|
else {
|
|
if(I->CSTmpl) {
|
|
cs = CoordSetCopy(I->CSTmpl);
|
|
} else if (I->NCSet > 0) {
|
|
cs = CoordSetCopy(I->CSet[0]);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjMolLoadRSTFile: Missing topology" ENDFB(G);
|
|
return (I);
|
|
}
|
|
CHECKOK(ok, cs);
|
|
if (ok){
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" ObjMolLoadRSTFile: Loading from \"%s\".\n", fname ENDFB(G);
|
|
p = buffer = FileGetContents(fname, nullptr);
|
|
if(!buffer)
|
|
ok = ErrMessage(G, __func__, "Unable to open file!");
|
|
}
|
|
if (ok){
|
|
p = nextline(p);
|
|
if (mode == 0) // skip NATOM,TIME
|
|
p = nextline(p);
|
|
}
|
|
a = 0;
|
|
b = 0;
|
|
c = 0;
|
|
f1 = 0.0;
|
|
f2 = 0.0;
|
|
while(ok && *p) {
|
|
p = ncopy(cc, p, nbyte);
|
|
if((++b) == ncolumn) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
f0 = f1;
|
|
f1 = f2;
|
|
if(sscanf(cc, "%f", &f2) == 1) {
|
|
if((++c) == 3) {
|
|
c = 0;
|
|
fp = cs->coordPtr(a);
|
|
*(fp++) = f0;
|
|
*(fp++) = f1;
|
|
*(fp++) = f2;
|
|
|
|
if((++a) == I->NAtom) {
|
|
a = 0;
|
|
if(b)
|
|
p = nextline(p);
|
|
if(mode == 2) // skip box
|
|
p = nextline(p);
|
|
b = 0;
|
|
/* add new coord set */
|
|
cs->invalidateRep(cRepAll, cRepInvRep);
|
|
if(frame < 0)
|
|
frame = I->NCSet;
|
|
if(!I->NCSet) {
|
|
zoom_flag = true;
|
|
}
|
|
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
CHECKOK(ok, I->CSet);
|
|
if (ok){
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
delete I->CSet[frame];
|
|
I->CSet[frame] = cs;
|
|
}
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: read coordinates into state %d...\n", frame + 1 ENDFB(G);
|
|
|
|
if (ok)
|
|
cs = CoordSetCopy(cs);
|
|
CHECKOK(ok, cs);
|
|
|
|
if (mode == 0) // restart file has only one frame
|
|
break;
|
|
|
|
frame += 1;
|
|
}
|
|
}
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjMolLoadRSTFile: atom/coordinate mismatch.\n" ENDFB(G);
|
|
break;
|
|
}
|
|
}
|
|
mfree(buffer);
|
|
}
|
|
delete cs;
|
|
|
|
SceneChanged(G);
|
|
SceneCountFrames(G);
|
|
if(zoom_flag){
|
|
if(SettingGetGlobal_i(G, cSetting_auto_zoom)) {
|
|
ExecutiveWindowZoom(G, I->Name, 0.0, -1, 0, 0, quiet); /* auto zoom (all states) */
|
|
}
|
|
}
|
|
return (I);
|
|
}
|
|
|
|
static const char *findflag(PyMOLGlobals * G, const char *p, const char *flag, const char *format)
|
|
{
|
|
|
|
char cc[MAXLINELEN];
|
|
char pat[MAXLINELEN] = "%FLAG ";
|
|
int l;
|
|
|
|
PRINTFD(G, FB_ObjectMolecule)
|
|
" findflag: flag %s format %s\n", flag, format ENDFD;
|
|
|
|
strcat(pat, flag);
|
|
l = strlen(pat);
|
|
while(*p) {
|
|
p = ncopy(cc, p, l);
|
|
if(WordMatch(G, cc, pat, true) < 0) {
|
|
p = nextline(p);
|
|
break;
|
|
}
|
|
p = nextline(p);
|
|
if(!*p) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjectMolecule-Error: Unrecognized file format (can't find \"%s\").\n", pat
|
|
ENDFB(G);
|
|
}
|
|
}
|
|
|
|
strcpy(pat, "%FORMAT(");
|
|
strcat(pat, format);
|
|
strcat(pat, ")");
|
|
l = strlen(pat);
|
|
while(*p) {
|
|
p = ncopy(cc, p, l);
|
|
if(WordMatch(G, cc, pat, true) < 0) {
|
|
p = nextline(p);
|
|
break;
|
|
}
|
|
p = nextline(p);
|
|
if(!*p) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Errors)
|
|
" ObjectMolecule-Error: Unrecognized file format (can't find \"%s\").\n", pat
|
|
ENDFB(G);
|
|
}
|
|
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
static CoordSet *ObjectMoleculeTOPStr2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr)
|
|
{
|
|
const char *p;
|
|
int nAtom;
|
|
int a, b, c, bi, last_i, at_i, aa, rc;
|
|
float *coord = nullptr;
|
|
float *f;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr, *ai;
|
|
BondType *bond = nullptr, *bd;
|
|
int nBond = 0;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
int amber7 = false;
|
|
|
|
WordType title;
|
|
ResName *resn;
|
|
|
|
char cc[MAXLINELEN];
|
|
int ok = true;
|
|
int i0, i1, i2;
|
|
|
|
/* trajectory parameters */
|
|
|
|
int NTYPES, NBONH, MBONA, NTHETH, MTHETA;
|
|
int NPHIH, MPHIA, NHPARM, NPARM, NNB, NRES;
|
|
int NBONA, NTHETA, NPHIA, NUMBND, NUMANG, NPTRA;
|
|
int NATYP, NPHB, IFPERT, NBPER, NGPER, NDPER;
|
|
int MBPER, MGPER, MDPER, IFBOX = 0, NMXRS, IFCAP;
|
|
int NEXTRA, IPOL = 0;
|
|
int wid, col;
|
|
float BETA;
|
|
float BOX1, BOX2, BOX3;
|
|
|
|
cset = CoordSetNew(G);
|
|
|
|
p = buffer;
|
|
nAtom = 0;
|
|
if(atInfoPtr)
|
|
atInfo = *atInfoPtr;
|
|
if(!atInfo)
|
|
ErrFatal(G, "TOPStr2CoordSet", "need atom information record!");
|
|
/* failsafe for old version.. */
|
|
|
|
ncopy(cc, p, 8);
|
|
if(strcmp(cc, "%VERSION") == 0) {
|
|
amber7 = true;
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: Attempting to read Amber7 topology file.\n" ENDFB(G);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: Assuming this is an Amber6 topology file.\n" ENDFB(G);
|
|
}
|
|
|
|
/* read title */
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "TITLE", "20a4");
|
|
}
|
|
|
|
p = ncopy(cc, p, 20);
|
|
title[0] = 0;
|
|
sscanf(cc, "%s", title);
|
|
p = nextline(p);
|
|
|
|
if(amber7) {
|
|
|
|
p = findflag(G, buffer, "POINTERS", "10I8");
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &nAtom) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NTYPES) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NBONH) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MBONA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NTHETH) == 1);
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MTHETA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NPHIH) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MPHIA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NHPARM) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NPARM) == 1);
|
|
|
|
p = nextline(p);
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NNB) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NRES) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NBONA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NTHETA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NPHIA) == 1);
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NUMBND) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NUMANG) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NPTRA) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NATYP) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NPHB) == 1);
|
|
|
|
p = nextline(p);
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &IFPERT) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NBPER) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NGPER) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NDPER) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MBPER) == 1);
|
|
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MGPER) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &MDPER) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &IFBOX) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NMXRS) == 1);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &IFCAP) == 1);
|
|
|
|
p = nextline(p);
|
|
p = ncopy(cc, p, 8);
|
|
ok = ok && (sscanf(cc, "%d", &NEXTRA) == 1);
|
|
|
|
} else {
|
|
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &nAtom) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NTYPES) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NBONH) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MBONA) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NTHETH) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MTHETA) == 1);
|
|
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NPHIH) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MPHIA) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NHPARM) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NPARM) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NNB) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NRES) == 1);
|
|
|
|
p = nextline(p);
|
|
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NBONA) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NTHETA) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NPHIA) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NUMBND) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NUMANG) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NPTRA) == 1);
|
|
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NATYP) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NPHB) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &IFPERT) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NBPER) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NGPER) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NDPER) == 1);
|
|
|
|
p = nextline(p);
|
|
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MBPER) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MGPER) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &MDPER) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &IFBOX) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &NMXRS) == 1);
|
|
p = ncopy(cc, p, 6);
|
|
ok = ok && (sscanf(cc, "%d", &IFCAP) == 1);
|
|
|
|
p = ncopy(cc, p, 6);
|
|
if(sscanf(cc, "%d", &NEXTRA) != 1)
|
|
NEXTRA = 0;
|
|
|
|
}
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading counts lines");
|
|
ok_raise(1);
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read counts line nAtom %d NBONA %d NBONH %d\n",
|
|
nAtom, NBONA, NBONH ENDFB(G);
|
|
}
|
|
|
|
switch (IFBOX) {
|
|
case 2:
|
|
cset->PeriodicBoxType = CoordSet::Octahedral;
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Details)
|
|
" TOPStrToCoordSet: Warning: can't currently image a truncated octahedron...\n"
|
|
ENDFB(G);
|
|
break;
|
|
case 1:
|
|
cset->PeriodicBoxType = CoordSet::Orthogonal;
|
|
break;
|
|
case 0:
|
|
default:
|
|
cset->PeriodicBoxType = CoordSet::NoPeriodicity;
|
|
break;
|
|
}
|
|
|
|
p = nextline(p);
|
|
|
|
if(ok) {
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "ATOM_NAME", "20a4");
|
|
}
|
|
/* read atoms */
|
|
|
|
b = 0;
|
|
for(a = 0; a < nAtom; a++) {
|
|
p = ntrim(cc, p, 4);
|
|
atInfo[a].name = LexIdx(G, cc);
|
|
if((++b) == 20) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading atom names");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read atom names.\n" ENDFB(G);
|
|
}
|
|
|
|
/* read charges */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "CHARGE", "5E16.8");
|
|
}
|
|
|
|
b = 0;
|
|
for(a = 0; a < nAtom; a++) {
|
|
p = ncopy(cc, p, 16);
|
|
ai = atInfo + a;
|
|
if(!sscanf(cc, "%f", &ai->partialCharge))
|
|
ok = false;
|
|
else {
|
|
ai->partialCharge /= 18.2223F; /* convert to electron charge */
|
|
}
|
|
if((++b) == 5) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading charges");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read charges.\n" ENDFB(G);
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!amber7) {
|
|
/* skip masses */
|
|
|
|
p = skip_fortran(nAtom, 5, p);
|
|
}
|
|
|
|
/* read LJ atom types */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "ATOM_TYPE_INDEX", "10I8");
|
|
col = 10;
|
|
wid = 8;
|
|
} else {
|
|
col = 12;
|
|
wid = 6;
|
|
}
|
|
|
|
b = 0;
|
|
for(a = 0; a < nAtom; a++) {
|
|
p = ncopy(cc, p, wid);
|
|
ai = atInfo + a;
|
|
if(!sscanf(cc, "%d", &ai->customType))
|
|
ok = false;
|
|
if((++b) == col) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error LJ atom types");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read LJ atom types.\n" ENDFB(G);
|
|
}
|
|
|
|
if(!amber7) {
|
|
/* skip excluded atom counts */
|
|
|
|
p = skip_fortran(nAtom, 12, p);
|
|
|
|
/* skip NB param arrays */
|
|
|
|
p = skip_fortran(NTYPES * NTYPES, 12, p);
|
|
}
|
|
|
|
/* read residue labels */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "RESIDUE_LABEL", "20a4");
|
|
}
|
|
|
|
resn = pymol::malloc<ResName>(NRES);
|
|
|
|
b = 0;
|
|
for(a = 0; a < NRES; a++) {
|
|
p = ncopy(cc, p, 4);
|
|
if(!sscanf(cc, "%s", resn[a]))
|
|
resn[a][0] = 0;
|
|
if((++b) == 20) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading residue labels");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read residue labels.\n" ENDFB(G);
|
|
}
|
|
|
|
/* read residue assignments */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "RESIDUE_POINTER", "10I8");
|
|
col = 10;
|
|
wid = 8;
|
|
} else {
|
|
col = 12;
|
|
wid = 6;
|
|
}
|
|
|
|
b = 0;
|
|
last_i = 0;
|
|
rc = 0;
|
|
for(a = 0; a < NRES; a++) {
|
|
p = ncopy(cc, p, wid);
|
|
if(sscanf(cc, "%d", &at_i)) {
|
|
if(last_i)
|
|
for(aa = (last_i - 1); aa < (at_i - 1); aa++) {
|
|
ai = atInfo + aa;
|
|
ai->resn = LexIdx(G, resn[a - 1]);
|
|
ai->resv = rc;
|
|
}
|
|
rc++;
|
|
last_i = at_i;
|
|
}
|
|
if((++b) == col) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
if(last_i)
|
|
for(aa = (last_i - 1); aa < nAtom; aa++) {
|
|
ai = atInfo + aa;
|
|
ai->resn = LexIdx(G, resn[NRES - 1]);
|
|
ai->resv = rc;
|
|
}
|
|
rc++;
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading residues");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read residues.\n" ENDFB(G);
|
|
}
|
|
|
|
FreeP(resn);
|
|
|
|
if(!amber7) {
|
|
/* skip bond force constants */
|
|
|
|
p = skip_fortran(NUMBND, 5, p);
|
|
|
|
/* skip bond lengths */
|
|
|
|
p = skip_fortran(NUMBND, 5, p);
|
|
|
|
/* skip angle force constant */
|
|
|
|
p = skip_fortran(NUMANG, 5, p);
|
|
|
|
/* skip angle eq */
|
|
|
|
p = skip_fortran(NUMANG, 5, p);
|
|
|
|
/* skip dihedral force constant */
|
|
|
|
p = skip_fortran(NPTRA, 5, p);
|
|
|
|
/* skip dihedral periodicity */
|
|
|
|
p = skip_fortran(NPTRA, 5, p);
|
|
|
|
/* skip dihedral phases */
|
|
|
|
p = skip_fortran(NPTRA, 5, p);
|
|
|
|
/* skip SOLTYs */
|
|
|
|
p = skip_fortran(NATYP, 5, p);
|
|
|
|
/* skip LJ terms r12 */
|
|
|
|
p = skip_fortran((NTYPES * (NTYPES + 1)) / 2, 5, p);
|
|
|
|
/* skip LJ terms r6 */
|
|
|
|
p = skip_fortran((NTYPES * (NTYPES + 1)) / 2, 5, p);
|
|
|
|
}
|
|
|
|
/* read bonds */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "BONDS_INC_HYDROGEN", "10I8");
|
|
col = 10;
|
|
wid = 8;
|
|
} else {
|
|
col = 12;
|
|
wid = 6;
|
|
}
|
|
|
|
nBond = NBONH + NBONA;
|
|
|
|
bond = VLACalloc(BondType, nBond);
|
|
|
|
bi = 0;
|
|
|
|
b = 0;
|
|
c = 0;
|
|
i0 = 0;
|
|
i1 = 0;
|
|
for(a = 0; a < 3 * NBONH; a++) {
|
|
p = ncopy(cc, p, wid);
|
|
i2 = i1;
|
|
i1 = i0;
|
|
if(!sscanf(cc, "%d", &i0))
|
|
ok = false;
|
|
if((++c) == 3) {
|
|
c = 0;
|
|
bd = bond + bi;
|
|
bd->index[0] = (abs(i2) / 3);
|
|
bd->index[1] = (abs(i1) / 3);
|
|
bd->order = 1;
|
|
bi++;
|
|
}
|
|
if((++b) == col) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error hydrogen containing bonds");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read %d hydrogen containing bonds.\n", NBONH ENDFB(G);
|
|
}
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "BONDS_WITHOUT_HYDROGEN", "10I8");
|
|
col = 10;
|
|
wid = 8;
|
|
} else {
|
|
col = 12;
|
|
wid = 6;
|
|
}
|
|
|
|
b = 0;
|
|
c = 0;
|
|
for(a = 0; a < 3 * NBONA; a++) {
|
|
p = ncopy(cc, p, wid);
|
|
i2 = i1;
|
|
i1 = i0;
|
|
if(!sscanf(cc, "%d", &i0))
|
|
ok = false;
|
|
if((++c) == 3) {
|
|
c = 0;
|
|
bd = bond + bi;
|
|
bd->index[0] = (abs(i2) / 3);
|
|
bd->index[1] = (abs(i1) / 3);
|
|
bd->order = 1; // PYMOL-2707
|
|
bi++;
|
|
}
|
|
if((++b) == col) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error hydrogen free bonds");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read %d hydrogen free bonds.\n", NBONA ENDFB(G);
|
|
}
|
|
|
|
if(!amber7) {
|
|
|
|
/* skip hydrogen angles */
|
|
|
|
p = skip_fortran(4 * NTHETH, 12, p);
|
|
|
|
/* skip non-hydrogen angles */
|
|
|
|
p = skip_fortran(4 * NTHETA, 12, p);
|
|
|
|
/* skip hydrogen dihedrals */
|
|
|
|
p = skip_fortran(5 * NPHIH, 12, p);
|
|
|
|
/* skip non hydrogen dihedrals */
|
|
|
|
p = skip_fortran(5 * NPHIA, 12, p);
|
|
|
|
/* skip nonbonded exclusions */
|
|
|
|
p = skip_fortran(NNB, 12, p);
|
|
|
|
/* skip hydrogen bonds ASOL */
|
|
|
|
p = skip_fortran(NPHB, 5, p);
|
|
|
|
/* skip hydrogen bonds BSOL */
|
|
|
|
p = skip_fortran(NPHB, 5, p);
|
|
|
|
/* skip HBCUT */
|
|
|
|
p = skip_fortran(NPHB, 5, p);
|
|
|
|
}
|
|
/* read AMBER atom types */
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "AMBER_ATOM_TYPE", "20a4");
|
|
}
|
|
|
|
b = 0;
|
|
for(a = 0; a < nAtom; a++) {
|
|
OrthoLineType temp;
|
|
p = ncopy(cc, p, 4);
|
|
ai = atInfo + a;
|
|
if(sscanf(cc, "%s", temp) != 1)
|
|
ok = false;
|
|
else {
|
|
ai->textType = LexIdx(G, temp);
|
|
}
|
|
if((++b) == 20) {
|
|
b = 0;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(b)
|
|
p = nextline(p);
|
|
|
|
if(!ok) {
|
|
ErrMessage(G, "TOPStrToCoordSet", "Error reading atom types");
|
|
} else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" TOPStr2CoordSet: read atom types.\n" ENDFB(G);
|
|
}
|
|
|
|
if(!amber7) {
|
|
/* skip TREE classification */
|
|
|
|
p = skip_fortran(nAtom, 20, p);
|
|
|
|
/* skip tree joining information */
|
|
|
|
p = skip_fortran(nAtom, 12, p);
|
|
|
|
/* skip last atom rotated blah blah blah */
|
|
|
|
p = skip_fortran(nAtom, 12, p);
|
|
|
|
}
|
|
|
|
if(IFBOX > 0) {
|
|
|
|
int IPTRES, NSPM = 0, NSPSOL;
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "SOLVENT_POINTERS", "3I8");
|
|
wid = 8;
|
|
} else {
|
|
wid = 6;
|
|
}
|
|
p = ncopy(cc, p, wid);
|
|
ok = ok && (sscanf(cc, "%d", &IPTRES) == 1);
|
|
p = ncopy(cc, p, wid);
|
|
ok = ok && (sscanf(cc, "%d", &NSPM) == 1);
|
|
p = ncopy(cc, p, wid);
|
|
ok = ok && (sscanf(cc, "%d", &NSPSOL) == 1);
|
|
|
|
ok_assert(1, ok);
|
|
|
|
p = nextline(p);
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "ATOMS_PER_MOLECULE", "10I8");
|
|
col = 10;
|
|
} else {
|
|
col = 12;
|
|
}
|
|
|
|
/* skip num atoms per box */
|
|
|
|
p = skip_fortran(NSPM, col, p);
|
|
|
|
if(amber7) {
|
|
p = findflag(G, buffer, "BOX_DIMENSIONS", "5E16.8");
|
|
}
|
|
wid = 16;
|
|
|
|
p = ncopy(cc, p, 16);
|
|
ok = ok && (sscanf(cc, "%f", &BETA) == 1);
|
|
p = ncopy(cc, p, 16);
|
|
ok = ok && (sscanf(cc, "%f", &BOX1) == 1);
|
|
p = ncopy(cc, p, 16);
|
|
ok = ok && (sscanf(cc, "%f", &BOX2) == 1);
|
|
p = ncopy(cc, p, 16);
|
|
ok = ok && (sscanf(cc, "%f", &BOX3) == 1);
|
|
|
|
if(ok) {
|
|
float angle[3] = {90.f, BETA, 90.f};
|
|
|
|
if((BETA > 109.47) && (BETA < 109.48)) {
|
|
cset->PeriodicBoxType = CoordSet::Octahedral;
|
|
angle[0] = 109.47122;
|
|
angle[1] = 109.47122;
|
|
angle[2] = 109.47122;
|
|
} else if(BETA == 60.0) {
|
|
angle[0] = 60.0; /* rhombic dodecahedron (from ptraj.c) */
|
|
angle[1] = 90.0;
|
|
angle[2] = 60.0;
|
|
}
|
|
|
|
cset->Symmetry = std::make_unique<CSymmetry>(G);
|
|
cset->Symmetry->Crystal.setDims(BOX1, BOX2, BOX3);
|
|
cset->Symmetry->Crystal.setAngles(angle);
|
|
}
|
|
/* skip periodic box */
|
|
|
|
p = nextline(p);
|
|
|
|
}
|
|
|
|
if(!amber7) {
|
|
|
|
if(IFCAP > 0) {
|
|
p = nextline(p);
|
|
p = nextline(p);
|
|
p = nextline(p);
|
|
}
|
|
|
|
if(IFPERT > 0) {
|
|
|
|
/* skip perturbed bond atoms */
|
|
|
|
p = skip_fortran(2 * NBPER, 12, p);
|
|
|
|
/* skip perturbed bond atom pointers */
|
|
|
|
p = skip_fortran(2 * NBPER, 12, p);
|
|
|
|
/* skip perturbed angles */
|
|
|
|
p = skip_fortran(3 * NGPER, 12, p);
|
|
|
|
/* skip perturbed angle pointers */
|
|
|
|
p = skip_fortran(2 * NGPER, 12, p);
|
|
|
|
/* skip perturbed dihedrals */
|
|
|
|
p = skip_fortran(4 * NDPER, 12, p);
|
|
|
|
/* skip perturbed dihedral pointers */
|
|
|
|
p = skip_fortran(2 * NDPER, 12, p);
|
|
|
|
/* skip residue names */
|
|
|
|
p = skip_fortran(NRES, 20, p);
|
|
|
|
/* skip atom names */
|
|
|
|
p = skip_fortran(nAtom, 20, p);
|
|
|
|
/* skip atom symbols */
|
|
|
|
p = skip_fortran(nAtom, 20, p);
|
|
|
|
/* skip unused field */
|
|
|
|
p = skip_fortran(nAtom, 5, p);
|
|
|
|
/* skip perturbed flags */
|
|
|
|
p = skip_fortran(nAtom, 12, p);
|
|
|
|
/* skip LJ atom flags */
|
|
|
|
p = skip_fortran(nAtom, 12, p);
|
|
|
|
/* skip perturbed charges */
|
|
|
|
p = skip_fortran(nAtom, 5, p);
|
|
|
|
}
|
|
|
|
if(IPOL > 0) {
|
|
|
|
/* skip atomic polarizabilities */
|
|
|
|
p = skip_fortran(nAtom, 5, p);
|
|
|
|
}
|
|
|
|
if((IPOL > 0) && (IFPERT > 0)) {
|
|
|
|
/* skip atomic polarizabilities */
|
|
|
|
p = skip_fortran(nAtom, 5, p);
|
|
|
|
}
|
|
}
|
|
/* for future reference
|
|
|
|
%FLAG LES_NTYP
|
|
%FORMAT(10I8)
|
|
%FLAG LES_TYPE
|
|
%FORMAT(10I8)
|
|
%FLAG LES_FAC
|
|
%FORMAT(5E16.8)
|
|
%FLAG LES_CNUM
|
|
%FORMAT(10I8)
|
|
%FLAG LES_ID
|
|
%FORMAT(10I8)
|
|
|
|
Here is the additional information for LES topology formats:
|
|
First, if NPARM ==1, LES entries are in topology (NPARM is the 10th
|
|
entry in the initial list of control parameters); otherwise the standard
|
|
format applies.
|
|
So, with NPARM=1, you just need to read a few more things at the very
|
|
end of topology file:
|
|
LES_NTYP (format: I6) ... one number, number of LES types
|
|
and four arrays:
|
|
LES_TYPE (12I6) ... NATOM integer entries
|
|
LES_FAC (E16.8) ... LES_NTYPxLES_NTYP float entries
|
|
LES_CNUM (12I6) ... NATOM integer entries
|
|
LES_ID (12I6) ... NATOM integer entries
|
|
|
|
and that's it. Your parser must have skipped this information because it
|
|
was at the end of the file. Maybe that's good enough.
|
|
|
|
*/
|
|
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
|
|
f = coord;
|
|
for(a = 0; a < nAtom; a++) {
|
|
*(f++) = 0.0;
|
|
*(f++) = 0.0;
|
|
*(f++) = 0.0;
|
|
ai = atInfo + a;
|
|
ai->id = a + 1; /* assign 1-based identifiers */
|
|
ai->rank = a;
|
|
AtomInfoAssignParameters(G, ai);
|
|
AtomInfoAssignColors(G, ai);
|
|
ai->visRep = auto_show;
|
|
}
|
|
}
|
|
if(ok) {
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
cset->NTmpBond = nBond;
|
|
} else {
|
|
ok_except1:
|
|
delete cset;
|
|
cset = nullptr;
|
|
ErrMessage(G, __func__, "failed");
|
|
}
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
|
|
return (cset);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
static ObjectMolecule *ObjectMoleculeReadTOPStr(PyMOLGlobals * G, ObjectMolecule * I,
|
|
char *TOPStr, int frame, int discrete)
|
|
{
|
|
CoordSet *cset = nullptr;
|
|
pymol::vla<AtomInfoType> atInfo(1);
|
|
int ok = true;
|
|
int isNew = true;
|
|
unsigned int nAtom = 0;
|
|
|
|
if(!I)
|
|
isNew = true;
|
|
else
|
|
isNew = false;
|
|
|
|
if(ok) {
|
|
if(isNew) {
|
|
I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
|
|
CHECKOK(ok, I);
|
|
if (ok)
|
|
std::swap(atInfo, I->AtomInfo);
|
|
}
|
|
if(ok && isNew) {
|
|
I->Color = AtomInfoUpdateAutoColor(G);
|
|
}
|
|
|
|
if (ok)
|
|
cset = ObjectMoleculeTOPStr2CoordSet(G, TOPStr, &atInfo);
|
|
CHECKOK(ok, cset);
|
|
}
|
|
|
|
/* include coordinate set */
|
|
if(ok) {
|
|
nAtom = cset->NIndex;
|
|
|
|
if(I->DiscreteFlag && atInfo) {
|
|
unsigned int a;
|
|
int fp1 = frame + 1;
|
|
AtomInfoType *ai = atInfo.data();
|
|
for(a = 0; a < nAtom; a++) {
|
|
(ai++)->discrete_state = fp1;
|
|
}
|
|
}
|
|
|
|
cset->Obj = I;
|
|
cset->enumIndices();
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
if(isNew) {
|
|
std::swap(I->AtomInfo, atInfo);
|
|
} else if (ok){
|
|
ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_AllMask, true);
|
|
}
|
|
if(isNew)
|
|
I->NAtom = nAtom;
|
|
/*
|
|
if(frame<0) frame=I->NCSet;
|
|
VLACheck(I->CSet,CoordSet*,frame);
|
|
if(I->NCSet<=frame) I->NCSet=frame+1;
|
|
if(I->CSet[frame]) I->CSet[frame]->fFree(I->CSet[frame]);
|
|
I->CSet[frame] = cset;
|
|
*/
|
|
|
|
if(ok && isNew)
|
|
ok = ObjectMoleculeConnect(I, cset, false);
|
|
if(cset->Symmetry && (!I->Symmetry)) {
|
|
I->Symmetry.reset(new CSymmetry(*cset->Symmetry));
|
|
CHECKOK(ok, I->Symmetry);
|
|
}
|
|
|
|
delete I->CSTmpl;
|
|
I->CSTmpl = cset; /* save template coordinate set */
|
|
|
|
SceneCountFrames(G);
|
|
if (ok)
|
|
ok &= ObjectMoleculeExtendIndices(I, -1);
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(I);
|
|
if (ok){
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
ObjectMoleculeUpdateNonbonded(I);
|
|
}
|
|
}
|
|
if (!ok){
|
|
DeleteP(I)
|
|
}
|
|
return (I);
|
|
}
|
|
|
|
ObjectMolecule *ObjectMoleculeLoadTOPFile(PyMOLGlobals * G, ObjectMolecule * obj,
|
|
const char *fname, int frame, int discrete)
|
|
{
|
|
ObjectMolecule *I = nullptr;
|
|
char *buffer;
|
|
|
|
buffer = FileGetContents(fname, nullptr);
|
|
|
|
if(!buffer)
|
|
ErrMessage(G, __func__, "Unable to open file!");
|
|
else {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" %s: Loading from %s.\n", __func__, fname ENDFB(G);
|
|
|
|
I = ObjectMoleculeReadTOPStr(G, obj, buffer, frame, discrete);
|
|
mfree(buffer);
|
|
}
|
|
|
|
return (I);
|
|
}
|
|
|
|
void ObjectMoleculeSculptClear(ObjectMolecule * I)
|
|
{
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: entered.\n", __func__ ENDFD;
|
|
if(I->Sculpt)
|
|
DeleteP(I->Sculpt);
|
|
}
|
|
|
|
void ObjectMoleculeSculptImprint(ObjectMolecule * I, int state, int match_state,
|
|
int match_by_segment)
|
|
{
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: entered.\n", __func__ ENDFD;
|
|
|
|
if(!I->Sculpt)
|
|
I->Sculpt = new CSculpt(I->G);
|
|
SculptMeasureObject(I->Sculpt, I, state, match_state, match_by_segment);
|
|
}
|
|
|
|
float ObjectMoleculeSculptIterate(ObjectMolecule * I, int state, int n_cycle,
|
|
float *center)
|
|
{
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: entered.\n", __func__ ENDFD;
|
|
if(I->Sculpt) {
|
|
return SculptIterateObject(I->Sculpt, I, state, n_cycle, center);
|
|
} else
|
|
return 0.0F;
|
|
}
|
|
|
|
/**
|
|
* - Assigns new id to all atoms with AtomInfoType.id == -1
|
|
* - Assigns ObjectMolecule.AtomCounter if -1
|
|
*
|
|
* Cost: O(NAtom + NBond)
|
|
*/
|
|
void ObjectMoleculeUpdateIDNumbers(ObjectMolecule * I)
|
|
{
|
|
int a;
|
|
int max;
|
|
AtomInfoType *ai;
|
|
|
|
if(I->AtomCounter < 0) {
|
|
max = -1;
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(ai->id > max)
|
|
max = ai->id;
|
|
ai++;
|
|
}
|
|
I->AtomCounter = max + 1;
|
|
}
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(ai->id < 0)
|
|
ai->id = I->AtomCounter++;
|
|
ai++;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeGetPhiPsi(ObjectMolecule * I, int ca, float *phi, float *psi, int state)
|
|
{
|
|
int np = -1;
|
|
int cm = -1;
|
|
int c = -1;
|
|
int n = -1;
|
|
int result = false;
|
|
const AtomInfoType *ai;
|
|
float v_ca[3];
|
|
float v_n[3];
|
|
float v_c[3];
|
|
float v_cm[3];
|
|
float v_np[3];
|
|
auto G = I->G;
|
|
|
|
ai = I->AtomInfo;
|
|
|
|
if(ai[ca].name == G->lex_const.CA) {
|
|
/* find C */
|
|
for (auto const& neighbor : AtomNeighbors(I, ca)) {
|
|
if (ai[neighbor.atm].name == G->lex_const.C) {
|
|
c = neighbor.atm;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* find N */
|
|
for (auto const& neighbor : AtomNeighbors(I, ca)) {
|
|
if (ai[neighbor.atm].name == G->lex_const.N) {
|
|
n = neighbor.atm;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* find NP */
|
|
if(c >= 0) {
|
|
for (auto const& neighbor : AtomNeighbors(I, c)) {
|
|
if (ai[neighbor.atm].name == G->lex_const.N) {
|
|
np = neighbor.atm;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* find CM */
|
|
if(n >= 0) {
|
|
for (auto const& neighbor : AtomNeighbors(I, n)) {
|
|
if (ai[neighbor.atm].name == G->lex_const.C) {
|
|
cm = neighbor.atm;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if((ca >= 0) && (np >= 0) && (c >= 0) && (n >= 0) && (cm >= 0)) {
|
|
if(ObjectMoleculeGetAtomVertex(I, state, ca, v_ca) &&
|
|
ObjectMoleculeGetAtomVertex(I, state, n, v_n) &&
|
|
ObjectMoleculeGetAtomVertex(I, state, c, v_c) &&
|
|
ObjectMoleculeGetAtomVertex(I, state, cm, v_cm) &&
|
|
ObjectMoleculeGetAtomVertex(I, state, np, v_np)) {
|
|
|
|
(*phi) = rad_to_deg(get_dihedral3f(v_c, v_ca, v_n, v_cm));
|
|
(*psi) = rad_to_deg(get_dihedral3f(v_np, v_c, v_ca, v_n));
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeCheckBondSep(ObjectMolecule * I, int a0, int a1, int dist)
|
|
{
|
|
int result = false;
|
|
int n0;
|
|
int stack[MAX_BOND_DIST + 1];
|
|
int history[MAX_BOND_DIST + 1];
|
|
int depth = 0;
|
|
int distinct;
|
|
int a;
|
|
if(dist > MAX_BOND_DIST)
|
|
return false;
|
|
|
|
auto* const Neighbor = I->getNeighborArray();
|
|
|
|
depth = 1;
|
|
history[depth] = a0;
|
|
stack[depth] = Neighbor[a0] + 1; /* go to first neighbor */
|
|
while(depth) { /* keep going until we've traversed tree */
|
|
while(Neighbor[stack[depth]] >= 0) { /* end of branches? go back up one bond */
|
|
n0 = Neighbor[stack[depth]]; /* get current neighbor index */
|
|
stack[depth] += 2; /* set up next neighbor */
|
|
distinct = true; /* check to see if current candidate is distinct from ancestors */
|
|
for(a = 1; a < depth; a++) {
|
|
if(history[a] == n0)
|
|
distinct = false;
|
|
}
|
|
if(distinct) {
|
|
if(depth < dist) { /* are not yet at the proper distance? */
|
|
if(distinct) {
|
|
depth++;
|
|
stack[depth] = Neighbor[n0] + 1; /* then keep moving outward */
|
|
history[depth] = n0;
|
|
}
|
|
} else if(n0 == a1) /* otherwise, see if we have a match */
|
|
result = true;
|
|
}
|
|
}
|
|
depth--;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectGotoState(pymol::CObject* I, int state)
|
|
{
|
|
auto nstates = I->getNFrame();
|
|
if (nstates > 1 || !SettingGet<bool>(I->G, cSetting_static_singletons)) {
|
|
if(state > nstates)
|
|
state = nstates - 1;
|
|
if(state < 0)
|
|
state = nstates - 1;
|
|
SceneSetFrame(I->G, 0, state);
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
CObjectState* ObjectMolecule::_getObjectState(int state)
|
|
{
|
|
return CSet[state];
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
pymol::copyable_ptr<CSetting>* ObjectMolecule::getSettingHandle(int state)
|
|
{
|
|
auto I = this;
|
|
|
|
if (state < -1) {
|
|
state = I->getCurrentState();
|
|
}
|
|
|
|
if(state < 0) {
|
|
return (&I->Setting);
|
|
} else if(state < I->NCSet) {
|
|
if(I->CSet[state]) {
|
|
return (&I->CSet[state]->Setting);
|
|
} else {
|
|
return (nullptr);
|
|
}
|
|
} else {
|
|
return (nullptr);
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
CSymmetry const* ObjectMolecule::getSymmetry(int state) const
|
|
{
|
|
auto cs = getCoordSet(state);
|
|
if (cs && cs->Symmetry) {
|
|
return cs->Symmetry.get();
|
|
}
|
|
|
|
return Symmetry.get();
|
|
}
|
|
|
|
/**
|
|
* @return False if state does not exist
|
|
*/
|
|
bool ObjectMolecule::setSymmetry(CSymmetry const& symmetry, int state)
|
|
{
|
|
bool const all_states = (state == cSelectorUpdateTableAllStates);
|
|
bool success = false;
|
|
|
|
if (all_states) {
|
|
Symmetry.reset(new CSymmetry(symmetry));
|
|
success = true;
|
|
}
|
|
|
|
for (StateIterator iter(G, Setting.get(), state, NCSet); iter.next();) {
|
|
auto cs = CSet[iter.state];
|
|
if (cs) {
|
|
cs->Symmetry.reset(all_states ? nullptr : new CSymmetry(symmetry));
|
|
cs->invalidateRep(cRepCell, cRepInvRep);
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ObjectMoleculeSetStateTitle(ObjectMolecule * I, int state, const char *text)
|
|
{
|
|
auto cs = I->getCoordSet(state);
|
|
if (!cs) {
|
|
return pymol::make_error("Invalid state ", state + 1);
|
|
}
|
|
cs->setTitle(text);
|
|
return {};
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
const char *ObjectMoleculeGetStateTitle(const ObjectMolecule * I, int state)
|
|
{
|
|
auto cs = I->getCoordSet(state);
|
|
if (!cs) {
|
|
PRINTFB(I->G, FB_ObjectMolecule, FB_Errors)
|
|
"Error: invalid state %d\n", state + 1 ENDFB(I->G);
|
|
return nullptr;
|
|
}
|
|
return cs->Name;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeRenderSele(ObjectMolecule* I, int curState, int sele,
|
|
bool vis_only, CGO& selIndicatorsCGO)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
CoordSet *cs;
|
|
int a, nIndex;
|
|
const float *coord, *v;
|
|
int flag = true;
|
|
int all_vis = !vis_only;
|
|
int visRep;
|
|
float tmp_matrix[16], v_tmp[3], *matrix = nullptr;
|
|
int use_matrices =
|
|
SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
|
|
if(use_matrices<0) use_matrices = 0;
|
|
|
|
if (SettingGetIfDefined_i(G, I->Setting.get(), cSetting_all_states, &a)) {
|
|
curState = a ? -1 : SettingGet_i(G, I->Setting.get(), nullptr, cSetting_state);
|
|
} else if (SettingGetIfDefined_i(G, I->Setting.get(), cSetting_state, &a)) {
|
|
curState = a - 1;
|
|
}
|
|
|
|
if(G->HaveGUI && G->ValidContext) {
|
|
const AtomInfoType *atInfo = I->AtomInfo.data();
|
|
|
|
for(StateIterator iter(G, I->Setting.get(), curState, I->NCSet);
|
|
iter.next();) {
|
|
if((cs = I->CSet[iter.state])) {
|
|
const auto* idx2atm = cs->IdxToAtm.data();
|
|
nIndex = cs->NIndex;
|
|
coord = cs->Coord;
|
|
if(use_matrices && !cs->Matrix.empty()) {
|
|
copy44d44f(cs->Matrix.data(), tmp_matrix);
|
|
matrix = tmp_matrix;
|
|
} else
|
|
matrix = nullptr;
|
|
|
|
if(I->TTTFlag) {
|
|
if(!matrix) {
|
|
convertTTTfR44f(I->TTT, tmp_matrix);
|
|
} else {
|
|
float ttt[16];
|
|
convertTTTfR44f(I->TTT, ttt);
|
|
left_multiply44f44f(ttt, tmp_matrix);
|
|
}
|
|
matrix = tmp_matrix;
|
|
}
|
|
|
|
for(a = 0; a < nIndex; a++) {
|
|
if(SelectorIsMember(G, atInfo[*(idx2atm++)].selEntry, sele)) {
|
|
if(all_vis)
|
|
flag = true;
|
|
else {
|
|
visRep = atInfo[idx2atm[-1]].visRep;
|
|
flag = false;
|
|
if(visRep & (cRepCylBit | cRepSphereBit | cRepSurfaceBit |
|
|
cRepLabelBit | cRepNonbondedSphereBit | cRepCartoonBit |
|
|
cRepRibbonBit | cRepLineBit | cRepMeshBit |
|
|
cRepDotBit | cRepNonbondedBit)){
|
|
flag = true;
|
|
}
|
|
}
|
|
if(flag) {
|
|
v = coord + a + a + a;
|
|
if(matrix) {
|
|
transform44f3f(matrix, v, v_tmp);
|
|
CGOVertexv(&selIndicatorsCGO, v_tmp);
|
|
} else {
|
|
CGOVertexv(&selIndicatorsCGO, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
static CoordSet *ObjectMoleculeXYZStr2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr, const char **restart)
|
|
{
|
|
const char *p, *p_store;
|
|
int nAtom;
|
|
int a, c;
|
|
float *coord = nullptr;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr, *ai;
|
|
char cc[MAXLINELEN];
|
|
int atomCount;
|
|
BondType *bond = nullptr;
|
|
int nBond = 0;
|
|
int b1, b2;
|
|
WordType tmp_name;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
int tinker_xyz = true;
|
|
int valid_atom;
|
|
int have_n_atom = false;
|
|
BondType *ii;
|
|
|
|
p = buffer;
|
|
nAtom = 0;
|
|
atInfo = *atInfoPtr;
|
|
|
|
p_store = p;
|
|
p = ncopy(cc, p, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%d", &nAtom) != 1) {
|
|
nAtom = 0;
|
|
tinker_xyz = false;
|
|
p = p_store;
|
|
} else {
|
|
have_n_atom = true;
|
|
p = nskip(p, 2);
|
|
p = ncopy(tmp_name, p, sizeof(WordType) - 1);
|
|
p = nextline(p);
|
|
}
|
|
|
|
if(tinker_xyz && nAtom) { /* test Tinker XYZ formatting assumption */
|
|
const char *pp = p;
|
|
int dummy_int;
|
|
float dummy_float;
|
|
AtomName dummy_name;
|
|
|
|
pp = ncopy(cc, pp, 6);
|
|
if(!sscanf(cc, "%d", &dummy_int))
|
|
tinker_xyz = false; /* id */
|
|
pp = nskip(pp, 2);
|
|
pp = ncopy(cc, pp, 3);
|
|
if(sscanf(cc, "%s", dummy_name) != 1)
|
|
tinker_xyz = false; /* name */
|
|
pp = ncopy(cc, pp, 12);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
tinker_xyz = false; /* x */
|
|
pp = ncopy(cc, pp, 12);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
tinker_xyz = false; /* y */
|
|
pp = ncopy(cc, pp, 12);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
tinker_xyz = false; /* z */
|
|
pp = ncopy(cc, pp, 6);
|
|
if(sscanf(cc, "%d", &dummy_int) != 1)
|
|
tinker_xyz = false; /* numeric type */
|
|
}
|
|
|
|
if(!tinker_xyz) {
|
|
const char *pp = p;
|
|
int have_atom_line = true;
|
|
float dummy_float;
|
|
AtomName dummy_name;
|
|
|
|
pp = ParseWordCopy(cc, pp, sizeof(AtomName) - 1);
|
|
if(sscanf(cc, "%s", dummy_name) != 1)
|
|
have_atom_line = false; /* name */
|
|
pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
have_atom_line = false; /* x */
|
|
pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
have_atom_line = false; /* y */
|
|
pp = ParseWordCopy(cc, pp, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", &dummy_float) != 1)
|
|
have_atom_line = false; /* z */
|
|
if(!have_atom_line) { /* copy the comment line into the title field */
|
|
p = ncopy(tmp_name, p, sizeof(WordType) - 1);
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
|
|
if(nAtom) {
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
if(atInfo)
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
} else {
|
|
coord = VLAlloc(float, 3);
|
|
}
|
|
|
|
if(tinker_xyz) {
|
|
nBond = 0;
|
|
bond = VLACalloc(BondType, 6 * nAtom); /* is this a safe assumption? */
|
|
ii = bond;
|
|
}
|
|
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" ObjectMoleculeReadXYZ: Found %i atoms...\n", nAtom ENDFB(G);
|
|
|
|
a = 0;
|
|
atomCount = 0;
|
|
while(*p) {
|
|
VLACheck(atInfo, AtomInfoType, atomCount);
|
|
ai = atInfo + atomCount;
|
|
|
|
if(!tinker_xyz) {
|
|
valid_atom = true;
|
|
|
|
p = ParseWordCopy(cc, p, MAXLINELEN - 1);
|
|
UtilCleanStr(cc);
|
|
if(!cc[0])
|
|
valid_atom = false;
|
|
if(valid_atom) {
|
|
strncpy(ai->elem, cc, cElemNameLen);
|
|
ai->name = LexIdx(G, cc);
|
|
|
|
ai->rank = atomCount;
|
|
ai->id = atomCount + 1;
|
|
|
|
VLACheck(coord, float, a * 3 + 2);
|
|
p = ParseWordCopy(cc, p, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", coord + a) != 1)
|
|
valid_atom = false;
|
|
p = ParseWordCopy(cc, p, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", coord + a + 1) != 1)
|
|
valid_atom = false;
|
|
p = ParseWordCopy(cc, p, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%f", coord + a + 2) != 1)
|
|
valid_atom = false;
|
|
|
|
ai->resn = LexIdx(G, "UNK");
|
|
|
|
ai->alt[0] = 0;
|
|
ai->chain = 0;
|
|
ai->resv = atomCount + 1;
|
|
|
|
ai->q = 1.0;
|
|
ai->b = 0.0;
|
|
|
|
ai->segi = 0;
|
|
|
|
ai->visRep = auto_show;
|
|
|
|
/* in the absense of external tinker information, assume hetatm */
|
|
|
|
ai->hetatm = 1;
|
|
|
|
AtomInfoAssignParameters(G, ai);
|
|
AtomInfoAssignColors(G, ai);
|
|
}
|
|
} else { /* tinker XYZ */
|
|
|
|
valid_atom = true;
|
|
|
|
p = ncopy(cc, p, 6);
|
|
if(!sscanf(cc, "%d", &ai->id))
|
|
break;
|
|
ai->rank = atomCount;
|
|
|
|
p = nskip(p, 2); /* to 12 */
|
|
p = ntrim(cc, p, 3);
|
|
ai->name = LexIdx(G, cc);
|
|
|
|
ai->resn = LexIdx(G, "UNK");
|
|
|
|
ai->alt[0] = 0;
|
|
ai->chain = 0;
|
|
|
|
ai->resv = atomCount + 1;
|
|
|
|
valid_atom = true;
|
|
|
|
p = ncopy(cc, p, 12);
|
|
sscanf(cc, "%f", coord + a);
|
|
p = ncopy(cc, p, 12);
|
|
sscanf(cc, "%f", coord + (a + 1));
|
|
p = ncopy(cc, p, 12);
|
|
sscanf(cc, "%f", coord + (a + 2));
|
|
|
|
ai->q = 1.0;
|
|
ai->b = 0.0;
|
|
|
|
ai->segi = 0;
|
|
ai->elem[0] = 0; /* let atom info guess/infer atom type */
|
|
|
|
ai->visRep = auto_show;
|
|
|
|
p = ncopy(cc, p, 6);
|
|
sscanf(cc, "%d", &ai->customType);
|
|
|
|
/* in the absense of external tinker information, assume hetatm */
|
|
|
|
ai->hetatm = 1;
|
|
|
|
AtomInfoAssignParameters(G, ai);
|
|
AtomInfoAssignColors(G, ai);
|
|
|
|
b1 = atomCount;
|
|
for(c = 0; c < 6; c++) {
|
|
p = ncopy(cc, p, 6);
|
|
if(!cc[0])
|
|
break;
|
|
if(!sscanf(cc, "%d", &b2))
|
|
break;
|
|
if(b1 < (b2 - 1)) {
|
|
VLACheck(bond, BondType, nBond);
|
|
ii = bond + nBond;
|
|
nBond++;
|
|
ii->index[0] = b1;
|
|
ii->index[1] = b2 - 1;
|
|
ii->order = 1; /* missing bond order information */
|
|
ii++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(valid_atom) {
|
|
PRINTFD(G, FB_ObjectMolecule)
|
|
" ObjectMolecule-DEBUG: %s %s %d %s %8.3f %8.3f %8.3f %6.2f %6.2f %s\n",
|
|
LexStr(G, ai->name), LexStr(G, ai->resn), ai->resv, LexStr(G, ai->chain),
|
|
*(coord + a), *(coord + a + 1), *(coord + a + 2), ai->b, ai->q, LexStr(G, ai->segi) ENDFD;
|
|
|
|
a += 3;
|
|
atomCount++;
|
|
|
|
}
|
|
p = nextline(p);
|
|
if(have_n_atom && (atomCount >= nAtom)) {
|
|
int dummy;
|
|
ncopy(cc, p, MAXLINELEN - 1);
|
|
if(sscanf(cc, "%d", &dummy) == 1)
|
|
*restart = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" XYZStr2CoordSet: Read %d bonds.\n", nBond ENDFB(G);
|
|
|
|
if(!tinker_xyz)
|
|
nAtom = atomCount; /* use number of atoms actually read */
|
|
|
|
cset = CoordSetNew(G);
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
cset->NTmpBond = nBond;
|
|
strcpy(cset->Name, tmp_name);
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
return (cset);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeAreAtomsBonded(ObjectMolecule * I, int i0, int i1)
|
|
{
|
|
int result = false;
|
|
int a;
|
|
const BondType *b;
|
|
b = I->Bond;
|
|
for(a = 0; a < I->NBond; a++) {
|
|
if(i0 == b->index[0]) {
|
|
if(i1 == b->index[1]) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
if(i1 == b->index[0]) {
|
|
if(i0 == b->index[1]) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
b++;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeRenameAtoms(ObjectMolecule * I, int *flag, int force)
|
|
{
|
|
PyMOLGlobals * G = I->G;
|
|
AtomInfoType *ai;
|
|
int a;
|
|
int result;
|
|
if(force) {
|
|
ai = I->AtomInfo.data();
|
|
if(!flag) {
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
LexAssign(G, ai->name, 0);
|
|
ai++;
|
|
}
|
|
} else {
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(flag[a])
|
|
LexAssign(G, ai->name, 0);
|
|
ai++;
|
|
}
|
|
}
|
|
}
|
|
result = AtomInfoUniquefyNames(I->G, nullptr, 0, I->AtomInfo.data(), flag, I->NAtom);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Transform coordinates of `cs` in place and then append it to `tcs`.
|
|
*
|
|
* @param coord_orig Original cs coordinates
|
|
* @param at0 Anchor atom index
|
|
* @param mat1_inv Source coordinate system
|
|
* @param valence_vec Valence vector. If nullptr, don't move. If null-vector, don't merge.
|
|
*/
|
|
static bool AddCoordinateIntoCoordSet(CoordSet* tcs, CoordSet* cs,
|
|
const float* coord_orig, int at0, const float* mat1_inv,
|
|
const float* valence_vec)
|
|
{
|
|
if (!tcs) {
|
|
return true;
|
|
}
|
|
|
|
if (valence_vec) {
|
|
if (lengthsq3f(valence_vec) < 0.1) {
|
|
// null-vector
|
|
return true;
|
|
}
|
|
|
|
// anchor
|
|
auto idx0 = tcs->atmToIdx(at0);
|
|
assert(idx0 >= 0);
|
|
const float* va0 = tcs->coordPtr(idx0);
|
|
|
|
// Target coordinate system
|
|
float matT[16], mat[16];
|
|
identity44f(matT);
|
|
copy3f(valence_vec, matT);
|
|
get_system1f3f(matT, matT + 4, matT + 8);
|
|
copy3(va0, matT + 12);
|
|
transpose44f44f(matT, mat);
|
|
|
|
// combine with source coordinate system
|
|
right_multiply44f44f(mat, mat1_inv);
|
|
|
|
// transform coordset
|
|
for (int idx = 0; idx < cs->NIndex; idx++) {
|
|
transform44f3f(mat, &coord_orig[3 * idx], cs->coordPtr(idx));
|
|
}
|
|
}
|
|
|
|
return CoordSetMerge(tcs->Obj, tcs, cs);
|
|
}
|
|
|
|
/**
|
|
* @param at0 Atom index
|
|
* @param index0 If different from at0: Index of atom to replace (e.g. Hydrogen)
|
|
* @param[out] out (3x1) Normalized open valence vector
|
|
* @return True if valid open valence vector found
|
|
*/
|
|
static bool GetTargetValenceVector(
|
|
const CoordSet* tcs, int at0, int index0, float* out)
|
|
{
|
|
if (!tcs) {
|
|
return false;
|
|
}
|
|
|
|
if (at0 != index0) {
|
|
// anchor
|
|
auto ca0 = tcs->atmToIdx(at0);
|
|
if (ca0 < 0) {
|
|
return false;
|
|
}
|
|
|
|
// hydrogen (or other replaced atom)
|
|
auto ch0 = tcs->atmToIdx(index0);
|
|
if (ch0 < 0) {
|
|
return false;
|
|
}
|
|
|
|
const float* vh0 = tcs->coordPtr(ch0);
|
|
const float* va0 = tcs->coordPtr(ca0);
|
|
subtract3f(vh0, va0, out);
|
|
return true;
|
|
}
|
|
|
|
return CoordSetFindOpenValenceVector(tcs, at0, out);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Merge src into I.
|
|
*
|
|
* Optionally creates a bond between index0 and index1, or the atoms bound to
|
|
* index0 and index1 if they are "single geometry" atoms like hydrogens.
|
|
*
|
|
* @param index0 Atom index in I
|
|
* @param index1 Atom index in src
|
|
* @param create_bond If false, then don't create a bond, just combine the
|
|
* objects. Implies move_flag=false.
|
|
* @param move_flag If true, then transform the source coordinates to form a
|
|
* reasonable bond geometry.
|
|
*/
|
|
pymol::Result<> ObjectMoleculeFuse(ObjectMolecule* I, int const index0,
|
|
const ObjectMolecule* src, int const index1, bool create_bond,
|
|
bool move_flag)
|
|
{
|
|
constexpr int state1 = 0;
|
|
constexpr bool edit = true;
|
|
|
|
auto* G = I->G;
|
|
|
|
if (!create_bond) {
|
|
move_flag = false;
|
|
}
|
|
|
|
auto const* scs = src->getCoordSet(state1);
|
|
if (!scs) {
|
|
return pymol::make_error("no source coordset");
|
|
}
|
|
|
|
auto* ai0 = I->AtomInfo.data();
|
|
auto const* ai1 = src->AtomInfo.data();
|
|
|
|
/**
|
|
* If we want a bond and atm has cAtomInfoSingle geometry, then return the
|
|
* atom index of its (only) neighbor. Otherwise, return atm.
|
|
*/
|
|
auto const get_anchor_atm = [create_bond](
|
|
const ObjectMolecule* obj, int atm) {
|
|
if (create_bond) {
|
|
if (obj->AtomInfo[atm].geom == cAtomInfoSingle) {
|
|
auto neighbors = AtomNeighbors(obj, atm);
|
|
if (neighbors.size() == 1) {
|
|
atm = neighbors[0].atm;
|
|
}
|
|
}
|
|
}
|
|
return atm;
|
|
};
|
|
|
|
int const at0 = get_anchor_atm(I, index0);
|
|
int const at1 = get_anchor_atm(src, index1);
|
|
|
|
assert(!(at0 < 0 || at1 < 0));
|
|
|
|
auto const anch1 = scs->atmToIdx(at1);
|
|
if (anch1 < 0) {
|
|
return pymol::make_error("no coordinate for source anchor atom");
|
|
}
|
|
|
|
/* copy atoms and atom info into a 1:1 direct mapping */
|
|
auto cs = std::make_unique<CoordSet>(G);
|
|
cs->setNIndex(scs->NIndex);
|
|
cs->enumIndices();
|
|
|
|
auto nai = pymol::vla<AtomInfoType>(scs->NIndex);
|
|
|
|
for (int idx = 0; idx < scs->NIndex; ++idx) {
|
|
copy3f(scs->coordPtr(idx), cs->coordPtr(idx));
|
|
auto atm1 = scs->IdxToAtm[idx];
|
|
|
|
AtomInfoCopy(G, ai1 + atm1, nai + idx);
|
|
|
|
if (edit) {
|
|
if (atm1 == at1) {
|
|
nai[idx].temp1 = 2; /* clear marks */
|
|
} else {
|
|
nai[idx].temp1 = 0; /* clear marks */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copy internal bond information */
|
|
cs->TmpBond.reserve(src->NBond);
|
|
cs->NTmpBond = 0;
|
|
for (int b = 0; b != src->NBond; ++b) {
|
|
auto const& b1 = src->Bond[b];
|
|
auto a0 = scs->atmToIdx(b1.index[0]);
|
|
auto a1 = scs->atmToIdx(b1.index[1]);
|
|
if (a0 >= 0 && a1 >= 0) {
|
|
auto& b0 = cs->TmpBond[cs->NTmpBond++];
|
|
b0 = b1;
|
|
b0.index[0] = a0;
|
|
b0.index[1] = a1;
|
|
}
|
|
}
|
|
|
|
// source coordinate system
|
|
float mat1_inv[16];
|
|
|
|
if (create_bond) {
|
|
const float * va1 = scs->coordPtr(anch1);
|
|
|
|
if (at0 != index0) {
|
|
ai0[index0].deleteFlag = true;
|
|
}
|
|
|
|
identity44f(mat1_inv);
|
|
|
|
if (at1 != index1) {
|
|
auto const hydr1 = scs->atmToIdx(index1);
|
|
if (hydr1 == -1) {
|
|
return pymol::make_error("no source attachment vector found");
|
|
}
|
|
nai[hydr1].deleteFlag = true;
|
|
const float* vh1 = scs->coordPtr(hydr1);
|
|
subtract3f(va1, vh1, mat1_inv);
|
|
} else {
|
|
auto found = CoordSetFindOpenValenceVector(scs, at1, mat1_inv);
|
|
if (!found) {
|
|
return pymol::make_error("no source attachment vector found");
|
|
}
|
|
scale3f(mat1_inv, -1.0F, mat1_inv);
|
|
}
|
|
|
|
// bond length
|
|
float const d = AtomInfoGetBondLength(G, ai0 + at0, ai1 + at1);
|
|
|
|
// rotation matrix
|
|
get_system1f3f(mat1_inv, mat1_inv + 4, mat1_inv + 8);
|
|
|
|
// translation vector
|
|
float mat1_trans[16];
|
|
identity44f(mat1_trans);
|
|
mat1_trans[3] = (mat1_inv[0] * d - va1[0]);
|
|
mat1_trans[7] = (mat1_inv[1] * d - va1[1]);
|
|
mat1_trans[11] = (mat1_inv[2] * d - va1[2]);
|
|
|
|
right_multiply44f44f(mat1_inv, mat1_trans);
|
|
|
|
/* set up the linking bond */
|
|
cs->TmpLinkBond.resize(1);
|
|
cs->NTmpLinkBond = 1;
|
|
auto* bond = cs->TmpLinkBond.data();
|
|
BondTypeInit2(bond, at0, anch1);
|
|
}
|
|
|
|
AtomInfoUniquefyNames(I, nai.data(), cs->NIndex);
|
|
|
|
/* set up tags which will enable use to continue editing bond */
|
|
|
|
if (edit) {
|
|
for (int atm = 0; atm < I->NAtom; ++atm) {
|
|
ai0[atm].temp1 = 0;
|
|
}
|
|
ai0[at0].temp1 = 1;
|
|
}
|
|
|
|
int const state0 = I->DiscreteFlag ? (ai0[at0].discrete_state - 1)
|
|
: cSelectorUpdateTableAllStates;
|
|
assert(!I->DiscreteFlag || I->DiscreteCSet[at0] == I->CSet[state0]);
|
|
|
|
// Get target valence vectors for all relevant coordinate sets. Do this before
|
|
// merging atoms to not leave the object molecule in an half-done state in
|
|
// case we can't find valence vectors.
|
|
std::vector<std::array<float, 3>> target_vectors;
|
|
if (move_flag) {
|
|
bool found_any_target_vector = false;
|
|
target_vectors.resize(I->NCSet);
|
|
|
|
for (StateIterator iter(G, nullptr, state0, I->NCSet); iter.next();) {
|
|
float* vec = target_vectors[iter.state].data();
|
|
if (GetTargetValenceVector(I->CSet[iter.state], at0, index0, vec)) {
|
|
found_any_target_vector = true;
|
|
} else {
|
|
zero3(vec);
|
|
}
|
|
}
|
|
|
|
if (!found_any_target_vector) {
|
|
return pymol::make_error("no target attachment vector found");
|
|
}
|
|
}
|
|
|
|
// will free nai, cs->TmpBond and cs->TmpLinkBond
|
|
// invalidates the ai0 pointer!
|
|
bool ok = ObjectMoleculeMerge(
|
|
I, std::move(nai), cs.get(), false, cAIC_AllMask, true) &&
|
|
ObjectMoleculeExtendIndices(I, state0);
|
|
p_return_val_if_fail(ok, pymol::Error::MEMORY);
|
|
|
|
// Get untransformed copy of source coordinates
|
|
pymol::vla<float> coord_orig;
|
|
if (move_flag) {
|
|
coord_orig = cs->Coord;
|
|
p_return_val_if_fail(coord_orig, pymol::Error::MEMORY);
|
|
}
|
|
|
|
// Add coordinates into the coordinate set(s)
|
|
for (StateIterator iter(G, nullptr, state0, I->NCSet); iter.next();) {
|
|
const float* const vec =
|
|
move_flag ? target_vectors[iter.state].data() : nullptr;
|
|
ok = AddCoordinateIntoCoordSet(I->CSet[iter.state], cs.get(),
|
|
coord_orig.data(), at0, mat1_inv, vec);
|
|
p_return_val_if_fail(ok, pymol::Error::MEMORY);
|
|
}
|
|
|
|
ok = ObjectMoleculeSort(I);
|
|
p_return_val_if_fail(ok, pymol::Error::MEMORY);
|
|
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
|
|
if (at0 != index0 || at1 != index1) {
|
|
ObjectMoleculePurge(I);
|
|
}
|
|
|
|
// edit the resulting bond
|
|
if (edit) {
|
|
int atm_pk1 = -1;
|
|
int atm_pk2 = -1;
|
|
for (int atm = 0; atm < I->NAtom; ++atm) {
|
|
if (I->AtomInfo[atm].temp1 == 1) {
|
|
atm_pk2 = atm;
|
|
} else if (I->AtomInfo[atm].temp1 == 2) {
|
|
atm_pk1 = atm;
|
|
}
|
|
}
|
|
if (atm_pk2 >= 0 && atm_pk1 >= 0) {
|
|
auto sele1 = pymol::string_format("%s`%d", I->Name, atm_pk1 + 1);
|
|
auto sele2 = pymol::string_format("%s`%d", I->Name, atm_pk2 + 1);
|
|
EditorSelect(G, sele1.data(), sele2.data(), "", "", false, true, true);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeVerifyChemistry(ObjectMolecule * I, int state)
|
|
{
|
|
int result = false;
|
|
const AtomInfoType *ai;
|
|
int a;
|
|
int flag;
|
|
|
|
if(state < 0) {
|
|
/* use the first defined state */
|
|
for(a = 0; a < I->NCSet; a++) {
|
|
if(I->CSet[a]) {
|
|
state = a;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ai = I->AtomInfo;
|
|
flag = true;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!ai->chemFlag) {
|
|
flag = false;
|
|
}
|
|
ai++;
|
|
}
|
|
if((!flag) && (state >= 0) && (state < I->NCSet)) {
|
|
if(I->CSet[state]) {
|
|
ObjectMoleculeInferChemFromBonds(I, state);
|
|
ObjectMoleculeInferChemFromNeighGeom(I, state);
|
|
ObjectMoleculeInferHBondFromChem(I);
|
|
/* ObjectMoleculeInferChemForProtein(I,0); */
|
|
}
|
|
flag = true;
|
|
ai = I->AtomInfo;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!ai->chemFlag) {
|
|
flag = false;
|
|
break;
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
if(flag)
|
|
result = true;
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeAttach(ObjectMolecule * I, int index,
|
|
pymol::vla<AtomInfoType>&& nai)
|
|
{
|
|
int a;
|
|
AtomInfoType *ai;
|
|
BondType* bond;
|
|
float v[3], v0[3], d;
|
|
CoordSet *cs = nullptr;
|
|
int ok = false;
|
|
|
|
ai = I->AtomInfo + index;
|
|
|
|
ok_assert(1, cs = CoordSetNew(I->G));
|
|
ok_assert(1, cs->Coord = pymol::vla<float>(3));
|
|
|
|
cs->NIndex = 1;
|
|
ok_assert(1, cs->TmpLinkBond = pymol::vla<BondType>(1));
|
|
|
|
cs->NTmpLinkBond = 1;
|
|
bond = cs->TmpLinkBond.data();
|
|
BondTypeInit2(bond, index, 0);
|
|
cs->enumIndices();
|
|
|
|
ok_assert(1, ObjectMoleculePrepareAtom(I, index, nai.data()));
|
|
d = AtomInfoGetBondLength(I->G, ai, nai);
|
|
|
|
ok_assert(1, ObjectMoleculeMerge(I, std::move(nai),
|
|
cs, false, cAIC_AllMask, true)); // will free nai and cs->TmpLinkBond
|
|
ok_assert(1, ObjectMoleculeExtendIndices(I, -1));
|
|
|
|
for(a = 0; a < I->NCSet; a++) { /* add atom to each coordinate set */
|
|
if (const auto* cs_a = I->CSet[a]) {
|
|
CoordSetGetAtomVertex(cs_a, index, v0);
|
|
CoordSetFindOpenValenceVector(cs_a, index, v);
|
|
scale3f(v, d, v);
|
|
add3f(v0, v, cs->Coord.data());
|
|
ok_assert(1, CoordSetMerge(I, I->CSet[a], cs));
|
|
}
|
|
}
|
|
|
|
ok_assert(1, ObjectMoleculeSort(I));
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
|
|
ok = true;
|
|
ok_except1:
|
|
delete cs;
|
|
return ok;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeFillOpenValences(ObjectMolecule * I, int index)
|
|
{
|
|
int a;
|
|
AtomInfoType *ai;
|
|
int result = 0;
|
|
int flag = true;
|
|
float v[3], v0[3], d;
|
|
CoordSet *cs = nullptr;
|
|
int ok = true;
|
|
|
|
if((index >= 0) && (index <= I->NAtom)) {
|
|
while(ok) {
|
|
ai = I->AtomInfo + index;
|
|
auto const nn = AtomNeighbors(I, index).size();
|
|
|
|
assert(flag);
|
|
|
|
if((nn >= ai->valence) || (!flag))
|
|
break;
|
|
flag = false;
|
|
|
|
if (ok)
|
|
cs = CoordSetNew(I->G);
|
|
CHECKOK(ok, cs);
|
|
if (ok){
|
|
cs->Coord = pymol::vla<float>(3);
|
|
CHECKOK(ok, cs->Coord);
|
|
|
|
cs->NIndex = 1;
|
|
if (ok)
|
|
cs->TmpLinkBond = pymol::vla<BondType>(1);
|
|
CHECKOK(ok, cs->TmpLinkBond);
|
|
if (ok){
|
|
cs->NTmpLinkBond = 1;
|
|
auto* bond = cs->TmpLinkBond.data();
|
|
BondTypeInit2(bond, index, 0);
|
|
}
|
|
}
|
|
|
|
if(ok)
|
|
cs->enumIndices();
|
|
auto atInfo = pymol::vla<AtomInfoType>(1);
|
|
AtomInfoType* nai = atInfo.data();
|
|
if (ok){
|
|
UtilNCopy(nai->elem, "H", 2);
|
|
nai->geom = cAtomInfoSingle;
|
|
nai->valence = 1;
|
|
ok &= ObjectMoleculePrepareAtom(I, index, nai);
|
|
d = AtomInfoGetBondLength(I->G, ai, nai);
|
|
if (ok)
|
|
ok &= ObjectMoleculeMerge(I, std::move(atInfo),
|
|
cs, false, cAIC_AllMask, true); /* will free nai and cs->TmpLinkBond */
|
|
}
|
|
if (ok)
|
|
ok &= ObjectMoleculeExtendIndices(I, -1);
|
|
for(a = 0; ok && a < I->NCSet; a++) { /* add atom to each coordinate set */
|
|
if (auto* cs_a = I->CSet[a]) {
|
|
CoordSetGetAtomVertex(cs_a, index, v0);
|
|
CoordSetFindOpenValenceVector(cs_a, index, v);
|
|
scale3f(v, d, v);
|
|
add3f(v0, v, cs->Coord.data());
|
|
ok &= CoordSetMerge(I, cs_a, cs);
|
|
}
|
|
}
|
|
delete cs;
|
|
result++;
|
|
flag = true;
|
|
}
|
|
}
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
return (result);
|
|
}
|
|
|
|
#define MaxOcc 100
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @param[out] normal (3x1) Normal vector of planar geometry at atom `atm`
|
|
*
|
|
* @return True if the atom has planar geometry and the normal could be computed
|
|
*
|
|
* @pre neighbors are defined
|
|
*/
|
|
static bool get_planer_normal(const CoordSet* cs, int const atm, float* normal)
|
|
{
|
|
int found = false;
|
|
int nOcc = 0;
|
|
float occ[MaxOcc * 3];
|
|
float v0[3], v1[3], v2[3], n0[3];
|
|
|
|
if (CoordSetGetAtomVertex(cs, atm, v0)) {
|
|
const auto* I = cs->Obj;
|
|
|
|
// look for an attached non-hydrogen as a base
|
|
for (auto const& neighbor : AtomNeighbors(I, atm)) {
|
|
if (CoordSetGetAtomVertex(cs, neighbor.atm, v1)) {
|
|
subtract3f(v1, v0, n0);
|
|
normalize3f(n0); /* n0's point away from center atom */
|
|
copy3f(n0, occ + 3 * nOcc);
|
|
nOcc++;
|
|
if(nOcc == MaxOcc) /* safety valve */
|
|
break;
|
|
}
|
|
}
|
|
switch (I->AtomInfo[atm].geom) {
|
|
case cAtomInfoPlanar:
|
|
if(nOcc > 1) {
|
|
cross_product3f(occ, occ + 3, normal);
|
|
if(nOcc > 2) {
|
|
cross_product3f(occ, occ + 6, v2);
|
|
if(dot_product3f(normal, v2) < 0) {
|
|
subtract3f(normal, v2, normal);
|
|
} else {
|
|
add3f(normal, v2, normal);
|
|
}
|
|
cross_product3f(occ + 3, occ + 6, v2);
|
|
if(dot_product3f(normal, v2) < 0) {
|
|
subtract3f(normal, v2, normal);
|
|
} else {
|
|
add3f(normal, v2, normal);
|
|
}
|
|
}
|
|
normalize3f(normal);
|
|
found = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @param atm Atom index
|
|
* @param[out] v (3x1) Normalized open valence vector
|
|
* @param seek (3x1) Primer for the direction, or nullptr for random priming.
|
|
* @param ignore_index Atom index of neighbor to ignore
|
|
* @return True if valid open valence vector found
|
|
*/
|
|
bool CoordSetFindOpenValenceVector(
|
|
const CoordSet* cs, int atm, float* v, const float* seek, int ignore_index)
|
|
{
|
|
int nOcc = 0;
|
|
float occ[MaxOcc * 3];
|
|
int last_occ = -1;
|
|
float v0[3], v1[3], n0[3] = {0.0F,0.0F,0.0F}, t[3];
|
|
int result = false;
|
|
float y[3], z[3];
|
|
|
|
/* default is +X */
|
|
v[0] = 1.0;
|
|
v[1] = 0.0;
|
|
v[2] = 0.0;
|
|
|
|
if(cs) {
|
|
const auto* I = cs->Obj;
|
|
|
|
if (atm >= 0 && atm <= I->NAtom) {
|
|
if (CoordSetGetAtomVertex(cs, atm, v0)) {
|
|
const auto* ai = I->AtomInfo.data() + atm;
|
|
|
|
// look for an attached non-hydrogen as a base
|
|
for (auto const& neighbor : AtomNeighbors(I, atm)) {
|
|
auto const a1 = neighbor.atm;
|
|
if(a1 != ignore_index) {
|
|
if (CoordSetGetAtomVertex(cs, a1, v1)) {
|
|
last_occ = a1;
|
|
subtract3f(v1, v0, n0);
|
|
normalize3f(n0); /* n0's point away from center atom */
|
|
copy3f(n0, occ + 3 * nOcc);
|
|
nOcc++;
|
|
if(nOcc == MaxOcc) /* safety valve */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if((!nOcc) || (nOcc > 4) || (ai->geom == cAtomInfoNone)) {
|
|
if(!seek)
|
|
get_random3f(v);
|
|
else
|
|
copy3f(seek, v);
|
|
result = true;
|
|
} else {
|
|
switch (nOcc) {
|
|
case 1: /* only one current occupied position */
|
|
switch (ai->geom) {
|
|
case cAtomInfoTetrahedral:
|
|
if(!seek) {
|
|
get_system1f3f(occ, y, z);
|
|
scale3f(occ, -0.334F, v);
|
|
scale3f(z, 0.943F, t);
|
|
add3f(t, v, v);
|
|
} else { /* point hydrogen towards sought vector */
|
|
copy3f(seek, z);
|
|
get_system2f3f(occ, z, y);
|
|
scale3f(occ, -0.334F, v);
|
|
scale3f(z, 0.943F, t);
|
|
add3f(t, v, v);
|
|
}
|
|
result = true;
|
|
break;
|
|
case cAtomInfoPlanar:
|
|
{
|
|
if(!seek) {
|
|
if (last_occ >= 0 && get_planer_normal(cs, last_occ, n0)) {
|
|
copy3f(n0, y);
|
|
get_system2f3f(occ, y, z);
|
|
} else {
|
|
get_system1f3f(occ, y, z);
|
|
}
|
|
scale3f(occ, -0.500F, v);
|
|
scale3f(z, 0.866F, t);
|
|
add3f(t, v, v);
|
|
} else {
|
|
copy3f(seek, z);
|
|
get_system2f3f(occ, z, y);
|
|
scale3f(occ, -0.500F, v);
|
|
scale3f(z, 0.866F, t);
|
|
add3f(t, v, v);
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
case cAtomInfoLinear:
|
|
scale3f(occ, -1.0F, v);
|
|
result = true;
|
|
break;
|
|
default:
|
|
if(!seek)
|
|
get_random3f(v);
|
|
else
|
|
copy3f(seek, v);
|
|
result = false;
|
|
break;
|
|
}
|
|
break;
|
|
case 2: /* only two current occupied positions */
|
|
switch (ai->geom) {
|
|
case cAtomInfoTetrahedral:
|
|
add3f(occ, occ + 3, t);
|
|
get_system2f3f(t, occ, z);
|
|
scale3f(t, -1.0F, v);
|
|
if(seek) {
|
|
if(dot_product3f(z, seek) < 0.0F) {
|
|
invert3f(z);
|
|
}
|
|
}
|
|
scale3f(z, 1.41F, t);
|
|
add3f(t, v, v);
|
|
result = true;
|
|
break;
|
|
case cAtomInfoPlanar:
|
|
add3f(occ, occ + 3, t);
|
|
scale3f(t, -1.0F, v);
|
|
result = true;
|
|
break;
|
|
default:
|
|
if(!seek) {
|
|
add3f(occ, occ + 3, t);
|
|
scale3f(t, -1.0F, v);
|
|
if(length3f(t) < 0.1)
|
|
get_random3f(v);
|
|
} else
|
|
copy3f(seek, v);
|
|
/* hypervalent */
|
|
result = false;
|
|
break;
|
|
}
|
|
break;
|
|
case 3: /* only three current occupied positions */
|
|
switch (ai->geom) {
|
|
case cAtomInfoTetrahedral:
|
|
add3f(occ, occ + 3, t);
|
|
add3f(occ + 6, t, t);
|
|
scale3f(t, -1.0F, v);
|
|
result = true;
|
|
break;
|
|
default:
|
|
if(!seek) {
|
|
add3f(occ, occ + 3, t);
|
|
add3f(occ + 6, t, t);
|
|
scale3f(t, -1.0F, v);
|
|
if(length3f(t) < 0.1)
|
|
get_random3f(v);
|
|
} else
|
|
copy3f(seek, v);
|
|
/* hypervalent */
|
|
result = false;
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
if(!seek)
|
|
get_random3f(v);
|
|
else
|
|
copy3f(seek, v);
|
|
/* hypervalent */
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
normalize3f(v);
|
|
return (result);
|
|
#undef MaxOcc
|
|
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeCreateSpheroid(ObjectMolecule * I, int average)
|
|
{
|
|
CoordSet *cs;
|
|
int a, b, c, a0;
|
|
SphereRec *sp;
|
|
float *v, *v0, *s, *f, ang, min_dist, *max_sq;
|
|
int *i;
|
|
float *center = nullptr;
|
|
float d0[3], n0[3], d1[3], d2[3];
|
|
float p0[3], p1[3], p2[3];
|
|
int t0, t1, t2, bt0, bt1, bt2;
|
|
float dp, l, *fsum = nullptr;
|
|
float spheroid_smooth;
|
|
float spheroid_fill;
|
|
float spheroid_ratio = 0.1F; /* minimum ratio of width over length */
|
|
float spheroid_minimum = 0.02F; /* minimum size - to insure valid normals */
|
|
int row, *count = nullptr, base;
|
|
int nRow;
|
|
int first = 0;
|
|
int last = 0;
|
|
int current;
|
|
int cscount;
|
|
int n_state = 0;
|
|
sp = GetSpheroidSphereRec(I->G);
|
|
|
|
nRow = I->NAtom * sp->nDot;
|
|
|
|
center = pymol::malloc<float>(I->NAtom * 3);
|
|
count = pymol::malloc<int>(I->NAtom);
|
|
fsum = pymol::malloc<float>(nRow);
|
|
max_sq = pymol::malloc<float>(I->NAtom);
|
|
|
|
spheroid_smooth = SettingGetGlobal_f(I->G, cSetting_spheroid_smooth);
|
|
spheroid_fill = SettingGetGlobal_f(I->G, cSetting_spheroid_fill);
|
|
/* first compute average coordinate */
|
|
|
|
if(average < 1)
|
|
average = I->NCSet;
|
|
current = 0;
|
|
cscount = 0;
|
|
while(current < I->NCSet) {
|
|
if(I->CSet[current]) {
|
|
if(!cscount)
|
|
first = current;
|
|
cscount++;
|
|
last = current + 1;
|
|
}
|
|
|
|
if(cscount == average || current == I->NCSet - 1) {
|
|
PRINTFB(I->G, FB_ObjectMolecule, FB_Details)
|
|
" ObjectMolecule: computing spheroid from states %d to %d.\n",
|
|
first + 1, last ENDFB(I->G);
|
|
|
|
auto spheroid = std::vector<float>(nRow);
|
|
|
|
v = center;
|
|
i = count;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
*(v++) = 0.0;
|
|
*(v++) = 0.0;
|
|
*(v++) = 0.0;
|
|
*(i++) = 0;
|
|
}
|
|
|
|
for(b = first; b < last; b++) {
|
|
cs = I->CSet[b];
|
|
if(cs) {
|
|
v = cs->Coord.data();
|
|
for(a = 0; a < cs->NIndex; a++) {
|
|
a0 = cs->IdxToAtm[a];
|
|
v0 = center + 3 * a0;
|
|
add3f(v, v0, v0);
|
|
(*(count + a0))++;
|
|
v += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
i = count;
|
|
v = center;
|
|
for(a = 0; a < I->NAtom; a++)
|
|
if(*i) {
|
|
(*(v++)) /= (*i);
|
|
(*(v++)) /= (*i);
|
|
(*(v++)) /= (*i++);
|
|
} else {
|
|
v += 3;
|
|
i++;
|
|
}
|
|
|
|
/* now go through and compute radial distances */
|
|
|
|
f = fsum;
|
|
s = spheroid.data();
|
|
for(a = 0; a < nRow; a++) {
|
|
*(f++) = 0.0;
|
|
*(s++) = 0.0;
|
|
}
|
|
|
|
v = max_sq;
|
|
for(a = 0; a < I->NAtom; a++)
|
|
*(v++) = 0.0;
|
|
|
|
for(b = first; b < last; b++) {
|
|
cs = I->CSet[b];
|
|
if(cs) {
|
|
v = cs->Coord.data();
|
|
for(a = 0; a < cs->NIndex; a++) {
|
|
a0 = cs->IdxToAtm[a];
|
|
base = (a0 * sp->nDot);
|
|
v0 = center + (3 * a0);
|
|
subtract3f(v, v0, d0); /* subtract from average */
|
|
l = lengthsq3f(d0);
|
|
if(l > max_sq[a0])
|
|
max_sq[a0] = l;
|
|
if(l > 0.0) {
|
|
float isq = (float) (1.0 / sqrt1d(l));
|
|
scale3f(d0, isq, n0);
|
|
for(c = 0; c < sp->nDot; c++) { /* average over spokes */
|
|
dp = dot_product3f(sp->dot[c], n0);
|
|
row = base + c;
|
|
if(dp >= 0.0) {
|
|
ang = (float) ((acos(dp) / spheroid_smooth) * (cPI / 2.0));
|
|
if(ang > spheroid_fill)
|
|
ang = spheroid_fill;
|
|
/* take envelop to zero over that angle */
|
|
if(ang <= (cPI / 2.0)) {
|
|
dp = (float) cos(ang);
|
|
fsum[row] += dp * dp;
|
|
spheroid[row] += l * dp * dp * dp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
v += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
f = fsum;
|
|
s = spheroid.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
min_dist = (float) (spheroid_ratio * sqrt(max_sq[a]));
|
|
if(min_dist < spheroid_minimum)
|
|
min_dist = spheroid_minimum;
|
|
for(b = 0; b < sp->nDot; b++) {
|
|
if(*f > R_SMALL4) {
|
|
(*s) = (float) (sqrt1d((*s) / (*(f++)))); /* we put the "rm" in "rms" */
|
|
} else {
|
|
f++;
|
|
}
|
|
if(*s < min_dist)
|
|
*s = min_dist;
|
|
s++;
|
|
}
|
|
}
|
|
|
|
/* set frame 0 coordinates to the average */
|
|
|
|
cs = I->CSet[first];
|
|
if(cs) {
|
|
v = cs->Coord.data();
|
|
for(a = 0; a < cs->NIndex; a++) {
|
|
a0 = cs->IdxToAtm[a];
|
|
v0 = center + 3 * a0;
|
|
copy3f(v0, v);
|
|
v += 3;
|
|
}
|
|
}
|
|
|
|
/* now compute surface normals */
|
|
|
|
auto norm = std::vector<float>(nRow * 3);
|
|
for(a = 0; a < nRow; a++) {
|
|
zero3f(norm.data() + a * 3);
|
|
}
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
base = a * sp->nDot;
|
|
for(b = 0; b < sp->NTri; b++) {
|
|
t0 = sp->Tri[b * 3];
|
|
t1 = sp->Tri[b * 3 + 1];
|
|
t2 = sp->Tri[b * 3 + 2];
|
|
bt0 = base + t0;
|
|
bt1 = base + t1;
|
|
bt2 = base + t2;
|
|
copy3f(sp->dot[t0], p0);
|
|
copy3f(sp->dot[t1], p1);
|
|
copy3f(sp->dot[t2], p2);
|
|
/* scale3f(sp->dot[t0].v,spheroid[bt0],p0);
|
|
scale3f(sp->dot[t1].v,spheroid[bt1],p1);
|
|
scale3f(sp->dot[t2].v,spheroid[bt2],p2); */
|
|
subtract3f(p1, p0, d1);
|
|
subtract3f(p2, p0, d2);
|
|
cross_product3f(d1, d2, n0);
|
|
normalize3f(n0);
|
|
v = norm.data() + bt0 * 3;
|
|
add3f(n0, v, v);
|
|
v = norm.data() + bt1 * 3;
|
|
add3f(n0, v, v);
|
|
v = norm.data() + bt2 * 3;
|
|
add3f(n0, v, v);
|
|
}
|
|
}
|
|
|
|
f = norm.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
base = a * sp->nDot;
|
|
for(b = 0; b < sp->nDot; b++) {
|
|
normalize3f(f);
|
|
f += 3;
|
|
}
|
|
}
|
|
|
|
if(I->CSet[first]) {
|
|
I->CSet[first]->Spheroid = std::move(spheroid);
|
|
I->CSet[first]->SpheroidNormal = std::move(norm);
|
|
}
|
|
|
|
for(b = first + 1; b < last; b++) {
|
|
delete I->CSet[b];
|
|
I->CSet[b] = nullptr;
|
|
}
|
|
|
|
if(n_state != first) {
|
|
I->CSet[n_state] = I->CSet[first];
|
|
I->CSet[first] = nullptr;
|
|
}
|
|
n_state++;
|
|
|
|
cscount = 0;
|
|
}
|
|
current++;
|
|
}
|
|
I->NCSet = n_state;
|
|
FreeP(center);
|
|
FreeP(count);
|
|
FreeP(fsum);
|
|
FreeP(max_sq);
|
|
|
|
I->invalidate(cRepSphere, cRepInvProp, -1);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeReplaceAtom(ObjectMolecule * I, int index, AtomInfoType&& ai)
|
|
{
|
|
if((index >= 0) && (index <= I->NAtom)) {
|
|
I->AtomInfo[index] = std::move(ai);
|
|
I->invalidate(cRepAll, cRepInvAtoms, -1);
|
|
/* could we put in a refinement step here? */
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculePrepareAtom(ObjectMolecule * I, int index, AtomInfoType * ai,
|
|
bool uniquefy)
|
|
{
|
|
/* match existing properties of the old atom */
|
|
AtomInfoType *ai0;
|
|
int ok = true;
|
|
|
|
if((index >= 0) && (index <= I->NAtom)) {
|
|
ai0 = I->AtomInfo + index;
|
|
ai->resv = ai0->resv;
|
|
ai->hetatm = ai0->hetatm;
|
|
ai->flags = ai0->flags;
|
|
|
|
if (!ai->geom)
|
|
ai->geom = ai0->geom;
|
|
|
|
ai->discrete_state = ai0->discrete_state;
|
|
ai->q = ai0->q;
|
|
ai->b = ai0->b;
|
|
strcpy(ai->alt, ai0->alt);
|
|
ai->inscode = ai0->inscode;
|
|
LexAssign(I->G, ai->segi, ai0->segi);
|
|
LexAssign(I->G, ai->chain, ai0->chain);
|
|
LexAssign(I->G, ai->resn, ai0->resn);
|
|
ai->visRep = ai0->visRep;
|
|
ai->id = -1;
|
|
ai->rank = -1;
|
|
|
|
AtomInfoAssignParameters(I->G, ai);
|
|
|
|
if (uniquefy) {
|
|
AtomInfoUniquefyNames(I->G, I->AtomInfo, I->NAtom, ai, nullptr, 1);
|
|
}
|
|
|
|
if((ai->elem[0] == ai0->elem[0]) && (ai->elem[1] == ai0->elem[1]))
|
|
ai->color = ai0->color;
|
|
else if((ai->elem[0] == 'C') && (ai->elem[1] == 0)) {
|
|
int found = false;
|
|
for (auto const& neighbor : AtomNeighbors(I, index)) {
|
|
AtomInfoType const* ai1 = I->AtomInfo.data() + neighbor.atm;
|
|
if(ai1->protons == cAN_C) {
|
|
ai->color = ai1->color;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(ok && !found) {
|
|
/* if no carbon nearby, then color according to the object color */
|
|
ai->color = I->Color;
|
|
}
|
|
} else {
|
|
AtomInfoAssignColors(I->G, ai);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculePreposReplAtom(ObjectMolecule * I, int index, AtomInfoType * ai)
|
|
{
|
|
float v0[3], v1[3], v[3];
|
|
float d0[3], d, n0[3];
|
|
int cnt;
|
|
float t[3], sum[3];
|
|
int a;
|
|
int ncycle;
|
|
int ok = true;
|
|
if (ok){
|
|
for(a = 0; a < I->NCSet; a++) {
|
|
if(I->CSet[a]) {
|
|
if(ObjectMoleculeGetAtomVertex(I, a, index, v0)) {
|
|
copy3f(v0, v); /* default is direct superposition */
|
|
ncycle = -1;
|
|
while(ncycle) {
|
|
cnt = 0;
|
|
zero3f(sum);
|
|
/* look for an attached non-hydrogen as a base */
|
|
for (auto const& neighbor : AtomNeighbors(I, index)) {
|
|
auto const a1 = neighbor.atm;
|
|
auto const* ai1 = I->AtomInfo.data() + a1;
|
|
if(ai1->protons != 1)
|
|
if(ObjectMoleculeGetAtomVertex(I, a, a1, v1)) {
|
|
d = AtomInfoGetBondLength(I->G, ai, ai1);
|
|
subtract3f(v0, v1, n0);
|
|
normalize3f(n0);
|
|
scale3f(n0, d, d0);
|
|
add3f(d0, v1, t);
|
|
add3f(t, sum, sum);
|
|
cnt++;
|
|
}
|
|
}
|
|
if(cnt) {
|
|
scale3f(sum, 1.0F / cnt, sum);
|
|
copy3f(sum, v0);
|
|
if((cnt > 1) && (ncycle < 0))
|
|
ncycle = 5;
|
|
}
|
|
ncycle = abs(ncycle) - 1;
|
|
}
|
|
if(cnt)
|
|
copy3f(sum, v);
|
|
ObjectMoleculeSetAtomVertex(I, a, index, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
#if 1
|
|
void ObjectMoleculeSaveUndo(ObjectMolecule * I, int state, int log)
|
|
{
|
|
CoordSet *cs;
|
|
PyMOLGlobals *G = I->G;
|
|
FreeP(I->UndoCoord[I->UndoIter]);
|
|
I->UndoState[I->UndoIter] = -1;
|
|
if(state < 0)
|
|
state = 0;
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
I->UndoCoord[I->UndoIter] = pymol::malloc<float>(cs->NIndex * 3);
|
|
memcpy(I->UndoCoord[I->UndoIter], cs->Coord, sizeof(float) * cs->NIndex * 3);
|
|
I->UndoState[I->UndoIter] = state;
|
|
I->UndoNIndex[I->UndoIter] = cs->NIndex;
|
|
}
|
|
I->UndoIter = cUndoMask & (I->UndoIter + 1);
|
|
ExecutiveSetLastObjectEdited(G, I);
|
|
if(log) {
|
|
OrthoLineType line;
|
|
if(SettingGetGlobal_i(I->G, cSetting_logging)) {
|
|
sprintf(line, "cmd.push_undo(\"%s\",%d)\n", I->Name, state + 1);
|
|
PLog(G, line, cPLog_no_flush);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeUndo(ObjectMolecule * I, int dir)
|
|
{
|
|
CoordSet *cs;
|
|
int state;
|
|
|
|
FreeP(I->UndoCoord[I->UndoIter]);
|
|
I->UndoState[I->UndoIter] = -1;
|
|
state = SceneGetState(I->G);
|
|
if(state < 0)
|
|
state = 0;
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
I->UndoCoord[I->UndoIter] = pymol::malloc<float>(cs->NIndex * 3);
|
|
memcpy(I->UndoCoord[I->UndoIter], cs->Coord, sizeof(float) * cs->NIndex * 3);
|
|
I->UndoState[I->UndoIter] = state;
|
|
I->UndoNIndex[I->UndoIter] = cs->NIndex;
|
|
}
|
|
|
|
I->UndoIter = cUndoMask & (I->UndoIter + dir);
|
|
if(!I->UndoCoord[I->UndoIter])
|
|
I->UndoIter = cUndoMask & (I->UndoIter - dir);
|
|
|
|
if(I->UndoState[I->UndoIter] >= 0) {
|
|
state = I->UndoState[I->UndoIter];
|
|
if(state < 0)
|
|
state = 0;
|
|
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
if(cs->NIndex == I->UndoNIndex[I->UndoIter]) {
|
|
memcpy(cs->Coord.data(), I->UndoCoord[I->UndoIter], sizeof(float) * cs->NIndex * 3);
|
|
I->UndoState[I->UndoIter] = -1;
|
|
FreeP(I->UndoCoord[I->UndoIter]);
|
|
cs->invalidateRep(cRepAll, cRepInvCoord);
|
|
SceneChanged(I->G);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int ObjectMoleculeAddBond(ObjectMolecule * I, int sele0, int sele1, int order, pymol::zstring_view symop)
|
|
{
|
|
int a1, a2;
|
|
AtomInfoType *ai1, *ai2;
|
|
int s1, s2;
|
|
int c = 0;
|
|
|
|
/* TO DO: optimize for performance -- we shouldn't be doing full
|
|
table scans */
|
|
|
|
ai1 = I->AtomInfo.data();
|
|
for(a1 = 0; a1 < I->NAtom; a1++) {
|
|
s1 = ai1->selEntry;
|
|
if(SelectorIsMember(I->G, s1, sele0)) {
|
|
ai2 = I->AtomInfo.data();
|
|
for(a2 = 0; a2 < I->NAtom; a2++) {
|
|
s2 = ai2->selEntry;
|
|
if(SelectorIsMember(I->G, s2, sele1)) {
|
|
if(!I->Bond){
|
|
I->Bond = pymol::vla<BondType>(1);
|
|
}
|
|
if(I->Bond) {
|
|
BondType* bnd = I->Bond.check(I->NBond);
|
|
BondTypeInit2(bnd, a1, a2, order);
|
|
|
|
assert(!bnd->symop_2);
|
|
if (!symop.empty()) {
|
|
bnd->symop_2.reset(symop.c_str());
|
|
}
|
|
|
|
I->NBond++;
|
|
c++;
|
|
I->AtomInfo[a1].chemFlag = false;
|
|
I->AtomInfo[a2].chemFlag = false;
|
|
I->AtomInfo[a1].bonded = true;
|
|
I->AtomInfo[a2].bonded = true;
|
|
}
|
|
}
|
|
ai2++;
|
|
}
|
|
}
|
|
ai1++;
|
|
}
|
|
if(c) {
|
|
I->invalidate(cRepAll, cRepInvBondsNoNonbonded, -1);
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ObjectMoleculeAddBondByIndices(
|
|
ObjectMolecule* I, unsigned atm1, unsigned atm2, int order)
|
|
{
|
|
if (atm1 >= I->NAtom || atm2 >= I->NAtom) {
|
|
return pymol::make_error("atom index out of bounds");
|
|
}
|
|
|
|
if (!I->Bond) {
|
|
I->Bond = pymol::vla<BondType>(1);
|
|
} else {
|
|
I->Bond.check(I->NBond);
|
|
}
|
|
|
|
if (!I->Bond) {
|
|
return pymol::Error::MEMORY;
|
|
}
|
|
|
|
auto& bnd = I->Bond[I->NBond++];
|
|
BondTypeInit2(&bnd, atm1, atm2, order);
|
|
|
|
I->AtomInfo[atm1].chemFlag = false;
|
|
I->AtomInfo[atm2].chemFlag = false;
|
|
I->AtomInfo[atm1].bonded = true;
|
|
I->AtomInfo[atm2].bonded = true;
|
|
|
|
I->invalidate(cRepAll, cRepInvBondsNoNonbonded, -1);
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeAdjustBonds(ObjectMolecule * I, int sele0, int sele1, int mode,
|
|
int order, pymol::zstring_view symop)
|
|
{
|
|
auto const G = I->G;
|
|
int a0, a1;
|
|
int cnt = 0;
|
|
BondType *b0;
|
|
int both;
|
|
int a;
|
|
|
|
if(I->Bond) {
|
|
b0 = I->Bond.data();
|
|
for(a = 0; a < I->NBond; a++) {
|
|
a0 = b0->index[0];
|
|
a1 = b0->index[1];
|
|
|
|
both = 0;
|
|
|
|
if (SelectorIsMember(G, I->AtomInfo[a0].selEntry, sele0) &&
|
|
SelectorIsMember(G, I->AtomInfo[a1].selEntry, sele1)) {
|
|
both = 2;
|
|
} else if ( //
|
|
SelectorIsMember(G, I->AtomInfo[a1].selEntry, sele0) &&
|
|
SelectorIsMember(G, I->AtomInfo[a0].selEntry, sele1)) {
|
|
std::swap(a0, a1);
|
|
both = 2;
|
|
}
|
|
|
|
if(both == 2) {
|
|
cnt++;
|
|
switch (mode) {
|
|
case 0: /* cycle */
|
|
switch(SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_editor_bond_cycle_mode)) {
|
|
case 1: /* 1 arom 2 3 */
|
|
switch(b0->order) {
|
|
case 1:
|
|
b0->order = 4;
|
|
break;
|
|
case 4:
|
|
b0->order = 2;
|
|
break;
|
|
case 2:
|
|
b0->order = 3;
|
|
break;
|
|
default:
|
|
b0->order = 1;
|
|
break;
|
|
}
|
|
break;
|
|
case 2: /* 1 2 3 arom */
|
|
b0->order++;
|
|
if(b0->order > 4)
|
|
b0->order = 1;
|
|
break;
|
|
default: /* old way -> 1 2 3 */
|
|
b0->order++;
|
|
if(b0->order > 3)
|
|
b0->order = 1;
|
|
break;
|
|
}
|
|
I->AtomInfo[a0].chemFlag = false;
|
|
I->AtomInfo[a1].chemFlag = false;
|
|
break;
|
|
case 1: /* set */
|
|
b0->order = order;
|
|
I->AtomInfo[a0].chemFlag = false;
|
|
I->AtomInfo[a1].chemFlag = false;
|
|
break;
|
|
}
|
|
|
|
if (!symop.empty()) {
|
|
b0->symop_2.reset(symop.c_str());
|
|
}
|
|
}
|
|
b0++;
|
|
}
|
|
if(cnt) {
|
|
I->invalidate(cRepLine, cRepInvBonds, -1);
|
|
I->invalidate(cRepCyl, cRepInvBonds, -1);
|
|
I->invalidate(cRepNonbonded, cRepInvBonds, -1);
|
|
I->invalidate(cRepNonbondedSphere, cRepInvBonds, -1);
|
|
I->invalidate(cRepRibbon, cRepInvBonds, -1);
|
|
I->invalidate(cRepCartoon, cRepInvBonds, -1);
|
|
}
|
|
}
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeRemoveBonds(ObjectMolecule * I, int sele0, int sele1)
|
|
{
|
|
int a0, a1;
|
|
int offset = 0;
|
|
BondType *b0, *b1;
|
|
int both;
|
|
int s;
|
|
int a;
|
|
|
|
if(I->Bond) {
|
|
offset = 0;
|
|
b0 = I->Bond.data();
|
|
b1 = I->Bond.data();
|
|
for(a = 0; a < I->NBond; a++) {
|
|
a0 = b0->index[0];
|
|
a1 = b0->index[1];
|
|
|
|
both = 0;
|
|
s = I->AtomInfo[a0].selEntry;
|
|
if(SelectorIsMember(I->G, s, sele0))
|
|
both++;
|
|
s = I->AtomInfo[a1].selEntry;
|
|
if(SelectorIsMember(I->G, s, sele1))
|
|
both++;
|
|
if(both < 2) { /* reverse combo */
|
|
both = 0;
|
|
s = I->AtomInfo[a1].selEntry;
|
|
if(SelectorIsMember(I->G, s, sele0))
|
|
both++;
|
|
s = I->AtomInfo[a0].selEntry;
|
|
if(SelectorIsMember(I->G, s, sele1))
|
|
both++;
|
|
}
|
|
|
|
if(both == 2) {
|
|
AtomInfoPurgeBond(I->G, b0);
|
|
offset--;
|
|
b0++;
|
|
I->AtomInfo[a0].chemFlag = false;
|
|
I->AtomInfo[a1].chemFlag = false;
|
|
} else if(offset) {
|
|
*(b1++) = *(b0++); /* copy bond info */
|
|
} else {
|
|
*(b1++) = *(b0++); /* copy bond info */
|
|
}
|
|
}
|
|
if(offset) {
|
|
I->NBond += offset;
|
|
VLASize(I->Bond, BondType, I->NBond);
|
|
I->invalidate(cRepLine, cRepInvBonds, -1);
|
|
I->invalidate(cRepCyl, cRepInvBonds, -1);
|
|
I->invalidate(cRepNonbonded, cRepInvBonds, -1);
|
|
I->invalidate(cRepNonbondedSphere, cRepInvBonds, -1);
|
|
I->invalidate(cRepRibbon, cRepInvBonds, -1);
|
|
I->invalidate(cRepCartoon, cRepInvBonds, -1);
|
|
}
|
|
}
|
|
|
|
return (-offset);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculePurge(ObjectMolecule * I)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
int offset = 0;
|
|
|
|
// remove the object selection and free up any selection entries.
|
|
// note that we don't delete atom selection members -- those may be needed in the new object.
|
|
SelectorDelete(G, I->Name);
|
|
|
|
// old-to-new mapping
|
|
auto oldToNew = std::vector<int>(size_t(I->NAtom), -1);
|
|
for (int atm = 0; atm < I->NAtom; ++atm) {
|
|
auto &ai0 = I->AtomInfo[atm];
|
|
if(ai0.deleteFlag) {
|
|
AtomInfoPurge(G, &ai0);
|
|
offset--;
|
|
assert(oldToNew[atm] == -1);
|
|
} else {
|
|
if(offset) {
|
|
I->AtomInfo[atm + offset] = std::move(ai0);
|
|
}
|
|
oldToNew[atm] = atm + offset;
|
|
}
|
|
}
|
|
if(offset) {
|
|
I->NAtom += offset;
|
|
I->AtomInfo.resize(I->NAtom);
|
|
for (int a = 0; a < I->NCSet; ++a) {
|
|
if (auto cs = I->CSet[a])
|
|
CoordSetAdjustAtmIdx(cs, oldToNew.data());
|
|
}
|
|
if (I->CSTmpl) {
|
|
CoordSetAdjustAtmIdx(I->CSTmpl, oldToNew.data());
|
|
}
|
|
}
|
|
|
|
I->updateAtmToIdx();
|
|
|
|
// step 4, bonds
|
|
|
|
offset = 0;
|
|
auto b0 = I->Bond.data();
|
|
auto b1 = I->Bond.data();
|
|
for (int b = 0; b < I->NBond; ++b) {
|
|
auto a0 = b0->index[0];
|
|
auto a1 = b0->index[1];
|
|
if(a0 < 0 || a1 < 0 || (oldToNew[a0] < 0) || (oldToNew[a1] < 0)) {
|
|
/* deleting bond */
|
|
AtomInfoPurgeBond(I->G, b0);
|
|
offset--;
|
|
b0++;
|
|
} else {
|
|
if(offset) {
|
|
*b1 = *b0;
|
|
}
|
|
b1->index[0] = oldToNew[a0]; /* copy bond info */
|
|
b1->index[1] = oldToNew[a1];
|
|
b0++;
|
|
b1++;
|
|
}
|
|
}
|
|
if(offset) {
|
|
I->NBond += offset;
|
|
VLASize(I->Bond, BondType, I->NBond);
|
|
}
|
|
|
|
I->invalidate(cRepAll, cRepInvAtoms, -1);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Determines hybridization from coordinates in those few cases where it is
|
|
* unambiguous.
|
|
*/
|
|
int ObjectMoleculeGetAtomGeometry(const ObjectMolecule* I, int state, int at)
|
|
{
|
|
|
|
int result = -1;
|
|
float v0[3], v1[3], v2[3], v3[3];
|
|
float d1[3], d2[3], d3[3];
|
|
float cp1[3], cp2[3], cp3[3];
|
|
float avg;
|
|
float dp;
|
|
auto const neighbors = AtomNeighbors(I, at);
|
|
auto const nn = neighbors.size();
|
|
if(nn == 4)
|
|
result = cAtomInfoTetrahedral;
|
|
else if(nn == 3) {
|
|
/* check cross products */
|
|
ObjectMoleculeGetAtomVertex(I, state, at, v0);
|
|
ObjectMoleculeGetAtomVertex(I, state, neighbors[0].atm, v1);
|
|
ObjectMoleculeGetAtomVertex(I, state, neighbors[1].atm, v2);
|
|
ObjectMoleculeGetAtomVertex(I, state, neighbors[2].atm, v3);
|
|
subtract3f(v1, v0, d1);
|
|
subtract3f(v2, v0, d2);
|
|
subtract3f(v3, v0, d3);
|
|
cross_product3f(d1, d2, cp1);
|
|
cross_product3f(d2, d3, cp2);
|
|
cross_product3f(d3, d1, cp3);
|
|
normalize3f(cp1);
|
|
normalize3f(cp2);
|
|
normalize3f(cp3);
|
|
avg = (dot_product3f(cp1, cp2) +
|
|
dot_product3f(cp2, cp3) + dot_product3f(cp3, cp1)) / 3.0F;
|
|
if(avg > 0.75)
|
|
result = cAtomInfoPlanar;
|
|
else
|
|
result = cAtomInfoTetrahedral;
|
|
} else if(nn == 2) {
|
|
ObjectMoleculeGetAtomVertex(I, state, at, v0);
|
|
ObjectMoleculeGetAtomVertex(I, state, neighbors[0].atm, v1);
|
|
ObjectMoleculeGetAtomVertex(I, state, neighbors[1].atm, v2);
|
|
subtract3f(v1, v0, d1);
|
|
subtract3f(v2, v0, d2);
|
|
normalize3f(d1);
|
|
normalize3f(d2);
|
|
dp = dot_product3f(d1, d2);
|
|
if(dp < -0.75)
|
|
result = cAtomInfoLinear;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
static float compute_avg_center_dot_cross_fn(ObjectMolecule * I, CoordSet * cs,
|
|
int n_atom, int *atix)
|
|
{
|
|
float result = 0.0F;
|
|
float *v[9];
|
|
int missing_flag = false;
|
|
{
|
|
int a, i;
|
|
for(i = 0; i < n_atom; i++) {
|
|
int a1 = atix[i];
|
|
a = cs->atmToIdx(a1);
|
|
if(a < 0) {
|
|
missing_flag = true;
|
|
break;
|
|
} else {
|
|
v[i] = cs->coordPtr(a);
|
|
}
|
|
}
|
|
}
|
|
if(!missing_flag) {
|
|
int i;
|
|
float d10[3], d20[3], cp[8][3];
|
|
float avg = 0.0;
|
|
|
|
v[n_atom] = v[1];
|
|
for(i = 1; i < n_atom; i++) {
|
|
subtract3f(v[i], v[0], d10);
|
|
subtract3f(v[i + 1], v[0], d20);
|
|
normalize3f(d10);
|
|
normalize3f(d20);
|
|
cross_product3f(d10, d20, cp[i]);
|
|
normalize3f(cp[i]);
|
|
if(i > 1) {
|
|
if(dot_product3f(cp[i - 1], cp[i]) < 0.0)
|
|
invert3f(cp[i]);
|
|
}
|
|
}
|
|
copy3f(cp[1], cp[n_atom]);
|
|
for(i = 1; i < n_atom; i++) {
|
|
avg += dot_product3f(cp[i], cp[i + 1]);
|
|
}
|
|
result = avg / (n_atom - 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int verify_planer_bonds(const ObjectMolecule* I, const CoordSet* cs,
|
|
int n_atom, const int* atix, const int* neighbor, const float* dir,
|
|
float cutoff)
|
|
{
|
|
int a, i;
|
|
for(i = 0; i < n_atom; i++) {
|
|
int a1 = atix[i];
|
|
a = cs->atmToIdx(a1);
|
|
if(a >= 0) {
|
|
const float* v0 = cs->coordPtr(a);
|
|
int n = neighbor[a1] + 1;
|
|
int a2;
|
|
while((a2 = neighbor[n]) >= 0) {
|
|
n += 2;
|
|
a = cs->atmToIdx(a2);
|
|
if(a >= 0) {
|
|
const float* v1 = cs->coordPtr(a);
|
|
float d10[] = { 0.f, 0.f, 0.f };
|
|
subtract3f(v1, v0, d10);
|
|
normalize3f(d10);
|
|
{
|
|
float dot = fabs(dot_product3f(d10, dir));
|
|
if(dot > cutoff) {
|
|
switch (I->AtomInfo[a1].protons) {
|
|
case cAN_C:
|
|
case cAN_N:
|
|
case cAN_O:
|
|
case cAN_S:
|
|
switch (I->AtomInfo[a2].protons) {
|
|
case cAN_C:
|
|
case cAN_N:
|
|
case cAN_O:
|
|
case cAN_S:
|
|
return 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static float compute_avg_ring_dot_cross_fn(ObjectMolecule * I, CoordSet * cs,
|
|
int n_atom, int *atix, float *dir)
|
|
{
|
|
float result = 0.0F;
|
|
float *v[9];
|
|
int missing_flag = false;
|
|
{
|
|
int a, i;
|
|
for(i = 0; i < n_atom; i++) {
|
|
int a1 = atix[i];
|
|
a = cs->atmToIdx(a1);
|
|
if(a < 0) {
|
|
missing_flag = true;
|
|
break;
|
|
} else {
|
|
v[i] = cs->coordPtr(a);
|
|
}
|
|
}
|
|
}
|
|
if(!missing_flag) {
|
|
int i;
|
|
float d10[3], d21[3], cp[8][3];
|
|
float avg = 0.0;
|
|
|
|
v[n_atom] = v[0];
|
|
v[n_atom + 1] = v[1];
|
|
for(i = 0; i < n_atom; i++) {
|
|
subtract3f(v[i + 0], v[i + 1], d10);
|
|
subtract3f(v[i + 2], v[i + 1], d21);
|
|
normalize3f(d10);
|
|
normalize3f(d21);
|
|
cross_product3f(d10, d21, cp[i]);
|
|
normalize3f(cp[i]);
|
|
if(i) {
|
|
if(dot_product3f(cp[i - 1], cp[i]) < 0.0)
|
|
invert3f(cp[i]);
|
|
}
|
|
add3f(cp[i], dir, dir);
|
|
}
|
|
copy3f(cp[0], cp[n_atom]);
|
|
for(i = 0; i < n_atom; i++) {
|
|
avg += dot_product3f(cp[i], cp[i + 1]);
|
|
}
|
|
result = avg / n_atom;
|
|
}
|
|
normalize3f(dir);
|
|
return result;
|
|
}
|
|
|
|
typedef struct {
|
|
int cyclic, planer, aromatic;
|
|
} ObservedInfo;
|
|
|
|
void ObjectMoleculeGuessValences(ObjectMolecule * I, int state, int *flag1, int *flag2,
|
|
int reset)
|
|
{
|
|
/* this a hacked 80% solution ...it will get things wrong, but it is
|
|
better than nothing! */
|
|
|
|
const float planer_cutoff = 0.96F;
|
|
CoordSet *cs = nullptr;
|
|
ObservedInfo *obs_atom = nullptr;
|
|
ObservedInfo *obs_bond = nullptr;
|
|
int *flag = nullptr;
|
|
int warning1 = 0, warning2 = 0;
|
|
|
|
/* WORKAROUND of a possible -funroll-loops inlining optimizer bug in gcc 3.3.3 */
|
|
|
|
float (*compute_avg_center_dot_cross) (ObjectMolecule *, CoordSet *, int, int *);
|
|
float (*compute_avg_ring_dot_cross) (ObjectMolecule *, CoordSet *, int, int *, float *);
|
|
|
|
compute_avg_center_dot_cross = compute_avg_center_dot_cross_fn;
|
|
compute_avg_ring_dot_cross = compute_avg_ring_dot_cross_fn;
|
|
|
|
|
|
/* end WORKAROUND */
|
|
|
|
if((state >= 0) && (state < I->NCSet)) {
|
|
cs = I->CSet[state];
|
|
}
|
|
if(cs) {
|
|
obs_atom = pymol::calloc<ObservedInfo>(I->NAtom);
|
|
obs_bond = pymol::calloc<ObservedInfo>(I->NBond);
|
|
}
|
|
flag = pymol::calloc<int>(I->NAtom);
|
|
if(flag) {
|
|
if(!flag1) {
|
|
int a, *flag_a = flag;
|
|
const AtomInfoType *ai = I->AtomInfo.data();
|
|
/* default behavior: only reset hetatm valences */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
*(flag_a++) = (ai++)->hetatm;
|
|
}
|
|
} else if(flag1 && flag2) {
|
|
int a, *flag_a = flag, *flag1_a = flag1, *flag2_a = flag2;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
*(flag_a++) = (*(flag1_a++) || *(flag2_a++));
|
|
}
|
|
} else if(flag1) {
|
|
int a, *flag_a = flag, *flag1_a = flag1;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
*(flag_a++) = *(flag1_a++);
|
|
}
|
|
}
|
|
}
|
|
if(!flag1)
|
|
flag1 = flag;
|
|
if(!flag2)
|
|
flag2 = flag;
|
|
if(reset) {
|
|
/* reset chemistry information and bond orders for selected atoms */
|
|
{
|
|
int a;
|
|
AtomInfoType *ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(flag[a])
|
|
ai->chemFlag = 0;
|
|
ai++;
|
|
}
|
|
}
|
|
{
|
|
int b;
|
|
BondType *bi = I->Bond.data();
|
|
for(b = 0; b < I->NBond; b++) {
|
|
int at0 = bi->index[0];
|
|
int at1 = bi->index[1];
|
|
if((flag1[at0] && flag2[at1]) || (flag1[at1] && flag2[at0]))
|
|
bi->order = 1;
|
|
bi++;
|
|
}
|
|
}
|
|
}
|
|
if(cs && obs_bond && obs_atom && flag && flag1 && flag2) {
|
|
int a;
|
|
int const* const neighbor = I->getNeighborArray();
|
|
AtomInfoType *atomInfo = I->AtomInfo.data();
|
|
BondType *bondInfo = I->Bond.data();
|
|
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
AtomInfoType *ai = atomInfo + a;
|
|
if((!ai->chemFlag) && (flag[a])) {
|
|
/* determine whether or not atom participates in a planer system with 5 or 6 atoms */
|
|
|
|
{
|
|
int mem[9];
|
|
int nbr[7];
|
|
const int ESCAPE_MAX = 500;
|
|
|
|
const int* atmToIdx = I->DiscreteFlag ? nullptr : cs->AtmToIdx.data();
|
|
|
|
int escape_count = ESCAPE_MAX; /* don't get bogged down with structures
|
|
that have unreasonable connectivity */
|
|
mem[0] = a;
|
|
nbr[0] = neighbor[mem[0]] + 1;
|
|
while(((mem[1] = neighbor[nbr[0]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[0]] >= 0))) {
|
|
nbr[1] = neighbor[mem[1]] + 1;
|
|
while(((mem[2] = neighbor[nbr[1]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[1]] >= 0))) {
|
|
if(mem[2] != mem[0]) {
|
|
nbr[2] = neighbor[mem[2]] + 1;
|
|
while(((mem[3] = neighbor[nbr[2]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[2]] >= 0))) {
|
|
if(mem[3] != mem[1]) {
|
|
nbr[3] = neighbor[mem[3]] + 1;
|
|
while(((mem[4] = neighbor[nbr[3]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[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) &&
|
|
((!atmToIdx) || (atmToIdx[mem[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 */
|
|
int i;
|
|
float dir[] = { 0.f, 0.f, 0.f } ;
|
|
float avg_dot_cross =
|
|
compute_avg_ring_dot_cross(I, cs, 5, mem, dir);
|
|
for(i = 0; i < 5; i++) {
|
|
obs_atom[mem[i]].cyclic = true;
|
|
obs_bond[neighbor[nbr[0] + 1]].cyclic = true;
|
|
}
|
|
if(avg_dot_cross > planer_cutoff) {
|
|
if(verify_planer_bonds
|
|
(I, cs, 5, mem, neighbor, dir, 0.35F)) {
|
|
for(i = 0; i < 5; i++) {
|
|
obs_atom[mem[i]].planer = true;
|
|
obs_bond[neighbor[nbr[i] + 1]].planer = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nbr[5] = neighbor[mem[5]] + 1;
|
|
while(((mem[6] = neighbor[nbr[5]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[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 */
|
|
int i;
|
|
float dir[3] = { 0.f, 0.f, 0.f } ;
|
|
float avg_dot_cross =
|
|
compute_avg_ring_dot_cross(I, cs, 6, mem, dir);
|
|
for(i = 0; i < 6; i++) {
|
|
obs_atom[mem[i]].cyclic = true;
|
|
obs_bond[neighbor[nbr[i] + 1]].cyclic = true;
|
|
}
|
|
if(avg_dot_cross > planer_cutoff) {
|
|
if(verify_planer_bonds
|
|
(I, cs, 6, mem, neighbor, dir, 0.35F)) {
|
|
for(i = 0; i < 6; i++) {
|
|
obs_atom[mem[i]].planer = true;
|
|
obs_bond[neighbor[nbr[i] + 1]].planer = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nbr[5] += 2;
|
|
}
|
|
}
|
|
nbr[4] += 2;
|
|
}
|
|
}
|
|
nbr[3] += 2;
|
|
}
|
|
}
|
|
nbr[2] += 2;
|
|
}
|
|
}
|
|
nbr[1] += 2;
|
|
}
|
|
nbr[0] += 2;
|
|
}
|
|
|
|
escape:
|
|
escape_count = ESCAPE_MAX; /* don't get bogged down with structures
|
|
that have unreasonable connectivity */
|
|
warning1 = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
AtomInfoType *ai = atomInfo + a;
|
|
if((!ai->chemFlag) && flag[a]) {
|
|
ObservedInfo *ob_at = obs_atom + a;
|
|
|
|
{
|
|
switch (ai->protons) {
|
|
case cAN_P:
|
|
case cAN_S:
|
|
case cAN_N:
|
|
case cAN_C:
|
|
{
|
|
int atm[5];
|
|
int bnd[5];
|
|
int n = neighbor[a];
|
|
int nn = neighbor[n++];
|
|
if(nn > 4)
|
|
nn = 4;
|
|
atm[0] = a;
|
|
{
|
|
int i;
|
|
for(i = 1; i <= nn; i++) {
|
|
atm[i] = neighbor[n];
|
|
bnd[i] = neighbor[n + 1];
|
|
n += 2;
|
|
}
|
|
}
|
|
{
|
|
int o1_at = -1, o2_at = -1, o3_at = -1, o4_at = -1;
|
|
int o1_bd = 0, o2_bd = 0, o3_bd = 0, o4_bd = 0;
|
|
float o1_len = 0.0F, o2_len = 0.0F;
|
|
float n1_v[3] = { 0.0F, 0.0F, 0.0F };
|
|
int n1_at = -1, n2_at = -1, n3_at = -1;
|
|
int n1_bd = 0, n2_bd = 0, n3_bd = 0;
|
|
float n1_len = 0.0F, n2_len = 0.0F, n3_len = 0.0F;
|
|
int c1_at = -1, c2_at = -1;
|
|
float c1_v[3] = { 0.0F, 0.0F, 0.0F };
|
|
float *v0 = nullptr;
|
|
|
|
{
|
|
int idx0 = cs->atmToIdx(a);
|
|
if(idx0 >= 0)
|
|
v0 = cs->coordPtr(idx0);
|
|
}
|
|
{
|
|
int i;
|
|
for(i = 1; i <= nn; i++) {
|
|
float *v1 = nullptr;
|
|
|
|
{
|
|
int idx1 = cs->atmToIdx(atm[i]);
|
|
if(idx1 >= 0)
|
|
v1 = cs->coordPtr(idx1);
|
|
}
|
|
if(v0 && v1) {
|
|
float diff[3];
|
|
subtract3f(v1, v0, diff);
|
|
{
|
|
|
|
float bond_len = length3f(diff);
|
|
|
|
switch (atomInfo[atm[i]].protons) {
|
|
case cAN_C:
|
|
if(c1_at < 0) {
|
|
c1_at = atm[i];
|
|
copy3f(diff, c1_v);
|
|
} else if(c2_at < 0) {
|
|
c2_at = atm[i];
|
|
}
|
|
break;
|
|
case cAN_O:
|
|
if(o1_at < 0) {
|
|
o1_at = atm[i];
|
|
o1_len = bond_len;
|
|
o1_bd = bnd[i];
|
|
} else if(o2_at < 0) {
|
|
o2_at = atm[i];
|
|
o2_len = bond_len;
|
|
o2_bd = bnd[i];
|
|
} else if(o3_at < 0) {
|
|
o3_at = atm[i];
|
|
o3_bd = bnd[i];
|
|
} else if(o4_at < 0) {
|
|
o4_at = atm[i];
|
|
o4_bd = bnd[i];
|
|
}
|
|
break;
|
|
case cAN_N:
|
|
if(n1_at < 0) {
|
|
copy3f(diff, n1_v);
|
|
n1_at = atm[i];
|
|
n1_len = bond_len;
|
|
n1_bd = bnd[i];
|
|
} else if(n2_at < 0) {
|
|
n2_at = atm[i];
|
|
n2_len = bond_len;
|
|
n2_bd = bnd[i];
|
|
} else if(n3_at < 0) {
|
|
n3_at = atm[i];
|
|
n3_len = bond_len;
|
|
n3_bd = bnd[i];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
{
|
|
float avg_dot_cross = 0.0F;
|
|
|
|
switch (ai->protons) {
|
|
case cAN_C: /* planer carbons */
|
|
if(nn == 3) {
|
|
avg_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm);
|
|
|
|
if(avg_dot_cross > planer_cutoff) {
|
|
|
|
if((n1_at >= 0) && (o1_at >= 0) && (o2_at < 0)) {
|
|
/* simple amide? */
|
|
if(o1_len < 1.38F) {
|
|
if(neighbor[neighbor[o1_at]] == 1)
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 2;
|
|
}
|
|
} else if((n1_at >= 0) && (o1_at >= 0) && (o2_at >= 0)) {
|
|
/* carbamyl */
|
|
if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1) &&
|
|
(o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1)) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 4;
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 4;
|
|
} else if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1)) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 2;
|
|
} else if((o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1))
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 2;
|
|
} else if((n1_at < 0) && (o1_at >= 0) && (o2_at < 0)) {
|
|
/* ketone */
|
|
if((o1_len < 1.31F) && (neighbor[neighbor[o1_at]] == 1)) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 2;
|
|
}
|
|
} else if((o1_at >= 0) && (o2_at >= 0) && (n1_at < 0)) {
|
|
/* simple carboxylate? */
|
|
if((o1_len < 1.38F) && (o2_len < 1.38F) &&
|
|
(neighbor[neighbor[o1_at]] == 1) &&
|
|
(neighbor[neighbor[o2_at]] == 1)) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 4;
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 4;
|
|
} else if((o1_len < 1.38F) && (neighbor[neighbor[o1_at]] == 1)) { /* esters */
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 2;
|
|
} else if((o2_len < 1.38F) && (neighbor[neighbor[o2_at]] == 1)) {
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 2;
|
|
}
|
|
} else if((n1_at >= 0) && (n2_at >= 0) && (n3_at < 0)
|
|
&& (c1_at >= 0) && (n1_len < 1.43F) && (n2_len < 1.43F)
|
|
&& obs_atom[c1_at].planer && obs_atom[c1_at].cyclic
|
|
&& (!ob_at->cyclic) && (!obs_atom[n1_at].cyclic)
|
|
&& (!obs_atom[n2_at].cyclic)) {
|
|
if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
|
|
bondInfo[n1_bd].order = 4;
|
|
atomInfo[n1_at].valence = 3;
|
|
atomInfo[n1_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n1_at].chemFlag = 2;
|
|
if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
|
|
bondInfo[n2_bd].order = 4;
|
|
atomInfo[n2_at].valence = 3;
|
|
atomInfo[n2_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n2_at].chemFlag = 2;
|
|
} else if((n1_at >= 0) && (n2_at >= 0) && (n3_at >= 0)) {
|
|
/* guanido with no hydrogens */
|
|
if((n1_len < 1.44F) && (n2_len < 1.44F) && (n3_len < 1.44F)) {
|
|
if((neighbor[neighbor[n1_at]] == 1) &&
|
|
(neighbor[neighbor[n2_at]] == 1) &&
|
|
(neighbor[neighbor[n3_at]] >= 2)) {
|
|
if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
|
|
bondInfo[n1_bd].order = 4;
|
|
atomInfo[n1_at].valence = 3;
|
|
atomInfo[n1_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n1_at].chemFlag = 2;
|
|
if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
|
|
bondInfo[n2_bd].order = 4;
|
|
atomInfo[n2_at].valence = 3;
|
|
atomInfo[n2_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n2_at].chemFlag = 2;
|
|
} else if((neighbor[neighbor[n1_at]] == 1) &&
|
|
(neighbor[neighbor[n2_at]] >= 2) &&
|
|
(neighbor[neighbor[n3_at]] == 1)) {
|
|
if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
|
|
bondInfo[n1_bd].order = 4;
|
|
atomInfo[n1_at].valence = 3;
|
|
atomInfo[n1_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n1_at].chemFlag = 2;
|
|
if((flag1[a] && flag2[n3_at]) || (flag2[a] && flag1[n3_at]))
|
|
bondInfo[n3_bd].order = 4;
|
|
atomInfo[n3_at].valence = 3;
|
|
atomInfo[n3_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n3_at].chemFlag = 2;
|
|
} else if((neighbor[neighbor[n1_at]] >= 2) &&
|
|
(neighbor[neighbor[n2_at]] == 1) &&
|
|
(neighbor[neighbor[n3_at]] == 1)) {
|
|
if((flag1[a] && flag2[n2_at]) || (flag2[a] && flag1[n2_at]))
|
|
bondInfo[n2_bd].order = 4;
|
|
atomInfo[n2_at].valence = 3;
|
|
atomInfo[n2_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n2_at].chemFlag = 2;
|
|
if((flag1[a] && flag2[n3_at]) || (flag2[a] && flag1[n3_at]))
|
|
bondInfo[n3_bd].order = 4;
|
|
atomInfo[n3_at].valence = 3;
|
|
atomInfo[n3_at].geom = cAtomInfoPlanar;
|
|
atomInfo[n3_at].chemFlag = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* any carbon */
|
|
|
|
/* handle imines and nitriles */
|
|
|
|
if((nn >= 2) && (nn <= 3) && (n1_at >= 0) && (o1_at < 0) &&
|
|
(n2_at < 0) && (n1_len < 1.36F) &&
|
|
(!ob_at->cyclic) && (!obs_bond[n1_bd].cyclic)
|
|
&& (!obs_atom[n1_at].planer) && ((nn == 2)
|
|
|| ((nn == 3)
|
|
&& (avg_dot_cross >
|
|
planer_cutoff)))) {
|
|
|
|
float n1_dot_cross = 1.0F;
|
|
int n2 = neighbor[n1_at];
|
|
int nn2 = neighbor[n2++];
|
|
|
|
{ /* check nitrogen planarity */
|
|
int atm2[5];
|
|
if(nn2 > 2) {
|
|
nn2 = 3;
|
|
atm2[0] = n1_at;
|
|
{
|
|
int i2;
|
|
for(i2 = 1; i2 <= nn2; i2++) {
|
|
atm2[i2] = neighbor[n2];
|
|
n2 += 2;
|
|
}
|
|
}
|
|
n1_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm2);
|
|
}
|
|
}
|
|
if(n1_dot_cross > planer_cutoff) {
|
|
if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
|
|
bondInfo[n1_bd].order = 2;
|
|
if((n1_len < 1.24F) && (c1_at >= 0) && (nn2 == 1)) {
|
|
normalize3f(n1_v);
|
|
normalize3f(c1_v);
|
|
if(dot_product3f(n1_v, c1_v) < -0.9) {
|
|
if((flag1[a] && flag2[n1_at]) || (flag2[a] && flag1[n1_at]))
|
|
bondInfo[n1_bd].order = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cAN_N:
|
|
if(nn == 3) {
|
|
avg_dot_cross = compute_avg_center_dot_cross(I, cs, 4, atm);
|
|
|
|
if((avg_dot_cross > planer_cutoff)) {
|
|
if((o1_at >= 0) && (o2_at >= 0) && (o3_at < 0)) {
|
|
/* nitro */
|
|
if(neighbor[neighbor[o1_at]] == 1) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 4;
|
|
}
|
|
if(neighbor[neighbor[o2_at]] == 1) {
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cAN_S:
|
|
case cAN_P:
|
|
if((o1_at >= 0) && (o2_at >= 0) && (o3_at >= 0) && (o4_at >= 0)) {
|
|
/* sulfate, phosphate */
|
|
int o1 = -1, o2 = -1, o3 = -1;
|
|
int a1 = 0;
|
|
if(neighbor[neighbor[o1_at]] == 1) {
|
|
o1 = o1_bd;
|
|
a1 = o1_at;
|
|
}
|
|
if(neighbor[neighbor[o2_at]] == 1) {
|
|
if(o1 < 0) {
|
|
o1 = o2_bd;
|
|
a1 = o2_at;
|
|
} else if(o2 < 0) {
|
|
o2 = o2_bd;
|
|
}
|
|
}
|
|
if(neighbor[neighbor[o3_at]] == 1) {
|
|
if(o1 < 0) {
|
|
o1 = o3_bd;
|
|
a1 = o3_at;
|
|
} else if(o2 < 0)
|
|
o2 = o3_bd;
|
|
else if(o3 < 0)
|
|
o3 = o3_bd;
|
|
}
|
|
if(neighbor[neighbor[o4_at]] == 1) {
|
|
if(o1 < 0) {
|
|
o1 = o4_bd;
|
|
a1 = o4_at;
|
|
} else if(o2 < 0)
|
|
o2 = o4_bd;
|
|
else if(o3 < 0)
|
|
o3 = o4_bd;
|
|
}
|
|
if(o2 >= 0) {
|
|
if((flag1[a] && flag2[a1]) || (flag2[a] && flag1[a1]))
|
|
bondInfo[o1].order = 2;
|
|
if(o2 == o2_bd) {
|
|
atomInfo[o2_at].formalCharge = -1;
|
|
} else if(o2 == o3_bd) {
|
|
atomInfo[o3_at].formalCharge = -1;
|
|
} else if(o2 == o4_bd) {
|
|
atomInfo[o4_at].formalCharge = -1;
|
|
}
|
|
}
|
|
} else if((o1_at >= 0) && (o2_at >= 0) && (o3_at >= 0) && (o4_at < 0)) {
|
|
/* sulfonamide */
|
|
int o1 = -1, o2 = -1;
|
|
int a1 = 0, a2 = 0;
|
|
if(neighbor[neighbor[o1_at]] == 1) {
|
|
o1 = o1_bd;
|
|
a1 = o1_at;
|
|
}
|
|
if(neighbor[neighbor[o2_at]] == 1) {
|
|
if(o1 < 0) {
|
|
o1 = o2_bd;
|
|
a1 = o2_at;
|
|
} else if(o2 < 0) {
|
|
o2 = o2_bd;
|
|
a2 = o2_at;
|
|
}
|
|
}
|
|
if(neighbor[neighbor[o3_at]] == 1) {
|
|
if(o1 < 0) {
|
|
o1 = o3_bd;
|
|
a1 = o3_at;
|
|
} else if(o2 < 0) {
|
|
o2 = o3_bd;
|
|
a2 = o3_at;
|
|
}
|
|
}
|
|
if(o1 >= 0) {
|
|
if((flag1[a] && flag2[a1]) || (flag2[a] && flag1[a1]))
|
|
bondInfo[o1].order = 2;
|
|
}
|
|
if(o2 >= 0) {
|
|
if((flag1[a] && flag2[a2]) || (flag2[a] && flag1[a2]))
|
|
bondInfo[o2].order = 2;
|
|
}
|
|
} else if((o1_at >= 0) && (o2_at >= 0) && (o3_at < 0)) {
|
|
/* sulphone */
|
|
if(neighbor[neighbor[o1_at]] == 1) {
|
|
if((flag1[a] && flag2[o1_at]) || (flag2[a] && flag1[o1_at]))
|
|
bondInfo[o1_bd].order = 2;
|
|
}
|
|
if(neighbor[neighbor[o2_at]] == 1) {
|
|
if((flag1[a] && flag2[o2_at]) || (flag2[a] && flag1[o2_at]))
|
|
bondInfo[o2_bd].order = 2;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* this sets aromatic bonds for cyclic planer systems */
|
|
|
|
if(ob_at->cyclic && ob_at->planer) {
|
|
|
|
switch (ai->protons) {
|
|
case cAN_C:
|
|
case cAN_N:
|
|
case cAN_O:
|
|
case cAN_S:
|
|
{
|
|
int n, a0, b0;
|
|
n = neighbor[a] + 1;
|
|
while(1) {
|
|
a0 = neighbor[n];
|
|
if(a0 < 0)
|
|
break;
|
|
b0 = neighbor[n + 1];
|
|
n += 2;
|
|
if(obs_atom[a0].cyclic && obs_atom[a0].planer && obs_bond[b0].cyclic) {
|
|
obs_atom[a0].aromatic = true;
|
|
switch (I->AtomInfo[a0].protons) {
|
|
case cAN_C:
|
|
case cAN_N:
|
|
case cAN_O:
|
|
case cAN_S:
|
|
if((flag1[a] && flag2[a0]) || (flag2[a] && flag1[a0]))
|
|
I->Bond[b0].order = 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now try to address some simple cases with aromatic nitrogens */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
AtomInfoType *ai = atomInfo + a;
|
|
if((!ai->chemFlag) && (ai->protons == cAN_N) &&
|
|
(ai->formalCharge == 0) && flag[a] &&
|
|
obs_atom[a].cyclic && obs_atom[a].aromatic) {
|
|
|
|
int n = neighbor[a];
|
|
int nn = neighbor[n++];
|
|
|
|
if(nn == 2) { /* only two explicit neighbors */
|
|
|
|
int mem[9];
|
|
int nbr[7];
|
|
const int ESCAPE_MAX = 500;
|
|
|
|
const int* atmToIdx = I->DiscreteFlag ? nullptr : cs->AtmToIdx.data();
|
|
|
|
int escape_count = ESCAPE_MAX; /* don't get bogged down with structures
|
|
that have unreasonable connectivity */
|
|
mem[0] = a;
|
|
nbr[0] = neighbor[mem[0]] + 1;
|
|
while(((mem[1] = neighbor[nbr[0]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[0]] >= 0))) {
|
|
nbr[1] = neighbor[mem[1]] + 1;
|
|
while(((mem[2] = neighbor[nbr[1]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[1]] >= 0))) {
|
|
if(mem[2] != mem[0]) {
|
|
nbr[2] = neighbor[mem[2]] + 1;
|
|
while(((mem[3] = neighbor[nbr[2]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[2]] >= 0))) {
|
|
if(mem[3] != mem[1]) {
|
|
nbr[3] = neighbor[mem[3]] + 1;
|
|
while(((mem[4] = neighbor[nbr[3]]) >= 0) &&
|
|
((!atmToIdx) || (atmToIdx[mem[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) &&
|
|
((!atmToIdx) || (atmToIdx[mem[4]] >= 0))) {
|
|
if(!(escape_count--))
|
|
goto escape2; /* BUG FIX: need a new escape2, instead of
|
|
mistakenly going back to the first escape,
|
|
which is in the loop above */
|
|
if((mem[5] != mem[3]) && (mem[5] != mem[2])
|
|
&& (mem[5] != mem[1])) {
|
|
if(mem[5] == mem[0] && (!ai->chemFlag)) {
|
|
|
|
/* unassigned aromatic nitrogen-containing five-cycle */
|
|
|
|
/* c1ccnc1 becomes c1ccn[H]c1 */
|
|
|
|
if((atomInfo[mem[1]].protons == cAN_C) &&
|
|
(atomInfo[mem[2]].protons == cAN_C) &&
|
|
(atomInfo[mem[3]].protons == cAN_C) &&
|
|
(atomInfo[mem[4]].protons == cAN_C) &&
|
|
obs_atom[mem[1]].aromatic &&
|
|
obs_atom[mem[2]].aromatic &&
|
|
obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
|
|
ai->valence = 3;
|
|
ai->chemFlag = 2;
|
|
ai->geom = cAtomInfoPlanar;
|
|
}
|
|
|
|
/* c1ncnc1 becomes c1n[H]cnc1 */
|
|
|
|
if((atomInfo[mem[1]].protons == cAN_C) &&
|
|
(atomInfo[mem[2]].protons == cAN_N) &&
|
|
(atomInfo[mem[2]].formalCharge == 0) &&
|
|
(!atomInfo[mem[2]].chemFlag) &&
|
|
(atomInfo[mem[3]].protons == cAN_C) &&
|
|
(atomInfo[mem[4]].protons == cAN_C) &&
|
|
obs_atom[mem[1]].aromatic &&
|
|
obs_atom[mem[2]].aromatic &&
|
|
obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
|
|
|
|
int n2 = neighbor[mem[2]];
|
|
int nn2 = neighbor[n2++];
|
|
if(nn2 == 2) { /* second nitrogen also ambiguous */
|
|
ai->valence = 3;
|
|
ai->chemFlag = 2;
|
|
ai->geom = cAtomInfoPlanar;
|
|
}
|
|
}
|
|
|
|
/* c1cnnc1 becomes c1cn[H]nc1 */
|
|
|
|
if((atomInfo[mem[1]].protons == cAN_N) &&
|
|
(atomInfo[mem[1]].formalCharge == 0) &&
|
|
(!atomInfo[mem[1]].chemFlag) &&
|
|
(atomInfo[mem[2]].protons == cAN_C) &&
|
|
(atomInfo[mem[3]].protons == cAN_C) &&
|
|
(atomInfo[mem[4]].protons == cAN_C) &&
|
|
obs_atom[mem[1]].aromatic &&
|
|
obs_atom[mem[2]].aromatic &&
|
|
obs_atom[mem[3]].aromatic && obs_atom[mem[4]].aromatic) {
|
|
|
|
int n2 = neighbor[mem[1]];
|
|
int nn2 = neighbor[n2++];
|
|
if(nn2 == 2) { /* second nitrogen also ambiguous */
|
|
ai->valence = 3;
|
|
ai->chemFlag = 2;
|
|
ai->geom = cAtomInfoPlanar;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
nbr[4] += 2;
|
|
}
|
|
}
|
|
nbr[3] += 2;
|
|
}
|
|
}
|
|
nbr[2] += 2;
|
|
}
|
|
}
|
|
nbr[1] += 2;
|
|
}
|
|
nbr[0] += 2;
|
|
}
|
|
escape2: /* BUG FIX: Need separate escape for this loop */
|
|
escape_count = ESCAPE_MAX; /* don't get bogged down with structures
|
|
that have unreasonable connectivity */
|
|
warning2 = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (warning1 || warning2){
|
|
PRINTFB(I->G, FB_ObjectMolecule, FB_Blather)
|
|
" %s(%d,%d): Unreasonable connectivity in heteroatom,\n unsuccessful in guessing valences.\n", __func__, warning1, warning2
|
|
ENDFB(I->G);
|
|
}
|
|
FreeP(obs_bond);
|
|
FreeP(obs_atom);
|
|
FreeP(flag);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
void ObjectMoleculeInferChemForProtein(ObjectMolecule * I, int state)
|
|
{
|
|
/* Infers chemical relations for a molecules under protein assumptions.
|
|
*
|
|
* NOTE: this routine needs an all-atom model (with hydrogens!)
|
|
* and it will make mistakes on non-protein atoms (if they haven't
|
|
* already been assigned)
|
|
*/
|
|
|
|
int changedFlag = true;
|
|
|
|
/* first, try to find all amids and acids */
|
|
while(changedFlag) {
|
|
changedFlag = false;
|
|
for (int a = 0; a < I->NAtom; a++) {
|
|
auto const* const ai = I->AtomInfo.data() + a;
|
|
if(ai->chemFlag) {
|
|
if(ai->geom == cAtomInfoPlanar)
|
|
if(ai->protons == cAN_C) {
|
|
auto const neighbors = AtomNeighbors(I, a);
|
|
if (neighbors.size() > 1) {
|
|
AtomInfoType* ai1 = nullptr;
|
|
for (auto const& neighbor : neighbors) {
|
|
auto* const ai0 = I->AtomInfo.data() + neighbor.atm;
|
|
if((ai0->protons == cAN_O) && (!ai0->chemFlag)) {
|
|
ai1 = ai0; /* found candidate carbonyl */
|
|
break;
|
|
}
|
|
}
|
|
if (ai1) {
|
|
for (auto const& neighbor : neighbors) {
|
|
auto* const ai0 = I->AtomInfo.data() + neighbor.atm;
|
|
if (ai0 != ai1) {
|
|
if(ai0->protons == cAN_O) {
|
|
if(!ai0->chemFlag) {
|
|
ai0->chemFlag = true; /* acid */
|
|
ai0->geom = cAtomInfoPlanar;
|
|
ai0->valence = 1;
|
|
ai1->chemFlag = true;
|
|
ai1->geom = cAtomInfoPlanar;
|
|
ai1->valence = 1;
|
|
changedFlag = true;
|
|
break;
|
|
}
|
|
} else if(ai0->protons == cAN_N) {
|
|
if(!ai0->chemFlag) {
|
|
ai0->chemFlag = true; /* amide N */
|
|
ai0->geom = cAtomInfoPlanar;
|
|
ai0->valence = 3;
|
|
ai1->chemFlag = true; /* amide =O */
|
|
ai1->geom = cAtomInfoPlanar;
|
|
ai1->valence = 1;
|
|
changedFlag = true;
|
|
break;
|
|
} else if(ai0->geom == cAtomInfoPlanar) {
|
|
ai1->chemFlag = true; /* amide =O */
|
|
ai1->geom = cAtomInfoPlanar;
|
|
ai1->valence = 1;
|
|
changedFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* then handle aldehydes and amines (partial amides - both missing a valence) */
|
|
|
|
changedFlag = true;
|
|
while(changedFlag) {
|
|
changedFlag = false;
|
|
for (int a = 0; a < I->NAtom; a++) {
|
|
auto* const ai = I->AtomInfo.data() + a;
|
|
if(!ai->chemFlag) {
|
|
if(ai->protons == cAN_C) {
|
|
auto const neighbors = AtomNeighbors(I, a);
|
|
if (neighbors.size() > 1) {
|
|
for (auto const& neighbor : neighbors) {
|
|
auto* const ai0 = I->AtomInfo.data() + neighbor.atm;
|
|
if((ai0->protons == cAN_O) && (!ai0->chemFlag)) { /* =O */
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoPlanar;
|
|
ai->valence = 1;
|
|
ai0->chemFlag = true;
|
|
ai0->geom = cAtomInfoPlanar;
|
|
ai0->valence = 3;
|
|
changedFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if(ai->protons == cAN_N) {
|
|
if((!ai->chemFlag) || ai->geom != cAtomInfoLinear) {
|
|
if(ai->formalCharge == 0) {
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoPlanar;
|
|
ai->valence = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Assigns:
|
|
* - geom
|
|
* - valence
|
|
* - chemFlag
|
|
*/
|
|
void ObjectMoleculeInferChemFromNeighGeom(ObjectMolecule * I, int state)
|
|
{
|
|
/* infers chemical relations from neighbors and geometry
|
|
* NOTE: very limited in scope */
|
|
|
|
int a;
|
|
int changedFlag = true;
|
|
int geom;
|
|
int carbonVal[10];
|
|
|
|
AtomInfoType *ai, *ai2;
|
|
|
|
carbonVal[cAtomInfoTetrahedral] = 4;
|
|
carbonVal[cAtomInfoPlanar] = 3;
|
|
carbonVal[cAtomInfoLinear] = 2;
|
|
|
|
while(changedFlag) {
|
|
changedFlag = false;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
ai = I->AtomInfo + a;
|
|
if(!ai->chemFlag) {
|
|
geom = ObjectMoleculeGetAtomGeometry(I, state, a);
|
|
switch (ai->protons) {
|
|
case cAN_K:
|
|
ai->chemFlag = 1;
|
|
ai->geom = cAtomInfoNone;
|
|
ai->valence = 0;
|
|
break;
|
|
case cAN_H:
|
|
case cAN_F:
|
|
case cAN_I:
|
|
case cAN_Br:
|
|
ai->chemFlag = 1;
|
|
ai->geom = cAtomInfoSingle;
|
|
ai->valence = 1;
|
|
break;
|
|
case cAN_O: {
|
|
auto const neighbors = AtomNeighbors(I, a);
|
|
auto const nn = neighbors.size();
|
|
if(nn != 1) { /* water, hydroxy, ether */
|
|
ai->chemFlag = 1;
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 2;
|
|
} else { /* hydroxy or carbonyl? check carbon geometry */
|
|
ai2 = I->AtomInfo.data() + neighbors[0].atm;
|
|
if(ai2->chemFlag) {
|
|
if((ai2->geom == cAtomInfoTetrahedral) || (ai2->geom == cAtomInfoLinear)) {
|
|
ai->chemFlag = 1; /* hydroxy */
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 2;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case cAN_C:
|
|
if(geom >= 0) {
|
|
ai->geom = geom;
|
|
ai->valence = carbonVal[geom];
|
|
ai->chemFlag = true;
|
|
} else {
|
|
auto const neighbors = AtomNeighbors(I, a);
|
|
auto const nn = neighbors.size();
|
|
if(nn == 1) { /* only one neighbor */
|
|
ai2 = I->AtomInfo.data() + neighbors[0].atm;
|
|
if(ai2->chemFlag && (ai2->geom == cAtomInfoTetrahedral)) {
|
|
ai->chemFlag = true; /* singleton carbon bonded to tetC must be tetC */
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case cAN_N:
|
|
if(geom == cAtomInfoPlanar) {
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoPlanar;
|
|
ai->valence = 3;
|
|
} else if(geom == cAtomInfoTetrahedral) {
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 4;
|
|
}
|
|
break;
|
|
case cAN_S: {
|
|
auto const nn = AtomNeighbors(I, a).size();
|
|
if(nn == 4) { /* sulfone */
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 4;
|
|
} else if(nn == 3) { /* suloxide */
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 3;
|
|
} else if(nn == 2) { /* thioether */
|
|
ai->chemFlag = true;
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
ai->valence = 2;
|
|
}
|
|
break;
|
|
}
|
|
case cAN_Cl:
|
|
ai->chemFlag = 1;
|
|
if(ai->formalCharge == 0) {
|
|
ai->geom = cAtomInfoSingle;
|
|
ai->valence = 1;
|
|
} else {
|
|
ai->geom = cAtomInfoNone;
|
|
ai->valence = 0;
|
|
}
|
|
break;
|
|
}
|
|
if(ai->chemFlag)
|
|
changedFlag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Assigns, based on valence, geom, formalCharge, and bonded atoms:
|
|
* - hb_donor
|
|
* - hb_acceptor
|
|
*/
|
|
void ObjectMoleculeInferHBondFromChem(ObjectMolecule * I)
|
|
{
|
|
int a;
|
|
AtomInfoType *ai;
|
|
int a1;
|
|
int has_hydro;
|
|
/* initialize accumulators on uncategorized atoms */
|
|
|
|
const lexborrow_t lex_pseudo = LexBorrow(I->G, "pseudo");
|
|
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
auto const neighbors = AtomNeighbors(I, a);
|
|
int nn = neighbors.size();
|
|
ai->hb_donor = false;
|
|
ai->hb_acceptor = false;
|
|
|
|
has_hydro = (nn < ai->valence); /* implicit hydrogens? */
|
|
|
|
if(!has_hydro) {
|
|
/* explicit hydrogens? */
|
|
switch (ai->protons) {
|
|
case cAN_N:
|
|
case cAN_O:
|
|
for (auto const& neighbor : neighbors) {
|
|
a1 = neighbor.atm;
|
|
if(I->AtomInfo[a1].protons == 1) {
|
|
has_hydro = true;
|
|
break;
|
|
}
|
|
if (I->AtomInfo[a1].name == lex_pseudo && --nn < ai->valence) {
|
|
has_hydro = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (ai->protons) {
|
|
/* cat-ions, lewis acids etc. */
|
|
case cAN_Fe:
|
|
case cAN_Ca:
|
|
case cAN_Cu:
|
|
case cAN_K:
|
|
case cAN_Na:
|
|
case cAN_Mg:
|
|
case cAN_Zn:
|
|
case cAN_Hg:
|
|
case cAN_Sr:
|
|
case cAN_Ba:
|
|
ai->hb_donor = true;
|
|
break;
|
|
case cAN_N:
|
|
if(has_hydro)
|
|
ai->hb_donor = true;
|
|
else {
|
|
int delocalized = false;
|
|
int has_double_bond = false;
|
|
int neighbor_has_double_bond = false;
|
|
int mem[3];
|
|
int nbr[3];
|
|
int const* neighbor = I->getNeighborArray();
|
|
|
|
mem[0] = a;
|
|
nbr[0] = neighbor[mem[0]] + 1;
|
|
while(((mem[1] = neighbor[nbr[0]]) >= 0)) {
|
|
int b_order = I->Bond[neighbor[nbr[0] + 1]].order;
|
|
if(b_order > 1) { /* any double/triple/aromatic bonds? */
|
|
delocalized = true;
|
|
}
|
|
if(b_order == 2) {
|
|
has_double_bond = true;
|
|
}
|
|
nbr[1] = neighbor[mem[1]] + 1;
|
|
while(((mem[2] = neighbor[nbr[1]]) >= 0)) {
|
|
if(mem[2] != mem[0]) {
|
|
int b_order2 = I->Bond[neighbor[nbr[1] + 1]].order;
|
|
if(b_order2 == 2) {
|
|
neighbor_has_double_bond = true;
|
|
}
|
|
}
|
|
nbr[1] += 2;
|
|
}
|
|
nbr[0] += 2;
|
|
}
|
|
if((ai->formalCharge <= 0) && delocalized && (nn < 3)) {
|
|
/* delocalized nitrogen can likely serve as an acceptor */
|
|
ai->hb_acceptor = true;
|
|
}
|
|
if(delocalized && (neighbor_has_double_bond) && (!has_double_bond) &&
|
|
(ai->geom == cAtomInfoPlanar) && (nn == 2) && (ai->formalCharge >= 0)) {
|
|
/* there's a fair chance of a resonance structure with this nitrogen as a donor */
|
|
ai->hb_donor = true;
|
|
}
|
|
if((ai->geom != cAtomInfoPlanar) && (nn == 3) && (ai->formalCharge >= 0)
|
|
&& (!delocalized)) {
|
|
/* tertiary amine case -- assume potential donor */
|
|
ai->hb_donor = true;
|
|
}
|
|
}
|
|
break;
|
|
case cAN_O:
|
|
if(ai->formalCharge <= 0)
|
|
ai->hb_acceptor = true;
|
|
if(has_hydro)
|
|
ai->hb_donor = true;
|
|
else {
|
|
int has_double_bond = false;
|
|
int neighbor_has_aromatic_bond = false;
|
|
int mem[3];
|
|
int nbr[3];
|
|
auto* const neighbor = I->getNeighborArray();
|
|
|
|
mem[0] = a;
|
|
nbr[0] = neighbor[mem[0]] + 1;
|
|
while(((mem[1] = neighbor[nbr[0]]) >= 0)) {
|
|
int b_order = I->Bond[neighbor[nbr[0] + 1]].order;
|
|
if(b_order == 2) {
|
|
has_double_bond = true;
|
|
}
|
|
nbr[1] = neighbor[mem[1]] + 1;
|
|
while(((mem[2] = neighbor[nbr[1]]) >= 0)) {
|
|
if(mem[2] != mem[0]) {
|
|
int b_order2 = I->Bond[neighbor[nbr[1] + 1]].order;
|
|
if(b_order2 == 4) {
|
|
neighbor_has_aromatic_bond = true;
|
|
}
|
|
}
|
|
nbr[1] += 2;
|
|
}
|
|
nbr[0] += 2;
|
|
}
|
|
|
|
if(has_double_bond && neighbor_has_aromatic_bond && (ai->formalCharge >= 0)) {
|
|
/* allow for phenolic resonance structures (and the like) */
|
|
ai->hb_donor = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
ai++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Assigns:
|
|
* - geom
|
|
* - valence
|
|
* - chemFlag
|
|
*/
|
|
void ObjectMoleculeInferChemFromBonds(ObjectMolecule * I, int state)
|
|
{
|
|
|
|
int a, b;
|
|
const BondType *b0;
|
|
AtomInfoType *ai, *ai0, *ai1 = nullptr;
|
|
int a0, a1;
|
|
int expect, order;
|
|
int changedFlag;
|
|
/* initialize accumulators on uncategorized atoms */
|
|
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!ai->chemFlag) {
|
|
ai->geom = 0;
|
|
ai->valence = 0;
|
|
}
|
|
ai++;
|
|
}
|
|
|
|
// Ignore "pseudo" atoms (Desmond virtual sites)
|
|
const lexborrow_t lex_pseudo = LexBorrow(I->G, "pseudo");
|
|
std::vector<unsigned char> pseudo_neighbor_count(
|
|
lex_pseudo == LEX_BORROW_NOTFOUND ? 0 : I->NAtom);
|
|
|
|
/* find maximum bond order for each atom */
|
|
|
|
b0 = I->Bond;
|
|
for(b = 0; b < I->NBond; b++) {
|
|
a0 = b0->index[0];
|
|
a1 = b0->index[1];
|
|
ai0 = I->AtomInfo + a0;
|
|
ai1 = I->AtomInfo + a1;
|
|
order = b0->order;
|
|
b0++;
|
|
|
|
// count "pseudo" neighbors
|
|
if (!pseudo_neighbor_count.empty()) {
|
|
if (ai0->name == lex_pseudo) {
|
|
pseudo_neighbor_count[a1] += 1;
|
|
}
|
|
if (ai1->name == lex_pseudo) {
|
|
pseudo_neighbor_count[a0] += 1;
|
|
}
|
|
}
|
|
|
|
if(!ai0->chemFlag) {
|
|
if(order > ai0->geom)
|
|
ai0->geom = order;
|
|
ai0->valence += order;
|
|
}
|
|
if(!ai1->chemFlag) {
|
|
if(order > ai1->geom)
|
|
ai1->geom = order;
|
|
ai1->valence += order;
|
|
}
|
|
if(order == 3) {
|
|
/* override existing chemistry * this is a temp fix to a pressing problem...
|
|
we need to rethink the chemisty assignment ordering (should bond
|
|
information come first? */
|
|
ai0->geom = cAtomInfoLinear;
|
|
ai1->geom = cAtomInfoLinear;
|
|
if(ai0->chemFlag != 2) {
|
|
switch (ai0->protons) {
|
|
case cAN_C:
|
|
ai0->valence = 2;
|
|
break;
|
|
default:
|
|
ai0->valence = 1;
|
|
}
|
|
ai0->chemFlag = true;
|
|
}
|
|
if(ai1->chemFlag != 2) {
|
|
switch (ai1->protons) {
|
|
case cAN_C:
|
|
ai1->valence = 2;
|
|
break;
|
|
default:
|
|
ai1->valence = 1;
|
|
}
|
|
ai1->chemFlag = true;
|
|
}
|
|
} else if(order == 4) {
|
|
ai0->geom = cAtomInfoPlanar;
|
|
ai1->geom = cAtomInfoPlanar;
|
|
if(ai0->chemFlag != 2) {
|
|
switch (ai0->protons) {
|
|
case cAN_O:
|
|
ai0->valence = 1;
|
|
break;
|
|
case cAN_N:
|
|
ai0->valence = (ai0->formalCharge == 1) ? 3 : 2; // TODO wrong for neutral -NH-
|
|
break;
|
|
case cAN_C:
|
|
ai0->valence = 3;
|
|
break;
|
|
case cAN_S:
|
|
ai0->valence = 2;
|
|
break;
|
|
default:
|
|
ai0->valence = 4;
|
|
}
|
|
ai0->chemFlag = true;
|
|
}
|
|
if(ai1->chemFlag != 2) {
|
|
switch (ai1->protons) {
|
|
case cAN_O:
|
|
ai1->valence = 1;
|
|
break;
|
|
case cAN_N:
|
|
ai1->valence = (ai1->formalCharge == 1) ? 3 : 2; // TODO wrong for neutral -NH-
|
|
break;
|
|
case cAN_C:
|
|
ai1->valence = 3;
|
|
break;
|
|
default:
|
|
ai1->valence = 1;
|
|
}
|
|
ai1->chemFlag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now set up valences and geometries */
|
|
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!ai->chemFlag) {
|
|
expect = AtomInfoGetExpectedValence(I->G, ai);
|
|
int nn = AtomNeighbors(I, a).size();
|
|
|
|
// don't count "pseudo" atom neighbors
|
|
if (!pseudo_neighbor_count.empty()) {
|
|
nn -= pseudo_neighbor_count[a];
|
|
}
|
|
|
|
if(ai->geom == 3) {
|
|
ai->geom = cAtomInfoLinear;
|
|
switch (ai->protons) {
|
|
case cAN_C:
|
|
ai->valence = 2;
|
|
break;
|
|
default:
|
|
ai->valence = 1;
|
|
}
|
|
ai->chemFlag = true;
|
|
} else {
|
|
if(expect < 0)
|
|
expect = -expect; /* for now, just ignore this issue */
|
|
/* printf("%d %d %d %d\n",ai->geom,ai->valence,nn,expect); */
|
|
if(ai->valence == expect) { /* sum of bond orders equals valence */
|
|
ai->chemFlag = true;
|
|
ai->valence = nn;
|
|
switch (ai->geom) { /* max bond order observed */
|
|
case 0:
|
|
ai->geom = cAtomInfoNone;
|
|
break;
|
|
case 2:
|
|
ai->geom = cAtomInfoPlanar;
|
|
break;
|
|
case 3:
|
|
ai->geom = cAtomInfoLinear;
|
|
break;
|
|
default:
|
|
if(expect == 1)
|
|
ai->geom = cAtomInfoSingle;
|
|
else
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
break;
|
|
}
|
|
} else if(ai->valence < expect) { /* missing a bond */
|
|
ai->chemFlag = true;
|
|
ai->valence = nn + (expect - ai->valence);
|
|
switch (ai->geom) {
|
|
case 2:
|
|
ai->geom = cAtomInfoPlanar;
|
|
break;
|
|
case 3:
|
|
ai->geom = cAtomInfoLinear;
|
|
break;
|
|
default:
|
|
if(expect == 1)
|
|
ai->geom = cAtomInfoSingle;
|
|
else
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
break;
|
|
}
|
|
} else if(ai->valence > expect) {
|
|
ai->chemFlag = true;
|
|
ai->valence = nn;
|
|
switch (ai->geom) {
|
|
case 2:
|
|
ai->geom = cAtomInfoPlanar;
|
|
break;
|
|
case 3:
|
|
ai->geom = cAtomInfoLinear;
|
|
break;
|
|
default:
|
|
if(expect == 1)
|
|
ai->geom = cAtomInfoSingle;
|
|
else
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
break;
|
|
}
|
|
if(nn > 3)
|
|
ai->geom = cAtomInfoTetrahedral;
|
|
}
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
|
|
/* now go through and make sure conjugated amines are planer */
|
|
changedFlag = true;
|
|
while(changedFlag) {
|
|
changedFlag = false;
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(ai->chemFlag) {
|
|
if(ai->protons == cAN_N)
|
|
if(ai->formalCharge < 1)
|
|
if(ai->geom == cAtomInfoTetrahedral) {
|
|
/* search for uncharged tetrahedral nitrogen */
|
|
for (auto const& neighbor : AtomNeighbors(I, a)) {
|
|
auto const* ai0 = I->AtomInfo.data() + neighbor.atm;
|
|
if((ai0->chemFlag) && (ai0->geom == cAtomInfoPlanar) &&
|
|
((ai0->protons == cAN_C) || (ai0->protons == cAN_N))) {
|
|
ai->geom = cAtomInfoPlanar; /* found probable delocalization */
|
|
if(ai->formalCharge == 0)
|
|
ai->valence = 3; /* just in case... */
|
|
changedFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
|
|
/* now go through and make sure conjugated anions are planer */
|
|
changedFlag = true;
|
|
while(changedFlag) {
|
|
changedFlag = false;
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(ai->chemFlag) {
|
|
if(ai->protons == cAN_O)
|
|
if(ai->formalCharge == -1)
|
|
if((ai->geom == cAtomInfoTetrahedral) || (ai->geom == cAtomInfoSingle)) {
|
|
/* search for anionic tetrahedral oxygen */
|
|
for (auto const& neighbor : AtomNeighbors(I, a)) {
|
|
auto const* ai0 = I->AtomInfo + neighbor.atm;
|
|
if((ai0->chemFlag) && (ai0->geom == cAtomInfoPlanar) &&
|
|
((ai0->protons == cAN_C) || (ai0->protons == cAN_N))) {
|
|
ai->geom = cAtomInfoPlanar; /* found probable delocalization */
|
|
changedFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeTransformSelection(ObjectMolecule * I, int state,
|
|
int sele, const float *matrix, int log,
|
|
const char *sname, int homogenous, int global)
|
|
{
|
|
/* called from "translate [5,5,5], objSele" */
|
|
/* if sele == -1, then the whole object state is transformed */
|
|
PyMOLGlobals *G = I->G;
|
|
int a, s;
|
|
int flag = false;
|
|
CoordSet *cs;
|
|
const AtomInfoType *ai;
|
|
int logging;
|
|
int all_states = false, inp_state;
|
|
int ok = true;
|
|
float homo_matrix[16], tmp_matrix[16];
|
|
const float* input_matrix = matrix;
|
|
|
|
inp_state = state;
|
|
if(state == -2)
|
|
state = ObjectGetCurrentState(I, false);
|
|
if(state < 0) {
|
|
all_states = true;
|
|
state = -1;
|
|
}
|
|
PRINTFD(G, FB_ObjectMolecule)
|
|
"ObjMolTransSele-Debug: state %d\n", state ENDFD;
|
|
while(1) {
|
|
if(all_states) {
|
|
state++;
|
|
if(state >= I->NCSet)
|
|
break;
|
|
}
|
|
if(state < I->NCSet) {
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
int use_matrices = SettingGet_i(G, I->Setting.get(),
|
|
nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
|
|
if(global &&!homogenous) { /* convert matrix to homogenous */
|
|
convertTTTfR44f(matrix, homo_matrix);
|
|
matrix = homo_matrix;
|
|
input_matrix = homo_matrix;
|
|
homogenous = true;
|
|
}
|
|
|
|
if(global &&((use_matrices && !cs->Matrix.empty()) || I->TTTFlag)) {
|
|
/* if input coordinates are in the global system,
|
|
they may need to be converted to local coordinates */
|
|
|
|
matrix = input_matrix;
|
|
|
|
/* global to object */
|
|
|
|
if(I->TTTFlag) {
|
|
float ttt[16];
|
|
if(matrix != tmp_matrix) {
|
|
copy44f(matrix, tmp_matrix);
|
|
}
|
|
convertTTTfR44f(I->TTT, ttt);
|
|
{
|
|
float ttt_inv[16];
|
|
invert_special44f44f(ttt, ttt_inv);
|
|
left_multiply44f44f(ttt_inv, tmp_matrix);
|
|
right_multiply44f44f(tmp_matrix, ttt);
|
|
}
|
|
matrix = tmp_matrix;
|
|
}
|
|
|
|
/* object to state */
|
|
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) {
|
|
double tmp_double[16], tmp_inv[16];
|
|
copy44f44d(matrix, tmp_double);
|
|
invert_special44d44d(cs->Matrix.data(), tmp_inv);
|
|
left_multiply44d44d(tmp_inv, tmp_double);
|
|
right_multiply44d44d(tmp_double, cs->Matrix.data());
|
|
copy44d44f(tmp_double, tmp_matrix);
|
|
matrix = tmp_matrix;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(sele >= 0) { /* transforming select atoms */
|
|
ai = I->AtomInfo;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(!(ai->protekted == cAtomProtected_explicit))
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
if(homogenous)
|
|
CoordSetTransformAtomR44f(cs, a, matrix);
|
|
else
|
|
CoordSetTransformAtomTTTf(cs, a, matrix);
|
|
flag = true;
|
|
}
|
|
ai++;
|
|
}
|
|
} else { /* transforming whole coordinate set */
|
|
if(!use_matrices) {
|
|
ai = I->AtomInfo;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(!(ai->protekted == cAtomProtected_explicit)) {
|
|
if(homogenous)
|
|
CoordSetTransformAtomR44f(cs, a, matrix);
|
|
else
|
|
CoordSetTransformAtomTTTf(cs, a, matrix);
|
|
}
|
|
ai++;
|
|
}
|
|
flag = true;
|
|
CoordSetRecordTxfApplied(cs, matrix, homogenous);
|
|
} else {
|
|
ObjectMoleculeTransformState44f(I, state, matrix, false, homogenous, false);
|
|
}
|
|
}
|
|
if(flag) {
|
|
cs->invalidateRep(cRepAll, cRepInvCoord);
|
|
ExecutiveUpdateCoordDepends(G, I);
|
|
}
|
|
}
|
|
}
|
|
if(!all_states)
|
|
break;
|
|
}
|
|
|
|
if(log) {
|
|
OrthoLineType line;
|
|
WordType sele_str = ",'";
|
|
logging = SettingGetGlobal_i(G, cSetting_logging);
|
|
if(sele >= 0) {
|
|
strcat(sele_str, sname);
|
|
}
|
|
strcat(sele_str, "'");
|
|
switch (logging) {
|
|
case cPLog_pml:
|
|
sprintf(line,
|
|
"_ cmd.transform_object('%s',[\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f,\\\n_ %15.9f,%15.9f,%15.9f,%15.9f\\\n_ ],%d,%d%s,%d)\n",
|
|
I->Name,
|
|
matrix[0], matrix[1], matrix[2], matrix[3],
|
|
matrix[4], matrix[5], matrix[6], matrix[7],
|
|
matrix[8], matrix[9], matrix[10], matrix[11],
|
|
matrix[12], matrix[13], matrix[14], matrix[15],
|
|
inp_state + 1, 0, sele_str, homogenous);
|
|
PLog(G, line, cPLog_no_flush);
|
|
break;
|
|
case cPLog_pym:
|
|
|
|
sprintf(line,
|
|
"cmd.transform_object('%s',[\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f,\n%15.9f,%15.9f,%15.9f,%15.9f\n],%d,%d%s,%d)\n",
|
|
I->Name,
|
|
matrix[0], matrix[1], matrix[2], matrix[3],
|
|
matrix[4], matrix[5], matrix[6], matrix[7],
|
|
matrix[8], matrix[9], matrix[10], matrix[11],
|
|
matrix[12], matrix[13], matrix[14], matrix[15],
|
|
inp_state + 1, 0, sele_str, homogenous);
|
|
PLog(G, line, cPLog_no_flush);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeGetAtomIndex(const ObjectMolecule* I, SelectorID_t sele)
|
|
{
|
|
if(sele < 0)
|
|
return (-1);
|
|
|
|
for (int a = 0; a < I->NAtom; a++) {
|
|
auto const s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(I->G, s, sele))
|
|
return (a);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Update all AtomInfoType.bonded
|
|
*
|
|
* Cost: O(NAtom + NBond)
|
|
*/
|
|
void ObjectMoleculeUpdateNonbonded(ObjectMolecule * I)
|
|
{
|
|
int a;
|
|
const BondType *b;
|
|
AtomInfoType *ai;
|
|
int nAtom = I->NAtom;
|
|
int nBond = I->NBond;
|
|
|
|
ai = I->AtomInfo.data();
|
|
|
|
for(a = 0; a < nAtom; a++)
|
|
(ai++)->bonded = false;
|
|
|
|
b = I->Bond;
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < nBond; a++) {
|
|
ai[b->index[0]].bonded = true;
|
|
ai[b->index[1]].bonded = true;
|
|
b++;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the raw ObjectMolecule::Neighbor storage structure, which is generated
|
|
* from the ObjectMolecule::Bond array. This structure is cached, to clear the
|
|
* cache, call ObjectMolecule::invalidate(level=cRepInvBonds).
|
|
*
|
|
* Direct usage of this array should be avoided, use the AtomNeighbors() class
|
|
* instead which is a higher-level and more readable interface to the same
|
|
* data.
|
|
*
|
|
* Changed in PyMOL 2.1.1: Ignore zero-order bonds (PYMOL-3025)
|
|
*
|
|
* @return Pointer to cached array, or nullptr if memory allocation failed
|
|
*
|
|
* @verbatim
|
|
0 list offset for atom 0 = n
|
|
1 list offset for atom 1 = n + m + 1
|
|
...
|
|
n-1 list offset for atom n-1
|
|
|
|
n count for atom 0
|
|
n+1 neighbor of atom 0
|
|
n+2 bond index
|
|
n+3 neighbor of atom 0
|
|
n+4 bond index
|
|
...
|
|
n+m -1 terminator for atom 0
|
|
|
|
n+m+1 count for atom 1
|
|
n+m+2 neighbor of atom 1
|
|
n+m+3 bond index
|
|
n+m+4 neighbor of atom 1
|
|
n+m+5 bond index
|
|
etc.
|
|
|
|
NOTE: all atoms have an offset and a terminator whether or not they have any bonds:
|
|
|
|
Here's an example of an ALA
|
|
[ offet for atom1, offset for atom2, ..., offset for atom9, \
|
|
{10, 16, 26, 32, 36, 46, 50, 54, 58, 62, \
|
|
(atom1) nbonds=2, neighbor index1=5, bond index=1, neighbor index2=1, bond index=0, null terminator=-1 \
|
|
2, 5, 1, 1, 0, -1,
|
|
(atom2) nbonds=4, nbr index1=6, bond id=4; nbr idx2=4, bond idx=3; nbr index3=2, bond idx=2; nbr index=0, bond index=0, null=-1 \
|
|
4, 6, 4, 4, 3, 2, 2, 0, 0, -1,
|
|
(atom3) ...
|
|
2, 3, 5, 1, 2, -1, 1, 2, 5, -1,
|
|
4, 9, 8, 8, 7, 7, 6, 1, 3, -1,
|
|
1, 0, 1, -1,
|
|
1, 1, 4, -1,
|
|
1, 4, 6, -1,
|
|
1, 4, 7, -1,
|
|
1, 4, 8, -1 }
|
|
@endverbatim
|
|
|
|
*/
|
|
int const* ObjectMolecule::getNeighborArray() const
|
|
{
|
|
auto const I = this;
|
|
|
|
/* If no neighbors have been calculated, fill in the table */
|
|
if(!I->Neighbor) {
|
|
|
|
/* Create/check the VLA */
|
|
auto const size = (I->NAtom * 3) + (I->NBond * 4);
|
|
|
|
const_cast<ObjectMolecule*>(this)->Neighbor.reset(new int[size]);
|
|
auto* const neighbor = this->Neighbor.get();
|
|
|
|
p_return_val_if_fail(neighbor, nullptr);
|
|
|
|
/* initialize; zero out neighbors */
|
|
std::fill_n(neighbor, I->NAtom, 0);
|
|
|
|
/* count neighbors for each atom */
|
|
const auto* bnd = I->Bond.data();
|
|
for(int b = 0; b < I->NBond; b++) {
|
|
if (bnd->order && !bnd->hasSymOp()) {
|
|
neighbor[bnd->index[0]]++;
|
|
neighbor[bnd->index[1]]++;
|
|
}
|
|
bnd++;
|
|
}
|
|
|
|
/* set up offsets and list terminators */
|
|
auto c = I->NAtom;
|
|
for (int a = 0; a < I->NAtom; a++) {
|
|
auto const d = neighbor[a]; /* get number of neighbors */
|
|
neighbor[c] = d; /* store neighbor count */
|
|
neighbor[a] = c + d + d + 1; /* set initial position to end of list, we'll fill backwards */
|
|
neighbor[neighbor[a]] = -1; /* store terminator */
|
|
c += d + d + 2;
|
|
}
|
|
|
|
/* now load neighbors in a sequential list for each atom (reverse order) */
|
|
bnd = I->Bond.data();
|
|
for(int b = 0; b < I->NBond; b++, ++bnd) {
|
|
auto const l0 = bnd->index[0];
|
|
auto const l1 = bnd->index[1];
|
|
|
|
if (!bnd->order || bnd->hasSymOp())
|
|
continue;
|
|
|
|
neighbor[l0]--;
|
|
neighbor[neighbor[l0]] = b; /* store bond indices (for I->Bond) */
|
|
neighbor[l0]--;
|
|
neighbor[neighbor[l0]] = l1; /* store neighbor references (I->AtomInfo, etc.) */
|
|
|
|
neighbor[l1]--;
|
|
neighbor[neighbor[l1]] = b; /* store bond indices (for I->Bond) */
|
|
neighbor[l1]--;
|
|
neighbor[neighbor[l1]] = l0; /* store neighbor references (I->AtomInfo, etc.) */
|
|
}
|
|
|
|
// adjust down to point to the count, not the first entry
|
|
for (int a = 0; a < I->NAtom; a++) {
|
|
if (neighbor[a] >= 0)
|
|
--neighbor[a];
|
|
}
|
|
}
|
|
return this->Neighbor.get();
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
#ifndef _PYMOL_NOPY
|
|
static void copy_and_trim_repr(char* dest, PyObject* obj)
|
|
{
|
|
PyObject* repr = PyObject_Repr(obj);
|
|
const char* src = PyString_AS_STRING(obj);
|
|
int srclen;
|
|
if (src[0] == '\'')
|
|
src++;
|
|
srclen = strlen(src);
|
|
if (src[srclen - 1] == '\'')
|
|
srclen -= 1;
|
|
strncpy(dest, src, srclen);
|
|
dest[srclen] = 0;
|
|
Py_DECREF(repr);
|
|
}
|
|
|
|
static CoordSet *ObjectMoleculeChemPyModel2CoordSet(PyMOLGlobals * G,
|
|
PyObject * model,
|
|
AtomInfoType ** atInfoPtr)
|
|
{
|
|
int nAtom, nBond;
|
|
int a, c;
|
|
float *coord = nullptr;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr, *ai;
|
|
float *f;
|
|
BondType *ii, *bond = nullptr;
|
|
int ok = true;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
int hetatm;
|
|
int ignore_ids;
|
|
PyObject *atomList = nullptr;
|
|
PyObject *bondList = nullptr;
|
|
PyObject *atom = nullptr;
|
|
PyObject *bnd = nullptr;
|
|
PyObject *index = nullptr;
|
|
PyObject *crd = nullptr;
|
|
PyObject *tmp = nullptr;
|
|
|
|
ignore_ids = !SettingGetGlobal_b(G, cSetting_preserve_chempy_ids);
|
|
|
|
nAtom = 0;
|
|
nBond = 0;
|
|
if(atInfoPtr)
|
|
atInfo = *atInfoPtr;
|
|
|
|
atomList = PyObject_GetAttrString(model, "atom");
|
|
if(atomList && PyList_Check(atomList))
|
|
nAtom = PyList_Size(atomList);
|
|
else
|
|
ok = ErrMessage(G, __func__, "can't get atom list");
|
|
|
|
if(ok) {
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
if(atInfo)
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
}
|
|
|
|
if(ok) {
|
|
|
|
f = coord;
|
|
for(a = 0; a < nAtom; a++) {
|
|
|
|
atom = PyList_GetItem(atomList, a);
|
|
if(!atom)
|
|
ok = ErrMessage(G, __func__, "can't get atom");
|
|
crd = PyObject_GetAttrString(atom, "coord");
|
|
if(!crd)
|
|
ok = ErrMessage(G, __func__, "can't get coordinates");
|
|
else {
|
|
for(c = 0; c < 3; c++) {
|
|
tmp = PySequence_GetItem(crd, c);
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, f++);
|
|
Py_XDECREF(tmp);
|
|
if(!ok) {
|
|
ErrMessage(G, __func__, "can't read coordinates");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Py_XDECREF(crd);
|
|
|
|
ai = atInfo + a;
|
|
ai->id = a; /* chempy models are zero-based */
|
|
if(!ignore_ids) {
|
|
if(ok) { /* get chempy atom id if extant */
|
|
if(PTruthCallStr(atom, "has", "id")) {
|
|
tmp = PyObject_GetAttrString(atom, "id");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &ai->id);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read atom identifier");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->id = -1;
|
|
}
|
|
}
|
|
}
|
|
ai->rank = a; /* ranks are always zero-based */
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "name");
|
|
if(tmp) {
|
|
WordType tmp_word;
|
|
|
|
ok = PConvPyObjectToStrMaxClean(tmp, tmp_word, sizeof(WordType) - 1);
|
|
AtomInfoCleanAtomName(tmp_word);
|
|
ai->name = LexIdx(G, tmp_word);
|
|
}
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read name");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
ai->textType = 0;
|
|
if(PTruthCallStr(atom, "has", "text_type")) {
|
|
tmp = PyObject_GetAttrString(atom, "text_type");
|
|
if(tmp) {
|
|
OrthoLineType temp;
|
|
ok = PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
|
|
ai->textType = LexIdx(G, temp);
|
|
}
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read text_type");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
ai->custom = 0;
|
|
if(PTruthCallStr(atom, "has", "custom")) {
|
|
tmp = PyObject_GetAttrString(atom, "custom");
|
|
if(tmp) {
|
|
OrthoLineType temp;
|
|
ok = PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
|
|
ai->custom = LexIdx(G, temp);
|
|
}
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read custom");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "vdw")) {
|
|
tmp = PyObject_GetAttrString(atom, "vdw");
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, &ai->vdw);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read vdw radius");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->vdw = 0.0f;
|
|
}
|
|
}
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "bohr")) {
|
|
tmp = PyObject_GetAttrString(atom, "bohr");
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, &ai->elec_radius);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read elec. radius");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->elec_radius = 0.0F;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "stereo")) {
|
|
tmp = PyObject_GetAttrString(atom, "stereo");
|
|
if(tmp) {
|
|
char tmp_char;
|
|
ok = PConvPyObjectToChar(tmp, &tmp_char);
|
|
ai->stereo = tmp_char;
|
|
}
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read stereo");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->stereo = 0;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "numeric_type")) {
|
|
tmp = PyObject_GetAttrString(atom, "numeric_type");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &ai->customType);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read numeric_type");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->customType = cAtomInfoNoType;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "formal_charge")) {
|
|
tmp = PyObject_GetAttrString(atom, "formal_charge");
|
|
if(tmp)
|
|
ok = PConvPyObjectToChar(tmp, (char *) &ai->formalCharge);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read formal_charge");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->formalCharge = 0;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "partial_charge")) {
|
|
tmp = PyObject_GetAttrString(atom, "partial_charge");
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, &ai->partialCharge);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read partial_charge");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->partialCharge = 0.0;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "flags")) {
|
|
tmp = PyObject_GetAttrString(atom, "flags");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, (int *) &ai->flags);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read flags");
|
|
Py_XDECREF(tmp);
|
|
} else {
|
|
ai->flags = 0;
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
char buf[8] = "";
|
|
tmp = PyObject_GetAttrString(atom, "resn");
|
|
if(tmp)
|
|
ok = PConvPyObjectToStrMaxClean(tmp, buf, sizeof(buf) - 1);
|
|
ai->resn = LexIdx(G, buf);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read resn");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "ins_code");
|
|
if(tmp) {
|
|
ResIdent tmp_ins_code;
|
|
ok = PConvPyObjectToStrMaxClean(tmp, tmp_ins_code, sizeof(ResIdent) - 1);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read ins_code");
|
|
else if(tmp_ins_code[0] != '?') {
|
|
ai->setInscode(tmp_ins_code[0]);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok) {
|
|
if(PTruthCallStr(atom, "has", "resi_number")) {
|
|
tmp = PyObject_GetAttrString(atom, "resi_number");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &ai->resv);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read resi_number");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
OrthoLineType temp = "";
|
|
tmp = PyObject_GetAttrString(atom, "segi");
|
|
if(tmp)
|
|
PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
|
|
ai->segi = LexIdx(G, temp);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read segi");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "b");
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, &ai->b);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read b value");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "u");
|
|
if(tmp) {
|
|
ok = PConvPyObjectToFloat(tmp, &ai->b);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read u value");
|
|
else
|
|
ai->b *= (8 * cPI * cPI); /* B-factor = 8 pi^2 U-factor */
|
|
} else {
|
|
assert(PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError));
|
|
PyErr_Clear();
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "u_aniso");
|
|
if(tmp) {
|
|
float u[6];
|
|
if(PConvPyListToFloatArrayInPlace(tmp, u, 6)) {
|
|
// only allocate if not all zero
|
|
if(u[0] || u[1] || u[2] || u[3] || u[4] || u[5])
|
|
std::copy_n(u, 6, ai->get_anisou());
|
|
}
|
|
Py_DECREF(tmp);
|
|
} else {
|
|
assert(PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError));
|
|
PyErr_Clear();
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "q");
|
|
if(tmp)
|
|
ok = PConvPyObjectToFloat(tmp, &ai->q);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read occupancy");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
OrthoLineType temp = "";
|
|
tmp = PyObject_GetAttrString(atom, "chain");
|
|
if(tmp)
|
|
PConvPyObjectToStrMaxClean(tmp, temp, sizeof(OrthoLineType) - 1);
|
|
ai->chain = LexIdx(G, temp);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read chain");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "hetatm");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &hetatm);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read hetatm");
|
|
else {
|
|
ai->hetatm = hetatm;
|
|
if(!PTruthCallStr(atom, "has", "flags")) {
|
|
if(ai->hetatm)
|
|
ai->flags = cAtomFlag_ignore;
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "alt");
|
|
if(tmp)
|
|
ok = PConvPyObjectToStrMaxClean(tmp, ai->alt, sizeof(Chain) - 1);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read alternate conformation");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "symbol");
|
|
if(tmp)
|
|
ok = PConvPyObjectToStrMaxClean(tmp, ai->elem, sizeof(ElemName) - 1);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read symbol");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok) {
|
|
tmp = PyObject_GetAttrString(atom, "ss");
|
|
if(tmp)
|
|
ok = PConvPyObjectToStrMaxClean(tmp, ai->ssType, sizeof(SSType) - 1);
|
|
if(!ok)
|
|
ErrMessage(G, __func__,
|
|
"can't read secondary structure");
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok && PyObject_HasAttrString(atom, "label")) {
|
|
tmp = PyObject_GetAttrString(atom, "label");
|
|
if(tmp) {
|
|
if (PyString_Check(tmp))
|
|
ai->label = LexIdx(G, PyString_AsString(tmp));
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok && PyObject_HasAttrString(atom, "sphere_scale")) {
|
|
tmp = PyObject_GetAttrString(atom, "sphere_scale");
|
|
if(tmp) {
|
|
float value;
|
|
if(PConvPyFloatToFloat(tmp, &value)) {
|
|
SettingSet(G, cSetting_sphere_scale, value, ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(atom, "cartoon_color")) {
|
|
tmp = PyObject_GetAttrString(atom, "cartoon_color");
|
|
if(tmp) {
|
|
int color_index;
|
|
ok = PConvPyObjectToInt(tmp, &color_index);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad cartoon color info");
|
|
else {
|
|
SettingSet(G, cSetting_cartoon_color, color_index, ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(atom, "cartoon_transparency")) {
|
|
tmp = PyObject_GetAttrString(atom, "cartoon_transparency");
|
|
if(tmp) {
|
|
float alpha_val;
|
|
ok = PConvPyObjectToFloat(tmp, &alpha_val);
|
|
if(!ok)
|
|
ErrMessage(G, __FUNCTION__, "bad alpha value");
|
|
else {
|
|
SettingSet(G, cSetting_cartoon_transparency, alpha_val, ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(atom, "cartoon_trgb")) {
|
|
tmp = PyObject_GetAttrString(atom, "cartoon_trgb");
|
|
if(tmp) {
|
|
unsigned int trgb;
|
|
ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad cartoon color info");
|
|
else {
|
|
char color_name[24];
|
|
sprintf(color_name, "0x%08x", trgb);
|
|
SettingSet(G, cSetting_cartoon_color, ColorGetIndex(G, color_name), ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(atom, "label_trgb")) {
|
|
tmp = PyObject_GetAttrString(atom, "label_trgb");
|
|
if(tmp) {
|
|
unsigned int trgb;
|
|
ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad label color info");
|
|
else {
|
|
char color_name[24];
|
|
sprintf(color_name, "0x%08x", trgb);
|
|
SettingSet(G, cSetting_label_color, ColorGetIndex(G, color_name), ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(atom, "ribbon_color")) {
|
|
tmp = PyObject_GetAttrString(atom, "ribbon_color");
|
|
if(tmp) {
|
|
int color_index;
|
|
ok = PConvPyObjectToInt(tmp, &color_index);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad ribbon color info");
|
|
else {
|
|
SettingSet(G, cSetting_ribbon_color, color_index, ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok && PyObject_HasAttrString(atom, "ribbon_trgb")) {
|
|
tmp = PyObject_GetAttrString(atom, "ribbon_trgb");
|
|
if(tmp) {
|
|
unsigned int trgb;
|
|
ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad cartoon color info");
|
|
else {
|
|
char color_name[24];
|
|
sprintf(color_name, "0x%08x", trgb);
|
|
SettingSet(G, cSetting_ribbon_color, ColorGetIndex(G, color_name), ai);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
atInfo[a].visRep = auto_show;
|
|
|
|
if(ok && PyObject_HasAttrString(atom, "visible")) {
|
|
unsigned int vis;
|
|
tmp = PyObject_GetAttrString(atom, "visible");
|
|
if(tmp) {
|
|
ok = PConvPyObjectToInt(tmp, (signed int *) &vis);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad visibility info");
|
|
else {
|
|
atInfo[a].visRep = vis;
|
|
|
|
if (!(ai->flags & cAtomFlag_class)) {
|
|
ai->flags |= cAtomFlag_inorganic; // suppress auto_show_classified
|
|
}
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
if(ok && atInfo) {
|
|
AtomInfoAssignParameters(G, ai);
|
|
AtomInfoAssignColors(G, ai);
|
|
}
|
|
|
|
if(ok && PyObject_HasAttrString(atom, "trgb")) {
|
|
/* were we given a Transparency-Red-Blue-Green value? */
|
|
unsigned int trgb;
|
|
tmp = PyObject_GetAttrString(atom, "trgb");
|
|
if(tmp) {
|
|
ok = PConvPyObjectToInt(tmp, (signed int *) &trgb);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "bad color info");
|
|
else {
|
|
char color_name[24];
|
|
sprintf(color_name, "0x%08x", trgb);
|
|
atInfo[a].color = ColorGetIndex(G, color_name);
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
|
|
if(!ok)
|
|
break;
|
|
|
|
assert(!PyErr_Occurred());
|
|
}
|
|
}
|
|
|
|
if(nAtom) {
|
|
bondList = PyObject_GetAttrString(model, "bond");
|
|
if(bondList && PyList_Check(bondList))
|
|
nBond = PyList_Size(bondList);
|
|
else
|
|
ok = ErrMessage(G, __func__, "can't get bond list");
|
|
|
|
if(ok) {
|
|
bond = VLACalloc(BondType, nBond);
|
|
ii = bond;
|
|
for(a = 0; a < nBond; a++) {
|
|
bnd = PyList_GetItem(bondList, a);
|
|
if(!bnd)
|
|
ok = ErrMessage(G, __func__, "can't get bond");
|
|
index = PyObject_GetAttrString(bnd, "index");
|
|
if(!index)
|
|
ok =
|
|
ErrMessage(G, __func__, "can't get bond indices");
|
|
else {
|
|
for(c = 0; c < 2; c++) {
|
|
tmp = PyList_GetItem(index, c);
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &ii->index[c]);
|
|
if(!ok) {
|
|
ErrMessage(G, __func__,
|
|
"can't read coordinates");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(ok) {
|
|
int order = 0;
|
|
tmp = PyObject_GetAttrString(bnd, "order");
|
|
if(tmp)
|
|
ok = PConvPyObjectToInt(tmp, &order);
|
|
if(!ok)
|
|
ErrMessage(G, __func__, "can't read bond order");
|
|
Py_XDECREF(tmp);
|
|
ii->order = order;
|
|
}
|
|
|
|
auto const set_symop = [&](const char* key) {
|
|
if (ok && PyObject_HasAttrString(bnd, key)) {
|
|
ok = false;
|
|
if (auto tmp = PyObject_GetAttrString(bnd, key)) {
|
|
if (auto const* code = PyUnicode_AsUTF8(tmp)) {
|
|
ii->symop_2.reset(code);
|
|
ok = true;
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
};
|
|
|
|
set_symop("symmetry_2");
|
|
|
|
if(ok && PyObject_HasAttrString(bnd, "stick_radius")) {
|
|
tmp = PyObject_GetAttrString(bnd, "stick_radius");
|
|
if(tmp) {
|
|
float value;
|
|
if(PConvPyFloatToFloat(tmp, &value)) {
|
|
SettingSet(G, cSetting_stick_radius, value, ii);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
if(ok && PyObject_HasAttrString(bnd, "valence")) {
|
|
tmp = PyObject_GetAttrString(bnd, "valence");
|
|
if(tmp) {
|
|
int value;
|
|
if(PConvPyIntToInt(tmp, &value)) {
|
|
SettingSet(G, cSetting_valence, value, ii);
|
|
}
|
|
}
|
|
Py_XDECREF(tmp);
|
|
}
|
|
|
|
Py_XDECREF(index);
|
|
ii++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Py_XDECREF(atomList);
|
|
Py_XDECREF(bondList);
|
|
|
|
if(ok) {
|
|
cset = CoordSetNew(G);
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->NTmpBond = nBond;
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
|
|
{
|
|
PyObject *propList = nullptr, *propName, *propValue, *propKeyValue;
|
|
OrthoLineType nameString;
|
|
int nProps = 0;
|
|
|
|
// need to check since fragments and other molecules use unpickle
|
|
// and pml files might not have molecule_properties
|
|
if (PyObject_HasAttrString(model, "molecule_properties")) {
|
|
propList = PyObject_GetAttrString(model, "molecule_properties");
|
|
if (propList && PyList_Check(propList))
|
|
nProps = PyList_Size(propList);
|
|
else
|
|
ok = ErrMessage(G, __func__, "can't get molecule properties");
|
|
}
|
|
if (nProps) {
|
|
for (a = 0; a < nProps; a++) {
|
|
propKeyValue = PyList_GetItem(propList, a);
|
|
propName = PyList_GetItem(propKeyValue, 0);
|
|
propValue = PyList_GetItem(propKeyValue, 1);
|
|
copy_and_trim_repr(nameString, propName);
|
|
PropertySet(G, cset, nameString, propValue);
|
|
}
|
|
}
|
|
Py_XDECREF(propList);
|
|
}
|
|
} else {
|
|
VLAFreeP(bond);
|
|
VLAFreeP(coord);
|
|
}
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
|
|
if(PyErr_Occurred())
|
|
PyErr_Print();
|
|
return (cset);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*========================================================================*/
|
|
ObjectMolecule *ObjectMoleculeLoadChemPyModel(PyMOLGlobals * G,
|
|
ObjectMolecule * I,
|
|
PyObject * model, int frame, int discrete)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return nullptr;
|
|
#else
|
|
CoordSet *cset = nullptr;
|
|
auto atInfo = pymol::vla<AtomInfoType>(10);
|
|
int ok = true;
|
|
int isNew = true;
|
|
unsigned int nAtom = 0;
|
|
int fractional = false;
|
|
int connect_mode = -1;
|
|
int auto_bond = false;
|
|
PyObject *tmp, *mol;
|
|
|
|
if(!I)
|
|
isNew = true;
|
|
else
|
|
isNew = false;
|
|
|
|
if(ok) {
|
|
|
|
if(isNew) {
|
|
I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
|
|
std::swap(atInfo, I->AtomInfo);
|
|
isNew = true;
|
|
} else {
|
|
if (discrete > -1)
|
|
ObjectMoleculeSetDiscrete(G, I, discrete);
|
|
}
|
|
|
|
if(isNew) {
|
|
I->Color = AtomInfoUpdateAutoColor(G);
|
|
}
|
|
cset = ObjectMoleculeChemPyModel2CoordSet(G, model, &atInfo);
|
|
|
|
if(!cset)
|
|
ok = false;
|
|
else {
|
|
mol = PyObject_GetAttrString(model, "molecule");
|
|
if(mol) {
|
|
if(PyObject_HasAttrString(mol, "title")) {
|
|
tmp = PyObject_GetAttrString(mol, "title");
|
|
if(tmp) {
|
|
UtilNCopy(cset->Name, PyString_AsString(tmp), sizeof(WordType));
|
|
Py_DECREF(tmp);
|
|
if(!strcmp(cset->Name, "untitled")) /* ignore untitled */
|
|
cset->Name[0] = 0;
|
|
}
|
|
}
|
|
Py_DECREF(mol);
|
|
} else if (PyErr_Occurred() == PyExc_AttributeError) {
|
|
PyErr_Clear();
|
|
}
|
|
if(PyObject_HasAttrString(model, "spheroid") &&
|
|
PyObject_HasAttrString(model, "spheroid_normals")) {
|
|
tmp = PyObject_GetAttrString(model, "spheroid");
|
|
if(tmp) {
|
|
PConvFromPyObject(G, tmp, cset->Spheroid);
|
|
Py_DECREF(tmp);
|
|
}
|
|
tmp = PyObject_GetAttrString(model, "spheroid_normals");
|
|
if(tmp) {
|
|
PConvFromPyObject(G, tmp, cset->SpheroidNormal);
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
if(PyObject_HasAttrString(model, "spacegroup") &&
|
|
PyObject_HasAttrString(model, "cell")) {
|
|
CSymmetry *symmetry = new CSymmetry(G);
|
|
if(symmetry) {
|
|
tmp = PyObject_GetAttrString(model, "spacegroup");
|
|
if(tmp) {
|
|
const char *tmp_str = nullptr;
|
|
if(PConvPyStrToStrPtr(tmp, &tmp_str)) {
|
|
symmetry->setSpaceGroup(tmp_str);
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
tmp = PyObject_GetAttrString(model, "cell");
|
|
if(tmp) {
|
|
float cell[6];
|
|
if(PConvPyListToFloatArrayInPlace(tmp, cell, 6)) {
|
|
symmetry->Crystal.setDims(cell);
|
|
symmetry->Crystal.setAngles(cell + 3);
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
cset->Symmetry = std::unique_ptr<CSymmetry>(symmetry);
|
|
}
|
|
}
|
|
if(PyObject_HasAttrString(model, "fractional")) {
|
|
tmp = PyObject_GetAttrString(model, "fractional");
|
|
if(tmp) {
|
|
int tmp_int = 0;
|
|
if(PConvPyIntToInt(tmp, &tmp_int)) {
|
|
fractional = tmp_int;
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
if(PyObject_HasAttrString(model, "connect_mode")) {
|
|
tmp = PyObject_GetAttrString(model, "connect_mode");
|
|
if(tmp) {
|
|
int tmp_int = 0;
|
|
if(PConvPyIntToInt(tmp, &tmp_int)) {
|
|
auto_bond = true;
|
|
connect_mode = tmp_int;
|
|
}
|
|
Py_DECREF(tmp);
|
|
}
|
|
}
|
|
nAtom = cset->NIndex;
|
|
}
|
|
}
|
|
/* include coordinate set */
|
|
if(ok) {
|
|
if(frame < 0)
|
|
frame = I->NCSet;
|
|
|
|
if(I->DiscreteFlag && atInfo) {
|
|
unsigned int a;
|
|
int fp1 = frame + 1;
|
|
AtomInfoType *ai = atInfo.data();
|
|
for(a = 0; a < nAtom; a++) {
|
|
(ai++)->discrete_state = fp1;
|
|
}
|
|
}
|
|
|
|
cset->Obj = I;
|
|
cset->enumIndices();
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
if(isNew) {
|
|
std::swap(I->AtomInfo, atInfo);
|
|
} else {
|
|
ObjectMoleculeMerge(I, std::move(atInfo), cset, false, cAIC_AllMask, true);
|
|
}
|
|
if(isNew)
|
|
I->NAtom = nAtom;
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
delete I->CSet[frame];
|
|
I->CSet[frame] = cset;
|
|
if (fractional && cset->Symmetry) {
|
|
CoordSetFracToReal(cset, &cset->Symmetry->Crystal);
|
|
}
|
|
if(ok && isNew)
|
|
ok &= ObjectMoleculeConnect(I, cset, auto_bond, connect_mode);
|
|
if(cset->Symmetry && (!I->Symmetry)) {
|
|
I->Symmetry.reset(new CSymmetry(*cset->Symmetry));
|
|
}
|
|
SceneCountFrames(G);
|
|
if (ok)
|
|
ok &= ObjectMoleculeExtendIndices(I, frame);
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(I);
|
|
if (ok){
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
ObjectMoleculeUpdateNonbonded(I);
|
|
}
|
|
}
|
|
return (I);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Update coordinates of an exisiting coordset or insert/append a new
|
|
* coordset.
|
|
*
|
|
* coords: flat coordinate array of length NIndex * 3
|
|
* coords_len: must be NIndex * 3
|
|
* frame: coordset index
|
|
*/
|
|
ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, ObjectMolecule * I,
|
|
const float * coords, int coords_len, int frame)
|
|
{
|
|
CoordSet *cset = nullptr;
|
|
int a;
|
|
bool is_new = false;
|
|
|
|
if(frame < 0) {
|
|
frame = I->NCSet;
|
|
} else if (frame < I->NCSet) {
|
|
cset = I->CSet[frame];
|
|
}
|
|
|
|
if (!cset) {
|
|
// template coordinate set, if available
|
|
cset = I->CSTmpl;
|
|
|
|
// find any coordinate set
|
|
for(a = 0; !cset && a < I->NCSet; ++a)
|
|
cset = I->CSet[a];
|
|
ok_assert(1, cset);
|
|
cset = CoordSetCopy(cset);
|
|
is_new = true;
|
|
}
|
|
|
|
// check atom count
|
|
if(coords_len != cset->NIndex * 3) {
|
|
ErrMessage(G, "LoadCoords", "atom count mismatch");
|
|
ok_raise(1);
|
|
}
|
|
|
|
// copy coordinates
|
|
for(a = 0; a < coords_len; ++a) {
|
|
cset->Coord[a] = coords[a];
|
|
}
|
|
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
|
|
// include coordinate set
|
|
if (is_new) {
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
I->CSet[frame] = cset;
|
|
SceneCountFrames(G);
|
|
}
|
|
|
|
// success
|
|
return (I);
|
|
|
|
// error handling
|
|
ok_except1:
|
|
if(is_new && cset)
|
|
delete cset;
|
|
ErrMessage(G, "LoadCoords", "failed");
|
|
return nullptr;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/* see above... but look up object by name
|
|
*/
|
|
ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, const char * name,
|
|
const float * coords, int coords_len, int frame)
|
|
{
|
|
pymol::CObject * cobj = ExecutiveFindObjectByName(G, name);
|
|
|
|
if(!cobj || cobj->type != cObjectMolecule) {
|
|
ErrMessage(G, "LoadCoords", "named object molecule not found.");
|
|
return nullptr;
|
|
}
|
|
|
|
return ObjectMoleculeLoadCoords(G, (ObjectMolecule *) cobj,
|
|
coords, coords_len, frame);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/* see above... but take coordinates from Python list
|
|
*
|
|
* coords: 2d Python float sequence with shape (NIndex, 3)
|
|
*/
|
|
ObjectMolecule *ObjectMoleculeLoadCoords(PyMOLGlobals * G, ObjectMolecule * I,
|
|
PyObject * coords, int frame)
|
|
{
|
|
#ifdef _PYMOL_NOPY
|
|
return nullptr;
|
|
#else
|
|
CoordSet *cset = nullptr;
|
|
int a, b, l;
|
|
PyObject *v, *w;
|
|
float *f;
|
|
bool is_new = false;
|
|
|
|
if(!PySequence_Check(coords)) {
|
|
ErrMessage(G, "LoadCoords", "passed argument is not a sequence");
|
|
ok_raise(1);
|
|
}
|
|
|
|
if(frame < 0) {
|
|
frame = I->NCSet;
|
|
} else if (frame < I->NCSet) {
|
|
cset = I->CSet[frame];
|
|
}
|
|
|
|
if (!cset) {
|
|
// template coordinate set, if available
|
|
cset = I->CSTmpl;
|
|
|
|
// find any coordinate set
|
|
for(a = 0; !cset && a < I->NCSet; ++a)
|
|
cset = I->CSet[a];
|
|
ok_assert(1, cset);
|
|
cset = CoordSetCopy(cset);
|
|
is_new = true;
|
|
}
|
|
|
|
// check atom count
|
|
l = PySequence_Size(coords);
|
|
if(l != cset->NIndex) {
|
|
ErrMessage(G, "LoadCoords", "atom count mismatch");
|
|
ok_raise(1);
|
|
}
|
|
|
|
// copy coordinates
|
|
f = cset->Coord.data();
|
|
for(a = 0; a < l; a++) {
|
|
v = PySequence_ITEM(coords, a);
|
|
|
|
for(b = 0; b < 3; b++) {
|
|
if(!(w = PySequence_GetItem(v, b)))
|
|
break;
|
|
|
|
f[a * 3 + b] = (float) PyFloat_AsDouble(w);
|
|
Py_DECREF(w);
|
|
}
|
|
|
|
Py_DECREF(v);
|
|
ok_assert(2, !PyErr_Occurred());
|
|
}
|
|
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
|
|
// include coordinate set
|
|
if (is_new) {
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
I->CSet[frame] = cset;
|
|
SceneCountFrames(G);
|
|
}
|
|
|
|
// success
|
|
return (I);
|
|
|
|
// error handling
|
|
ok_except2:
|
|
PyErr_Print();
|
|
ok_except1:
|
|
if(is_new && cset)
|
|
delete cset;
|
|
ErrMessage(G, "LoadCoords", "failed");
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeExtendIndices(ObjectMolecule * I, int state)
|
|
{
|
|
int a;
|
|
CoordSet *cs;
|
|
|
|
if(I->DiscreteFlag && (state >= 0)) {
|
|
/* if object is discrete, then we don't need to extend each state,
|
|
just the current one (which updates object DiscreteAtmToIdx) */
|
|
cs = I->CSTmpl;
|
|
ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
|
|
if(state < I->NCSet) {
|
|
cs = I->CSet[state];
|
|
ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
|
|
}
|
|
} else { /* do all states */
|
|
for(a = -1; a < I->NCSet; a++) {
|
|
cs = (a < 0) ? I->CSTmpl : I->CSet[a];
|
|
ok_assert(1, (!cs) || cs->extendIndices(I->NAtom));
|
|
}
|
|
}
|
|
return true;
|
|
ok_except1:
|
|
return false;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
static char* sdl_mol_prop_name(const char* prop, short* isprop)
|
|
{
|
|
int i, j, len;
|
|
char* name = nullptr;
|
|
|
|
/* for now, need to handle all special characters */
|
|
if (strchr(prop, '/') || strchr(prop, '\\')) {
|
|
if (isprop)
|
|
*isprop = false;
|
|
name = (char*) strdup(prop);
|
|
return name;
|
|
}
|
|
/* Rule 1: If <prop>, return substring between angle brackets */
|
|
for (i = 0; i < strlen(prop); i++) {
|
|
if (prop[i] == '<') {
|
|
for (len = 0, j = i + 1; j < strlen(prop); j++) {
|
|
if (prop[j] == '>') {
|
|
len = j - i - 1; /* change to j-i+1 to keep <> */
|
|
name = (char*) malloc(len + 1);
|
|
strncpy(name, prop + i + 1, len); /* change to prop+i to keep <> */
|
|
name[len] = '\0';
|
|
if (isprop)
|
|
*isprop = true;
|
|
return name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rule 2: Else if prop contains DT\d+, return that. */
|
|
for (i = 0; i < strlen(prop); i++) {
|
|
if (prop[i] == 'D' && prop[i + 1] == 'T' && isdigit(prop[i + 2])) {
|
|
for (len = 0, j = i + 3; j < strlen(prop); j++) {
|
|
if (!isdigit(prop[j])) {
|
|
len = j - i;
|
|
name = (char*) malloc(len + 1);
|
|
strncpy(name, prop + i, len);
|
|
name[len] = '\0';
|
|
if (isprop)
|
|
*isprop = true;
|
|
return name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rule 3: Else, strip [ribs]_*_ prefix itself. */
|
|
for (i = 0; i < strlen(prop); i++) {
|
|
if (prop[i] == '_') {
|
|
for (j = i + 1; j < strlen(prop); j++) {
|
|
if (prop[j] == '_') {
|
|
name = (char*) malloc(strlen(prop + j + 1) + 1);
|
|
strcpy(name, prop + j + 1);
|
|
if (isprop)
|
|
*isprop = true;
|
|
return name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rule 4: else return prop itself */
|
|
if (isprop)
|
|
*isprop = false;
|
|
name = (char*) strdup(prop);
|
|
return name;
|
|
}
|
|
|
|
static short is_sdl_mol_prop_name(const char* prop)
|
|
{
|
|
short isprop;
|
|
char* cctmp = pymol::malloc<char>(strlen(prop) + 1);
|
|
strcpy(cctmp, prop);
|
|
free(sdl_mol_prop_name(cctmp, &isprop));
|
|
FreeP(cctmp);
|
|
return isprop;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
static CoordSet *ObjectMoleculeMOLStr2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr, const char **restart
|
|
, short loadpropertiesall, OVLexicon *loadproplex
|
|
)
|
|
{
|
|
const char *p;
|
|
int nAtom, nBond;
|
|
int a, cnt, atm, chg;
|
|
float *coord = nullptr;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr;
|
|
char cc[MAXLINELEN], cc1[MAXLINELEN], resn[MAXLINELEN] = "UNK";
|
|
float *f;
|
|
BondType *ii;
|
|
BondType *bond = nullptr;
|
|
int ok = true;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
WordType nameTmp;
|
|
|
|
p = buffer;
|
|
nAtom = 0;
|
|
if(atInfoPtr)
|
|
atInfo = *atInfoPtr;
|
|
|
|
/* p=ParseWordCopy(nameTmp,p,sizeof(WordType)-1); */
|
|
p = ParseNCopy(nameTmp, p, sizeof(WordType) - 1);
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" ObjMolMOLStr2CoordSet: title '%s'\n", nameTmp ENDFB(G)
|
|
p = nextline(p);
|
|
p = nextline(p);
|
|
p = nextline(p);
|
|
|
|
if(ok) {
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &nAtom) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad atom count");
|
|
}
|
|
|
|
if(ok) {
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &nBond) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad bond count");
|
|
}
|
|
|
|
if(ok) {
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
if(atInfo)
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
}
|
|
|
|
p = nextline(p);
|
|
|
|
/* read coordinates and atom names */
|
|
|
|
if(ok) {
|
|
f = coord;
|
|
for(a = 0; a < nAtom; a++) {
|
|
if(ok) {
|
|
p = ncopy(cc, p, 10);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 10);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 10);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = nskip(p, 1);
|
|
p = ntrim(cc, p, 3);
|
|
strncpy(atInfo[a].elem, cc, cElemNameLen);
|
|
atInfo[a].name = LexIdx(G, cc);
|
|
atInfo[a].visRep = auto_show;
|
|
}
|
|
if(ok) {
|
|
int tmp_int;
|
|
p = nskip(p, 2);
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%hhi", &atInfo[a].formalCharge) == 1) {
|
|
if(atInfo[a].formalCharge) {
|
|
atInfo[a].formalCharge = 4 - atInfo[a].formalCharge;
|
|
}
|
|
}
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &tmp_int) != 1)
|
|
atInfo[a].stereo = 0;
|
|
else
|
|
atInfo[a].stereo = tmp_int;
|
|
}
|
|
if(ok && atInfo) {
|
|
atInfo[a].id = a + 1;
|
|
atInfo[a].rank = a;
|
|
atInfo[a].resn = LexIdx(G, resn);
|
|
atInfo[a].hetatm = true;
|
|
AtomInfoAssignParameters(G, atInfo + a);
|
|
AtomInfoAssignColors(G, atInfo + a);
|
|
atInfo[a].alt[0] = 0;
|
|
atInfo[a].segi = 0;
|
|
atInfo[a].inscode = 0;
|
|
}
|
|
p = nextline(p);
|
|
if(!ok)
|
|
break;
|
|
}
|
|
}
|
|
if(ok) {
|
|
bond = VLACalloc(BondType, nBond);
|
|
ii = bond;
|
|
for(a = 0; a < nBond; a++) {
|
|
if(ok) {
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &ii->index[0]) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad bond atom");
|
|
}
|
|
|
|
if(ok) {
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &ii->index[1]) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad bond atom");
|
|
}
|
|
|
|
if(ok) {
|
|
int order = 0;
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &order) != 1)
|
|
ok = ErrMessage(G, "ReadMOLFile", "bad bond order");
|
|
ii->order = order;
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 3);
|
|
// stereo
|
|
}
|
|
ii++;
|
|
if(!ok)
|
|
break;
|
|
p = nextline(p);
|
|
}
|
|
ii = bond;
|
|
for(a = 0; a < nBond; a++) {
|
|
ii->index[0]--; /* adjust bond indexs down one */
|
|
ii->index[1]--;
|
|
ii++;
|
|
}
|
|
}
|
|
while(*p) { /* read M CHG records */
|
|
auto p_line = p;
|
|
p = ncopy(cc, p, 6);
|
|
if(cc[5] == ' ')
|
|
cc[5] = 0;
|
|
if((!strcmp(cc, "M END")) || (!strcmp(cc, "M END"))) {
|
|
/* denotes end of MOL block */
|
|
break;
|
|
}
|
|
if((!strcmp(cc, "M CHG")) || (!strcmp(cc, "M CHG"))) {
|
|
p = ncopy(cc, p, 3);
|
|
if(sscanf(cc, "%d", &cnt) == 1) {
|
|
while(cnt--) {
|
|
p = ncopy(cc, p, 4);
|
|
p = ncopy(cc1, p, 4);
|
|
if(!((*cc) || (*cc1)))
|
|
break;
|
|
if((sscanf(cc, "%d", &atm) == 1) && (sscanf(cc1, "%d", &chg) == 1)) {
|
|
atm--;
|
|
if((atm >= 0) && (atm < nAtom))
|
|
atInfo[atm].formalCharge = chg;
|
|
}
|
|
}
|
|
}
|
|
} else if (!strcmp(cc, "M V30")) {
|
|
p = MOLV3000Parse(G, p_line, atInfo, bond, coord, nAtom, nBond);
|
|
if (!p) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
p = nextline(p);
|
|
}
|
|
if(ok) {
|
|
(*restart) = p;
|
|
cset = CoordSetNew(G);
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->NTmpBond = nBond;
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
strcpy(cset->Name, nameTmp);
|
|
p = nextline(p);
|
|
// Need to read in the properties
|
|
{
|
|
OVreturn_word ovresult;
|
|
const char* ptmp = p;
|
|
const char* np = ptmp;
|
|
char *cc = VLACalloc(char, 1024), *cctmp = VLACalloc(char, 1024);
|
|
int ccp = 0;
|
|
char* prop_name = nullptr;
|
|
short is_prop_name, line = 0;
|
|
int nchs;
|
|
cc[0] = 0;
|
|
ncopy(cctmp, np, 4);
|
|
cctmp[4] = 0;
|
|
while (strcmp(cctmp, "$$$$")) {
|
|
ptmp = np;
|
|
np = nextline(np);
|
|
if (np == ptmp)
|
|
break;
|
|
nchs = (np - ptmp - 1);
|
|
if (nchs) {
|
|
VLACheck(cctmp, char, nchs + 1);
|
|
ncopy(cctmp, ptmp, nchs);
|
|
cctmp[nchs] = 0;
|
|
is_prop_name = is_sdl_mol_prop_name(cctmp);
|
|
if (is_prop_name) {
|
|
// is_prop_name
|
|
if (prop_name) {
|
|
short load = loadpropertiesall;
|
|
if (!load && loadproplex) {
|
|
ovresult = OVLexicon_BorrowFromCString(loadproplex, prop_name);
|
|
load = (ovresult.status == OVstatus_SUCCESS);
|
|
}
|
|
if (load) {
|
|
PropertyCheckUniqueID(G, cset);
|
|
PropertySetromString(G, cset->prop_id, prop_name, cc);
|
|
ccp = 0;
|
|
cc[0] = 0;
|
|
line = 0;
|
|
}
|
|
free(prop_name);
|
|
}
|
|
prop_name = sdl_mol_prop_name(cctmp, nullptr);
|
|
} else {
|
|
if (prop_name) {
|
|
int sz = strlen(cctmp);
|
|
if (line == 1) {
|
|
VLACheck(cc, char, ccp + 2);
|
|
cc[ccp++] = '\n';
|
|
}
|
|
VLACheck(cc, char, ccp + sz + 2);
|
|
ncopy(&cc[ccp], cctmp, sz);
|
|
ccp += sz;
|
|
VLACheck(cc, char, ccp + 2);
|
|
if (line) {
|
|
cc[ccp++] = '\n';
|
|
}
|
|
cc[ccp] = 0;
|
|
line++;
|
|
}
|
|
}
|
|
}
|
|
ncopy(cctmp, np, 4);
|
|
}
|
|
if (prop_name) {
|
|
short load = loadpropertiesall;
|
|
if (!load && loadproplex) {
|
|
ovresult = OVLexicon_BorrowFromCString(loadproplex, prop_name);
|
|
load = (ovresult.status == OVstatus_SUCCESS);
|
|
}
|
|
if (load) {
|
|
PropertyCheckUniqueID(G, cset);
|
|
PropertySetromString(G, cset->prop_id, prop_name, cc);
|
|
ccp = 0;
|
|
cc[0] = 0;
|
|
line = 0;
|
|
}
|
|
free(prop_name);
|
|
}
|
|
VLAFreeP(cc);
|
|
VLAFreeP(cctmp);
|
|
}
|
|
} else {
|
|
VLAFreeP(bond);
|
|
VLAFreeP(coord);
|
|
(*restart) = nullptr;
|
|
}
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
return (cset);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
static CoordSet *ObjectMoleculeSDF2Str2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr,
|
|
const char **next_mol
|
|
, short loadpropertiesall, OVLexicon *loadproplex
|
|
)
|
|
{
|
|
char cc[MAXLINELEN];
|
|
const char *p;
|
|
CoordSet *result = nullptr;
|
|
result = ObjectMoleculeMOLStr2CoordSet(G, buffer, atInfoPtr, next_mol
|
|
, loadpropertiesall, loadproplex
|
|
);
|
|
p = *next_mol;
|
|
if(p) {
|
|
while(*p) { /* we simply need to skip until we've read past the end of the SDF record */
|
|
p = ncopy(cc, p, 4);
|
|
p = nextline(p);
|
|
if(!strcmp(cc, "$$$$")) { /* SDF record separator... */
|
|
break;
|
|
}
|
|
}
|
|
if(!*p)
|
|
p = nullptr;
|
|
}
|
|
*next_mol = p;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the sum of bond orders for every atom. For this purpose, aromatic
|
|
* bonds will be considered either single or double bond, based on
|
|
* the entire system (heuristic method, room for improvement).
|
|
*/
|
|
static
|
|
std::vector<signed char> get_bond_order_sums(ObjectMolecule * obj) {
|
|
std::vector<signed char> valences(obj->NAtom);
|
|
std::vector<signed char> freevalences(obj->NAtom);
|
|
std::vector<signed char> orders(obj->NBond);
|
|
|
|
// bond order sums as if all aromatic bonds were single bonds
|
|
for (int b = 0; b < obj->NBond; ++b) {
|
|
auto bond = obj->Bond + b;
|
|
auto order = bond->order;
|
|
orders[b] = order;
|
|
if (order > 3)
|
|
order = 1;
|
|
valences[bond->index[0]] += order;
|
|
valences[bond->index[1]] += order;
|
|
}
|
|
|
|
// determine free valences as if all aromatic bonds were single bonds
|
|
for (int atm = 0; atm < obj->NAtom; ++atm) {
|
|
int tmp = 0;
|
|
switch (obj->AtomInfo[atm].protons) {
|
|
case cAN_C: tmp = 4; break;
|
|
case cAN_N: case cAN_P: tmp = 5; break;
|
|
case cAN_O: case cAN_S: tmp = 6; break;
|
|
}
|
|
if (tmp) {
|
|
tmp -= valences[atm];
|
|
if (tmp) {
|
|
freevalences[atm] = (tmp - 1) % 2 + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// (This is a heuristic, gets most of the ZINC dataset correct)
|
|
// Do two passes over all aromatic bonds.
|
|
// 1st pass: assign double bonds between atoms with `freevalence == 1`
|
|
// 2nd pass: assign double bonds between atoms with `freevalence >= 1`
|
|
for (int secondpass = 0; secondpass < 2; ++secondpass) {
|
|
for (int b = 0; b < obj->NBond; ++b) {
|
|
if (orders[b] == 4) {
|
|
auto atm1 = obj->Bond[b].index[0];
|
|
auto atm2 = obj->Bond[b].index[1];
|
|
if (secondpass ?
|
|
(freevalences[atm1] >= 1 && freevalences[atm2] >= 1 ) :
|
|
(freevalences[atm1] == 1 && freevalences[atm2] == 1)) {
|
|
freevalences[atm1] = 0;
|
|
freevalences[atm2] = 0;
|
|
valences[atm1] += 1;
|
|
valences[atm2] += 1;
|
|
orders[b] = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return valences;
|
|
}
|
|
|
|
/**
|
|
* For each atom, set `formalCharge` based on mol2 `textType`
|
|
*
|
|
* See also: getMOL2Type() in layer2/Mol2Typing.cpp
|
|
*/
|
|
static void ObjectMoleculeMOL2SetFormalCharges(PyMOLGlobals *G, ObjectMolecule *obj){
|
|
|
|
// check if structure has explicit hydrogens
|
|
bool has_hydrogens = false;
|
|
for (int at = 0; at < obj->NAtom; ++at) {
|
|
auto ai = obj->AtomInfo + at;
|
|
if (ai->isHydrogen()) {
|
|
has_hydrogens = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!has_hydrogens) {
|
|
// could eventually do PDB nomenclature charge assignment here...
|
|
return;
|
|
}
|
|
|
|
// Unfortunately, aromatic bonds (type 4) can't be used to determine
|
|
// formal charges. The following uses a heuristic to guess bond orders
|
|
// for aromatic bonds.
|
|
auto valences = get_bond_order_sums(obj);
|
|
|
|
// (period currently incompatible with G->lex_const)
|
|
auto lex_N_4 = LexBorrow(G, "N.4");
|
|
|
|
for (int at = 0; at < obj->NAtom; ++at) {
|
|
int fcharge = 0;
|
|
auto ai = obj->AtomInfo + at;
|
|
|
|
if (ai->protons == cAN_N) {
|
|
if (ai->textType == lex_N_4) {
|
|
fcharge = 1;
|
|
} else if (valences[at] == 2) {
|
|
fcharge = -1;
|
|
} else if (valences[at] == 4) {
|
|
fcharge = 1;
|
|
}
|
|
} else if (ai->protons == cAN_O) {
|
|
if (valences[at] == 1) {
|
|
fcharge = -1;
|
|
}
|
|
}
|
|
|
|
ai->formalCharge = fcharge;
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
static CoordSet *ObjectMoleculeMOL2Str2CoordSet(PyMOLGlobals * G,
|
|
const char *buffer,
|
|
AtomInfoType ** atInfoPtr,
|
|
const char **next_mol)
|
|
{
|
|
const char *p;
|
|
int nAtom, nBond, nSubst, nFeat, nSet;
|
|
int a;
|
|
float *coord = nullptr;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr;
|
|
char cc[MAXLINELEN];
|
|
float *f;
|
|
const char *last_p;
|
|
BondType *ii;
|
|
BondType *bond = nullptr;
|
|
int ok = true;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
int have_molecule = false;
|
|
WordType nameTmp;
|
|
bool has_non_hetatm = false;
|
|
|
|
// Maestro writes <0> as subst_name for waters
|
|
const lexidx_t empty_subst_name = LexIdx(G, "<0>");
|
|
|
|
p = buffer;
|
|
nAtom = 0;
|
|
if(atInfoPtr)
|
|
atInfo = *atInfoPtr;
|
|
|
|
while((*p) && ok) {
|
|
|
|
last_p = p;
|
|
/* top level -- looking for an RTI, assuming p points to the beginning of a line */
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(cc[0] == '@') {
|
|
if(WordMatchExact(G, cc, "@<TRIPOS>MOLECULE", true)
|
|
|| WordMatchExact(G, cc, "@MOLECULE", true)) {
|
|
if(have_molecule) {
|
|
*next_mol = last_p;
|
|
break; /* next record of multi-mol2 */
|
|
}
|
|
p = ParseNextLine(p);
|
|
p = ParseNTrim(nameTmp, p, sizeof(WordType) - 1); /* get mol name */
|
|
p = ParseNextLine(p);
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &nAtom) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad atom count");
|
|
else {
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
if(atInfo)
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &nBond) != 1) {
|
|
nBond = 0;
|
|
}
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &nSubst) != 1) {
|
|
nSubst = 0;
|
|
}
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &nFeat) != 1) {
|
|
nFeat = 0;
|
|
}
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &nSet) != 1) {
|
|
nSet = 0;
|
|
}
|
|
|
|
}
|
|
p = ParseNextLine(p);
|
|
p = ParseNextLine(p);
|
|
have_molecule = true;
|
|
} else if(WordMatchExact(G, cc, "@<TRIPOS>ATOM", true)
|
|
|| WordMatchExact(G, cc, "@ATOM", true)) {
|
|
if(!have_molecule) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "@ATOM before @MOLECULE!");
|
|
break;
|
|
}
|
|
p = ParseNextLine(p);
|
|
f = coord;
|
|
for(a = 0; a < nAtom; a++) {
|
|
AtomInfoType *ai = atInfo + a;
|
|
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%d", &ai->id) != 1) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad atom id");
|
|
}
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
cc[cAtomNameLen] = 0;
|
|
UtilCleanStr(cc);
|
|
if(cc[0])
|
|
LexAssign(G, ai->name, cc);
|
|
else
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad atom name");
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad x coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad y coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad z coordinate");
|
|
}
|
|
if(ok) {
|
|
OrthoLineType temp;
|
|
p = ParseWordCopy(cc, p, OrthoLineLength - 1);
|
|
if(sscanf(cc, "%s", temp) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad atom type");
|
|
else { /* convert atom type to elem symbol */
|
|
char *tt = temp;
|
|
char *el = ai->elem;
|
|
int elc = 0;
|
|
while(*tt && ((*tt) != '.')) {
|
|
*(el++) = *(tt++);
|
|
elc++;
|
|
if(elc > cElemNameLen)
|
|
break;
|
|
}
|
|
*el = 0;
|
|
if(el[2])
|
|
el[0] = 0;
|
|
|
|
ai->textType = LexIdx(G, temp);
|
|
}
|
|
}
|
|
if(ok) {
|
|
char cc_resi[8];
|
|
p = ParseWordCopy(cc_resi, p, sizeof(cc_resi) - 1);
|
|
if(cc_resi[0]) { /* subst_id is residue identifier */
|
|
ai->setResi(cc_resi);
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(cc[0]) {
|
|
if (nSubst == 0) {
|
|
// without substructure information (e.g. OpenBabel exported MOL2):
|
|
// if subst_name includes the subst_id (e.g. 5 ALA5) then strip the number
|
|
int len_resi = strlen(cc_resi);
|
|
int len_resn = strlen(cc);
|
|
if (len_resn > len_resi) {
|
|
if (strcmp(cc_resi, cc + len_resn - len_resi) == 0) {
|
|
cc[len_resn - len_resi] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
LexAssign(G, ai->resn, cc);
|
|
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if(cc[0]) {
|
|
if(sscanf(cc, "%f", &ai->partialCharge) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad atom charge");
|
|
}
|
|
|
|
// status_bit
|
|
p = ParseWordCopy(cc, p, MAXLINELEN);
|
|
if (cc[0]) {
|
|
// for water, substitute resn "<0>" with "HOH" for
|
|
// correct classification as "solvent"
|
|
if (ai->resn == empty_subst_name && strstr(cc, "WATER")) {
|
|
LexAssign(G, ai->resn, G->lex_const.HOH);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = ParseNextLine(p);
|
|
|
|
ai->visRep = auto_show;
|
|
|
|
ai->id = a + 1;
|
|
ai->rank = a;
|
|
ai->hetatm = true;
|
|
AtomInfoAssignParameters(G, atInfo + a);
|
|
AtomInfoAssignColors(G, atInfo + a);
|
|
ai->alt[0] = 0;
|
|
ai->segi = 0;
|
|
|
|
}
|
|
} else if(WordMatchExact(G, cc, "@<TRIPOS>SET", true)
|
|
|| WordMatchExact(G, cc, "@SET", true)) {
|
|
if(!have_molecule) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "@SET before @MOLECULE!");
|
|
break;
|
|
}
|
|
p = ParseNextLine(p);
|
|
if(ok) {
|
|
for(a = 0; a < nSet; a++) {
|
|
if(ok) {
|
|
char ss = 0;
|
|
p = ParseWordCopy(cc, p, 50);
|
|
cc[5] = 0;
|
|
if(!strcmp("HELIX", cc))
|
|
ss = 'H';
|
|
if(!strcmp("SHEET", cc))
|
|
ss = 'S';
|
|
p = ParseWordCopy(cc, p, 50);
|
|
if(!strcmp("STATIC", cc)) {
|
|
int nMember;
|
|
|
|
p = ParseNextLine(p);
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &nMember) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad member count");
|
|
else {
|
|
while(ok && (nMember > 0)) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if((!cc[0]) && (!*p)) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if(cc[0] != '\\') {
|
|
int atom_id;
|
|
if(sscanf(cc, "%d", &atom_id) != 1) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad member");
|
|
} else {
|
|
atom_id--;
|
|
if((atom_id >= 0) && (atom_id < nAtom)) {
|
|
atInfo[atom_id].ssType[0] = ss;
|
|
atInfo[atom_id].ssType[1] = 0;
|
|
}
|
|
nMember--;
|
|
}
|
|
} else {
|
|
p = ParseNextLine(p);
|
|
}
|
|
}
|
|
}
|
|
p = ParseNextLine(p);
|
|
} else {
|
|
p = ParseNextLine(p);
|
|
p = ParseNextLine(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if(WordMatchExact(G, cc, "@<TRIPOS>BOND", true)
|
|
|| WordMatchExact(G, cc, "@BOND", true)) {
|
|
if(!have_molecule) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "@BOND before @MOLECULE!");
|
|
break;
|
|
}
|
|
p = ParseNextLine(p);
|
|
|
|
if(ok) {
|
|
bond = VLACalloc(BondType, nBond);
|
|
ii = bond;
|
|
for(a = 0; a < nBond; a++) {
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
}
|
|
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &ii->index[0]) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad source atom id");
|
|
}
|
|
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &ii->index[1]) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad target atom id");
|
|
}
|
|
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(!cc[1]) {
|
|
switch (cc[0]) {
|
|
case '1':
|
|
ii->order = 1;
|
|
break;
|
|
case '2':
|
|
ii->order = 2;
|
|
break;
|
|
case '3':
|
|
ii->order = 3;
|
|
break;
|
|
case '4':
|
|
ii->order = 4;
|
|
break;
|
|
}
|
|
} else if(WordMatchExact(G, "ar", cc, true)) {
|
|
ii->order = 4;
|
|
} else if(WordMatchExact(G, "am", cc, true)) {
|
|
ii->order = 1;
|
|
} else if(WordMatchExact(G, "un", cc, true)) {
|
|
ii->order = 1;
|
|
} else if(WordMatchExact(G, "nc", cc, true)) {
|
|
ii->order = 0; /* is this legal in PyMOL? */
|
|
} else if(WordMatchExact(G, "du", cc, true)) {
|
|
ii->order = 1;
|
|
} else
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad bond type");
|
|
}
|
|
ii++;
|
|
if(!ok)
|
|
break;
|
|
p = ParseNextLine(p);
|
|
}
|
|
ii = bond;
|
|
for(a = 0; a < nBond; a++) {
|
|
ii->index[0]--; /* adjust bond indexs down one */
|
|
ii->index[1]--;
|
|
ii++;
|
|
}
|
|
}
|
|
} else if(WordMatchExact(G, cc, "@<TRIPOS>SUBSTRUCTURE", true)
|
|
|| WordMatchExact(G, cc, "@SUBSTRUCTURE", true)) {
|
|
if(!have_molecule) {
|
|
ok = ErrMessage(G, "ReadMOL2File", "@SUBSTSRUCTURE before @MOLECULE!");
|
|
break;
|
|
}
|
|
p = ParseNextLine(p);
|
|
if(ok) {
|
|
WordType subst_name;
|
|
SegIdent segment; /* what MOL2 calls chain */
|
|
lexidx_t chain = 0;
|
|
WordType subst_type;
|
|
ResIdent resi;
|
|
ResName resn;
|
|
int id, dict_type, root, resv;
|
|
int end_line, seg_flag, subst_flag, resi_flag;
|
|
int chain_flag, resn_flag;
|
|
std::unordered_map<int, std::vector<int>> resv_map;
|
|
|
|
{
|
|
int b;
|
|
AtomInfoType *ai = atInfo;
|
|
for(b = 0; b < nAtom; b++) {
|
|
resv_map[ai->resv].push_back(b);
|
|
ai++;
|
|
}
|
|
}
|
|
|
|
for(a = 0; a < nSubst; a++) {
|
|
bool hetatm = true;
|
|
segment[0] = 0;
|
|
subst_name[0] = 0;
|
|
LexAssign(G, chain, 0);
|
|
resn[0] = 0;
|
|
resi[0] = 0;
|
|
end_line = false;
|
|
seg_flag = false;
|
|
subst_flag = false;
|
|
|
|
resi_flag = false;
|
|
chain_flag = false;
|
|
resn_flag = false;
|
|
|
|
if(ok) {
|
|
const char *save_p = p;
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &id) != 1) {
|
|
if(cc[0] != '@')
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad substructure id");
|
|
else {
|
|
p = save_p;
|
|
break; /* missing substructure... */
|
|
}
|
|
}
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
|
|
if(sscanf(cc, "%s", subst_name) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad substructure name");
|
|
}
|
|
if(ok) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &root) != 1)
|
|
ok = ErrMessage(G, "ReadMOL2File", "bad target root atom id");
|
|
else
|
|
root--; /* convert 1-based to 0-based */
|
|
}
|
|
|
|
/* optional data */
|
|
if(ok && (!end_line)) {
|
|
p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
|
|
if(sscanf(cc, "%s", subst_type) != 1) {
|
|
end_line = true;
|
|
subst_type[0] = 0;
|
|
} else {
|
|
subst_flag = 1;
|
|
}
|
|
}
|
|
|
|
if(ok && (!end_line)) {
|
|
p = ParseWordCopy(cc, p, 20);
|
|
if(sscanf(cc, "%d", &dict_type) != 1)
|
|
end_line = true;
|
|
}
|
|
|
|
if(ok && (!end_line)) {
|
|
p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
|
|
cc[cSegiLen] = 0;
|
|
if(sscanf(cc, "%s", segment) != 1) {
|
|
end_line = true;
|
|
segment[0] = 0;
|
|
} else if(strcmp(segment, "****")) {
|
|
seg_flag = true;
|
|
if(!segment[1]) { /* if segment is single letter, then also assign to chain field */
|
|
LexAssign(G, chain, segment);
|
|
chain_flag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ok && (!end_line)) {
|
|
p = ParseWordCopy(cc, p, sizeof(WordType) - 1);
|
|
cc[cResnLen] = 0;
|
|
if(!strcmp(cc, "****")) { /* whoops -- no residue name... */
|
|
char *pp;
|
|
UtilNCopy(resn, subst_name, sizeof(ResName));
|
|
pp = resn;
|
|
/* any numbers? */
|
|
while(*pp) {
|
|
if(((*pp) >= '0') && ((*pp) <= '9'))
|
|
break;
|
|
pp++;
|
|
}
|
|
/* trim them off */
|
|
while(pp >= resn) {
|
|
if(((*pp) >= '0') && ((*pp) <= '9'))
|
|
*pp = 0;
|
|
else
|
|
break;
|
|
pp--;
|
|
}
|
|
|
|
if(resn[0])
|
|
resn_flag = true;
|
|
} else {
|
|
if(sscanf(cc, "%s", resn) != 1) {
|
|
end_line = true;
|
|
resn[0] = 0;
|
|
} else {
|
|
resn_flag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ok && (root >= 0) && (root < nAtom)) {
|
|
|
|
if((strlen(subst_name) == 4) &&
|
|
((subst_name[0] >= '1') && (subst_name[0] <= '9')) &&
|
|
((((subst_name[1] >= 'A') && (subst_name[1] <= 'Z')) ||
|
|
((subst_name[1] >= 'a') && (subst_name[1] <= 'z'))) ||
|
|
(((subst_name[2] >= 'A') && (subst_name[2] <= 'Z')) ||
|
|
((subst_name[2] >= 'a') && (subst_name[2] <= 'z'))) ||
|
|
(((subst_name[3] >= 'A') && (subst_name[3] <= 'Z')) ||
|
|
((subst_name[3] >= 'a') && (subst_name[3] <= 'z'))))) {
|
|
|
|
/* if subst_name looks like a PDB code, then it isn't a residue identifier
|
|
so do nothing... */
|
|
} else {
|
|
|
|
if(!subst_flag) {
|
|
|
|
/* Merck stuffs the chain ID and the residue ID in the
|
|
subst_name field, so handle that case first */
|
|
|
|
/* get the residue value */
|
|
|
|
ParseIntCopy(cc, subst_name, 20);
|
|
if(sscanf(cc, "%d", &resv) == 1) {
|
|
/* we have the resv, so now get the chain if there is one */
|
|
char *pp = subst_name;
|
|
if(!((pp[0] >= '0') && (pp[0] <= '9'))) {
|
|
char tmp[2] = {pp[0], 0};
|
|
LexAssign(G, chain, tmp);
|
|
chain_flag = true;
|
|
pp++;
|
|
}
|
|
UtilNCopy(resi, pp, cResiLen); /* now get the textual residue identifier */
|
|
|
|
if(resi[0]) {
|
|
resi_flag = true;
|
|
}
|
|
}
|
|
} else {
|
|
/* normal mode: assume that the substructure ID is a vanilla residue identifier */
|
|
|
|
char *pp = subst_name;
|
|
|
|
if(resn_flag) { /* if we have a resn, then remove it from the subst_name */
|
|
char *qq = resn;
|
|
while((*pp) && (*qq)) {
|
|
if(*pp == *qq) {
|
|
pp++;
|
|
qq++;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
ParseIntCopy(cc, pp, 20);
|
|
if(sscanf(cc, "%d", &resv) == 1) {
|
|
UtilNCopy(resi, pp, cResiLen); /* now get the textual residue identifier */
|
|
if(resi[0]) {
|
|
resi_flag = true;
|
|
}
|
|
}
|
|
|
|
if (strcmp(subst_type, "RESIDUE") == 0) {
|
|
hetatm = false;
|
|
has_non_hetatm = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* for now, assume that atom ids are 1-based and sequential */
|
|
|
|
if(ok) {
|
|
if(resi_flag || chain_flag || resn_flag || seg_flag) {
|
|
auto it = resv_map.find(id);
|
|
if (it != resv_map.end()) {
|
|
for (auto b : it->second) {
|
|
assert(b >= 0 && b < nAtom);
|
|
{
|
|
AtomInfoType *ai = atInfo + b;
|
|
if(resi_flag)
|
|
ai->setResi(resi);
|
|
if(chain_flag) {
|
|
LexAssign(G, ai->chain, chain);
|
|
}
|
|
if(resn_flag)
|
|
LexAssign(G, ai->resn, resn);
|
|
if(seg_flag)
|
|
LexAssign(G, ai->segi, segment);
|
|
ai->hetatm = hetatm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!ok)
|
|
break;
|
|
p = ParseNextLine(p);
|
|
}
|
|
LexDec(G, chain);
|
|
}
|
|
} else
|
|
p = ParseNextLine(p);
|
|
} else
|
|
p = ParseNextLine(p);
|
|
}
|
|
|
|
LexDec(G, empty_subst_name);
|
|
|
|
if (has_non_hetatm) {
|
|
// contains "residues" (assume polymer) so ignore hetatm for surfacing
|
|
for (auto ai = atInfo, ai_end = atInfo + nAtom; ai != ai_end; ++ai) {
|
|
if (ai->hetatm) {
|
|
ai->flags |= cAtomFlag_ignore;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ok) {
|
|
cset = CoordSetNew(G);
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->NTmpBond = nBond;
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
strcpy(cset->Name, nameTmp);
|
|
} else {
|
|
VLAFreeP(bond);
|
|
VLAFreeP(coord);
|
|
}
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
return (cset);
|
|
}
|
|
|
|
/**
|
|
* Read one molecule in MOL2, MOL, SDF, MMD or XYZ format from the string pointed
|
|
* to by (*next_entry). All these formats (except MOL) support multiple
|
|
* concatenated entries in one file. If multiplex=1, then read only one
|
|
* molecule (one state) and set the next_entry pointer to the beginning of the
|
|
* next entry. Otherwise, read a multi-state molecule.
|
|
*/
|
|
ObjectMolecule *ObjectMoleculeReadStr(PyMOLGlobals * G, ObjectMolecule * I,
|
|
const char **next_entry,
|
|
cLoadType_t content_format, int frame,
|
|
int discrete, int quiet, int multiplex,
|
|
char *new_name,
|
|
short loadpropertiesall, OVLexicon *loadproplex)
|
|
{
|
|
int ok = true;
|
|
CoordSet *cset = nullptr;
|
|
pymol::vla<AtomInfoType> atInfo;
|
|
int isNew;
|
|
int nAtom;
|
|
const char *restart = nullptr, *start = *next_entry;
|
|
int repeatFlag = true;
|
|
int successCnt = 0;
|
|
char tmpName[WordLength];
|
|
int deferred_tasks = false;
|
|
int skip_out;
|
|
int connect = false;
|
|
int set_formal_charges = false;
|
|
*next_entry = nullptr;
|
|
int aic_mask = cAIC_MOLMask;
|
|
|
|
while(repeatFlag) {
|
|
repeatFlag = false;
|
|
skip_out = false;
|
|
|
|
if(!I)
|
|
isNew = true;
|
|
else
|
|
isNew = false;
|
|
|
|
if(isNew) {
|
|
I = (ObjectMolecule *) new ObjectMolecule(G, (discrete > 0));
|
|
std::swap(atInfo, I->AtomInfo);
|
|
} else {
|
|
atInfo = pymol::vla<AtomInfoType>(10);
|
|
}
|
|
|
|
if(isNew) {
|
|
I->Color = AtomInfoUpdateAutoColor(G);
|
|
}
|
|
|
|
restart = nullptr;
|
|
switch (content_format) {
|
|
case cLoadTypeMOL2:
|
|
case cLoadTypeMOL2Str:
|
|
cset = ObjectMoleculeMOL2Str2CoordSet(G, start, &atInfo, &restart);
|
|
if (cset){
|
|
set_formal_charges = true;
|
|
}
|
|
break;
|
|
case cLoadTypeMOL:
|
|
case cLoadTypeMOLStr:
|
|
cset = ObjectMoleculeMOLStr2CoordSet(G, start, &atInfo, &restart
|
|
, loadpropertiesall, loadproplex
|
|
);
|
|
restart = nullptr;
|
|
break;
|
|
case cLoadTypeSDF2:
|
|
case cLoadTypeSDF2Str:
|
|
cset = ObjectMoleculeSDF2Str2CoordSet(G, start, &atInfo, &restart
|
|
, loadpropertiesall, loadproplex
|
|
);
|
|
break;
|
|
case cLoadTypeXYZ:
|
|
case cLoadTypeXYZStr:
|
|
cset = ObjectMoleculeXYZStr2CoordSet(G, start, &atInfo, &restart);
|
|
if(!cset->TmpBond)
|
|
connect = true;
|
|
break;
|
|
case cLoadTypeMMD:
|
|
case cLoadTypeMMDStr:
|
|
cset = ObjectMoleculeMMDStr2CoordSet(G, start, &atInfo, &restart);
|
|
aic_mask = cAIC_MMDMask;
|
|
break;
|
|
}
|
|
|
|
|
|
if(!cset) {
|
|
if(!isNew)
|
|
VLAFreeP(atInfo);
|
|
if(!successCnt) {
|
|
if (isNew)
|
|
std::swap(I->AtomInfo, atInfo);
|
|
DeleteP(I);
|
|
ok = false;
|
|
} else {
|
|
skip_out = true;
|
|
}
|
|
}
|
|
|
|
if(ok && !skip_out) {
|
|
|
|
if((discrete>0 && !I->DiscreteFlag) || // should set to discrete if explicitly defined
|
|
((discrete < 0) && (restart) && isNew && (multiplex <= 0))) {
|
|
/* if default discrete behavior and new object, with
|
|
multi-coordinate set file, and not multiplex, then set
|
|
discrete */
|
|
|
|
ObjectMoleculeSetDiscrete(G, I, true);
|
|
}
|
|
|
|
if(frame < 0)
|
|
frame = I->NCSet;
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
|
|
nAtom = cset->NIndex;
|
|
|
|
if(I->DiscreteFlag && atInfo) {
|
|
int a;
|
|
int fp1 = frame + 1;
|
|
AtomInfoType *ai = atInfo.data();
|
|
for(a = 0; a < nAtom; a++) {
|
|
(ai++)->discrete_state = fp1;
|
|
}
|
|
}
|
|
|
|
if(multiplex > 0)
|
|
UtilNCopy(tmpName, cset->Name, WordLength);
|
|
|
|
cset->Obj = I;
|
|
cset->enumIndices();
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
if(isNew) {
|
|
std::swap(I->AtomInfo, atInfo);
|
|
} else {
|
|
ObjectMoleculeMerge(I, std::move(atInfo), cset, false, aic_mask, false);
|
|
/* NOTE: will release atInfo */
|
|
}
|
|
|
|
if(isNew)
|
|
I->NAtom = nAtom;
|
|
if(frame < 0)
|
|
frame = I->NCSet;
|
|
VLACheck(I->CSet, CoordSet *, frame);
|
|
if(I->NCSet <= frame)
|
|
I->NCSet = frame + 1;
|
|
delete I->CSet[frame];
|
|
I->CSet[frame] = cset;
|
|
|
|
if(ok && isNew)
|
|
ok &= ObjectMoleculeConnect(I, cset, connect);
|
|
|
|
if (ok) {
|
|
if (I->DiscreteFlag) {
|
|
ok &= ObjectMoleculeExtendIndices(I, frame);
|
|
}
|
|
}
|
|
|
|
deferred_tasks = true;
|
|
successCnt++;
|
|
if(!quiet) {
|
|
if(successCnt > 1) {
|
|
if(successCnt == 2) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Actions)
|
|
" %s: read through molecule %d.\n", __func__, 1 ENDFB(G);
|
|
}
|
|
if(UtilShouldWePrintQuantity(successCnt)) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Actions)
|
|
" %s: read through molecule %d.\n", __func__, successCnt ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
if(multiplex > 0) {
|
|
UtilNCopy(new_name, tmpName, WordLength);
|
|
if(restart) {
|
|
*next_entry = restart;
|
|
}
|
|
} else if(restart) {
|
|
repeatFlag = true;
|
|
start = restart;
|
|
frame = frame + 1;
|
|
}
|
|
}
|
|
}
|
|
if(deferred_tasks && I) { /* defer time-consuming tasks until all states have been loaded */
|
|
if (!I->DiscreteFlag) {
|
|
ObjectMoleculeExtendIndices(I, cStateAll);
|
|
}
|
|
ObjectMoleculeSort(I);
|
|
if (set_formal_charges){
|
|
ObjectMoleculeMOL2SetFormalCharges(G, I);
|
|
}
|
|
SceneCountFrames(G);
|
|
I->invalidate(cRepAll, cRepInvAll, -1);
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
ObjectMoleculeUpdateNonbonded(I);
|
|
}
|
|
return (I);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
typedef int CompareFn(PyMOLGlobals *, const AtomInfoType *, const AtomInfoType *);
|
|
int ObjectMoleculeMerge(ObjectMolecule * I, pymol::vla<AtomInfoType>&& ai,
|
|
CoordSet * cs, int bondSearchFlag, int aic_mask, int invalidate)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
int *index, *outdex;
|
|
int a, b, lb = 0, ac;
|
|
int c, nb, a1, a2;
|
|
int found;
|
|
int nAt, nBd, nBond;
|
|
int expansionFlag = false;
|
|
int ok = true;
|
|
|
|
const auto n_index = cs->getNIndex();
|
|
const auto oldNAtom = I->NAtom;
|
|
const auto oldNBond = I->NBond;
|
|
|
|
/* first, sort the coodinate set */
|
|
|
|
index = AtomInfoGetSortedIndex(G, I, ai, n_index, &outdex);
|
|
CHECKOK(ok, index);
|
|
if (!ok)
|
|
return false;
|
|
|
|
auto ai2 = pymol::vla<AtomInfoType>(n_index);
|
|
if (ok){
|
|
for(a = 0; a < n_index; a++)
|
|
ai2[a] = std::move(ai[index[a]]); /* creates a sorted list of atom info records */
|
|
}
|
|
ai = std::move(ai2);
|
|
|
|
/* now, match it up with the current object's atomic information */
|
|
|
|
if (ok){
|
|
for(a = 0; a < n_index; a++) {
|
|
index[a] = -1;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
const auto n_atom = I->NAtom;
|
|
const AtomInfoType* const atInfo = I->AtomInfo.data();
|
|
CompareFn *fCompare;
|
|
|
|
if(SettingGetGlobal_b(G, cSetting_pdb_hetatm_sort)) {
|
|
fCompare = AtomInfoCompareIgnoreRank;
|
|
} else {
|
|
fCompare = AtomInfoCompareIgnoreRankHet;
|
|
}
|
|
|
|
c = 0;
|
|
b = 0;
|
|
lb = 0;
|
|
|
|
for(a = 0; a < n_index; a++) {
|
|
int reverse = false;
|
|
const auto* const ai_a = ai + a;
|
|
found = false;
|
|
if(!I->DiscreteFlag) { /* don't even try matching for discrete objects */
|
|
while(b < n_atom) {
|
|
ac = (fCompare(G, ai_a, atInfo + b));
|
|
if(!ac) {
|
|
found = true;
|
|
break;
|
|
} else if(ac < 0) { /* atom is before current, so try going the other way */
|
|
reverse = true;
|
|
break;
|
|
} else if(!b) {
|
|
int idx;
|
|
ac = (fCompare(G, ai_a, atInfo + n_atom - 1));
|
|
if(ac > 0) { /* atom is after all atoms in list, so don't even bother searching */
|
|
break;
|
|
}
|
|
/* next, try to jump to an appropriate position in the list */
|
|
idx = ((a * n_atom) / n_index) - 1;
|
|
if((idx > 0) && (b != idx) && (idx < n_atom)) {
|
|
ac = (fCompare(G, ai_a, atInfo + idx));
|
|
if(ac > 0)
|
|
b = idx;
|
|
}
|
|
}
|
|
lb = b; /* last b before atom */
|
|
b++;
|
|
}
|
|
if(reverse && !found) { /* searching going backwards... */
|
|
while(b >= 0) {
|
|
ac = (fCompare(G, ai_a, atInfo + b));
|
|
if(!ac) {
|
|
found = true;
|
|
break;
|
|
} else if(ac > 0) { /* atom after current -- no good */
|
|
break;
|
|
}
|
|
lb = b;
|
|
b--;
|
|
}
|
|
if(b < 0)
|
|
b = 0;
|
|
}
|
|
}
|
|
if(found) {
|
|
index[a] = b; /* store real atom index b for a in index[a] */
|
|
b++;
|
|
} else {
|
|
index[a] = I->NAtom + c; /* otherwise, this is a new atom */
|
|
c++;
|
|
b = lb;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* first, reassign atom info for matched atoms */
|
|
|
|
/* allocate additional space */
|
|
if (ok){
|
|
if(c) {
|
|
expansionFlag = true;
|
|
nAt = I->NAtom + c;
|
|
} else {
|
|
nAt = I->NAtom;
|
|
}
|
|
if(expansionFlag) {
|
|
VLACheck(I->AtomInfo, AtomInfoType, nAt);
|
|
CHECKOK(ok, I->AtomInfo);
|
|
}
|
|
}
|
|
|
|
if (ok){
|
|
for(a = 0; a < n_index; a++) { /* a is in original file space */
|
|
a1 = outdex[cs->IdxToAtm[a]]; /* a1 is in sorted atom info space */
|
|
a2 = index[a1];
|
|
cs->IdxToAtm[a] = a2; /* a2 is in object space */
|
|
if(a2 < oldNAtom)
|
|
AtomInfoCombine(G, I->AtomInfo + a2, std::move(ai[a1]), aic_mask);
|
|
else
|
|
I->AtomInfo[a2] = std::move(ai[a1]);
|
|
}
|
|
}
|
|
|
|
if(ok && I->DiscreteFlag) {
|
|
ok = I->setNDiscrete(nAt);
|
|
}
|
|
|
|
I->NAtom = nAt;
|
|
|
|
if (ok){
|
|
cs->updateNonDiscreteAtmToIdx(nAt);
|
|
}
|
|
|
|
VLAFreeP(ai); /* note that we're trusting AtomInfoCombine to have
|
|
already purged all atom-dependent storage (such as strings) */
|
|
|
|
AtomInfoFreeSortedIndexes(G, &index, &outdex);
|
|
|
|
/* now find and integrate and any new bonds */
|
|
if(ok && expansionFlag) { /* expansion flag means we have introduced at least 1 new atom */
|
|
pymol::vla<BondType> bond;
|
|
ok &= ObjectMoleculeConnect(I, nBond, bond, cs, bondSearchFlag, -1);
|
|
if(nBond) {
|
|
index = pymol::malloc<int>(nBond);
|
|
CHECKOK(ok, index);
|
|
c = 0;
|
|
b = 0;
|
|
nb = 0;
|
|
if (ok){
|
|
for(a = 0; a < nBond; a++) { /* iterate over new bonds */
|
|
found = false;
|
|
if(!I->DiscreteFlag) { /* don't even try matching for discrete objects */
|
|
b = nb; /* pick up where we left off */
|
|
while(b < I->NBond) {
|
|
ac = BondCompare(bond + a, I->Bond + b);
|
|
if(!ac) { /* zero is a match */
|
|
found = true;
|
|
break;
|
|
} else if(ac < 0) { /* gone past position of this bond */
|
|
break;
|
|
} else if(!b) {
|
|
int idx;
|
|
ac = BondCompare(bond + a, I->Bond + I->NBond - 1);
|
|
|
|
if(ac > 0) /* bond is after all bonds in list, so don't even bother searching */
|
|
break;
|
|
/* next, try to jump to an appropriate position in the list */
|
|
idx = ((a * I->NBond) / nBond) - 1;
|
|
if((idx > 0) && (b != idx) && (idx < I->NBond)) {
|
|
ac = BondCompare(bond + a, I->Bond + idx);
|
|
if(ac > 0)
|
|
b = idx;
|
|
}
|
|
}
|
|
b++; /* no match yet, keep looking */
|
|
}
|
|
}
|
|
if(found) {
|
|
index[a] = b; /* existing bond... */
|
|
nb = b + 1;
|
|
} else { /* this is a new bond, save index and increment */
|
|
index[a] = I->NBond + c;
|
|
c++;
|
|
}
|
|
}
|
|
}
|
|
/* first, reassign atom info for matched atoms */
|
|
if(c) {
|
|
/* allocate additional space */
|
|
nBd = I->NBond + c;
|
|
|
|
if(!I->Bond)
|
|
I->Bond = pymol::vla<BondType>(1);
|
|
CHECKOK(ok, I->Bond);
|
|
if (ok)
|
|
VLACheck(I->Bond, BondType, nBd);
|
|
CHECKOK(ok, I->Bond);
|
|
for(a = 0; a < nBond; a++) { /* copy the new bonds */
|
|
a2 = index[a];
|
|
if(a2 >= I->NBond) {
|
|
I->Bond[a2] = bond[a];
|
|
}
|
|
}
|
|
I->NBond = nBd;
|
|
}
|
|
FreeP(index);
|
|
}
|
|
}
|
|
if(invalidate) {
|
|
if(oldNAtom) {
|
|
if(oldNAtom == I->NAtom) {
|
|
if(oldNBond != I->NBond) {
|
|
I->invalidate(cRepAll, cRepInvBonds, -1);
|
|
}
|
|
} else {
|
|
I->invalidate(cRepAll, cRepInvAtoms, -1);
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* @param state Object state or -2 for current state
|
|
* @return nullptr if there is no CoordSet for the given state
|
|
*/
|
|
CoordSet* ObjectMolecule::getCoordSet(int state)
|
|
{
|
|
return static_cast<CoordSet*>(getObjectState(state));
|
|
}
|
|
const CoordSet* ObjectMolecule::getCoordSet(int state) const
|
|
{
|
|
return static_cast<const CoordSet*>(getObjectState(state));
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeTransformTTTf(ObjectMolecule * I, float *ttt, int frame)
|
|
{
|
|
int b;
|
|
CoordSet *cs;
|
|
for(b = 0; b < I->NCSet; b++) {
|
|
if((frame < 0) || (frame == b)) {
|
|
cs = I->CSet[b];
|
|
if(cs) {
|
|
cs->invalidateRep(cRepAll, cRepInvCoord);
|
|
MatrixTransformTTTfN3f(cs->NIndex, cs->Coord.data(), ttt, cs->Coord.data());
|
|
CoordSetRecordTxfApplied(cs, ttt, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
bool ObjectMoleculeSeleOp(ObjectMolecule * I, int sele, ObjectMoleculeOpRec * op)
|
|
{
|
|
float *coord;
|
|
int a, b, s;
|
|
int c, t_i;
|
|
int a1 = 0, ind;
|
|
float rms;
|
|
float v1[3], v2, *vv1, *vv2, *vt, *vt1, *vt2;
|
|
int hit_flag = false;
|
|
int ok = true;
|
|
int cnt;
|
|
int skip_flag;
|
|
int match_flag = false;
|
|
int offset;
|
|
int priority;
|
|
int use_matrices = false;
|
|
CoordSet *cs;
|
|
AtomInfoType *ai, *ai0;
|
|
PyMOLGlobals *G = I->G;
|
|
#ifndef _PYMOL_NOPY
|
|
PyObject* expr_co = nullptr;
|
|
int compileType = Py_single_input;
|
|
#endif
|
|
#ifdef _WEBGL
|
|
#endif
|
|
PRINTFD(G, FB_ObjectMolecule)
|
|
" %s-DEBUG: sele %d op->code %d\n", __func__, sele, op->code ENDFD;
|
|
if(sele >= 0) {
|
|
const char *errstr = "Alter";
|
|
/* always run on entry */
|
|
switch (op->code) {
|
|
case OMOP_LABL:
|
|
errstr = "Label";
|
|
if (op->i2 != cExecutiveLabelEvalOn){
|
|
break;
|
|
}
|
|
#ifndef _PYMOL_NOPY
|
|
compileType = Py_eval_input;
|
|
#endif
|
|
case OMOP_ALTR:
|
|
case OMOP_AlterState:
|
|
// assume blocked interpreter
|
|
|
|
if (op->s1 && op->s1[0]){
|
|
#ifndef _PYMOL_NOPY
|
|
expr_co = Py_CompileString(op->s1, "", compileType);
|
|
if(expr_co == nullptr) {
|
|
ok = ErrMessage(G, errstr, "failed to compile expression");
|
|
}
|
|
#else
|
|
#ifndef _WEBGL
|
|
ok = ErrMessage(G, errstr, "failed to compile expression");
|
|
#endif
|
|
#endif
|
|
}
|
|
/* PBlockAndUnlockAPI() is not safe.
|
|
* what if "v" is invalidated by another thread? */
|
|
break;
|
|
}
|
|
switch (op->code) {
|
|
case OMOP_ReferenceStore:
|
|
case OMOP_ReferenceRecall:
|
|
case OMOP_ReferenceValidate:
|
|
case OMOP_ReferenceSwap:
|
|
for(b = 0; b < I->NCSet; b++) {
|
|
if(I->CSet[b]) {
|
|
if((b == op->i1) || (op->i1 < 0)) {
|
|
int inv_flag = false;
|
|
cs = I->CSet[b];
|
|
if(cs && CoordSetValidateRefPos(cs)) {
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ind = cs->atmToIdx(a);
|
|
if(ind >= 0) {
|
|
float *v = cs->coordPtr(ind);
|
|
RefPosType *rp = cs->RefPos + ind;
|
|
switch (op->code) {
|
|
case OMOP_ReferenceStore:
|
|
copy3f(v, rp->coord);
|
|
rp->specified = true;
|
|
break;
|
|
case OMOP_ReferenceRecall:
|
|
if(rp->specified) {
|
|
copy3f(rp->coord, v);
|
|
inv_flag = true;
|
|
}
|
|
break;
|
|
case OMOP_ReferenceValidate:
|
|
if(!rp->specified) {
|
|
copy3f(v, rp->coord);
|
|
rp->specified = true;
|
|
}
|
|
break;
|
|
case OMOP_ReferenceSwap:
|
|
if(rp->specified) {
|
|
copy3f(rp->coord, v1);
|
|
copy3f(v, rp->coord);
|
|
copy3f(v1, v);
|
|
inv_flag = true;
|
|
}
|
|
break;
|
|
}
|
|
op->i2++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(inv_flag && cs) {
|
|
cs->invalidateRep(cRepAll, cRepInvRep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_AddHydrogens:
|
|
if (ok) {
|
|
ok &= ObjectMoleculeAddSeleHydrogensRefactored(I, sele, op->i1);
|
|
}
|
|
break;
|
|
case OMOP_FixHydrogens:
|
|
if (ok)
|
|
ok &= ObjectMoleculeFixSeleHydrogens(I, sele, -1); /* state? */
|
|
break;
|
|
case OMOP_RevalenceFromSource:
|
|
case OMOP_RevalenceByGuessing:
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(SelectorIsMember(G, ai->selEntry, sele)) {
|
|
hit_flag = true;
|
|
break;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_PrepareFromTemplate:
|
|
ai0 = op->ai; /* template atom */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ai = I->AtomInfo + a;
|
|
if(op->i1 != 3) {
|
|
ai->hetatm = ai0->hetatm;
|
|
ai->flags = ai0->flags;
|
|
LexAssign(G, ai->chain, ai0->chain);
|
|
strcpy(ai->alt, ai0->alt);
|
|
LexAssign(G, ai->segi, ai0->segi);
|
|
}
|
|
if(op->i1 == 1) { /* mode 1, merge residue information */
|
|
ai->inscode = ai0->inscode;
|
|
ai->resv = ai0->resv;
|
|
LexAssign(G, ai->resn, ai0->resn);
|
|
}
|
|
AtomInfoAssignColors(G, ai);
|
|
if(op->i3) {
|
|
if((ai->elem[0] == ai0->elem[0]) && (ai->elem[1] == ai0->elem[1]))
|
|
ai->color = ai0->color;
|
|
else if(ai->protons == cAN_C) {
|
|
ai->color = op->i4;
|
|
}
|
|
}
|
|
ai->visRep = ai0->visRep;
|
|
ai->id = -1;
|
|
ai->rank = -1;
|
|
op->i2++;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_Sort:
|
|
for(a = 0; ok && a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(I);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_SetAtomicSetting:
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
if(SelectorIsMember(G, ai->selEntry, sele)) {
|
|
int uid = AtomInfoCheckUniqueID(G, ai);
|
|
ai->has_setting = true;
|
|
if (SettingUniqueSetTypedValue(G, uid, op->i1, op->i2, op->ii1))
|
|
op->i4++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_Pop:
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
if(SelectorMoveMember(G, s, sele, op->i1)) {
|
|
op->i2--;
|
|
op->i3++;
|
|
}
|
|
if(!op->i2)
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_AVRT: /* average vertex coordinate */
|
|
case OMOP_StateVRT: /* state vertex coordinate */
|
|
{
|
|
int op_i2 = op->i2;
|
|
int obj_TTTFlag = I->TTTFlag;
|
|
int b_end = I->NCSet;
|
|
if (op->code == OMOP_StateVRT && op->i1 < b_end) {
|
|
b_end = op->i1 + 1;
|
|
}
|
|
if(op_i2) {
|
|
use_matrices =
|
|
SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
}
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(!(priority = SelectorIsMember(G, s, sele)))
|
|
continue;
|
|
|
|
cnt = 0;
|
|
|
|
// all states for AVRT, one state for StateVRT (don't use
|
|
// StateIterator which depends on settings)
|
|
for(b = op->i1; b < b_end; ++b) {
|
|
if(!(cs = I->CSet[b]))
|
|
continue;
|
|
|
|
if((a1 = cs->atmToIdx(a)) == -1)
|
|
continue;
|
|
|
|
if(!cnt) {
|
|
VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
|
|
}
|
|
cnt++;
|
|
vv2 = cs->coordPtr(a1);
|
|
|
|
if(op_i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), vv2, v1);
|
|
vv2 = v1;
|
|
}
|
|
}
|
|
if(obj_TTTFlag) {
|
|
transformTTT44f3f(I->TTT, vv2, v1);
|
|
vv2 = v1;
|
|
}
|
|
}
|
|
|
|
// sum up coordinates over states
|
|
vv1 = op->vv1 + (op->nvv1 * 3);
|
|
add3f(vv1, vv2, vv1);
|
|
}
|
|
|
|
// number of summed up coordinates (states) for this atom
|
|
VLACheck(op->vc1, int, op->nvv1);
|
|
op->vc1[op->nvv1] = cnt;
|
|
|
|
// ordered_selections
|
|
if(op->vp1) {
|
|
VLACheck(op->vp1, int, op->nvv1);
|
|
op->vp1[op->nvv1] = priority;
|
|
}
|
|
|
|
// atom pointer VLA
|
|
if(op->ai1VLA) {
|
|
VLACheck(op->ai1VLA, AtomInfoType *, op->nvv1);
|
|
op->ai1VLA[op->nvv1] = I->AtomInfo + a;
|
|
I->AtomInfo[a].temp1 = a;
|
|
/* KLUDGE ALERT!!! storing atom index in the temp1 field... */
|
|
}
|
|
|
|
// number of atoms in selection (incl. the ones with no coordinates)
|
|
op->nvv1++;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_SFIT: /* state fitting within a single object */
|
|
vt = pymol::malloc<float>(3 * op->nvv2); /* temporary (matching) target vertex pointers */
|
|
cnt = 0;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
cnt++;
|
|
break;
|
|
}
|
|
}
|
|
if(cnt) { /* only perform action for selected object */
|
|
|
|
for(b = 0; b < I->NCSet; b++) {
|
|
rms = -1.0;
|
|
vt1 = vt; /* reset target vertex pointers */
|
|
vt2 = op->vv2;
|
|
t_i = 0; /* original target vertex index */
|
|
if(I->CSet[b] && (b != op->i2)) {
|
|
op->nvv1 = 0;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
a1 = I->CSet[b]->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
|
|
match_flag = false;
|
|
while(t_i < op->nvv2) {
|
|
if(op->i1VLA[t_i] == a) { /* same atom? */
|
|
match_flag = true;
|
|
break;
|
|
}
|
|
if(op->i1VLA[t_i] < a) { /* catch up? */
|
|
t_i++;
|
|
vt2 += 3;
|
|
} else
|
|
break;
|
|
}
|
|
if(match_flag) {
|
|
VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
|
|
vv2 = I->CSet[b]->coordPtr(a1);
|
|
vv1 = op->vv1 + (op->nvv1 * 3);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vt1++) = *(vt2);
|
|
*(vt1++) = *(vt2 + 1);
|
|
*(vt1++) = *(vt2 + 2);
|
|
op->nvv1++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(op->nvv1 != op->nvv2) {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"Executive-Warning: Missing atoms in state %d (%d instead of %d).\n",
|
|
b + 1, op->nvv1, op->nvv2 ENDFB(G);
|
|
}
|
|
if(op->nvv1) {
|
|
if(op->i1 != 0) /* fitting flag */
|
|
rms = MatrixFitRMSTTTf(G, op->nvv1, op->vv1, vt, nullptr, op->ttt);
|
|
else
|
|
rms = MatrixGetRMS(G, op->nvv1, op->vv1, vt, nullptr);
|
|
if(op->i1 == 2) {
|
|
ObjectMoleculeTransformTTTf(I, op->ttt, b);
|
|
|
|
if(op->i3) {
|
|
const float divisor = (float) op->i3;
|
|
const float premult = (float) op->i3 - 1.0F;
|
|
|
|
/* mix flag is set, so average the prior target
|
|
coordinates with these coordinates */
|
|
|
|
vt2 = op->vv2;
|
|
t_i = 0; /* original target vertex index */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
a1 = I->CSet[b]->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
|
|
match_flag = false;
|
|
while(t_i < op->nvv2) {
|
|
if(op->i1VLA[t_i] == a) { /* same atom? */
|
|
match_flag = true;
|
|
break;
|
|
}
|
|
if(op->i1VLA[t_i] < a) { /* catch up? */
|
|
t_i++;
|
|
vt2 += 3;
|
|
} else
|
|
break;
|
|
}
|
|
if(match_flag) {
|
|
vv2 = I->CSet[b]->coordPtr(a1);
|
|
*(vt2) = ((premult * (*vt2)) + *(vv2++)) / divisor;
|
|
*(vt2 + 1) = ((premult * (*(vt2 + 1))) + *(vv2++)) / divisor;
|
|
*(vt2 + 2) = ((premult * (*(vt2 + 2))) + *(vv2++)) / divisor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
PRINTFB(G, FB_Executive, FB_Warnings)
|
|
"Executive-Warning: No matches found for state %d.\n", b + 1 ENDFB(G);
|
|
}
|
|
}
|
|
VLACheck(op->f1VLA, float, b);
|
|
op->f1VLA[b] = rms;
|
|
}
|
|
VLASize(op->f1VLA, float, I->NCSet); /* NOTE this action is object-specific! */
|
|
}
|
|
FreeP(vt);
|
|
break;
|
|
case OMOP_OnOff:
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
hit_flag = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_SaveUndo: /* save undo */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
hit_flag = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_IdentifyObjects: /* identify atoms */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
VLACheck(op->i1VLA, int, op->i1);
|
|
op->i1VLA[op->i1] = I->AtomInfo[a].id;
|
|
if (op->obj1VLA != nullptr) {
|
|
VLACheck(op->obj1VLA, ObjectMolecule*, op->i1);
|
|
op->obj1VLA[op->i1] = I;
|
|
}
|
|
op->i1++;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_Index: /* identify atoms */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
VLACheck(op->i1VLA, int, op->i1);
|
|
op->i1VLA[op->i1] = a; /* NOTE: need to incr by 1 before python */
|
|
VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
|
|
op->obj1VLA[op->i1] = I;
|
|
op->i1++;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_GetObjects: /* identify atoms */
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = I->AtomInfo[a].selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
|
|
op->obj1VLA[op->i1] = I;
|
|
op->i1++;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_CountAtoms: /* count atoms in object, in selection */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele))
|
|
op->i1++;
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_PhiPsi:
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
VLACheck(op->i1VLA, int, op->i1);
|
|
op->i1VLA[op->i1] = a;
|
|
VLACheck(op->obj1VLA, ObjectMolecule *, op->i1);
|
|
op->obj1VLA[op->i1] = I;
|
|
VLACheck(op->f1VLA, float, op->i1);
|
|
VLACheck(op->f2VLA, float, op->i1);
|
|
if(ObjectMoleculeGetPhiPsi
|
|
(I, a, op->f1VLA + op->i1, op->f2VLA + op->i1, op->i2))
|
|
op->i1++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_Cartoon: /* adjust cartoon type */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
if (ai->cartoon!=op->i1){
|
|
op->i3++;
|
|
}
|
|
ai->cartoon = op->i1;
|
|
op->i2++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_Protect: /* protect atoms from movement */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ai->protekted = op->i1;
|
|
op->i2++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_Mask: /* protect atoms from selection */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ai->masked = op->i1;
|
|
op->i2++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_SetB: /* set B-value */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ai->b = op->f1;
|
|
op->i2++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_Remove: /* flag atoms for deletion */
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
ai->deleteFlag = false;
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
ai->deleteFlag = true;
|
|
op->i1++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_GetChains:
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
// pointer hack
|
|
((std::set<lexidx_t> *) (void*) op->ii1)->insert(ai->chain);
|
|
op->i1++;
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
|
|
case OMOP_Spectrum:
|
|
ai = I->AtomInfo.data();
|
|
ai0 = nullptr;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
skip_flag = false;
|
|
if(op->i4 && ai0) /* byres and we've done a residue */
|
|
if(AtomInfoSameResidue(G, ai, ai0))
|
|
skip_flag = true;
|
|
if(!skip_flag) {
|
|
c = (int) (0.49999 + op->i1 * (op->ff1[op->i3] - op->f1) / op->f2);
|
|
if(c < 0)
|
|
c = 0;
|
|
if(c >= op->i1)
|
|
c = op->i1 - 1;
|
|
ai->color = op->ii1[c];
|
|
|
|
/* printf("%8.3 %8.3\n",ai->partial_charge, */
|
|
if(op->i4) { /* byres */
|
|
offset = -1;
|
|
while((a + offset) >= 0) {
|
|
ai0 = I->AtomInfo + a + offset;
|
|
if(AtomInfoSameResidue(G, ai, ai0)) {
|
|
ai0->color = op->ii1[c];
|
|
hit_flag = true;
|
|
} else
|
|
break;
|
|
offset--;
|
|
}
|
|
offset = 1;
|
|
while((a + offset) < I->NAtom) {
|
|
ai0 = I->AtomInfo + a + offset;
|
|
if(AtomInfoSameResidue(G, ai, ai0)) {
|
|
ai0->color = op->ii1[c];
|
|
hit_flag = true;
|
|
} else
|
|
break;
|
|
offset++;
|
|
}
|
|
}
|
|
ai0 = ai;
|
|
}
|
|
op->i3++;
|
|
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
|
|
case OMOP_SingleStateVertices: /* same as OMOP_VERT for a single state */
|
|
ai = I->AtomInfo.data();
|
|
if(op->cs1 < I->NCSet) {
|
|
if(I->CSet[op->cs1]) {
|
|
b = op->cs1;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
op->i1++;
|
|
a1 = I->CSet[b]->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
|
|
vv2 = I->CSet[b]->coordPtr(a1);
|
|
vv1 = op->vv1 + (op->nvv1 * 3);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
op->nvv1++;
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_CSetIdxGetAndFlag:
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
for(b = op->cs1; b <= op->cs2; b++) {
|
|
offset = b - op->cs1;
|
|
if(b < I->NCSet) {
|
|
if(I->CSet[b]) {
|
|
a1 = I->CSet[b]->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
op->ii1[op->i1 * offset + op->i2] = 1; /* presence flag */
|
|
vv1 = op->vv1 + 3 * (op->i1 * offset + op->i2); /* atom-based offset */
|
|
vv2 = I->CSet[b]->coordPtr(a1);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
op->nvv1++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
op->i2++; /* atom index field for atoms within selection... */
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_CSetIdxSetFlagged:
|
|
ai = I->AtomInfo.data();
|
|
hit_flag = false;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
for(b = op->cs1; b <= op->cs2; b++) {
|
|
offset = b - op->cs1;
|
|
if(b < I->NCSet) {
|
|
if(I->CSet[b]) {
|
|
a1 = I->CSet[b]->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
if(op->ii1[op->i1 * offset + op->i2]) { /* copy flag */
|
|
vv1 = op->vv1 + 3 * (op->i1 * offset + op->i2); /* atom-based offset */
|
|
vv2 = I->CSet[b]->coordPtr(a1);
|
|
*(vv2++) = *(vv1++);
|
|
*(vv2++) = *(vv1++);
|
|
*(vv2++) = *(vv1++);
|
|
op->nvv1++;
|
|
hit_flag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
op->i2++; /* atom index field for atoms within selection... */
|
|
}
|
|
ai++;
|
|
}
|
|
break;
|
|
case OMOP_SUMC: /* performance optimized to speed center & zoom actions */
|
|
{
|
|
/* given a selection, sum up all the coordinates (for centering) */
|
|
float *op_v1 = op->v1;
|
|
int op_i1 = op->i1;
|
|
int op_i2 = op->i2;
|
|
int obj_TTTFlag = I->TTTFlag;
|
|
int i_NCSet = I->NCSet;
|
|
int i_NAtom = I->NAtom;
|
|
int i_DiscreteFlag = I->DiscreteFlag;
|
|
CoordSet* const* i_CSet = I->CSet.data();
|
|
if(op_i2) {
|
|
use_matrices =
|
|
SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
}
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < i_NAtom; a++) {
|
|
s = ai->selEntry;
|
|
/* for each atom, if this current atom is in the selection */
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
/* loop over all atoms; regardless of state */
|
|
for(b = 0; b < i_NCSet; b++) {
|
|
if(i_DiscreteFlag) {
|
|
if((cs = I->DiscreteCSet[a]))
|
|
a1 = I->DiscreteAtmToIdx[a];
|
|
} else {
|
|
if((cs = i_CSet[b]))
|
|
a1 = cs->AtmToIdx[a];
|
|
}
|
|
/* if valid coordinate set and atom info for this atom */
|
|
if(cs && (a1 >= 0)) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op_i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(obj_TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
/* op_v1 += coord */
|
|
add3f(op_v1, coord, op_v1);
|
|
/* count += 1 */
|
|
op_i1++;
|
|
}
|
|
if(i_DiscreteFlag)
|
|
break;
|
|
}
|
|
}
|
|
/* next atom */
|
|
ai++;
|
|
}
|
|
op->i1 = op_i1;
|
|
}
|
|
break;
|
|
case OMOP_MNMX: /* performance optimized to speed center & zoom actions */
|
|
{
|
|
float *op_v1 = op->v1;
|
|
float *op_v2 = op->v2;
|
|
int op_i1 = op->i1;
|
|
int op_i2 = op->i2;
|
|
int obj_TTTFlag = I->TTTFlag;
|
|
int i_NCSet = I->NCSet;
|
|
int i_NAtom = I->NAtom;
|
|
int i_DiscreteFlag = I->DiscreteFlag;
|
|
CoordSet* const* i_CSet = I->CSet.data();
|
|
if(op_i2) {
|
|
use_matrices =
|
|
SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
}
|
|
ai = I->AtomInfo.data();
|
|
for(a = 0; a < i_NAtom; a++) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
for(b = 0; b < i_NCSet; b++) {
|
|
if(i_DiscreteFlag) {
|
|
if((cs = I->DiscreteCSet[a]))
|
|
a1 = I->DiscreteAtmToIdx[a];
|
|
} else {
|
|
if((cs = i_CSet[b]))
|
|
a1 = cs->AtmToIdx[a];
|
|
}
|
|
if(cs && (a1 >= 0)) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op_i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(obj_TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(op_i1) {
|
|
if(op_v1[0] > coord[0])
|
|
op_v1[0] = coord[0];
|
|
if(op_v1[1] > coord[1])
|
|
op_v1[1] = coord[1];
|
|
if(op_v1[2] > coord[2])
|
|
op_v1[2] = coord[2];
|
|
if(op_v2[0] < coord[0])
|
|
op_v2[0] = coord[0];
|
|
if(op_v2[1] < coord[1])
|
|
op_v2[1] = coord[1];
|
|
if(op_v2[2] < coord[2])
|
|
op_v2[2] = coord[2];
|
|
} else {
|
|
op_v1[0] = coord[0];
|
|
op_v1[1] = coord[1];
|
|
op_v1[2] = coord[2];
|
|
op_v2[0] = coord[0];
|
|
op_v2[1] = coord[1];
|
|
op_v2[2] = coord[2];
|
|
}
|
|
op_i1++;
|
|
}
|
|
if(i_DiscreteFlag)
|
|
break;
|
|
}
|
|
}
|
|
ai++;
|
|
}
|
|
op->i1 = op_i1;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
int inv_flag;
|
|
#ifdef _PYMOL_IP_EXTRAS
|
|
int use_stereo = 0, use_text_type = 0;
|
|
#endif
|
|
|
|
switch (op->code) {
|
|
case OMOP_INVA:
|
|
/* set up an important optimization... */
|
|
for(b = 0; b < I->NCSet; b++) {
|
|
cs = I->CSet[b];
|
|
if(cs)
|
|
cs->objMolOpInvalidated = false;
|
|
}
|
|
break;
|
|
}
|
|
use_matrices = SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
ai = I->AtomInfo.data();
|
|
|
|
#ifdef _PYMOL_IP_EXTRAS
|
|
// use stereo or text_type ?
|
|
// only do this for "label2" command (better logic in WrapperObjectSubScript)
|
|
if (op->code == OMOP_LABL && op->i2 == cExecutiveLabelEvalAlt) {
|
|
use_stereo = PLabelExprUsesVariable(G, op->s1, "stereo");
|
|
use_text_type = PLabelExprUsesVariable(G, op->s1, "text_type");
|
|
}
|
|
|
|
#ifdef NO_MMLIBS
|
|
if (use_stereo) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
|
|
" NO_MMLIBS-Warning: stereochemistry not supported in this PyMOL build.\n" ENDFB(G);
|
|
}
|
|
if (use_text_type) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Warnings)
|
|
" NO_MMLIBS-Warning: automatic 'text_type' assignment not supported in this PyMOL build.\n" ENDFB(G);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
switch (op->code) {
|
|
case OMOP_Flag:
|
|
ai->flags &= op->i2; /* clear flag using mask */
|
|
op->i4++;
|
|
/* no break here is intentional! */
|
|
case OMOP_FlagSet:
|
|
case OMOP_FlagClear:
|
|
case OMOP_COLR: /* normal atom based loops */
|
|
case OMOP_VISI:
|
|
case OMOP_CheckVis:
|
|
case OMOP_TTTF:
|
|
case OMOP_ALTR:
|
|
case OMOP_LABL:
|
|
case OMOP_AlterState:
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
switch (op->code) {
|
|
case OMOP_Flag:
|
|
case OMOP_FlagSet:
|
|
ai->flags |= op->i1; /* set flag */
|
|
op->i3++;
|
|
break;
|
|
case OMOP_FlagClear:
|
|
ai->flags &= op->i2; /* clear flag */
|
|
op->i3++;
|
|
break;
|
|
case OMOP_VISI:
|
|
switch (op->i2) {
|
|
case cVis_HIDE:
|
|
ai->visRep &= ~(op->i1);
|
|
I->visRep &= ~(op->i1); // cell
|
|
break;
|
|
case cVis_SHOW:
|
|
ai->visRep |= op->i1 & cRepsAtomMask;
|
|
I->visRep |= op->i1 & cRepsObjectMask; // cell
|
|
break;
|
|
case cVis_AS:
|
|
ai->visRep = op->i1 & cRepsAtomMask;
|
|
I->visRep = op->i1 & cRepsObjectMask; // cell
|
|
break;
|
|
}
|
|
break;
|
|
case OMOP_CheckVis:
|
|
if((ai->visRep & op->i1)) {
|
|
op->i2 = true;
|
|
}
|
|
break;
|
|
case OMOP_COLR:
|
|
if(op->i1 == cColorAtomic)
|
|
ai->color = AtomInfoGetColor(G, ai);
|
|
else
|
|
ai->color = op->i1;
|
|
hit_flag = true;
|
|
op->i2++;
|
|
break;
|
|
case OMOP_TTTF:
|
|
hit_flag = true;
|
|
break;
|
|
case OMOP_LABL:
|
|
if(ok) {
|
|
if(!op->s1[0]) {
|
|
if(ai->label) {
|
|
op->i1--; /* negative if unlabelling */
|
|
LexAssign(G, ai->label, 0);
|
|
}
|
|
ai->visRep &= ~cRepLabelBit;
|
|
hit_flag = true;
|
|
} else {
|
|
switch (op->i2) {
|
|
case cExecutiveLabelEvalOn:
|
|
{
|
|
/* python label expression evaluation */
|
|
CoordSet *cs = nullptr;
|
|
if(I->DiscreteFlag && I->DiscreteCSet) {
|
|
cs = I->DiscreteCSet[a];
|
|
} else if (I->NCSet == 1){
|
|
cs = I->CSet[0];
|
|
}
|
|
#ifndef _PYMOL_NOPY
|
|
if(PLabelAtom(I->G, I, cs, expr_co, a)) {
|
|
if (ai->label){
|
|
op->i1++; /* only if the string has been set, report labelled */
|
|
}
|
|
ai->visRep |= cRepLabelBit;
|
|
hit_flag = true;
|
|
} else {
|
|
ok = false;
|
|
}
|
|
#else
|
|
ok = false;
|
|
#endif
|
|
}
|
|
break;
|
|
case cExecutiveLabelEvalAlt:
|
|
{
|
|
if(PLabelAtomAlt(I->G, &I->AtomInfo[a], I->Name, op->s1, a)) {
|
|
if (ai->label){
|
|
op->i1++; /* only if the string has been set, report labelled */
|
|
}
|
|
ai->visRep |= cRepLabelBit;
|
|
hit_flag = true;
|
|
} else {
|
|
ok = false;
|
|
}
|
|
}
|
|
break;
|
|
case cExecutiveLabelEvalOff:
|
|
{
|
|
/* simple string label text */
|
|
AtomInfoType *ai = I->AtomInfo + a;
|
|
LexDec(G, ai->label);
|
|
ai->label = LexIdx(G, op->s1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_ALTR:
|
|
if(ok) {
|
|
CoordSet *cs = nullptr;
|
|
if(I->DiscreteFlag && I->DiscreteCSet) {
|
|
cs = I->DiscreteCSet[a];
|
|
} else if (I->NCSet == 1){
|
|
cs = I->CSet[0];
|
|
}
|
|
#ifndef _PYMOL_NOPY
|
|
if(PAlterAtom
|
|
(I->G, I, cs, expr_co, op->i2, a,
|
|
op->py_ob1))
|
|
op->i1++;
|
|
else
|
|
#endif
|
|
#ifdef _WEBGL
|
|
#endif
|
|
ok = false;
|
|
}
|
|
break;
|
|
case OMOP_AlterState:
|
|
if(ok) {
|
|
if(op->i2 < I->NCSet) {
|
|
cs = I->CSet[op->i2];
|
|
if(cs) {
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
#ifndef _PYMOL_NOPY
|
|
if(PAlterAtomState(I->G, expr_co, op->i3,
|
|
I, cs, a, a1, op->i2, op->py_ob1)) {
|
|
op->i1++;
|
|
hit_flag = true;
|
|
} else
|
|
#endif
|
|
#ifdef _WEBGL
|
|
#endif
|
|
ok = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* coord-set based properties, iterating only a single coordinate set */
|
|
case OMOP_CSetMinMax:
|
|
case OMOP_CSetCameraMinMax:
|
|
case OMOP_CSetMaxDistToPt:
|
|
case OMOP_CSetSumSqDistToPt:
|
|
case OMOP_CSetSumVertices:
|
|
case OMOP_CSetMoment:
|
|
cs = nullptr;
|
|
if((op->cs1 >= 0) && (op->cs1 < I->NCSet)) {
|
|
cs = I->CSet[op->cs1];
|
|
} else if(op->include_static_singletons) {
|
|
if((I->NCSet == 1)
|
|
&& (SettingGet_b(G, nullptr, I->Setting.get(), cSetting_static_singletons))) {
|
|
cs = I->CSet[0]; /*treat static singletons as present in each state */
|
|
}
|
|
}
|
|
|
|
if(cs) {
|
|
s = ai->selEntry;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
switch (op->code) {
|
|
case OMOP_CSetSumVertices:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
add3f(op->v1, coord, op->v1);
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_CSetMinMax:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(op->i1) {
|
|
for(c = 0; c < 3; c++) {
|
|
if(*(op->v1 + c) > *(coord + c))
|
|
*(op->v1 + c) = *(coord + c);
|
|
if(*(op->v2 + c) < *(coord + c))
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
} else {
|
|
for(c = 0; c < 3; c++) {
|
|
*(op->v1 + c) = *(coord + c);
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
}
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_CSetCameraMinMax:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
MatrixTransformC44fAs33f3f(op->mat1, coord, v1);
|
|
/* convert to view-space */
|
|
coord = v1;
|
|
if(op->i1) {
|
|
for(c = 0; c < 3; c++) {
|
|
if(*(op->v1 + c) > *(coord + c))
|
|
*(op->v1 + c) = *(coord + c);
|
|
if(*(op->v2 + c) < *(coord + c))
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
} else {
|
|
for(c = 0; c < 3; c++) {
|
|
*(op->v1 + c) = *(coord + c);
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
}
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_CSetSumSqDistToPt:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
float dist;
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
dist = (float) diff3f(op->v1, coord);
|
|
op->d1 += dist * dist;
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_CSetMaxDistToPt:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
float dist;
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
dist = (float) diff3f(op->v1, coord);
|
|
if(dist > op->f1)
|
|
op->f1 = dist;
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_CSetMoment:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
subtract3f(cs->coordPtr(a1), op->v1, v1);
|
|
v2 = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
|
|
op->d[0][0] += v2 - v1[0] * v1[0];
|
|
op->d[0][1] += -v1[0] * v1[1];
|
|
op->d[0][2] += -v1[0] * v1[2];
|
|
op->d[1][0] += -v1[1] * v1[0];
|
|
op->d[1][1] += v2 - v1[1] * v1[1];
|
|
op->d[1][2] += -v1[1] * v1[2];
|
|
op->d[2][0] += -v1[2] * v1[0];
|
|
op->d[2][1] += -v1[2] * v1[1];
|
|
op->d[2][2] += v2 - v1[2] * v1[2];
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
/* coord-set based properties, iterating as all coordsets within atoms */
|
|
for(b = 0; b < I->NCSet; b++) {
|
|
if(I->DiscreteFlag) {
|
|
cs = I->DiscreteCSet[a];
|
|
} else {
|
|
cs = I->CSet[b];
|
|
}
|
|
if(cs) {
|
|
s = ai->selEntry;
|
|
inv_flag = false;
|
|
if(SelectorIsMember(G, s, sele)) {
|
|
switch (op->code) {
|
|
case OMOP_CameraMinMax:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
MatrixTransformC44fAs33f3f(op->mat1, coord, v1);
|
|
/* convert to view-space */
|
|
coord = v1;
|
|
if(op->i1) {
|
|
for(c = 0; c < 3; c++) {
|
|
if(*(op->v1 + c) > *(coord + c))
|
|
*(op->v1 + c) = *(coord + c);
|
|
if(*(op->v2 + c) < *(coord + c))
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
} else {
|
|
for(c = 0; c < 3; c++) {
|
|
*(op->v1 + c) = *(coord + c);
|
|
*(op->v2 + c) = *(coord + c);
|
|
}
|
|
}
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_MaxDistToPt:
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
float dist;
|
|
coord = cs->coordPtr(a1);
|
|
if(op->i2) { /* do we want transformed coordinates? */
|
|
if(use_matrices) {
|
|
if(!cs->Matrix.empty()) { /* state transformation */
|
|
transform44d3f(cs->Matrix.data(), coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
if(I->TTTFlag) {
|
|
transformTTT44f3f(I->TTT, coord, v1);
|
|
coord = v1;
|
|
}
|
|
}
|
|
dist = (float) diff3f(coord, op->v1);
|
|
if(dist > op->f1)
|
|
op->f1 = dist;
|
|
op->i1++;
|
|
}
|
|
break;
|
|
case OMOP_INVA:
|
|
if(!cs->objMolOpInvalidated) {
|
|
/* performance optimization: avoid repeatedly
|
|
calling invalidate on the same coord sets */
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) /* selection touches this coordinate set */
|
|
inv_flag = true; /* so set the invalidation flag */
|
|
}
|
|
break;
|
|
case OMOP_VERT:
|
|
/* get the atom index whether it's discrete or not */
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
/* if a1 is a valid atom index, then copy it's xyz coordinates
|
|
* into vv1; increment the counter, nvv1 */
|
|
VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
|
|
vv2 = cs->coordPtr(a1);
|
|
vv1 = op->vv1 + (op->nvv1 * 3);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
op->nvv1++;
|
|
}
|
|
break;
|
|
case OMOP_SVRT:
|
|
/* gives us only vertices for a specific coordinate set */
|
|
if(b == op->i1) {
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
VLACheck(op->vv1, float, (op->nvv1 * 3) + 2);
|
|
VLACheck(op->i1VLA, int, op->nvv1);
|
|
op->i1VLA[op->nvv1] = a; /* save atom index for later comparisons */
|
|
vv2 = cs->coordPtr(a1);
|
|
vv1 = op->vv1 + (op->nvv1 * 3);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
*(vv1++) = *(vv2++);
|
|
op->nvv1++;
|
|
}
|
|
}
|
|
break;
|
|
case OMOP_MOME:
|
|
/* Moment of inertia tensor - unweighted - assumes v1 is center of molecule */
|
|
a1 = cs->atmToIdx(a);
|
|
if(a1 >= 0) {
|
|
subtract3f(cs->coordPtr(a1), op->v1, v1);
|
|
v2 = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
|
|
op->d[0][0] += v2 - v1[0] * v1[0];
|
|
op->d[0][1] += -v1[0] * v1[1];
|
|
op->d[0][2] += -v1[0] * v1[2];
|
|
op->d[1][0] += -v1[1] * v1[0];
|
|
op->d[1][1] += v2 - v1[1] * v1[1];
|
|
op->d[1][2] += -v1[1] * v1[2];
|
|
op->d[2][0] += -v1[2] * v1[0];
|
|
op->d[2][1] += -v1[2] * v1[1];
|
|
op->d[2][2] += v2 - v1[2] * v1[2];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
switch (op->code) {
|
|
/* full coord-set based */
|
|
case OMOP_INVA:
|
|
/* shouldn't this be calling the object invalidation routine instead? */
|
|
if(inv_flag) {
|
|
cs->objMolOpInvalidated = true;
|
|
for (auto d = cRep_t(0); d < cRepCnt; ++d) {
|
|
if ((1 << d) & op->i1) {
|
|
cs->invalidateRep(d, cRepInv_t(op->i2));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if(I->DiscreteFlag) {
|
|
/* don't iterate every coordinate set for discrete objects! */
|
|
break;
|
|
}
|
|
}
|
|
} /* end coordset section */
|
|
break;
|
|
}
|
|
ai++;
|
|
}
|
|
} /* case: default */
|
|
break;
|
|
}
|
|
if(hit_flag) {
|
|
switch (op->code) {
|
|
case OMOP_COLR:
|
|
ExecutiveUpdateColorDepends(I->G, I);
|
|
break;
|
|
case OMOP_TTTF:
|
|
ObjectMoleculeTransformTTTf(I, op->ttt, -1);
|
|
break;
|
|
case OMOP_LABL:
|
|
I->invalidate(cRepLabel, cRepInvText, -1);
|
|
break;
|
|
case OMOP_AlterState: /* overly coarse - doing all states, could do just 1 */
|
|
if(!op->i3) { /* not read_only? */
|
|
I->invalidate(cRepAll, cRepInvRep, -1);
|
|
SceneChanged(G);
|
|
}
|
|
break;
|
|
case OMOP_CSetIdxSetFlagged:
|
|
I->invalidate(cRepAll, cRepInvRep, -1);
|
|
SceneChanged(G);
|
|
break;
|
|
case OMOP_SaveUndo:
|
|
op->i2 = true;
|
|
ObjectMoleculeSaveUndo(I, op->i1, false);
|
|
break;
|
|
case OMOP_OnOff:
|
|
ExecutiveSetObjVisib(G, I->Name, op->i1, false);
|
|
break;
|
|
case OMOP_RevalenceFromSource:
|
|
if(ObjectMoleculeXferValences(I, op->i1, op->i2,
|
|
op->i3, op->obj3, op->i4, op->i5, op->i6)) {
|
|
ObjectMoleculeVerifyChemistry(I, op->i3);
|
|
I->invalidate(cRepAll, cRepInvBonds, op->i3);
|
|
}
|
|
break;
|
|
case OMOP_RevalenceByGuessing:
|
|
{
|
|
int *flag1 = pymol::calloc<int>(I->NAtom);
|
|
int *flag2 = pymol::calloc<int>(I->NAtom);
|
|
if(flag1 && flag2) {
|
|
int a;
|
|
int *f1 = flag1;
|
|
int *f2 = flag2;
|
|
const AtomInfoType *ai = I->AtomInfo.data();
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
*(f1++) = SelectorIsMember(G, ai->selEntry, op->i1);
|
|
*(f2++) = SelectorIsMember(G, ai->selEntry, op->i2);
|
|
ai++;
|
|
}
|
|
{
|
|
int target_state = op->i3;
|
|
if(target_state < 0)
|
|
target_state = 0; /* TO DO */
|
|
ObjectMoleculeGuessValences(I, target_state, flag1, flag2, op->i4);
|
|
ObjectMoleculeVerifyChemistry(I, target_state);
|
|
I->invalidate(cRepAll, cRepInvBonds, target_state);
|
|
}
|
|
FreeP(flag1);
|
|
FreeP(flag2);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* always run on exit... */
|
|
switch (op->code) {
|
|
case OMOP_LABL:
|
|
if (op->i2 != cExecutiveLabelEvalOn){
|
|
break;
|
|
}
|
|
case OMOP_ALTR:
|
|
case OMOP_AlterState:
|
|
#ifndef _PYMOL_NOPY
|
|
Py_XDECREF(expr_co);
|
|
#endif
|
|
break;
|
|
}
|
|
/* */
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeGetAtomSele(const ObjectMolecule * I, int index, char *buffer)
|
|
{
|
|
PyMOLGlobals * G = I->G;
|
|
assert(index < I->NAtom);
|
|
auto* ai = I->AtomInfo + index;
|
|
char inscode_str[2] = { ai->inscode, '\0' };
|
|
|
|
snprintf(buffer, OrthoLineLength, "/%s/%s/%s/%s`%d%s/%s`%s", I->Name,
|
|
LexStr(G, ai->segi),
|
|
LexStr(G, ai->chain),
|
|
LexStr(G, ai->resn), ai->resv, inscode_str,
|
|
LexStr(G, ai->name), ai->alt);
|
|
}
|
|
|
|
/**
|
|
* Get Atom selection string
|
|
* @param I target Object Molecule
|
|
* @param index atom index of I
|
|
*/
|
|
|
|
std::string ObjectMoleculeGetAtomSele(const ObjectMolecule * I, int index)
|
|
{
|
|
PyMOLGlobals * G = I->G;
|
|
assert(index < I->NAtom);
|
|
auto* ai = I->AtomInfo + index;
|
|
char inscode_str[2] = { ai->inscode, '\0' };
|
|
std::string buffer = pymol::string_format("/%s/%s/%s/%s`%d%s/%s`%s", I->Name,
|
|
LexStr(G, ai->segi),
|
|
LexStr(G, ai->chain),
|
|
LexStr(G, ai->resn), ai->resv, inscode_str,
|
|
LexStr(G, ai->name), ai->alt);
|
|
return buffer;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
|
|
/**
|
|
* Get Atom selection string
|
|
* @param I target Object Molecule
|
|
* @param index atom index of I
|
|
*/
|
|
|
|
std::string ObjectMolecule::describeElement(int index) const
|
|
{
|
|
auto I = this;
|
|
auto buffer = ObjectMoleculeGetAtomSele(I, index);
|
|
if(!I->AtomInfo[index].alt[0]) {
|
|
// don't include the trailing backtick
|
|
buffer.pop_back();
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
std::string ObjectMoleculeGetAtomSeleLog(const ObjectMolecule* I, int index, int quote)
|
|
{
|
|
OrthoLineType buffer;
|
|
ObjectMoleculeGetAtomSeleLog(I, index, buffer, quote);
|
|
return buffer;
|
|
}
|
|
|
|
void ObjectMoleculeGetAtomSeleLog(const ObjectMolecule * I, int index, char *buffer, int quote)
|
|
{
|
|
char *p = quote ? buffer + 1 : buffer;
|
|
|
|
if(SettingGetGlobal_b(I->G, cSetting_robust_logs)) {
|
|
ObjectMoleculeGetAtomSele(I, index, p);
|
|
} else {
|
|
sprintf(p, "(%s`%d)", I->Name, index + 1);
|
|
}
|
|
|
|
if (quote) {
|
|
int len = strlen(p);
|
|
buffer[0] = buffer[len + 1] = '"';
|
|
buffer[len + 2] = 0;
|
|
}
|
|
}
|
|
|
|
std::string ObjectMoleculeGetAtomSeleFast(const ObjectMolecule* I, int index)
|
|
{
|
|
auto G = I->G;
|
|
auto* ai = I->AtomInfo + index;
|
|
char inscodestr[2] = {ai->inscode, 0};
|
|
return pymol::string_format("(/'%s'/'%s'/'%s'/'%s'`%d%s/'%s'`'%s')", I->Name,
|
|
LexStr(G, ai->segi), LexStr(G, ai->chain), LexStr(G, ai->resn), ai->resv,
|
|
inscodestr, ai->name, ai->alt);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMolecule::getNFrame() const
|
|
{
|
|
return NCSet;
|
|
}
|
|
|
|
struct _CCoordSetUpdateThreadInfo {
|
|
CoordSet *cs;
|
|
int a;
|
|
};
|
|
|
|
void CoordSetUpdateThread(CCoordSetUpdateThreadInfo * T)
|
|
{
|
|
if(T->cs) {
|
|
T->cs->update(T->a);
|
|
}
|
|
}
|
|
|
|
#ifndef _PYMOL_NOPY
|
|
static void ObjMolCoordSetUpdateSpawn(PyMOLGlobals * G,
|
|
CCoordSetUpdateThreadInfo * Thread, int n_thread,
|
|
int n_total)
|
|
{
|
|
if(n_total == 1) {
|
|
CoordSetUpdateThread(Thread);
|
|
} else if(n_total) {
|
|
int blocked;
|
|
PyObject *info_list;
|
|
int a, n = 0;
|
|
blocked = PAutoBlock(G);
|
|
|
|
PRINTFB(G, FB_Scene, FB_Blather)
|
|
" Scene: updating coordinate sets with %d threads...\n", n_thread ENDFB(G);
|
|
info_list = PyList_New(n_total);
|
|
for(a = 0; a < n_total; a++) {
|
|
PyList_SetItem(info_list, a, PyCapsule_New(Thread + a, nullptr, nullptr));
|
|
n++;
|
|
}
|
|
PXDecRef(PYOBJECT_CALLMETHOD
|
|
(G->P_inst->cmd, "_coordset_update_spawn", "Oi", info_list, n_thread));
|
|
Py_DECREF(info_list);
|
|
PAutoUnblock(G, blocked);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMolecule::update()
|
|
{
|
|
auto I = this;
|
|
int a; /*, ok; */
|
|
|
|
OrthoBusyPrime(G);
|
|
/* if the cached representation is invalid, reset state */
|
|
if(!I->RepVisCacheValid) {
|
|
/* note which representations are active */
|
|
/* for each atom in each coordset, blank out the representation cache */
|
|
if(I->NCSet > 1) {
|
|
const AtomInfoType *ai = I->AtomInfo.data();
|
|
I->RepVisCache = 0;
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
I->RepVisCache |= ai->visRep;
|
|
ai++;
|
|
}
|
|
} else {
|
|
I->RepVisCache = cRepBitmask; /* if only one coordinate set, then
|
|
* there's no benefit to pre-filtering
|
|
* the representations... */
|
|
}
|
|
I->RepVisCacheValid = true;
|
|
}
|
|
{
|
|
/* determine the start/stop states */
|
|
int start = 0;
|
|
int stop = I->NCSet;
|
|
/* set start and stop given an object */
|
|
ObjectAdjustStateRebuildRange(I, &start, &stop);
|
|
if((I->NCSet == 1)
|
|
&& (SettingGet_b(G, I->Setting.get(), nullptr, cSetting_static_singletons))) {
|
|
start = 0;
|
|
stop = 1;
|
|
}
|
|
if(stop > I->NCSet)
|
|
stop = I->NCSet;
|
|
|
|
/* single and multithreaded coord set updates */
|
|
{
|
|
#ifndef _PYMOL_NOPY
|
|
int n_thread = SettingGetGlobal_i(G, cSetting_max_threads);
|
|
int multithread = SettingGetGlobal_i(G, cSetting_async_builds);
|
|
|
|
if(multithread && (n_thread) && (stop - start) > 1) {
|
|
int cnt = 0;
|
|
|
|
/* must precalculate to avoid race-condition since this isn't
|
|
mutexed yet and neighbors are needed by cartoons */
|
|
this->getNeighborArray();
|
|
|
|
for(a = start; a < stop; a++)
|
|
if((a<I->NCSet) && I->CSet[a])
|
|
cnt++;
|
|
{
|
|
CCoordSetUpdateThreadInfo *thread_info = pymol::malloc<CCoordSetUpdateThreadInfo>(cnt);
|
|
if(thread_info) {
|
|
cnt = 0;
|
|
for(a = start; a < stop; a++) {
|
|
if((a<I->NCSet) && I->CSet[a]) {
|
|
thread_info[cnt].cs = I->CSet[a];
|
|
thread_info[cnt].a = a;
|
|
cnt++;
|
|
}
|
|
}
|
|
ObjMolCoordSetUpdateSpawn(G, thread_info, n_thread, cnt);
|
|
FreeP(thread_info);
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
|
#endif
|
|
{ /* single thread */
|
|
for(a = start; a < stop; a++) {
|
|
if((a<I->NCSet) && I->CSet[a] && (!G->Interrupt)) {
|
|
/* status bar */
|
|
OrthoBusySlow(G, a, I->NCSet);
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Blather)
|
|
" ObjectMolecule-DEBUG: updating representations for state %d of \"%s\".\n",
|
|
a + 1, I->Name ENDFB(G);
|
|
I->CSet[a]->update(a);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} /* end block */
|
|
|
|
PRINTFD(G, FB_ObjectMolecule)
|
|
" ObjectMolecule: updates complete for object %s.\n", I->Name ENDFD;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
void ObjectMolecule::invalidate(cRep_t rep, cRepInv_t level, int state)
|
|
{
|
|
auto I = this;
|
|
int a;
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: entered. rep: %d level: %d\n", __func__, rep, level ENDFD;
|
|
|
|
auto const level_actual = level;
|
|
|
|
// Remove the "purge" bit
|
|
level = static_cast<decltype(level)>(level & ~cRepInvPurgeMask);
|
|
|
|
if(level >= cRepInvVisib) {
|
|
I->RepVisCacheValid = false;
|
|
}
|
|
|
|
if (level >= cRepInvBondsNoNonbonded) {
|
|
if (level < cRepInvBonds) {
|
|
level = cRepInvBonds;
|
|
} else {
|
|
ObjectMoleculeUpdateNonbonded(I);
|
|
}
|
|
}
|
|
|
|
if(level >= cRepInvBonds) {
|
|
this->Neighbor.reset();
|
|
if(I->Sculpt) {
|
|
DeleteP(I->Sculpt);
|
|
}
|
|
if(level >= cRepInvAtoms) {
|
|
SelectorUpdateObjectSele(I->G, I);
|
|
}
|
|
}
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: invalidating representations...\n", __func__ ENDFD;
|
|
|
|
if ( level >= cRepInvColor ) {
|
|
/* after label, this gets called, so we shouldn't invalidate types b/c PYMOL-317
|
|
not sure if that is the exact level we should cut this off at
|
|
1/28/13 BB: changed from >cRepInvText to >=cRepInvColor for invalidating surface
|
|
colors during gadget invalidation */
|
|
|
|
int start = 0;
|
|
int stop = I->NCSet;
|
|
|
|
if(state >= 0) {
|
|
start = state;
|
|
stop = state + 1;
|
|
}
|
|
if(stop > I->NCSet)
|
|
stop = I->NCSet;
|
|
for(a = start; a < stop; a++) {
|
|
CoordSet *cset = 0;
|
|
cset = I->CSet[a];
|
|
if(cset) {
|
|
cset->invalidateRep(rep, level_actual);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" %s: leaving...\n", __func__ ENDFD;
|
|
|
|
}
|
|
|
|
void ObjectMoleculeInvalidateAtomType(ObjectMolecule *I, int state){
|
|
CoordSet *cset = 0;
|
|
int ai, atm;
|
|
AtomInfoType *at;
|
|
cset = I->CSet[state];
|
|
if (state < 0){
|
|
for (ai=0; ai < I->NAtom; ai++){
|
|
at = &I->AtomInfo[ai];
|
|
at->textType = 0;
|
|
}
|
|
} else {
|
|
for (ai=0; ai < cset->NIndex; ai++){
|
|
atm = cset->IdxToAtm[ai];
|
|
if (atm>=0){
|
|
at = &I->AtomInfo[ai];
|
|
at->textType = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeMoveAtom(ObjectMolecule * I, int state, int index, const float *v, int mode,
|
|
int log)
|
|
{
|
|
int result = 0;
|
|
PyMOLGlobals *G = I->G;
|
|
CoordSet *cs;
|
|
if(!(I->AtomInfo[index].protekted == cAtomProtected_explicit)) {
|
|
if(state < 0)
|
|
state = 0;
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
if((!I->CSet[state]) && (SettingGet_b(G, I->Setting.get(), nullptr, cSetting_all_states)))
|
|
state = 0;
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
result = CoordSetMoveAtom(I->CSet[state], index, v, mode);
|
|
cs->invalidateRep(cRepAll, cRepInvCoord);
|
|
ExecutiveUpdateCoordDepends(G, I);
|
|
}
|
|
}
|
|
if(log) {
|
|
OrthoLineType line, buffer;
|
|
if(SettingGetGlobal_i(G, cSetting_logging)) {
|
|
ObjectMoleculeGetAtomSele(I, index, buffer);
|
|
sprintf(line, "cmd.translate_atom(\"%s\",%15.9f,%15.9f,%15.9f,%d,%d,%d)\n",
|
|
buffer, v[0], v[1], v[2], state + 1, mode, 0);
|
|
PLog(G, line, cPLog_no_flush);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeMoveAtomLabel(ObjectMolecule * I, int state, int index, float *v, int log, float *diff)
|
|
{
|
|
int result = 0;
|
|
CoordSet *cs;
|
|
if(!(I->AtomInfo[index].protekted == cAtomProtected_explicit)) {
|
|
if(state < 0)
|
|
state = 0;
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
if((!I->CSet[state])
|
|
&& (SettingGet_b(I->G, I->Setting.get(), nullptr, cSetting_all_states)))
|
|
state = 0;
|
|
cs = I->CSet[state];
|
|
if(cs) {
|
|
result = CoordSetMoveAtomLabel(I->CSet[state], index, v, diff);
|
|
cs->invalidateRep(cRepLabel, cRepInvCoord);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeInitBondPath(ObjectMolecule * I, ObjectMoleculeBPRec * bp)
|
|
{
|
|
int a;
|
|
bp->dist = pymol::malloc<int>(I->NAtom);
|
|
bp->list = pymol::malloc<int>(I->NAtom);
|
|
for(a = 0; a < I->NAtom; a++)
|
|
bp->dist[a] = -1;
|
|
bp->n_atom = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculePurgeBondPath(ObjectMolecule * I, ObjectMoleculeBPRec * bp)
|
|
{
|
|
FreeP(bp->dist);
|
|
FreeP(bp->list);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeGetBondPaths(ObjectMolecule * I, int atom,
|
|
int max, ObjectMoleculeBPRec * bp)
|
|
{
|
|
/* returns list of bond counts from atom to all others
|
|
dist and list must be vla array pointers or nullptr */
|
|
|
|
int b_cnt = 0;
|
|
|
|
/* reinitialize dist array (if we've done at least one pass) */
|
|
|
|
for (int a = 0; a < bp->n_atom; a++)
|
|
bp->dist[bp->list[a]] = -1;
|
|
|
|
bp->n_atom = 0;
|
|
bp->dist[atom] = 0;
|
|
bp->list[bp->n_atom] = atom;
|
|
bp->n_atom++;
|
|
|
|
int cur = 0;
|
|
while(1) {
|
|
b_cnt++;
|
|
if(b_cnt > max)
|
|
break;
|
|
|
|
unsigned n_cur = bp->n_atom - cur;
|
|
|
|
/* iterate through all current atoms */
|
|
|
|
if(!n_cur)
|
|
break;
|
|
while(n_cur--) {
|
|
int const a1 = bp->list[cur++];
|
|
for (auto const& neighbor : AtomNeighbors(I, a1)) {
|
|
auto const a2 = neighbor.atm;
|
|
if(bp->dist[a2] < 0) { /* for each atom not yet sampled... */
|
|
bp->dist[a2] = b_cnt;
|
|
bp->list[bp->n_atom] = a2;
|
|
bp->n_atom++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (bp->n_atom);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ***ObjectMoleculeGetBondPrint(ObjectMolecule * I, int max_bond, int max_type,
|
|
int *dim)
|
|
{
|
|
int a, b, i, c;
|
|
int at1, at2;
|
|
int ***result = nullptr;
|
|
ObjectMoleculeBPRec bp;
|
|
|
|
dim[0] = max_type + 1;
|
|
dim[1] = max_type + 1;
|
|
dim[2] = max_bond + 1;
|
|
|
|
result = (int ***) UtilArrayCalloc((unsigned int *) dim, 3, sizeof(int));
|
|
|
|
ObjectMoleculeInitBondPath(I, &bp);
|
|
for(a = 0; a < I->NAtom; a++) {
|
|
at1 = I->AtomInfo[a].customType;
|
|
if((at1 >= 0) && (at1 <= max_type)) {
|
|
ObjectMoleculeGetBondPaths(I, a, max_bond, &bp);
|
|
for(b = 0; b < bp.n_atom; b++) {
|
|
i = bp.list[b];
|
|
at2 = I->AtomInfo[i].customType;
|
|
if((at2 >= 0) && (at2 <= max_type)) {
|
|
c = bp.dist[i];
|
|
result[at1][at2][c]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ObjectMoleculePurgeBondPath(I, &bp);
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
float ObjectMoleculeGetAvgHBondVector(ObjectMolecule * I, int atom,
|
|
int state, float *v, float *incoming)
|
|
|
|
|
|
/* computes average / optima hydrogen bonding vector for an atom */
|
|
{
|
|
float result = 0.0;
|
|
int vec_cnt = 0;
|
|
float v_atom[3], v_neigh[3], v_diff[3], v_acc[3] = { 0.0, 0.0, 0.0 };
|
|
int sp2_flag = false;
|
|
int order;
|
|
|
|
auto const* cs = I->getCoordSet(state);
|
|
if(cs) {
|
|
if (CoordSetGetAtomVertex(cs, atom, v_atom)) { /* atom exists in this C-set */
|
|
for (auto const& neighbor : AtomNeighbors(I, atom)) {
|
|
auto const a2 = neighbor.atm;
|
|
order = I->Bond[neighbor.bond].order;
|
|
if((order == 2) || (order == 4)) {
|
|
sp2_flag = true;
|
|
}
|
|
|
|
const auto& ai = I->AtomInfo[atom];
|
|
|
|
bool isH = I->AtomInfo[a2].protons == cAN_H;
|
|
|
|
if (!isH || (ai.protons == cAN_O && isH))
|
|
{ /* ignore hydrogens unless water*/
|
|
if (CoordSetGetAtomVertex(cs, a2, v_neigh)) {
|
|
subtract3f(v_atom, v_neigh, v_diff);
|
|
normalize3f(v_diff);
|
|
add3f(v_diff, v_acc, v_acc);
|
|
vec_cnt++;
|
|
}
|
|
}
|
|
}
|
|
if(vec_cnt) {
|
|
result = (float) length3f(v_acc);
|
|
result = result / vec_cnt;
|
|
normalize23f(v_acc, v);
|
|
} else {
|
|
copy3f(v_acc, v);
|
|
}
|
|
|
|
if(incoming && (vec_cnt == 1) && (fabs(dot_product3f(v, incoming)) < 0.99F)) {
|
|
/* if we know where the donor is, and the acceptor can
|
|
potentially rotate the lone pair, then we should optimally
|
|
orient the acceptor, if possible */
|
|
AtomInfoType *ai = I->AtomInfo + atom;
|
|
float v_perp[3];
|
|
float v_tmp1[3], v_tmp2[3];
|
|
if(((ai->protons == cAN_O) && (!sp2_flag)) || /* C-O-H */
|
|
((ai->protons == cAN_N) && (sp2_flag))) { /* C=N-H */
|
|
|
|
remove_component3f(incoming, v, v_perp);
|
|
normalize3f(v_perp);
|
|
scale3f(v, 0.333644F, v_tmp1);
|
|
scale3f(v_perp, 0.942699F, v_tmp2);
|
|
add3f(v_tmp1, v_tmp2, v_tmp2);
|
|
subtract3f(v, v_tmp2, v);
|
|
normalize3f(v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeGetAtomVertex(const ObjectMolecule * I, int state, int index, float *v)
|
|
{
|
|
int result = 0;
|
|
if(state < 0)
|
|
state = SettingGet_i(I->G, nullptr, I->Setting.get(), cSetting_state) - 1;
|
|
if(state < 0)
|
|
state = SceneGetState(I->G);
|
|
if(I->NCSet == 1)
|
|
state = 0; /* static singletons always active here it seems */
|
|
state = state % I->NCSet;
|
|
if((!I->CSet[state])
|
|
&& (SettingGet_b(I->G, I->Setting.get(), nullptr, cSetting_all_states)))
|
|
state = 0;
|
|
if(I->CSet[state])
|
|
result = CoordSetGetAtomVertex(I->CSet[state], index, v);
|
|
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeGetAtomTxfVertex(const ObjectMolecule * I, int state, int index, float *v)
|
|
{
|
|
int result = 0;
|
|
const CoordSet* cs = nullptr;
|
|
if (I->DiscreteFlag){
|
|
cs = I->DiscreteCSet[index];
|
|
}
|
|
if(state < 0){
|
|
state = SettingGet_i(I->G, nullptr, I->Setting.get(), cSetting_state) - 1;
|
|
}
|
|
if(state < 0)
|
|
state = SceneGetState(I->G);
|
|
if(I->NCSet == 1)
|
|
state = 0; /* static singletons always active here it seems */
|
|
state = state % I->NCSet;
|
|
{
|
|
if (!cs)
|
|
cs = I->CSet[state];
|
|
if((!cs) && (SettingGet_b(I->G, I->Setting.get(), nullptr, cSetting_all_states))) {
|
|
state = 0;
|
|
cs = I->CSet[state];
|
|
}
|
|
if(cs) {
|
|
result = CoordSetGetAtomTxfVertex(cs, index, v);
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*========================================================================*/
|
|
int ObjectMoleculeSetAtomVertex(ObjectMolecule * I, int state, int index, float *v)
|
|
{
|
|
int result = 0;
|
|
if(state < 0)
|
|
state = SettingGet_i(I->G, nullptr, I->Setting.get(), cSetting_state) - 1;
|
|
if(state < 0)
|
|
state = SceneGetState(I->G);
|
|
if(I->NCSet == 1)
|
|
state = 0;
|
|
state = state % I->NCSet;
|
|
if((!I->CSet[state])
|
|
&& (SettingGet_b(I->G, I->Setting.get(), nullptr, cSetting_all_states)))
|
|
state = 0;
|
|
if(I->CSet[state])
|
|
result = CoordSetSetAtomVertex(I->CSet[state], index, v);
|
|
return (result);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMolecule::render(RenderInfo * info)
|
|
{
|
|
auto I = this;
|
|
int state = info->state;
|
|
const RenderPass pass = info->pass;
|
|
CoordSet *cs;
|
|
int pop_matrix = false;
|
|
int use_matrices = SettingGet_i(I->G, I->Setting.get(), nullptr, cSetting_matrix_mode);
|
|
if(use_matrices<0) use_matrices = 0;
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" ObjectMolecule: rendering %s pass %d...\n", I->Name, static_cast<int>(pass) ENDFD;
|
|
|
|
ObjectPrepareContext(I, info);
|
|
|
|
for(StateIterator iter(G, I->Setting.get(), state, I->NCSet); iter.next();) {
|
|
cs = I->CSet[iter.state];
|
|
if(cs) {
|
|
if(use_matrices)
|
|
pop_matrix = ObjectStatePushAndApplyMatrix(cs, info);
|
|
cs->render(info);
|
|
if(pop_matrix)
|
|
ObjectStatePopMatrix(cs, info);
|
|
}
|
|
}
|
|
PRINTFD(I->G, FB_ObjectMolecule)
|
|
" ObjectMolecule: rendering complete for object %s.\n", I->Name ENDFD;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeDummyUpdate(ObjectMolecule * I, int mode)
|
|
{
|
|
switch (mode) {
|
|
case cObjectMoleculeDummyOrigin:
|
|
SceneOriginGet(I->G, I->CSet[0]->Coord.data());
|
|
break;
|
|
case cObjectMoleculeDummyCenter:
|
|
SceneGetCenter(I->G, I->CSet[0]->Coord.data());
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
ObjectMolecule *ObjectMoleculeDummyNew(PyMOLGlobals * G, int type)
|
|
{
|
|
auto I = new ObjectMolecule(G, false);
|
|
I->AtomInfo.resize(I->NAtom = 1);
|
|
|
|
auto* cset = new CoordSet(G);
|
|
cset->Obj = I;
|
|
cset->setNIndex(1);
|
|
cset->enumIndices();
|
|
|
|
I->CSet.resize(I->NCSet = 1);
|
|
I->CSet[0] = cset;
|
|
|
|
return I;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
|
|
ObjectMolecule::ObjectMolecule(PyMOLGlobals * G, int discreteFlag) : pymol::CObject(G)
|
|
{
|
|
auto I = this;
|
|
int a;
|
|
I->type = cObjectMolecule;
|
|
I->CSet = pymol::vla<CoordSet*>(10); /* auto-zero */
|
|
I->DiscreteFlag = discreteFlag;
|
|
if(I->DiscreteFlag) { /* discrete objects don't share atoms between states */
|
|
I->DiscreteAtmToIdx = pymol::vla<int>(0);
|
|
I->DiscreteCSet = pymol::vla<CoordSet*>(0);
|
|
} else {
|
|
I->DiscreteAtmToIdx = nullptr;
|
|
I->DiscreteCSet = nullptr;
|
|
}
|
|
I->AtomInfo = pymol::vla<AtomInfoType>(10);
|
|
for(a = 0; a <= cUndoMask; a++) {
|
|
I->UndoCoord[a] = nullptr;
|
|
I->UndoState[a] = -1;
|
|
}
|
|
I->UndoIter = 0;
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
void ObjectMoleculeCopyNoAlloc(const ObjectMolecule* obj, ObjectMolecule* I)
|
|
{
|
|
PyMOLGlobals * G = const_cast<PyMOLGlobals*>(obj->G);
|
|
|
|
int a;
|
|
BondType *i0;
|
|
const BondType *i1;
|
|
(*I) = (*obj);
|
|
I->Sculpt = nullptr;
|
|
I->Setting.reset(SettingCopyAll(G, obj->Setting.get(), nullptr));
|
|
|
|
I->ViewElem = nullptr;
|
|
I->gridSlotSelIndicatorsCGO = nullptr;
|
|
|
|
for(a = 0; a <= cUndoMask; a++)
|
|
I->UndoCoord[a] = nullptr;
|
|
I->CSet = pymol::vla<CoordSet*>(I->NCSet); /* auto-zero */
|
|
for(a = 0; a < I->NCSet; a++) {
|
|
I->CSet[a] = CoordSetCopy(obj->CSet[a]);
|
|
if (I->CSet[a])
|
|
I->CSet[a]->Obj = I;
|
|
}
|
|
|
|
if(obj->CSTmpl)
|
|
I->CSTmpl = CoordSetCopy(obj->CSTmpl);
|
|
|
|
if (obj->DiscreteFlag){
|
|
int sz = VLAGetSize(obj->DiscreteAtmToIdx);
|
|
I->DiscreteAtmToIdx = VLACopy2(obj->DiscreteAtmToIdx);
|
|
I->DiscreteCSet = pymol::vla<CoordSet*>(sz);
|
|
I->updateAtmToIdx();
|
|
}
|
|
I->Bond = pymol::vla<BondType>(I->NBond);
|
|
i0 = I->Bond.data();
|
|
i1 = obj->Bond.data();
|
|
for(a = 0; a < I->NBond; a++) {
|
|
AtomInfoBondCopy(G, i1++, i0++);
|
|
}
|
|
|
|
// unfortunately, the AtomInfoType is not copy-constructable yet
|
|
// assert copy done correct
|
|
if (I->AtomInfo.size() != obj->AtomInfo.size()) {
|
|
throw "AtomInfo copy failed";
|
|
}
|
|
AtomInfoType *a0 = I->AtomInfo.data();
|
|
const AtomInfoType* a1 = obj->AtomInfo.data();
|
|
memset(a0, 0, sizeof(AtomInfoType) * I->NAtom);
|
|
for(a = 0; a < I->NAtom; a++)
|
|
AtomInfoCopy(G, a1++, a0++);
|
|
}
|
|
|
|
ObjectMolecule *ObjectMoleculeCopy(const ObjectMolecule * obj)
|
|
{
|
|
PyMOLGlobals * G = const_cast<PyMOLGlobals*>(obj->G);
|
|
auto I = new ObjectMolecule(G, obj->DiscreteFlag);
|
|
ObjectMoleculeCopyNoAlloc(obj, I);
|
|
return (I);
|
|
|
|
}
|
|
|
|
/*========================================================================*/
|
|
/**
|
|
* Set the order of coordinate sets with an index array
|
|
*/
|
|
int ObjectMoleculeSetStateOrder(ObjectMolecule * I, int * order, int len) {
|
|
int a;
|
|
CoordSet ** csets = VLAlloc(CoordSet *, I->NCSet);
|
|
|
|
ok_assert(1, len == I->NCSet);
|
|
|
|
// invalidate
|
|
I->invalidate(cRepAll, cRepInvAll, -1);
|
|
|
|
// new coord set array
|
|
for(a = 0; a < I->NCSet; a++) {
|
|
int i = order[a];
|
|
ok_assert(1, 0 <= i && i < I->NCSet);
|
|
csets[a] = I->CSet[i];
|
|
}
|
|
|
|
VLAFreeP(I->CSet);
|
|
I->CSet = pymol::vla_take_ownership(csets);
|
|
|
|
return true;
|
|
ok_except1:
|
|
ErrMessage(I->G, "ObjectMoleculeSetStateOrder", "failed");
|
|
VLAFreeP(csets);
|
|
return false;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
pymol::Result<> ObjectMoleculeDeleteStates(ObjectMolecule* I, const std::vector<int>& states)
|
|
{
|
|
auto& CSet = I->CSet;
|
|
|
|
// first pass, check for invalid states
|
|
for (auto state : states) {
|
|
if (state < 0 || state >= I->NCSet) {
|
|
auto msg = pymol::string_format("Invalid state index: %d", state);
|
|
I->G->Feedback->addColored(msg.c_str(), FB_Errors);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
// second pass, delete states
|
|
for (auto it = states.rbegin(); it != states.rend(); ++it) {
|
|
int state = *it;
|
|
DeleteP(CSet[state]);
|
|
CSet.erase(state, 1);
|
|
}
|
|
I->NCSet -= states.size();
|
|
CSet.resize(I->NCSet);
|
|
|
|
// Update Rep States
|
|
for (int i = 0; i < I->NCSet; ++i) {
|
|
if (CSet[i]) {
|
|
auto& cset = *CSet[i];
|
|
for (int r = 0; r < cRepCnt; ++r) {
|
|
if (cset.Rep[r] && cset.Rep[r]->context.state != 0) {
|
|
cset.Rep[r]->context.state = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/*========================================================================*/
|
|
ObjectMolecule::~ObjectMolecule()
|
|
{
|
|
auto I = this;
|
|
int a;
|
|
SelectorPurgeObjectMembers(I->G, I);
|
|
for(a = 0; a < I->NCSet; a++){
|
|
if(I->CSet[a]) {
|
|
delete I->CSet[a];
|
|
I->CSet[a] = nullptr;
|
|
}
|
|
}
|
|
VLAFreeP(I->DiscreteAtmToIdx);
|
|
VLAFreeP(I->DiscreteCSet);
|
|
VLAFreeP(I->CSet);
|
|
I->m_ciffile.reset(); // free data
|
|
|
|
{
|
|
int nAtom = I->NAtom;
|
|
AtomInfoType *ai = I->AtomInfo.data();
|
|
|
|
for(a = 0; a < nAtom; a++) {
|
|
AtomInfoPurge(I->G, ai);
|
|
ai++;
|
|
}
|
|
VLAFreeP(I->AtomInfo);
|
|
}
|
|
{
|
|
int nBond = I->NBond;
|
|
BondType *bi = I->Bond.data();
|
|
|
|
for(a = 0; a < nBond; a++) {
|
|
AtomInfoPurgeBond(I->G, bi);
|
|
bi++;
|
|
}
|
|
VLAFreeP(I->Bond);
|
|
}
|
|
for(a = 0; a <= cUndoMask; a++)
|
|
FreeP(I->UndoCoord[a]);
|
|
if(I->Sculpt)
|
|
DeleteP(I->Sculpt);
|
|
delete I->CSTmpl;
|
|
}
|
|
|
|
/*========================================================================*/
|
|
ObjectMolecule *ObjectMoleculeReadPDBStr(PyMOLGlobals * G, ObjectMolecule * I,
|
|
const char *PDBStr, int state, int discrete,
|
|
char *pdb_name,
|
|
const char **next_pdb, PDBInfoRec * pdb_info,
|
|
int quiet, int *model_number)
|
|
{
|
|
CoordSet *cset = nullptr;
|
|
pymol::vla<AtomInfoType> atInfo;
|
|
int ok = true;
|
|
int isNew = true;
|
|
unsigned int nAtom = 0;
|
|
const char *start, *restart = nullptr;
|
|
int repeatFlag = true;
|
|
int successCnt = 0;
|
|
unsigned int aic_mask = cAIC_PDBMask;
|
|
|
|
SegIdent segi_override = ""; /* saved segi for corrupted NMR pdb files */
|
|
|
|
start = PDBStr;
|
|
while(repeatFlag) {
|
|
repeatFlag = false;
|
|
|
|
if(!I)
|
|
isNew = true;
|
|
else
|
|
isNew = false;
|
|
|
|
if(ok) {
|
|
if(isNew) {
|
|
I = (ObjectMolecule *) new ObjectMolecule(G, discrete);
|
|
CHECKOK(ok, I);
|
|
if (ok)
|
|
std::swap(atInfo, I->AtomInfo);
|
|
isNew = true;
|
|
} else {
|
|
atInfo = pymol::vla<AtomInfoType>(10);
|
|
CHECKOK(ok, atInfo);
|
|
isNew = false;
|
|
}
|
|
if(ok && isNew) {
|
|
I->Color = AtomInfoUpdateAutoColor(G);
|
|
|
|
if (pdb_info->variant == PDB_VARIANT_VDB ||
|
|
pdb_info->variant == PDB_VARIANT_PQR) {
|
|
// pqr files have no chain identifier by default
|
|
// vdb files have same chain identifiers in all symmetry copies
|
|
SettingSet(cSetting_retain_order, 1, I);
|
|
}
|
|
}
|
|
if (ok)
|
|
cset = ObjectMoleculePDBStr2CoordSet(G, start, &atInfo, &restart,
|
|
segi_override, pdb_name,
|
|
next_pdb, pdb_info, quiet, model_number);
|
|
CHECKOK(ok, cset);
|
|
if (ok){
|
|
nAtom = cset->NIndex;
|
|
}
|
|
}
|
|
if(ok && pdb_name && (*next_pdb)) {
|
|
/* problematic scenario? */
|
|
}
|
|
|
|
/* include coordinate set */
|
|
if(ok) {
|
|
if(I->DiscreteFlag && atInfo) {
|
|
unsigned int a;
|
|
int fp1 = state + 1;
|
|
AtomInfoType *ai = atInfo.data();
|
|
for(a = 0; a < nAtom; a++) {
|
|
(ai++)->discrete_state = fp1;
|
|
}
|
|
}
|
|
|
|
cset->Obj = I;
|
|
cset->enumIndices();
|
|
cset->invalidateRep(cRepAll, cRepInvRep);
|
|
if(isNew) {
|
|
std::swap(I->AtomInfo, atInfo);
|
|
} else {
|
|
ok &= ObjectMoleculeMerge(I, std::move(atInfo), cset, true, aic_mask, true);
|
|
/* NOTE: will release atInfo */
|
|
}
|
|
if(isNew)
|
|
I->NAtom = nAtom;
|
|
if(state < 0)
|
|
state = I->NCSet;
|
|
if(*model_number > 0) {
|
|
if(SettingGetGlobal_b(G, cSetting_pdb_honor_model_number))
|
|
state = *model_number - 1;
|
|
}
|
|
VLACheck(I->CSet, CoordSet *, state);
|
|
CHECKOK(ok, I->CSet);
|
|
if(ok){
|
|
if(I->NCSet <= state)
|
|
I->NCSet = state + 1;
|
|
delete I->CSet[state];
|
|
I->CSet[state] = cset;
|
|
}
|
|
if(ok && isNew)
|
|
ok &= ObjectMoleculeConnect(I, cset);
|
|
if(ok && cset->Symmetry) {
|
|
I->Symmetry.reset(new CSymmetry(*cset->Symmetry));
|
|
}
|
|
if (I->Symmetry) {
|
|
/* check scale records */
|
|
if(pdb_info &&
|
|
pdb_info->scale.flag[0] &&
|
|
pdb_info->scale.flag[1] && pdb_info->scale.flag[2]) {
|
|
|
|
float *sca = pdb_info->scale.matrix;
|
|
sca[15] = 1.0F;
|
|
|
|
CoordSetInsureOrthogonal(G, cset, sca, &I->Symmetry->Crystal, quiet);
|
|
}
|
|
}
|
|
SceneCountFrames(G);
|
|
if (ok)
|
|
ok &= ObjectMoleculeExtendIndices(I, state);
|
|
if (ok)
|
|
ok &= ObjectMoleculeSort(I);
|
|
if (ok){
|
|
ObjectMoleculeUpdateIDNumbers(I);
|
|
ObjectMoleculeUpdateNonbonded(I);
|
|
ObjectMoleculeAutoDisableAtomNameWildcard(I);
|
|
}
|
|
if(SettingGetGlobal_b(G, cSetting_pdb_hetatm_guess_valences)) {
|
|
ObjectMoleculeGuessValences(I, state, nullptr, nullptr, false);
|
|
}
|
|
|
|
successCnt++;
|
|
if(!quiet) {
|
|
if(successCnt > 1) {
|
|
if(successCnt == 2) {
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Actions)
|
|
" %s: read MODEL %d\n", __func__, 1 ENDFB(G);
|
|
}
|
|
PRINTFB(G, FB_ObjectMolecule, FB_Actions)
|
|
" %s: read MODEL %d\n", __func__, successCnt ENDFB(G);
|
|
}
|
|
}
|
|
}
|
|
if(restart) {
|
|
repeatFlag = true;
|
|
start = restart;
|
|
state = state + 1;
|
|
}
|
|
}
|
|
if (!ok && isNew){
|
|
DeleteP(I);
|
|
}
|
|
return (I);
|
|
}
|
|
|
|
|
|
/*========================================================================*/
|
|
static
|
|
CoordSet *ObjectMoleculeMMDStr2CoordSet(PyMOLGlobals * G, const char *buffer,
|
|
AtomInfoType ** atInfoPtr, const char **restart)
|
|
{
|
|
const char *p;
|
|
int nAtom, nBond;
|
|
int a, c, bPart, bOrder;
|
|
float *coord = nullptr;
|
|
CoordSet *cset = nullptr;
|
|
AtomInfoType *atInfo = nullptr, *ai;
|
|
char cc[MAXLINELEN];
|
|
WordType title;
|
|
|
|
float *f;
|
|
BondType *ii, *bond = nullptr;
|
|
int ok = true;
|
|
int auto_show = RepGetAutoShowMask(G);
|
|
|
|
p = buffer;
|
|
nAtom = 0;
|
|
if(atInfoPtr)
|
|
atInfo = *atInfoPtr;
|
|
|
|
if(ok) {
|
|
p = ncopy(cc, p, 6); // could this be too small for large molecules with #atoms > 999999 ?
|
|
if(sscanf(cc, "%d", &nAtom) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad atom count");
|
|
}
|
|
|
|
if(ok) {
|
|
coord = VLAlloc(float, 3 * nAtom);
|
|
if(atInfo)
|
|
VLACheck(atInfo, AtomInfoType, nAtom);
|
|
}
|
|
|
|
if(!atInfo) {
|
|
ErrFatal(G, "MMDStr2CoordSet", "need atom information record!");
|
|
/* failsafe for old version.. */
|
|
}
|
|
|
|
nBond = 0;
|
|
if(ok) {
|
|
bond = VLACalloc(BondType, 6 * nAtom);
|
|
}
|
|
|
|
p = ParseWordCopy(title, p, sizeof(WordType) - 1);
|
|
UtilCleanStr(title);
|
|
|
|
p = nextline(p);
|
|
|
|
/* read coordinates and atom names */
|
|
|
|
if(ok) {
|
|
f = coord;
|
|
ii = bond;
|
|
for(a = 0; a < nAtom; a++) {
|
|
ai = atInfo + a;
|
|
|
|
ai->id = a + 1;
|
|
ai->rank = a;
|
|
if(ok) {
|
|
p = ncopy(cc, p, 4);
|
|
if(sscanf(cc, "%d", &ai->customType) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad atom type");
|
|
}
|
|
if(ok) {
|
|
if(ai->customType <= 14)
|
|
strcpy(ai->elem, "C");
|
|
else if(ai->customType <= 23)
|
|
strcpy(ai->elem, "O");
|
|
else if(ai->customType <= 40)
|
|
strcpy(ai->elem, "N");
|
|
else if(ai->customType <= 48)
|
|
strcpy(ai->elem, "H");
|
|
else if(ai->customType <= 52)
|
|
strcpy(ai->elem, "S");
|
|
else if(ai->customType <= 53)
|
|
strcpy(ai->elem, "P");
|
|
else if(ai->customType <= 55)
|
|
strcpy(ai->elem, "B");
|
|
else if(ai->customType <= 56)
|
|
strcpy(ai->elem, "F");
|
|
else if(ai->customType <= 57)
|
|
strcpy(ai->elem, "Cl");
|
|
else if(ai->customType <= 58)
|
|
strcpy(ai->elem, "Br");
|
|
else if(ai->customType <= 59)
|
|
strcpy(ai->elem, "I");
|
|
else if(ai->customType <= 60)
|
|
strcpy(ai->elem, "Si");
|
|
else if(ai->customType <= 61)
|
|
strcpy(ai->elem, "Du");
|
|
else if(ai->customType <= 62)
|
|
strcpy(ai->elem, "Z0");
|
|
else if(ai->customType <= 63)
|
|
strcpy(ai->elem, "Lp");
|
|
else
|
|
ai->elem[0] = 0;
|
|
/* else strcpy(ai->elem,"?"); WLD 090305 -- guess instead. */
|
|
}
|
|
for(c = 0; c < 6; c++) {
|
|
if(ok) {
|
|
p = ncopy(cc, p, 8);
|
|
if(sscanf(cc, "%d%d", &bPart, &bOrder) != 2)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad bond record");
|
|
else {
|
|
if(bPart && bOrder && (a < (bPart - 1))) {
|
|
nBond++;
|
|
ii->index[0] = a;
|
|
ii->index[1] = bPart - 1;
|
|
ii->order = bOrder;
|
|
ii++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 12);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 12);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = ncopy(cc, p, 12);
|
|
if(sscanf(cc, "%f", f++) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad coordinate");
|
|
}
|
|
if(ok) {
|
|
p = nskip(p, 1);
|
|
p = ncopy(cc, p, 5);
|
|
ai->setResi(cc);
|
|
|
|
// single-letter code
|
|
p = nskip(p, 1);
|
|
|
|
// chain
|
|
p = ncopy(cc, p, 1);
|
|
LexAssign(G, ai->chain, cc);
|
|
}
|
|
if(ok) {
|
|
p = nskip(p, 4);
|
|
p = ncopy(cc, p, 9);
|
|
if(sscanf(cc, "%f", &ai->partialCharge) != 1)
|
|
ok = ErrMessage(G, "ReadMMDFile", "bad charge");
|
|
}
|
|
if(ok) {
|
|
p = nskip(p, 10);
|
|
p = ncopy(cc, p, 3);
|
|
UtilCleanStr(cc);
|
|
LexAssign(G, ai->resn, cc);
|
|
ai->hetatm = true;
|
|
}
|
|
|
|
ai->segi = 0;
|
|
ai->alt[0] = 0;
|
|
|
|
if(ok) {
|
|
p = nskip(p, 2);
|
|
p = ntrim(cc, p, 4);
|
|
if(!cc[0]) {
|
|
sprintf(cc, "%s%02d", ai->elem, a + 1);
|
|
}
|
|
ai->name = LexIdx(G, cc);
|
|
|
|
ai->visRep = auto_show;
|
|
}
|
|
if(ok) {
|
|
AtomInfoAssignParameters(G, ai);
|
|
AtomInfoAssignColors(G, ai);
|
|
}
|
|
if(!ok)
|
|
break;
|
|
p = nextline(p);
|
|
}
|
|
}
|
|
if(ok)
|
|
VLASize(bond, BondType, nBond);
|
|
if(ok) {
|
|
cset = CoordSetNew(G);
|
|
cset->NIndex = nAtom;
|
|
cset->Coord = pymol::vla_take_ownership(coord);
|
|
cset->NTmpBond = nBond;
|
|
cset->TmpBond = pymol::vla_take_ownership(bond);
|
|
strcpy(cset->Name, title);
|
|
} else {
|
|
VLAFreeP(bond);
|
|
VLAFreeP(coord);
|
|
}
|
|
if(atInfoPtr)
|
|
*atInfoPtr = atInfo;
|
|
*restart = *p ? p : nullptr;
|
|
return (cset);
|
|
}
|
|
|
|
#ifdef _PYMOL_IP_EXTRAS
|
|
#endif
|
|
|
|
CPythonVal* ObjectMoleculeGetProperty(
|
|
ObjectMolecule* I, const char* propname, int state, int quiet)
|
|
{
|
|
PyMOLGlobals* G = I->G;
|
|
for (StateIterator iter(G, I->Setting.get(), state, I->NCSet); iter.next();) {
|
|
if (I->CSet[iter.state])
|
|
return PropertyGetPyObject(G, I->CSet[iter.state], propname);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int ObjectMoleculeSetProperty(ObjectMolecule* I, const char* propname,
|
|
CPythonVal* value, const PropertyType& proptype, int state, int quiet)
|
|
{
|
|
int ok = true;
|
|
PyMOLGlobals* G = I->G;
|
|
for (StateIterator iter(G, I->Setting.get(), state, I->NCSet); iter.next();) {
|
|
if (I->CSet[iter.state])
|
|
ok &= PropertySet(G, I->CSet[iter.state], propname, value, proptype);
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
void AtomInfoSettingGenerateSideEffects(PyMOLGlobals * G, ObjectMolecule *obj, int index, int id){
|
|
switch(index){
|
|
case cSetting_label_position:
|
|
case cSetting_label_placement_offset:
|
|
case cSetting_label_screen_point:
|
|
case cSetting_label_relative_mode:
|
|
obj->invalidate(cRepLabel, cRepInvCoord, -1);
|
|
}
|
|
}
|
|
|
|
static int AtomInfoInOrder(PyMOLGlobals * G, const AtomInfoType * atom, int atom1, int atom2)
|
|
{
|
|
return (AtomInfoCompare(G, atom + atom1, atom + atom2) <= 0);
|
|
}
|
|
|
|
static int AtomInfoInOrderIgnoreHet(PyMOLGlobals * G, const AtomInfoType * atom,
|
|
int atom1, int atom2)
|
|
{
|
|
return (AtomInfoCompareIgnoreHet(G, atom + atom1, atom + atom2) <= 0);
|
|
}
|
|
|
|
static int AtomInfoInOrigOrder(PyMOLGlobals * G, const AtomInfoType * atom,
|
|
int atom1, int atom2)
|
|
{
|
|
if(atom[atom1].rank == atom[atom2].rank)
|
|
return (AtomInfoCompare(G, atom + atom1, atom + atom2) <= 0);
|
|
return (atom[atom1].rank < atom[atom2].rank);
|
|
}
|
|
|
|
int *AtomInfoGetSortedIndex(PyMOLGlobals * G,
|
|
const ObjectMolecule* obj,
|
|
const AtomInfoType* rec, int n, int **outdex)
|
|
{
|
|
int *index;
|
|
int a;
|
|
const CSetting* setting = nullptr;
|
|
|
|
ok_assert(1, index = pymol::malloc<int>(n + 1));
|
|
ok_assert(1, (*outdex) = pymol::malloc<int>(n + 1));
|
|
|
|
if(obj && obj->DiscreteFlag) {
|
|
for(a = 0; a < n; a++)
|
|
index[a] = a;
|
|
} else {
|
|
if(obj)
|
|
setting = obj->Setting.get();
|
|
|
|
UtilSortIndexGlobals(G, n, rec, index, (UtilOrderFnGlobals *) (
|
|
SettingGet_b(G, setting, nullptr, cSetting_retain_order) ?
|
|
AtomInfoInOrigOrder :
|
|
SettingGet_b(G, setting, nullptr, cSetting_pdb_hetatm_sort) ?
|
|
AtomInfoInOrder :
|
|
AtomInfoInOrderIgnoreHet));
|
|
}
|
|
|
|
for(a = 0; a < n; a++)
|
|
(*outdex)[index[a]] = a;
|
|
|
|
return index;
|
|
|
|
ok_except1:
|
|
FreeP(index);
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Set the size of the DiscreteAtmToIdx and DiscreteCSet VLAs and pad
|
|
* them with -1/NULL if necessary.
|
|
*/
|
|
bool ObjectMolecule::setNDiscrete(int natom) {
|
|
int n = VLAGetSize(DiscreteAtmToIdx);
|
|
|
|
if (n == natom)
|
|
return true;
|
|
|
|
VLASize(DiscreteAtmToIdx, int, natom);
|
|
VLASize(DiscreteCSet, CoordSet*, natom);
|
|
|
|
if (!DiscreteAtmToIdx || !DiscreteCSet)
|
|
return false;
|
|
|
|
for (int i = n; i < natom; ++i) {
|
|
DiscreteAtmToIdx[i] = -1;
|
|
DiscreteCSet[i] = nullptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Update the AtmToIdx or DiscreteAtmToIdx/DiscreteCSet VLAs from the
|
|
* IdxToAtm arrays
|
|
*/
|
|
bool ObjectMolecule::updateAtmToIdx() {
|
|
if (DiscreteFlag) {
|
|
ok_assert(1, setNDiscrete(NAtom));
|
|
}
|
|
|
|
for (int i = -1; i < NCSet; ++i) {
|
|
CoordSet * cset = (i < 0) ? CSTmpl : CSet[i];
|
|
|
|
if (!cset)
|
|
continue;
|
|
|
|
if (!DiscreteFlag) {
|
|
cset->updateNonDiscreteAtmToIdx(NAtom);
|
|
} else {
|
|
for (int idx = 0; idx < cset->NIndex; ++idx) {
|
|
int atm = cset->IdxToAtm[idx];
|
|
assert(atm < NAtom);
|
|
DiscreteAtmToIdx[atm] = idx;
|
|
DiscreteCSet[atm] = cset;
|
|
AtomInfo[atm].discrete_state = i + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
ok_except1:
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if this atom has coordinates in any state
|
|
*/
|
|
bool ObjectMolecule::atomHasAnyCoordinates(size_t atm) const
|
|
{
|
|
for (size_t i = 0; i < NCSet; ++i) {
|
|
auto cset = CSet[i];
|
|
if (cset && cset->atmToIdx(atm) != -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
pymol::CObject* ObjectMolecule::clone() const
|
|
{
|
|
return ObjectMoleculeCopy(this);
|
|
}
|
|
|
|
AtomNeighbors::AtomNeighbors(const ObjectMolecule* I, int atm)
|
|
{
|
|
auto* Neighbor = I->getNeighborArray();
|
|
|
|
m_neighbor = Neighbor + Neighbor[atm];
|
|
}
|