layer2/Sculpt.cpp (2,112 lines of code) (raw):

/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include"os_python.h" #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"OOMac.h" #include"Feedback.h" #include"Util.h" #include"Sculpt.h" #include"SculptCache.h" #include"Scene.h" #include"Vector.h" #include"Word.h" #include"Editor.h" #include "Lex.h" #include"CGO.h" #ifndef R_SMALL8 #define R_SMALL8 0.00000001 #endif #define NB_HASH_SIZE 262144 #define EX_HASH_SIZE 65536 #define nb_hash(v) \ (((((int)*(v ))>> 2)&0x0003F)|\ ((((int)*(v+1))<< 4)&0x00FC0)|\ ((((int)*(v+2))<<10)&0x3F000)) #define nb_hash_off_i0(v0i,d) \ ((((d)+v0i)>> 2)&0x0003F) #define nb_hash_off_i1(v1i,e) \ ((((e)+v1i)<< 4)&0x00FC0) #define nb_hash_off_i2(v2i,f) \ ((((f)+v2i)<<10)&0x3F000) #define nb_hash_off(v,d,e,f) \ (((((d)+(int)*(v ))>> 2)&0x0003F)|\ ((((e)+(int)*(v+1))<< 4)&0x00FC0)|\ ((((f)+(int)*(v+2))<<10)&0x3F000)) /* below are empirically optimized */ #define ex_hash_i0(a) \ (((a)^((a)>>5))&0x00FF) #define ex_hash_i1(b) \ (( ((b)<<5))&0xFF00) #define ex_hash(a,b) \ (((((a)^((a)>>5)))&0x00FF)|\ ((( ((b)<<5)))&0xFF00)) #ifdef _PYMOL_INLINE __inline__ #endif static float ShakerDoDist(float target, float *v0, float *v1, float *d0to1, float *d1to0, float wt) { float d[3], push[3]; float len, dev, dev_2, sc, result; subtract3f(v0, v1, d); len = (float) length3f(d); dev = target - len; if((result = (float) fabs(dev)) > R_SMALL8) { dev_2 = wt * dev / 2.0F; if(len > R_SMALL8) { /* nonoverlapping */ sc = dev_2 / len; scale3f(d, sc, push); add3f(push, d0to1, d0to1); subtract3f(d1to0, push, d1to0); } else { /* overlapping, so just push along X */ float rd[3]; get_random3f(rd); d0to1[0] -= rd[0] * dev_2; d1to0[0] += rd[0] * dev_2; d0to1[1] -= rd[1] * dev_2; d1to0[1] += rd[1] * dev_2; d0to1[2] -= rd[2] * dev_2; d1to0[2] += rd[2] * dev_2; } } else result = 0.0; return result; } #ifdef _PYMOL_INLINE __inline__ #endif static float ShakerDoTors(int type, float *v0, float *v1, float *v2, float *v3, float *p0, float *p1, float *p2, float *p3, float tole, float wt) { float push0[3], push3[3]; float axis[3], seg0[3], seg1[3], perp0[3], perp1[3]; float dir[3]; float sc; float sign, dp; float result = 0.0F; /* v0 v3 \ / v1__v2 */ subtract3f(v2, v1, axis); subtract3f(v0, v1, seg0); subtract3f(v3, v2, seg1); cross_product3f(seg0, axis, perp0); cross_product3f(axis, seg1, perp1); normalize3f(perp0); normalize3f(perp1); dp = dot_product3f(perp0, perp1); switch (type) { case cShakerTorsSP3SP3: if(dp < -0.5F) { result = ((float) fabs(dp)) - 0.5F; if(result < tole) /* discontinuous low bottom well */ result = result / 5.0F; } else if(dp < 0.5) { result = -0.5F - dp; } else { result = 1.0F - dp; } break; case cShakerTorsFlat: if(fabs(dp) < 0.5F) /* don't attempt to resolve when ambiguous */ return 0.0F; if(dp > 0.0F) { result = 1.0F - dp; } else { result = -1.0F - dp; } result *= 5.0F; /* emphasize */ break; case cShakerTorsAmide: if(dp > -0.7F) { /* highly biased in favor of the input state */ result = 1.0F - dp; } else { result = -1.0F - dp; } result *= 50.0F; /* emphasize */ break; case cShakerTorsDisulfide: if(fabs(dp) < tole) return 0.0F; result = -dp; if(result < tole) result = result / 25.F; break; } cross_product3f(perp0, perp1, dir); sign = dot_product3f(axis, dir); if(sign < 0.0F) sc = wt * result; else sc = -wt * result; scale3f(perp0, sc, push0); scale3f(perp1, sc, push3); add3f(p0, push0, p0); add3f(p3, push3, p3); subtract3f(p1, push0, p1); subtract3f(p2, push3, p2); return result; } #ifdef _PYMOL_INLINE __inline__ #endif static float ShakerDoDistLimit(float target, float *v0, float *v1, float *d0to1, float *d1to0, float wt) { float d[3], push[3]; float len, dev, dev_2, sc; subtract3f(v0, v1, d); len = (float) length3f(d); dev = len - target; if(dev > 0.0F) { /* assuming len is non-zero since it is above target */ dev_2 = wt * dev * (-0.5F); sc = dev_2 / len; scale3f(d, sc, push); add3f(push, d0to1, d0to1); subtract3f(d1to0, push, d1to0); return dev; } else { return 0.0F; } } #ifdef _PYMOL_INLINE __inline__ #endif static float ShakerDoDistMinim(float target, float *v0, float *v1, float *d0to1, float *d1to0, float wt) { float d[3], push[3]; float len, dev, dev_2, sc; subtract3f(v0, v1, d); len = (float) length3f(d); dev = len - target; if(dev < 0.0F) { /* assuming len is non-zero since it is above target */ dev_2 = -wt * dev * 0.5F; sc = dev_2 / len; scale3f(d, sc, push); add3f(push, d0to1, d0to1); subtract3f(d1to0, push, d1to0); return -dev; } else { return 0.0F; } } CSculpt *SculptNew(PyMOLGlobals * G) { OOAlloc(G, CSculpt); I->G = G; I->Shaker = ShakerNew(G); I->NBList = VLAlloc(int, 150000); I->NBHash = Calloc(int, NB_HASH_SIZE); I->EXList = VLAlloc(int, 100000); I->EXHash = Calloc(int, EX_HASH_SIZE); I->Don = VLAlloc(int, 1000); I->Acc = VLAlloc(int, 1000); { int a; for(a = 1; a < 256; a++) I->inverse[a] = 1.0F / a; } return (I); } typedef struct { int *neighbor; AtomInfoType *ai; int *atm2idx1, *atm2idx2; } CountCall; typedef struct { PyMOLGlobals *G; CShaker *Shaker; AtomInfoType *ai; int *atm2idx; CoordSet *cSet, **discCSet; float *coord; int *neighbor; int atom0; int min, max, mode; } ATLCall; static int count_branch(CountCall * CNT, int atom, int limit) { AtomInfoType *ai = CNT->ai + atom; int count = 0; if(!ai->temp1) { count = (ai->isHydrogen() ? 0 : 1); if(count) { if((CNT->atm2idx1[atom] < 0) || (CNT->atm2idx2[atom] < 0)) count = 0; } if(count && (limit > 0)) { int n0 = CNT->neighbor[atom] + 1; int b1; ai->temp1 = true; while((b1 = CNT->neighbor[n0]) >= 0) { count += count_branch(CNT, b1, limit - 1); n0 += 2; } ai->temp1 = false; } } return count; } static void add_triangle_limits(ATLCall * ATL, int prev, int cur, float dist, int count) { ATLCall *I = ATL; int n0; int n1; float dist_limit; int atom1; n0 = I->neighbor[cur]; if((count >= I->min) && (count > 1)) { int add_flag = false; switch (I->mode) { case 1: add_flag = 1; /* all */ break; case 2: add_flag = (count && !(count & 1)); /* evens */ break; case 3: add_flag = ((count & (count - 1)) == 0); /* powers of two */ break; case 0: default: add_flag = (!I->ai[I->atom0].isHydrogen()); /* all heavies */ break; } if(add_flag) { n1 = n0 + 1; /* first mark and register */ while((atom1 = I->neighbor[n1]) >= 0) { if((!I->ai[atom1].temp1) && (I->atom0 < atom1)) { int ref = prev; if(count & 0x1) { /* odd */ ref = cur; } if(((!I->discCSet) || ((I->cSet == I->discCSet[ref]) && (I->cSet == I->discCSet[atom1]))) && ((I->mode != 0) || (!I->ai[atom1].isHydrogen()))) { int ia = I->atm2idx[ref]; int ib = I->atm2idx[atom1]; if((ia >= 0) && (ib >= 0)) { float *va = I->coord + 3 * ia; float *vb = I->coord + 3 * ib; dist_limit = dist + diff3f(va, vb); ShakerAddDistCon(I->Shaker, I->atom0, atom1, dist_limit, cShakerDistLimit, 1.0F); } } I->ai[atom1].temp1 = 1; } n1 += 2; } } } if(count <= I->max) { /* then recurse */ n1 = n0 + 1; while((atom1 = I->neighbor[n1]) >= 0) { if(I->ai[atom1].temp1 < 2) { dist_limit = dist; if(!(count & 0x1)) { /* accumulate distances between even atoms only */ if((!I->discCSet) || ((I->cSet == I->discCSet[prev]) && (I->cSet == I->discCSet[atom1]))) { int ia = I->atm2idx[prev]; int ib = I->atm2idx[atom1]; if((ia >= 0) && (ib >= 0)) { float *va = I->coord + 3 * ia; float *vb = I->coord + 3 * ib; dist_limit += diff3f(va, vb); } } I->ai[atom1].temp1 = 2; } I->ai[atom1].temp1 = 2; add_triangle_limits(I, cur, atom1, dist_limit, count + 1); } n1 += 2; } } } void SculptMeasureObject(CSculpt * I, ObjectMolecule * obj, int state, int match_state, int match_by_segment) { PyMOLGlobals *G = I->G; int a, a0, a1, a2, a3, b0, b1, b2, b3, b4; BondType *b; float *v0, *v1, *v2, *v3, d, dummy; CoordSet *cs; int n0, n1, n2, n3; int *planar = NULL; int *linear = NULL; int *single = NULL; int *crdidx = NULL; int nex = 1; int *j, *k, xhash; int ex_type; AtomInfoType *ai, *ai1, *ai2, *obj_atomInfo; int xoffset; int use_cache = 1; PRINTFD(G, FB_Sculpt) " SculptMeasureObject-Debug: entered.\n" ENDFD; if(match_state < 0) match_state = state; if(state < 0) state = ObjectGetCurrentState(&obj->Obj, true); ShakerReset(I->Shaker); UtilZeroMem(I->NBHash, NB_HASH_SIZE * sizeof(int)); UtilZeroMem(I->EXHash, EX_HASH_SIZE * sizeof(int)); if((state >= 0) && (state < obj->NCSet) && (obj->CSet[state])) { obj_atomInfo = obj->AtomInfo; VLACheck(I->Don, int, obj->NAtom); VLACheck(I->Acc, int, obj->NAtom); ai = obj_atomInfo; for(a = 0; a < obj->NAtom; a++) { I->Don[a] = false; I->Acc[a] = false; AtomInfoCheckUniqueID(G, ai); ai++; } ObjectMoleculeVerifyChemistry(obj, state); ObjectMoleculeUpdateNeighbors(obj); cs = obj->CSet[state]; use_cache = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_memory); if(obj->NBond) { int *neighbor = obj->Neighbor; int n_atom = obj->NAtom; planar = Alloc(int, n_atom); linear = Alloc(int, n_atom); single = Alloc(int, n_atom); crdidx = Alloc(int, n_atom); ai = obj_atomInfo; for(a = 0; a < n_atom; a++) { planar[a] = (ai->geom == cAtomInfoPlanar); linear[a] = (ai->geom == cAtomInfoLinear); single[a] = (ai->geom == cAtomInfoSingle); if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[a]) { a0 = obj->DiscreteAtmToIdx[a]; } else { a0 = -1; } } else { a0 = cs->AtmToIdx[a]; } crdidx[a] = a0; ai++; } /* brain-dead donor/acceptor assignment * REPLACE later on with pattern-based system */ /* pass 1 */ b = obj->Bond; for(a = 0; a < obj->NBond; a++) { b1 = b->index[0]; b2 = b->index[1]; ai1 = obj_atomInfo + b1; ai2 = obj_atomInfo + b2; /* make blanket assumption that all nitrogens with <3 bonds are donors -- we qualify this below... */ if(ai1->protons == cAN_N) { n1 = neighbor[b1]; if(neighbor[n1] < 3) { /* N with L.P. */ I->Don[b1] = true; } } if(ai2->protons == cAN_N) { n2 = neighbor[b2]; if(neighbor[n2] < 3) { /* N with L.P. */ I->Don[b2] = true; } } /* assume O is always an acceptor... */ if(ai1->protons == cAN_O) I->Acc[b1] = true; if(ai2->protons == cAN_O) I->Acc[b2] = true; b++; } /* pass 2 */ b = obj->Bond; for(a = 0; a < obj->NBond; a++) { b1 = b->index[0]; b2 = b->index[1]; /* nitrogens with lone pairs are acceptors (not donors as assumed above) */ ai1 = obj_atomInfo + b1; ai2 = obj_atomInfo + b2; if(ai1->protons == cAN_N) { if(b->order == 2) { n1 = neighbor[b1]; if(neighbor[n1] < 3) { /* N with L.P. */ I->Acc[b1] = true; I->Don[b1] = false; } } } if(ai2->protons == cAN_N) { if(b->order == 2) { n2 = neighbor[b2]; if(neighbor[n2] < 3) { /* N with L.P. */ I->Acc[b2] = true; I->Don[b2] = false; } } } b++; } /* pass 3 */ b = obj->Bond; for(a = 0; a < obj->NBond; a++) { b1 = b->index[0]; b2 = b->index[1]; ai1 = obj_atomInfo + b1; ai2 = obj_atomInfo + b2; /* however, every NH is a donor, even if it's SP2 */ if(ai1->protons == cAN_H) { /* donors: any H attached to O, N */ switch (ai2->protons) { case cAN_O: I->Don[b1] = true; I->Don[b2] = true; /* mark heavy atom too... */ break; case cAN_N: I->Don[b1] = true; I->Don[b2] = true; break; } } else if(ai2->protons == cAN_H) { switch (ai1->protons) { case cAN_O: I->Don[b1] = true; I->Don[b2] = true; /* mark heavy atom too... */ break; case cAN_N: I->Don[b1] = true; I->Don[b2] = true; /* mark heavy atom too... */ break; } } b++; } /* atom pass */ ai1 = obj_atomInfo; for(a = 0; a < n_atom; a++) { /* make sure all nonbonded atoms get categorized */ n0 = neighbor[a]; if(neighbor[n0] == 0) { /* nonbonded */ if(ai1->protons == cAN_O) { I->Don[a] = true; I->Acc[a] = true; } else if(ai1->protons == cAN_N) { I->Don[a] = true; } } /* if(I->Acc[a]) { printf("ACC %s %s %s\n",ai1->chain,ai1->resi,ai1->name); } if(I->Don[a]) { printf("DON %s %s %s\n",ai1->chain,ai1->resi,ai1->name); } */ ai1++; } /* exclusions */ b = obj->Bond; for(a = 0; a < obj->NBond; a++) { b1 = b->index[0]; b2 = b->index[1]; ai1 = obj_atomInfo + b1; ai2 = obj_atomInfo + b2; xhash = ((b2 > b1) ? ex_hash(b1, b2) : ex_hash(b2, b1)); VLACheck(I->EXList, int, nex + 3); j = I->EXList + nex; *(j++) = *(I->EXHash + xhash); if(b2 > b1) { *(j++) = b1; *(j++) = b2; } else { *(j++) = b2; *(j++) = b1; } *(j++) = 2; /* 1-2 exclusion */ *(I->EXHash + xhash) = nex; nex += 4; a1 = crdidx[b1]; a2 = crdidx[b2]; if((a1 >= 0) && (a2 >= 0)) { v1 = cs->Coord + 3 * a1; v2 = cs->Coord + 3 * a2; d = (float) diff3f(v1, v2); if(use_cache) { if(!SculptCacheQuery(G, cSculptBond, obj_atomInfo[b1].unique_id, obj_atomInfo[b2].unique_id, 0, 0, &d)) SculptCacheStore(G, cSculptBond, obj_atomInfo[b1].unique_id, obj_atomInfo[b2].unique_id, 0, 0, d); } ShakerAddDistCon(I->Shaker, b1, b2, d, cShakerDistBond, 1.0F); /* NOTE: storing atom indices, not coord. ind.! */ } b++; } /* triangle relationships */ { ATLCall atl; ai1 = obj_atomInfo; atl.G = I->G; atl.Shaker = I->Shaker; atl.ai = obj_atomInfo; atl.cSet = cs; if(obj->DiscreteFlag) { atl.atm2idx = obj->DiscreteAtmToIdx; atl.discCSet = obj->DiscreteCSet; } else { atl.atm2idx = cs->AtmToIdx; atl.discCSet = NULL; } atl.coord = cs->Coord; atl.neighbor = neighbor; atl.min = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tri_min); atl.max = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tri_max); atl.mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tri_mode); for(a = 0; a < n_atom; a++) { atl.atom0 = a; /* clear the flag -- TODO replace with array */ { int aa; ai = obj_atomInfo; for(aa = 0; aa < n_atom; aa++) { ai->temp1 = false; ai++; } } ai1->temp1 = true; add_triangle_limits(&atl, a, a, 0.0F, 1); ai1++; } } /* if we have a match state, establish minimum distances */ if((match_state >= 0) && (match_state < obj->NCSet) && (!obj->DiscreteFlag)) { CoordSet *cs2 = obj->CSet[match_state]; int n_site = 0; if(cs2) { float minim_min = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_min_min); float minim_max = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_min_max); float maxim_min = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_max_min); float maxim_max = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_max_max); int *site = Calloc(int, n_atom); float *weight = Calloc(float, n_atom); /* first, find candidate atoms with sufficient connectivity */ CountCall cnt; cnt.ai = obj_atomInfo; cnt.neighbor = neighbor; cnt.atm2idx1 = cs->AtmToIdx; cnt.atm2idx2 = cs2->AtmToIdx; { int aa; ai = obj_atomInfo; for(aa = 0; aa < n_atom; aa++) { ai->temp1 = false; ai++; } } ai1 = obj_atomInfo; for(b0 = 0; b0 < n_atom; b0++) { int n_qual_branch = 0, cb; int adj_site = false; ai1->temp1 = true; n0 = neighbor[b0] + 1; while((b1 = neighbor[n0]) >= 0) { if(site[b1]) { adj_site = true; break; } cb = count_branch(&cnt, b1, 3); if(cb > 3) { n_qual_branch++; } n0 += 2; } ai1->temp1 = false; if((n_qual_branch > 2) && (!adj_site)) { site[b0] = 10; } else if(!adj_site) { const char * name = LexStr(G, ai1->name); switch (name[0]) { case 'O': if(!name[1]) if(AtomInfoKnownPolymerResName(LexStr(G, ai1->resn))) site[b0] = 40; /* main-chain carbonyl */ break; case 'C': switch (name[1]) { case 'Z': switch (name[2]) { case 0: if(ai1->resn == G->lex_const.ARG) site[b0] = 20; /* ARG/CZ */ else if(ai1->resn == G->lex_const.TYR) site[b0] = 20; /* TYR/CZ */ else if(ai1->resn == G->lex_const.PHE) site[b0] = 20; /* PHE/CZ */ break; } break; case 'E': switch (name[2]) { case 0: if(ai1->resn == G->lex_const.LYS) site[b0] = 20; /* LYS/CE */ break; } break; case 'D': switch (name[2]) { case 0: if(ai1->resn == G->lex_const.GLU) site[b0] = 20; /* GLU/CD */ else if(ai1->resn == G->lex_const.GLN) site[b0] = 20; /* GLN/CD */ break; } break; case 'G': switch (name[2]) { case 0: if(ai1->resn == G->lex_const.LEU) site[b0] = 20; /* LEU/CG */ else if(ai1->resn == G->lex_const.ASP) site[b0] = 20; /* ASP/CG */ else if(ai1->resn == G->lex_const.ASN) site[b0] = 20; /* ASN/CG */ break; } break; } break; case 'S': switch (name[1]) { case 'D': switch (name[2]) { case 0: if(ai1->resn == G->lex_const.MET) site[b0] = 20; /* MET/SD */ break; } break; } break; } } ai1++; } for(b0 = 0; b0 < n_atom; b0++) { if(site[b0]) { weight[n_site] = 10.0F / site[b0]; site[n_site] = b0; n_site++; } } { for(a0 = 0; a0 < n_site; a0++) { for(a1 = a0 + 1; a1 < n_site; a1++) { float wt = weight[a0] * weight[a1]; b0 = site[a0]; b1 = site[a1]; { int i0a = cs->AtmToIdx[b0]; int i1a = cs->AtmToIdx[b1]; int i0b = cs2->AtmToIdx[b0]; int i1b = cs2->AtmToIdx[b1]; if((i0a >= 0) && (i1a >= 0) && (i0b >= 0) && (i1b >= 0) && ((!match_by_segment) || (obj_atomInfo[b0].segi == obj_atomInfo[b1].segi))) { float *v0a = cs->Coord + 3 * i0a; float *v1a = cs->Coord + 3 * i1a; float *v0b = cs2->Coord + 3 * i0b; float *v1b = cs2->Coord + 3 * i1b; float dist0, dist1, min_dist, max_dist; dist0 = diff3f(v0a, v1a); dist1 = diff3f(v0b, v1b); min_dist = (dist0 < dist1) ? dist0 : dist1; if((min_dist >= minim_min) && (min_dist <= minim_max)) { ShakerAddDistCon(I->Shaker, b0, b1, min_dist, cShakerDistMinim, wt); } max_dist = (dist0 > dist1) ? dist0 : dist1; if((max_dist >= maxim_min) && (max_dist <= maxim_max)) { ShakerAddDistCon(I->Shaker, b0, b1, max_dist, cShakerDistMaxim, wt); } } } } } } FreeP(weight); FreeP(site); } } /* now pick up those 1-3 interations */ /* b1-b0-b2 */ for(b0 = 0; b0 < n_atom; b0++) { n0 = neighbor[b0] + 1; while(neighbor[n0] >= 0) { b1 = neighbor[n0]; n1 = n0 + 2; while(neighbor[n1] >= 0) { b2 = neighbor[n1]; xhash = ((b2 > b1) ? ex_hash(b1, b2) : ex_hash(b2, b1)); VLACheck(I->EXList, int, nex + 3); j = I->EXList + nex; *(j++) = *(I->EXHash + xhash); if(b2 > b1) { *(j++) = b1; *(j++) = b2; } else { *(j++) = b2; *(j++) = b1; } *(j++) = 3; /* 1-3 exclusion */ *(I->EXHash + xhash) = nex; nex += 4; a0 = crdidx[b0]; a1 = crdidx[b1]; a2 = crdidx[b2]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0)) { v1 = cs->Coord + 3 * a1; v2 = cs->Coord + 3 * a2; d = (float) diff3f(v1, v2); if(use_cache) { if(!SculptCacheQuery(G, cSculptAngl, obj_atomInfo[b0].unique_id, obj_atomInfo[b1].unique_id, obj_atomInfo[b2].unique_id, 0, &d)) SculptCacheStore(G, cSculptAngl, obj_atomInfo[b0].unique_id, obj_atomInfo[b1].unique_id, obj_atomInfo[b2].unique_id, 0, d); } ShakerAddDistCon(I->Shaker, b1, b2, d, cShakerDistAngle, 1.0F); if(linear[b0] && (linear[b1] || linear[b2])) { if(use_cache) { if(!SculptCacheQuery(G, cSculptLine, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, 0, &dummy)) SculptCacheStore(G, cSculptLine, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, 0, 0.0); } ShakerAddLineCon(I->Shaker, b1, b0, b2); } } n1 += 2; } n0 += 2; } } /* and record the pyramidal and planar geometries */ /* b1-b0-b2 * | * b3 */ for(b0 = 0; b0 < n_atom; b0++) { n0 = neighbor[b0] + 1; while(neighbor[n0] >= 0) { b1 = neighbor[n0]; n1 = n0 + 2; while(neighbor[n1] >= 0) { b2 = neighbor[n1]; n2 = n1 + 2; while(neighbor[n2] >= 0) { b3 = neighbor[n2]; a0 = crdidx[b0]; a1 = crdidx[b1]; a2 = crdidx[b2]; a3 = crdidx[b3]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && (a3 >= 0)) { float d2 = 0.0F; v0 = cs->Coord + 3 * a0; v1 = cs->Coord + 3 * a1; v2 = cs->Coord + 3 * a2; v3 = cs->Coord + 3 * a3; d = ShakerGetPyra(&d2, v0, v1, v2, v3); if(fabs(d) < 0.05) { planar[b0] = true; } if(planar[b0]) d = 0.0; if(use_cache) { if(!SculptCacheQuery(G, cSculptPyra, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, &d)) SculptCacheStore(G, cSculptPyra, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, d); if(!SculptCacheQuery(G, cSculptPyra + 1, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, &d2)) SculptCacheStore(G, cSculptPyra + 1, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, d2); } ShakerAddPyraCon(I->Shaker, b0, b1, b2, b3, d, d2); } n2 += 2; } n1 += 2; } n0 += 2; } } /* b1\b0_b2/b3 */ for(b0 = 0; b0 < n_atom; b0++) { n0 = neighbor[b0] + 1; while((b1 = neighbor[n0]) >= 0) { n1 = neighbor[b0] + 1; while((b2 = neighbor[n1]) >= 0) { if(b1 != b2) { n2 = neighbor[b2] + 1; while((b3 = neighbor[n2]) >= 0) { if((b3 != b0) && (b3 > b1)) { if(!(planar[b0] || planar[b2] || linear[b0] || linear[b2])) { int type; if((obj_atomInfo[b0].protons == cAN_S) && (obj_atomInfo[b2].protons == cAN_S)) type = cShakerTorsDisulfide; else type = cShakerTorsSP3SP3; ShakerAddTorsCon(I->Shaker, b1, b0, b2, b3, type); } if(planar[b0] && planar[b2]) { /* special extra-rigid torsion for hydrogens on planar acyclic systems (amides, etc.) */ if(((obj_atomInfo[b1].protons == cAN_H) && single[b1] && (obj_atomInfo[b3].protons != cAN_H) && planar[b3]) || ((obj_atomInfo[b3].protons == cAN_H) && single[b3] && (obj_atomInfo[b1].protons != cAN_H) && planar[b1])) { int cycle = 0; /* b1\b0_b2/b3-b4-b5-b6-b7... */ int b5, b6, b7, b8, b9, b10; int n4, n5, n6, n7, n8, n9; n3 = neighbor[b2] + 1; while((!cycle) && (b4 = neighbor[n3]) >= 0) { if(b4 != b0) { n4 = neighbor[b4] + 1; while((!cycle) && (b5 = neighbor[n4]) >= 0) { if(b5 != b2) { n5 = neighbor[b5] + 1; while((!cycle) && (b6 = neighbor[n5]) >= 0) { if(b6 == b0) { /* 4-cycle */ cycle = 4; } else if((b6 != b4) && (b6 != b2)) { n6 = neighbor[b6] + 1; while((!cycle) && (b7 = neighbor[n6]) >= 0) { if(b7 == b0) { /* 5-cycle */ cycle = 5; } else if((b7 != b5) && (b7 != b2)) { n7 = neighbor[b7] + 1; while((!cycle) && (b8 = neighbor[n7]) >= 0) { if(b8 == b0) { /* 6-cycle */ cycle = 6; } else if((b8 != b6) && (b8 != b2)) { n8 = neighbor[b8] + 1; while((!cycle) && (b9 = neighbor[n8]) >= 0) { if(b9 == b0) { /* 7-cycle */ cycle = 7; } else if((b9 != b7) && (b9 != b2)) { n9 = neighbor[b9] + 1; while((!cycle) && (b10 = neighbor[n9]) >= 0) { if(b10 == b0) { /* 8-cycle */ cycle = 8; } n9 += 2; } } n8 += 2; } } n7 += 2; } } n6 += 2; } } n5 += 2; } } n4 += 2; } } n3 += 2; } if(!cycle) { /* don't add special amide constraints within small rings */ if(((obj_atomInfo[b1].protons == cAN_H) && single[b1] && (obj_atomInfo[b0].protons == cAN_N) && (obj_atomInfo[b2].protons == cAN_C) && (obj_atomInfo[b3].protons == cAN_O) && planar[b3]) || ((obj_atomInfo[b1].protons == cAN_H) && single[b3] && (obj_atomInfo[b2].protons == cAN_N) && (obj_atomInfo[b0].protons == cAN_C) && (obj_atomInfo[b1].protons == cAN_O) && planar[b1])) { /* biased, asymmetric term for amides */ ShakerAddTorsCon(I->Shaker, b1, b0, b2, b3, cShakerTorsAmide); } else { /* biased, symmetric term for all others */ ShakerAddTorsCon(I->Shaker, b1, b0, b2, b3, cShakerTorsFlat); } } } } /* check 1-4 exclusion */ xhash = ex_hash(b1, b3); ex_type = 4; xoffset = *(I->EXHash + xhash); while(xoffset) { k = I->EXList + xoffset; if((abs(*(k + 3)) == 4) && (*(k + 1) == b1) && (*(k + 2) == b3)) { if((b0 != *(k + 4)) && (b2 != *(k + 5))) { if(planar[b0] && planar[b2] && planar[*(k + 4)] && planar[*(k + 5)]) { /* two planar paths -> likely a planar aromatic system */ *(k + 3) = -4; } } ex_type = 0; /* duplicate, skip */ break; } xoffset = *k; } if(ex_type) { VLACheck(I->EXList, int, nex + 5); j = I->EXList + nex; *(j++) = *(I->EXHash + xhash); *(j++) = b1; *(j++) = b3; if(planar[b0] && planar[b2]) *(j++) = -4; else *(j++) = ex_type; *(j++) = b0; *(j++) = b2; *(I->EXHash + xhash) = nex; nex += 6; } /* planarity */ a0 = crdidx[b0]; a1 = crdidx[b1]; a2 = crdidx[b2]; a3 = crdidx[b3]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && (a3 >= 0)) { v0 = cs->Coord + 3 * a0; v1 = cs->Coord + 3 * a1; v2 = cs->Coord + 3 * a2; v3 = cs->Coord + 3 * a3; d = 0.0; if(planar[b0] && planar[b2]) { float deg = get_dihedral3f(v1, v0, v2, v3); if(fabs(deg) < deg_to_rad(10.0)) d = 1.0; else if(fabs(deg) > deg_to_rad(170)) d = -1.0; { int cycle = false; /* look for 4, 5, 6, 7, or 8 cycle that connects back to b1 if found, then this planar system is fixed (either at zero or 180 -- it can't flip it over) */ /* b1\b0_b2/b3-b4-b5-b6-b7... */ int b5, b6, b7, b8, b9, b10; int n4, n5, n6, n7, n8, n9; n3 = neighbor[b2] + 1; while((!cycle) && (b4 = neighbor[n3]) >= 0) { if(b4 != b0) { n4 = neighbor[b4] + 1; while((!cycle) && (b5 = neighbor[n4]) >= 0) { if(b5 != b2) { n5 = neighbor[b5] + 1; while((!cycle) && (b6 = neighbor[n5]) >= 0) { if(b6 == b0) { /* 4-cycle */ cycle = 4; } else if((b6 != b4) && (b6 != b2)) { n6 = neighbor[b6] + 1; while((!cycle) && (b7 = neighbor[n6]) >= 0) { if(b7 == b0) { /* 5-cycle */ cycle = 5; } else if((b7 != b5) && (b7 != b2)) { n7 = neighbor[b7] + 1; while((!cycle) && (b8 = neighbor[n7]) >= 0) { if(b8 == b0) { /* 6-cycle */ cycle = 6; } else if((b8 != b6) && (b8 != b2)) { n8 = neighbor[b8] + 1; while((!cycle) && (b9 = neighbor[n8]) >= 0) { if(b9 == b0) { /* 7-cycle */ cycle = 7; } else if((b9 != b7) && (b9 != b2)) { n9 = neighbor[b9] + 1; while((!cycle) && (b10 = neighbor[n9]) >= 0) { if(b10 == b0) { /* 8-cycle */ cycle = 8; } n9 += 2; } } n8 += 2; } } n7 += 2; } } n6 += 2; } } n5 += 2; } } n4 += 2; } } n3 += 2; } /* don't get jacked by pseudo-planar PRO */ if(((obj_atomInfo[b0].protons != cAN_N) || (!WordMatchExact(G, obj_atomInfo[b0].resn, G->lex_const.PRO, true))) && ((obj_atomInfo[b2].protons != cAN_N) || (!WordMatchExact(G, obj_atomInfo[b2].resn, G->lex_const.PRO, true)))) { if(use_cache) { if(!SculptCacheQuery(G, cSculptPlan, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, &d)) SculptCacheStore(G, cSculptPlan, obj_atomInfo[b1].unique_id, obj_atomInfo[b0].unique_id, obj_atomInfo[b2].unique_id, obj_atomInfo[b3].unique_id, d); } ShakerAddPlanCon(I->Shaker, b1, b0, b2, b3, d, cycle); if(planar[b1] && planar[b3] && ((cycle == 5) || (cycle == 6))) { /* also add minimum distance constraints to keep small rings from folding */ d = (float) diff3f(v1, v3); ShakerAddDistCon(I->Shaker, b1, b3, d, cShakerDistBond, 1.0F); } } } } } } n2 += 2; } } n1 += 2; } n0 += 2; } } /* add 1,5 exclusions for hydrogens off arg-like planar systems */ /* b1\b0_b2_b3/b4 */ for(b0 = 0; b0 < n_atom; b0++) { n0 = neighbor[b0] + 1; while((b1 = neighbor[n0]) >= 0) { if(obj_atomInfo[b1].protons == cAN_H) { n1 = neighbor[b0] + 1; while((b2 = neighbor[n1]) >= 0) { if(b1 != b2) { n2 = neighbor[b2] + 1; while((b3 = neighbor[n2]) >= 0) { if(b3 != b0) { if(planar[b0] && planar[b2] && planar[b3]) { n3 = neighbor[b3] + 1; while((b4 = neighbor[n3]) >= 0) { if((b4 != b2) && (b4 > b1) && (obj_atomInfo[b4].protons == cAN_H)) { xhash = ex_hash(b1, b4); ex_type = 5; xoffset = *(I->EXHash + xhash); while(xoffset) { k = I->EXList + xoffset; if(((*(k + 3)) == ex_type) && (*(k + 1) == b1) && (*(k + 2) == b4)) { ex_type = 0; /* duplicate, skip */ break; } xoffset = *k; } if(ex_type) { VLACheck(I->EXList, int, nex + 6); j = I->EXList + nex; *(j++) = *(I->EXHash + xhash); *(j++) = b1; *(j++) = b4; *(j++) = ex_type; *(j++) = b0; *(j++) = b2; *(j++) = b3; *(I->EXHash + xhash) = nex; nex += 6; } } n3 += 2; } } } n2 += 2; } } n1 += 2; } } n0 += 2; } } { /* longer-range exclusions (1-5,1-6,1-7,1-8,1-9) -- only locate & store when needed */ int mask = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_field_mask); int max_excl = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_avd_excl); if(max_excl > 9) max_excl = 9; if((cSculptAvoid & mask) && (max_excl > 4)) { int b_stack[10]; int n_stack[10]; int stop_depth = max_excl - 1; int depth; int bd, skip; for(b0 = 0; b0 < n_atom; b0++) { b_stack[0] = b0; n_stack[0] = neighbor[b_stack[0]] + 1; depth = 0; while(depth >= 0) { if((bd = neighbor[n_stack[depth]]) < 0) { depth--; if(depth >= 0) { /* iterate next atom */ n_stack[depth] += 2; } } else { skip = (depth == stop_depth); if(!skip) { for(a = 0; a < depth; a++) { if(b_stack[a] == bd) { skip = true; break; } } } if(!skip) { depth++; b_stack[depth] = bd; n_stack[depth] = neighbor[bd] + 1; if((depth > 3) && (b0 < bd)) { xhash = ex_hash(b0, bd); VLACheck(I->EXList, int, nex + 3); j = I->EXList + nex; *(j++) = *(I->EXHash + xhash); *(j++) = b0; *(j++) = bd; *(j++) = depth + 1; /* 1-5, 1-6, 1-7 etc. */ *(I->EXHash + xhash) = nex; nex += 4; } } else { n_stack[depth] += 2; } } } } } } FreeP(planar); FreeP(linear); FreeP(single); FreeP(crdidx); } } PRINTFB(G, FB_Sculpt, FB_Blather) " Sculpt: I->Shaker->NDistCon %d\n", I->Shaker->NDistCon ENDFB(G); PRINTFB(G, FB_Sculpt, FB_Blather) " Sculpt: I->Shaker->NPyraCon %d\n", I->Shaker->NPyraCon ENDFB(G); PRINTFB(G, FB_Sculpt, FB_Blather) " Sculpt: I->Shaker->NPlanCon %d\n", I->Shaker->NPlanCon ENDFB(G); PRINTFD(G, FB_Sculpt) " SculptMeasureObject-Debug: leaving...\n" ENDFD; } #ifdef _PYMOL_INLINE __inline__ #endif static int SculptCheckBump(float *v1, float *v2, float *diff, float *dist, float cutoff) { float d2; diff[0] = (v1[0] - v2[0]); diff[1] = (v1[1] - v2[1]); if(fabs(diff[0]) > cutoff) return (false); diff[2] = (v1[2] - v2[2]); if(fabs(diff[1]) > cutoff) return (false); if(fabs(diff[2]) > cutoff) return (false); d2 = (diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]); if(d2 < (cutoff * cutoff)) { *dist = (float) sqrt(d2); return (true); } return (false); } #ifdef _PYMOL_INLINE __inline__ #endif static int SculptCGOBump(float *v1, float *v2, float vdw1, float vdw2, float cutoff, float min, float mid, float max, float *good_color, float *bad_color, int mode, CGO * cgo) { float d2; float diff[3]; float dist; float min_cutoff = cutoff - min; diff[0] = (v1[0] - v2[0]); diff[1] = (v1[1] - v2[1]); if(fabs(diff[0]) > min_cutoff) return (false); diff[2] = (v1[2] - v2[2]); if(fabs(diff[1]) > min_cutoff) return (false); if(fabs(diff[2]) > min_cutoff) return (false); d2 = (diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]); if(d2 > (min_cutoff * min_cutoff)) { return false; } else { dist = (float) sqrt(d2); if(dist <= min_cutoff) { float avg[3], vv1[3], vv2[3], tmp[3], color[3]; float good_bad = cutoff - dist; /* if negative, then good */ float color_factor; float radius = 0.5 * (good_bad - min); if(good_bad < mid) { color_factor = 0.0F; } else { color_factor = (good_bad - mid) / max; if(color_factor > 1.0F) color_factor = 1.0F; } { float one_minus_color_factor = 1.0F - color_factor; scale3f(bad_color, color_factor, color); scale3f(good_color, one_minus_color_factor, tmp); add3f(tmp, color, color); switch (mode) { case 2: if(good_bad > mid) { CGOLinewidth(cgo, 1 + color_factor * 3); CGOColorv(cgo, color); { float *vertexVals = CGODrawArrays(cgo, GL_LINES, CGO_VERTEX_ARRAY, 2); copy3f(v1, vertexVals); copy3f(v2, &vertexVals[3]); } } break; case 1: { float delta, one_minus_delta; if(good_bad < 0.0) { delta = fabs(good_bad); } else { delta = 0.5 * (0.01F + fabs(good_bad)) / (cutoff); } if(delta < 0.01F) delta = 0.01F; if(delta > 0.1F) { delta = 0.1F; } if(radius < 0.01F) radius = 0.01F; one_minus_delta = 1.0F - delta; scale3f(v2, vdw1, avg); scale3f(v1, vdw2, tmp); add3f(tmp, avg, avg); { float inv = 1.0F / (vdw1 + vdw2); scale3f(avg, inv, avg); } scale3f(v1, delta, vv1); scale3f(avg, one_minus_delta, tmp); add3f(tmp, vv1, vv1); scale3f(v2, delta, vv2); scale3f(avg, one_minus_delta, tmp); add3f(tmp, vv2, vv2); if(good_bad < 0.0F) { CGOLinewidth(cgo, 1 + color_factor * 3); CGOResetNormal(cgo, true); CGOColorv(cgo, color); { float *vertexVals = CGODrawArrays(cgo, GL_LINES, CGO_VERTEX_ARRAY, 2); copy3f(vv1, vertexVals); copy3f(vv2, &vertexVals[3]); } } else { CGOCustomCylinderv(cgo, vv1, vv2, radius, color, color, 1, 1); } } break; } } } if(dist > cutoff) return false; return (true); } } #ifdef _PYMOL_INLINE __inline__ #endif static int SculptDoBump(float target, float actual, float *d, float *d0to1, float *d1to0, float wt, float *strain) { float push[3]; float dev, dev_2, sc, abs_dev; dev = target - actual; if((abs_dev = (float) fabs(dev)) > R_SMALL8) { dev_2 = wt * dev / 2.0F; (*strain) += abs_dev; if(actual > R_SMALL8) { /* nonoverlapping */ sc = dev_2 / actual; scale3f(d, sc, push); add3f(push, d0to1, d0to1); subtract3f(d1to0, push, d1to0); } else { /* overlapping, so just push along X */ d0to1[0] -= dev_2; d1to0[0] += dev_2; } return 1; } return 0; } #ifdef _PYMOL_INLINE __inline__ #endif static int SculptCheckAvoid(float *v1, float *v2, float *diff, float *dist, float avoid, float range) { float d2, l2; float cutoff = avoid + range; float low_cutoff; diff[0] = (v1[0] - v2[0]); diff[1] = (v1[1] - v2[1]); if(fabs(diff[0]) > cutoff) return (false); diff[2] = (v1[2] - v2[2]); if(fabs(diff[1]) > cutoff) return (false); low_cutoff = avoid - range; if(fabs(diff[2]) > cutoff) return (false); l2 = low_cutoff * low_cutoff; d2 = (diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]); if((d2 < (cutoff * cutoff)) && (d2 > l2)) { /* we are in the avoid range */ *dist = (float) sqrt(d2); return (true); } return (false); } #ifdef _PYMOL_INLINE __inline__ #endif static int SculptDoAvoid(float avoid, float range, float actual, float *d, float *d0to1, float *d1to0, float wt, float *strain) { float push[3]; float dev, dev_2, sc, abs_dev; float target; if(actual > avoid) { target = avoid + range; } else { target = avoid - range; } dev = target - actual; if((abs_dev = (float) fabs(dev)) > R_SMALL8) { dev_2 = wt * dev / 2.0F; (*strain) += abs_dev; if(actual > R_SMALL8) { /* nonoverlapping */ sc = dev_2 / actual; scale3f(d, sc, push); add3f(push, d0to1, d0to1); subtract3f(d1to0, push, d1to0); } else { /* overlapping, so just push along X */ d0to1[0] -= dev_2; d1to0[0] += dev_2; } return 1; } return 0; } float SculptIterateObject(CSculpt * I, ObjectMolecule * obj, int state, int n_cycle, float *center) { PyMOLGlobals *G = I->G; CShaker *shk; int a0, a1, a2, a3, b0, b3; int aa; CoordSet *cs; float *disp = NULL; float *v, *v0, *v1, *v2, *v3; float diff[3], len; int *atm2idx = NULL; int *cnt = NULL; int *i; int hash; int nb_next; int h, k, l; int offset; float cutoff, vdw_cutoff; int ex; int eval_flag; int mask; float wt; float vdw; float vdw14; float vdw_wt; float vdw_wt14; float bond_wt; float angl_wt; float pyra_wt, pyra_inv_wt; float plan_wt; float line_wt; float tors_wt; float tors_tole; int active_flag = false; float hb_overlap, hb_overlap_base; int *active, n_active; int *exclude; AtomInfoType *ai0, *ai1; double task_time; float vdw_magnify, vdw_magnified = 1.0F; int nb_skip, nb_skip_count; float total_strain = 0.0F, strain; int total_count = 1; CGO *cgo = NULL; float good_color[3] = { 0.2, 1.0, 0.2 }; float bad_color[3] = { 1.0, 0.2, 0.2 }; int vdw_vis_mode; float vdw_vis_min = 0.0F, vdw_vis_mid = 0.0F, vdw_vis_max = 0.0F; float tri_sc, tri_wt; float min_sc, min_wt; float max_sc = 1.025F, max_wt = 0.75F; float *cs_coord; float solvent_radius; float avd_wt, avd_gp, avd_rg; int avd_ex; PRINTFD(G, FB_Sculpt) " SculptIterateObject-Debug: entered state=%d n_cycle=%d\n", state, n_cycle ENDFD; if(!n_cycle) n_cycle = -1; if((state < obj->NCSet) && obj->CSet[state] && n_cycle) { disp = Alloc(float, 3 * obj->NAtom); atm2idx = Alloc(int, obj->NAtom); cnt = Alloc(int, obj->NAtom); active = Alloc(int, obj->NAtom); exclude = Calloc(int, obj->NAtom); shk = I->Shaker; PRINTFD(G, FB_Sculpt) " SIO-Debug: NDistCon %d\n", shk->NDistCon ENDFD; cs = obj->CSet[state]; cs_coord = cs->Coord; vdw = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_scale); vdw14 = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_scale14); vdw_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_weight); vdw_wt14 = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_weight14); bond_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_bond_weight); angl_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_angl_weight); pyra_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_pyra_weight); pyra_inv_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_pyra_inv_weight); plan_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_plan_weight); line_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_line_weight); tri_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tri_weight); tri_sc = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tri_scale); min_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_min_weight); min_sc = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_min_scale); max_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_max_weight); max_sc = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_max_scale); mask = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_field_mask); hb_overlap = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_hb_overlap); hb_overlap_base = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_hb_overlap_base); tors_tole = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tors_tolerance); tors_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_tors_weight); vdw_vis_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_vis_mode); solvent_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_solvent_radius); avd_wt = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_avd_weight); avd_gp = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_avd_gap); avd_rg = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_avd_range); avd_ex = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_avd_excl); if(avd_gp < 0.0F) avd_gp = 1.5F * solvent_radius; if(avd_rg < 0.0F) avd_rg = solvent_radius; if(vdw_vis_mode) { vdw_vis_min = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_vis_min); vdw_vis_mid = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_vis_mid); vdw_vis_max = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_vdw_vis_max); if(!cs->SculptCGO) cs->SculptCGO = CGONew(G); else CGOReset(cs->SculptCGO); } else if(cs->SculptCGO) { CGOReset(cs->SculptCGO); } cgo = cs->SculptCGO; nb_skip = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_sculpt_nb_interval); if(nb_skip > n_cycle) nb_skip = n_cycle; if(nb_skip < 0) nb_skip = 0; n_active = 0; ai0 = obj->AtomInfo; { int a; for(a = 0; a < obj->NAtom; a++) { if(ai0->flags & cAtomFlag_exclude) { exclude[a] = true; a1 = -1; } else { if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[a]) { a1 = obj->DiscreteAtmToIdx[a]; } else { a1 = -1; } } else { a1 = cs->AtmToIdx[a]; } } if(a1 >= 0) { active_flag = true; active[n_active] = a; n_active++; } atm2idx[a] = a1; ai0++; } } if(active_flag) { /* first, create coordinate -> vertex mapping */ /* and count number of constraints */ task_time = UtilGetSeconds(G); vdw_magnify = 1.0F; nb_skip_count = 0; if(center) { int *a_ptr = active; int a; for(aa = 0; aa < n_active; aa++) { a = *(a_ptr++); { AtomInfoType *ai = obj->AtomInfo + a; if((ai->protekted != 1) && !(ai->flags & cAtomFlag_fix)) { v2 = cs_coord + 3 * atm2idx[a]; center[4] += *(v2); center[5] += *(v2 + 1); center[6] += *(v2 + 2); center[7] += 1.0F; } } } } while(n_cycle--) { total_strain = 0.0F; total_count = 0; /* initialize displacements to zero */ v = disp; i = cnt; for(aa = 0; aa < n_active; aa++) { int a = active[aa]; v = disp + a * 3; cnt[a] = 0; *(v) = 0.0F; *(v + 1) = 0.0F; *(v + 2) = 0.0F; } /* apply distance constraints */ { ShakerDistCon *sdc = shk->DistCon; int a,ndc = shk->NDistCon; for(a = 0; a < ndc; a++) { int sdc_type = sdc->type; int b1 = sdc->at0; int b2 = sdc->at1; switch (sdc_type) { case cShakerDistBond: eval_flag = cSculptBond & mask; wt = bond_wt; break; case cShakerDistAngle: eval_flag = cSculptAngl & mask; wt = angl_wt; break; case cShakerDistLimit: eval_flag = cSculptTri & mask; wt = tri_wt; break; case cShakerDistMinim: eval_flag = cSculptMin & mask; wt = min_wt * sdc->weight; break; case cShakerDistMaxim: eval_flag = cSculptMax & mask; wt = max_wt * sdc->weight; break; default: eval_flag = false; wt = 0.0F; break; } if(eval_flag && !(exclude[b1] || exclude[b2])) { a1 = atm2idx[b1]; /* coordinate set indices */ a2 = atm2idx[b2]; if((a1 >= 0) && (a2 >= 0)) { v1 = cs_coord + 3 * a1; v2 = cs_coord + 3 * a2; switch (sdc_type) { case cShakerDistLimit: strain = ShakerDoDistLimit(sdc->targ * tri_sc, v1, v2, disp + b1 * 3, disp + b2 * 3, wt); if(strain > 0.0F) { cnt[b1]++; cnt[b2]++; total_strain += strain; total_count++; } break; case cShakerDistMaxim: strain = ShakerDoDistLimit(sdc->targ * max_sc, v1, v2, disp + b1 * 3, disp + b2 * 3, wt); if(strain > 0.0F) { cnt[b1]++; cnt[b2]++; total_strain += strain; total_count++; } break; case cShakerDistMinim: strain = ShakerDoDistMinim(sdc->targ * min_sc, v1, v2, disp + b1 * 3, disp + b2 * 3, wt); if(strain > 0.0F) { cnt[b1]++; cnt[b2]++; total_strain += strain; total_count++; } break; default: total_strain += ShakerDoDist(sdc->targ, v1, v2, disp + b1 * 3, disp + b2 * 3, wt); cnt[b1]++; cnt[b2]++; total_count++; } } } sdc++; } } /* apply line constraints */ if(cSculptLine & mask) { ShakerLineCon *slc = shk->LineCon; int nlc = shk->NLineCon; int a,b1,b2; for(a = 0; a < nlc; a++) { b0 = slc->at0; b1 = slc->at1; b2 = slc->at2; a0 = atm2idx[b0]; /* coordinate set indices */ a1 = atm2idx[b1]; a2 = atm2idx[b2]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && !(exclude[b0] || exclude[b1] || exclude[b2])) { cnt[b0]++; cnt[b1]++; cnt[b2]++; v0 = cs_coord + 3 * a0; v1 = cs_coord + 3 * a1; v2 = cs_coord + 3 * a2; total_strain += ShakerDoLine(v0, v1, v2, disp + b0 * 3, disp + b1 * 3, disp + b2 * 3, line_wt); total_count++; } slc++; } } /* apply pyramid constraints */ if(cSculptPyra & mask) { ShakerPyraCon *spc = shk->PyraCon; int npc = shk->NPyraCon; int a,b1,b2; for(a = 0; a < npc; a++) { b0 = spc->at0; b1 = spc->at1; b2 = spc->at2; b3 = spc->at3; a0 = atm2idx[b0]; a1 = atm2idx[b1]; a2 = atm2idx[b2]; a3 = atm2idx[b3]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && (a3 >= 0) && !(exclude[b0] || exclude[b1] || exclude[b2] || exclude[b3])) { v0 = cs_coord + 3 * a0; v1 = cs_coord + 3 * a1; v2 = cs_coord + 3 * a2; v3 = cs_coord + 3 * a3; total_strain += ShakerDoPyra(spc->targ1, spc->targ2, v0, v1, v2, v3, disp + b0 * 3, disp + b1 * 3, disp + b2 * 3, disp + b3 * 3, pyra_wt, pyra_inv_wt); total_count++; cnt[b0]++; cnt[b1]++; cnt[b2]++; cnt[b3]++; } spc++; } } if(cSculptPlan & mask) { ShakerPlanCon *snc = shk->PlanCon; int npc = shk->NPlanCon; int a,b1,b2; /* apply planarity constraints */ snc = shk->PlanCon; for(a = 0; a < npc; a++) { b0 = snc->at0; b1 = snc->at1; b2 = snc->at2; b3 = snc->at3; a0 = atm2idx[b0]; a1 = atm2idx[b1]; a2 = atm2idx[b2]; a3 = atm2idx[b3]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && (a3 >= 0) && !(exclude[b0] || exclude[b1] || exclude[b2] || exclude[b3])) { v0 = cs_coord + 3 * a0; v1 = cs_coord + 3 * a1; v2 = cs_coord + 3 * a2; v3 = cs_coord + 3 * a3; total_strain += ShakerDoPlan(v0, v1, v2, v3, disp + b0 * 3, disp + b1 * 3, disp + b2 * 3, disp + b3 * 3, snc->target, snc->fixed, plan_wt); total_count++; cnt[b0]++; cnt[b1]++; cnt[b2]++; cnt[b3]++; } snc++; } } /* apply torsion constraints */ if(cSculptTors & mask) { ShakerTorsCon *stc = shk->TorsCon; int ntc = shk->NTorsCon; int a,b1,b2; /* apply planarity constraints */ for(a = 0; a < ntc; a++) { b0 = stc->at0; b1 = stc->at1; b2 = stc->at2; b3 = stc->at3; a0 = atm2idx[b0]; a1 = atm2idx[b1]; a2 = atm2idx[b2]; a3 = atm2idx[b3]; if((a0 >= 0) && (a1 >= 0) && (a2 >= 0) && (a3 >= 0) && !(exclude[b0] || exclude[b1] || exclude[b2] || exclude[b3])) { v0 = cs_coord + 3 * a0; v1 = cs_coord + 3 * a1; v2 = cs_coord + 3 * a2; v3 = cs_coord + 3 * a3; total_strain += ShakerDoTors(stc->type, v0, v1, v2, v3, disp + b0 * 3, disp + b1 * 3, disp + b2 * 3, disp + b3 * 3, tors_tole, tors_wt); total_count++; cnt[b0]++; cnt[b1]++; cnt[b2]++; cnt[b3]++; } stc++; } } /* apply nonbonded interactions */ if((n_cycle > 0) && (nb_skip_count > 0)) { /*skip and then weight extra */ nb_skip_count--; vdw_magnify += 1.0F; } else { int nb_off0, nb_off1; int v0i, v1i, v2i; int x0i; int don_b0; int acc_b0; int b1; vdw_magnified = vdw_magnify; vdw_magnify = 1.0F; nb_skip_count = nb_skip; if((cSculptVDW | cSculptVDW14 | cSculptAvoid) & mask) { /* compute non-bonded interations */ /* construct nonbonded hash */ nb_next = 1; for(aa = 0; aa < n_active; aa++) { b0 = active[aa]; a0 = atm2idx[b0]; VLACheck(I->NBList, int, nb_next + 2); v0 = cs_coord + 3 * a0; hash = nb_hash(v0); i = I->NBList + nb_next; *(i++) = *(I->NBHash + hash); *(i++) = hash; *(i++) = b0; *(I->NBHash + hash) = nb_next; nb_next += 3; } /* find neighbors for each atom */ if((cSculptVDW | cSculptVDW14) & mask) { for(aa = 0; aa < n_active; aa++) { b0 = active[aa]; a0 = atm2idx[b0]; ai0 = obj->AtomInfo + b0; v0 = cs_coord + 3 * a0; don_b0 = I->Don[b0]; acc_b0 = I->Acc[b0]; v0i = (int) (*v0); v1i = (int) (*(v0 + 1)); v2i = (int) (*(v0 + 2)); x0i = ex_hash_i0(b0); for(h = -4; h < 5; h += 4) { nb_off0 = nb_hash_off_i0(v0i, h); for(k = -4; k < 5; k += 4) { nb_off1 = nb_off0 | nb_hash_off_i1(v1i, k); for(l = -4; l < 5; l += 4) { /* offset = *(I->NBHash+nb_hash_off(v0,h,k,l)); */ offset = *(I->NBHash + (nb_off1 | nb_hash_off_i2(v2i, l))); while(offset) { i = I->NBList + offset; b1 = *(i + 2); if(b1 > b0) { /* determine exclusion (if any) */ { int xoffset; int *I_EXList = I->EXList; int ex1; int *j; xoffset = *(I->EXHash + (x0i | ex_hash_i1(b1))); ex = 10; while(xoffset) { xoffset = (*(j = I_EXList + xoffset)); if((*(j + 1) == b0) && (*(j + 2) == b1)) { ex1 = *(j + 3); if(ex1 < ex) { ex = ex1; } } } } if(ex > 3) { ai1 = obj->AtomInfo + b1; cutoff = ai0->vdw + ai1->vdw; if(ex == 10) { /* standard interaction -- no exclusion */ if(don_b0 && I->Acc[b1]) { /* h-bond */ if(ai0->protons == cAN_H) { cutoff -= hb_overlap; } else { cutoff -= hb_overlap_base; } } else if(acc_b0 && I->Don[b1]) { /* h-bond */ if(ai1->protons == cAN_H) { cutoff -= hb_overlap; } else { cutoff -= hb_overlap_base; } } if(cSculptVDW & mask) { vdw_cutoff = cutoff * vdw; wt = vdw_wt * vdw_magnified; a1 = atm2idx[b1]; v1 = cs_coord + 3 * a1; if(vdw_vis_mode && cgo && (n_cycle < 1) && ((!((ai0->protekted && ai1->protekted) || (ai0->flags & ai1->flags & cAtomFlag_fix)) ) || (ai0->flags & cAtomFlag_study) || (ai1->flags & cAtomFlag_study))) { SculptCGOBump(v0, v1, ai0->vdw, ai1->vdw, cutoff, vdw_vis_min, vdw_vis_mid, vdw_vis_max, good_color, bad_color, vdw_vis_mode, cgo); } if(SculptCheckBump(v0, v1, diff, &len, vdw_cutoff)) if(SculptDoBump(vdw_cutoff, len, diff, disp + b0 * 3, disp + b1 * 3, wt, &total_strain)) { cnt[b0]++; cnt[b1]++; total_count++; } } } else if(ex == 4) { /* 1-4 interation */ cutoff *= vdw14; wt = vdw_wt14 * vdw_magnified; if(cSculptVDW14 & mask) { a1 = atm2idx[b1]; v1 = cs_coord + 3 * a1; if(SculptCheckBump(v0, v1, diff, &len, cutoff)) { if(SculptDoBump(cutoff, len, diff, disp + b0 * 3, disp + b1 * 3, wt, &total_strain)) { cnt[b0]++; cnt[b1]++; total_count++; } } } } else if(ex == 5) { /* do nothing */ } } } offset = (*i); } } } } } } if(cSculptAvoid & mask) { float target; float range = solvent_radius * 0.75; /* tweak nb distances to avoid sitting in the surface rendition danger zone for too long (vdw1+vdw2+0.75*solvent) */ for(aa = 0; aa < n_active; aa++) { b0 = active[aa]; a0 = atm2idx[b0]; ai0 = obj->AtomInfo + b0; v0 = cs_coord + 3 * a0; don_b0 = I->Don[b0]; acc_b0 = I->Acc[b0]; v0i = (int) (*v0); v1i = (int) (*(v0 + 1)); v2i = (int) (*(v0 + 2)); x0i = ex_hash_i0(b0); for(h = -8; h < 9; h += 4) { nb_off0 = nb_hash_off_i0(v0i, h); for(k = -8; k < 9; k += 4) { nb_off1 = nb_off0 | nb_hash_off_i1(v1i, k); for(l = -8; l < 9; l += 4) { /* offset = *(I->NBHash+nb_hash_off(v0,h,k,l)); */ offset = *(I->NBHash + (nb_off1 | nb_hash_off_i2(v2i, l))); while(offset) { i = I->NBList + offset; b1 = *(i + 2); if(b1 > b0) { /* determine exclusion (if any) */ { int xoffset; int *I_EXList = I->EXList; int ex1; int *j; xoffset = *(I->EXHash + (x0i | ex_hash_i1(b1))); ex = 10; while(xoffset) { xoffset = (*(j = I_EXList + xoffset)); if((*(j + 1) == b0) && (*(j + 2) == b1)) { ex1 = *(j + 3); if(ex1 < ex) { ex = ex1; } } } } if(ex > avd_ex) { /* either non-covalent or extended chain */ ai1 = obj->AtomInfo + b1; target = ai0->vdw + ai1->vdw + avd_gp; a1 = atm2idx[b1]; v1 = cs_coord + 3 * a1; if(SculptCheckAvoid(v0, v1, diff, &len, target, avd_rg)) { if(SculptDoAvoid(target, range, len, diff, disp + b0 * 3, disp + b1 * 3, avd_wt, &total_strain)) { cnt[b0]++; cnt[b1]++; total_count++; } } } } offset = (*i); } } } } } } /* clean up nonbonded hash */ i = I->NBList + 2; while(nb_next > 1) { *(I->NBHash + *i) = 0; i += 3; nb_next -= 3; } } } /* average the displacements */ if(n_cycle >= 0) { int cnt_a,a; float _1 = 1.0F; float inv_cnt; int *a_ptr = active; float *lookup_inverse = I->inverse; for(aa = 0; aa < n_active; aa++) { if((cnt_a = cnt[(a = *(a_ptr++))])) { AtomInfoType *ai = obj->AtomInfo + a; RefPosType *cs_refpos = cs->RefPos; int flags; if(!(ai->protekted || ((flags = ai->flags) & cAtomFlag_fix))) { v1 = disp + 3 * a; v2 = cs_coord + 3 * atm2idx[a]; if((flags & cAtomFlag_restrain) && cs_refpos) { RefPosType *rp = cs_refpos + atm2idx[a]; if(rp->specified) { float *v3 = rp->coord; cnt_a++; v1[0] += v3[0] - v2[0]; v1[1] += v3[1] - v2[1]; v1[2] += v3[2] - v2[2]; } } if(!(cnt_a & 0xFFFFFF00)) /* don't divide -- too slow! */ inv_cnt = lookup_inverse[cnt_a]; else inv_cnt = _1 / cnt_a; *(v2) += (*(v1)) * inv_cnt; *(v2 + 1) += (*(v1 + 1)) * inv_cnt; *(v2 + 2) += (*(v1 + 2)) * inv_cnt; } } } cs->invalidateRep(cRepAll, cRepInvCoord); } else if(cgo) { SceneDirty(G); } if(n_cycle <= 0) { int *a_ptr = active; if(center) for(aa = 0; aa < n_active; aa++) { int a = *(a_ptr++); { AtomInfoType *ai = obj->AtomInfo + a; if((ai->protekted != 1) && !(ai->flags & cAtomFlag_fix)) { v2 = cs_coord + 3 * atm2idx[a]; center[0] += *(v2); center[1] += *(v2 + 1); center[2] += *(v2 + 2); center[3] += 1.0F; } } } break; } } task_time = UtilGetSeconds(G) - task_time; PRINTFB(G, FB_Sculpt, FB_Blather) " Sculpt: %2.5f seconds %8.3f %d %8.3f\n", task_time, total_strain, total_count, 100 * total_strain / total_count ENDFB(G); if(total_count) total_strain = (1000 * total_strain) / total_count; } FreeP(exclude); FreeP(active); FreeP(cnt); FreeP(disp); FreeP(atm2idx); if(cgo) { CGOStop(cgo); { int est = CGOCheckComplex(cgo); if(est) { cs->SculptCGO = CGOSimplify(cgo, est); CGOFree(cgo); CGOFree(cs->SculptShaderCGO); } } } EditorDihedralInvalid(G, obj); } PRINTFD(G, FB_Sculpt) " SculptIterateObject-Debug: leaving...\n" ENDFD; return total_strain; } void SculptFree(CSculpt * I) { VLAFreeP(I->Don); VLAFreeP(I->Acc); VLAFreeP(I->NBList); VLAFreeP(I->EXList); FreeP(I->NBHash); FreeP(I->EXHash); ShakerFree(I->Shaker); OOFreeP(I); }