in layer2/RepCartoon.cpp [2824:3120]
void RepCartoonGeneratePASS1(PyMOLGlobals *G, RepCartoon *I, ObjectMolecule *obj, CoordSet * cs,
nuc_acid_data *ndata){
int st, nd;
int a, a1, a3, a4 = 0;
char *lv = I->LastVisib;
int trace, trace_mode;
AtomInfoType *ai, *last_ai = NULL;
int cartoon_side_chain_helper;
int cylindrical_helices;
int fancy_helices;
int fancy_sheets;
int parity = 1;
float *v_c, *v_n, *v_o;
int cur_car;
nuc_acid_cap leading_O5p(G, ndata, cs, 3);
nuc_acid_cap trailing_O3p(G, ndata, cs, 2);
// settings
fancy_sheets =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_fancy_sheets);
fancy_helices =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_fancy_helices);
cylindrical_helices =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_cylindrical_helices);
cartoon_side_chain_helper =
SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_side_chain_helper);
int trace_ostate =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_trace_atoms);
trace_mode =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_trace_atoms_mode);
auto gap_cutoff =
SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_gap_cutoff);
// iterate over (sorted) atoms
for(CoordSetAtomIterator iter(cs); iter.next();) {
ai = iter.getAtomInfo();
// cartoon rep for this atom?
if(!(*(lv++) = GET_BIT(ai->visRep, cRepCartoon)))
continue;
const char * ai_name = LexStr(G, ai->name);
// atom indices
a = iter.getIdx();
a1 = iter.getAtm();
// rings
if(ndata->ring_anchor
&& ai->protons != cAN_H
&& (ndata->ring_finder_eff >= 3 /* all 5-7 atom rings */
|| (ndata->ring_finder_eff <= 2 /* C4-containing rings */
&& (WordMatchExact(G, "C4", ai_name, 1)))
|| (ndata->ring_finder_eff == 1
&& (WordMatchExact(G, "C4*", ai_name, 1)
|| WordMatchExact(G, "C4'", ai_name, 1))))) {
VLACheck(ndata->ring_anchor, int, ndata->n_ring);
ndata->ring_anchor[ndata->n_ring] = a1;
ndata->n_ring++;
}
// handle alternative conformations
if (ai->alt[0]) {
if (!ndata->alt) {
ndata->alt = ai->alt[0];
} else if (ai->alt[0] != ndata->alt) {
if (ai->alt[0] > ndata->alt &&
(!ndata->next_alt || ai->alt[0] < ndata->next_alt))
ndata->next_alt = ai->alt[0];
continue;
}
}
// atom level setting
trace = AtomSettingGetWD(G, ai, cSetting_cartoon_trace_atoms, trace_ostate);
// CA or cartoon_trace_atoms
if(trace || (ai->protons == cAN_C
&& WordMatchExact(G, "CA", ai_name, true)
&& !AtomInfoSameResidueP(G, last_ai, ai))) {
PRINTFD(G, FB_RepCartoon)
" RepCartoon: found CA in %d%c; a2 %d\n", ai->resv, ai->getInscode(true), ndata->a2 ENDFD;
// 3' nucleic acid cap
if(trailing_O3p.cap())
ndata->a2 = -1;
bool is_gap = false;
// auto-detect CA-only models
if (!ai->bonded)
trace = true;
// check for gap
if(ndata->a2 >= 0) {
if(!trace) {
// CA->N->C->CA = 3 bonds
if(!ObjectMoleculeCheckBondSep(obj, a1, ndata->a2, 3))
is_gap = true;
} else {
if(!AtomInfoSequential(G, obj->AtomInfo + ndata->a2, ai, trace_mode))
is_gap = true;
}
}
cur_car = ai->cartoon;
if (is_gap) {
int delta = ai->resv - last_ai->resv;
if (delta < 1 || delta > gap_cutoff || !AtomInfoSameChainP(G, ai, last_ai)) {
ndata->a2 = -1;
} else {
(ndata->cc - 1)->setCCOut(cCartoon_dash);
}
}
last_ai = ai;
// start new segment?
if(ndata->a2 < 0)
ndata->nSeg++;
(*ndata->fp) = ai->flags; /* store atom flags */
// side_chain_helper
if ((ai->visRep & (cRepLineBit | cRepCylBit | cRepSphereBit)) &&
AtomSettingGetWD(G, ai, cSetting_cartoon_side_chain_helper,
cartoon_side_chain_helper)) {
(*ndata->fp) |= cAtomFlag_no_smooth;
}
// secondary structure type
switch (ai->ssType[0]) {
case 'H':
case 'h':
if(cur_car == cCartoon_auto) {
cur_car = cylindrical_helices ? cCartoon_skip_helix :
fancy_helices ? cCartoon_dumbbell : cCartoon_oval;
}
(*ndata->ss) = 1; /* helix */
parity = 0;
break;
case 'S':
case 's':
if(cur_car == cCartoon_auto) {
cur_car = fancy_sheets ? cCartoon_arrow : cCartoon_rect;
}
(*ndata->ss) = 2; /* sheet */
parity = !parity;
break;
default: /* 'L', 'T', 0, etc. */
if(cur_car == cCartoon_auto) {
cur_car = cCartoon_loop;
}
parity = 0;
(*ndata->ss) = 0;
break;
}
if(cur_car == cCartoon_putty)
ndata->putty_flag = true;
// coordinates
copy3f(cs->Coord + 3 * a, ndata->vptr);
*((ndata->cc)++) = cur_car;
ndata->a2 = a1;
ndata->vptr += 3;
ndata->ss++;
ndata->fp++;
*(ndata->sptr++) = ndata->nSeg;
ndata->nAt++;
*(ndata->iptr++) = a;
if (trace) {
if (a1 == 0 || a1 + 1 == cs->NAtIndex ||
(a3 = cs->atmToIdx(a1 - 1)) == -1 ||
(a4 = cs->atmToIdx(a1 + 1)) == -1) {
zero3f(ndata->voptr);
} else {
float t0[3], t1[3];
subtract3f(cs->coordPtr(a), cs->coordPtr(a3), t0);
subtract3f(cs->coordPtr(a), cs->coordPtr(a4), t1);
add3f(t0, t1, ndata->voptr);
normalize3f(ndata->voptr);
}
ndata->voptr += 3;
continue;
}
// pointers to C+N+O coordinates
v_c = NULL;
v_n = NULL;
v_o = NULL;
// get start (st) and end (nd) indices of residue atoms
AtomInfoBracketResidueFast(G, obj->AtomInfo, obj->NAtom, a1, &st, &nd);
// find C+N+O coordinates
for(a3 = st; a3 <= nd; a3++) {
a4 = cs->atmToIdx(a3);
if (a4 == -1)
continue;
const char * a3name = LexStr(G, obj->AtomInfo[a3].name);
if(WordMatchExact(G, "C", a3name, true)) {
v_c = cs->Coord + 3 * a4;
} else if(WordMatchExact(G, "N", a3name, true)) {
v_n = cs->Coord + 3 * a4;
} else if(WordMatchExact(G, "O", a3name, true)) {
v_o = cs->Coord + 3 * a4;
}
}
// orientation vector
if(!(v_c && v_n && v_o)) {
zero3f(ndata->voptr);
} else {
float t0[3], t1[3];
subtract3f(v_n, v_c, t0); /* t0 = N<---C */
normalize3f(t0);
subtract3f(v_n, v_o, t1); /* t1 = N<---O */
normalize3f(t1);
cross_product3f(t0, t1, ndata->voptr);
normalize3f(ndata->voptr);
if(parity) {
invert3f(ndata->voptr);
}
}
ndata->voptr += 3;
} else if(
!AtomInfoSameResidueP(G, last_ai, ai)
&& (ndata->na_mode != 1 ?
// P atom
(ai->protons == cAN_P && WordMatchExact(G, "P", ai_name, true)) :
// C3* C3' atom
(ai->protons == cAN_C && (WordMatchExact(G, NUCLEIC_NORMAL1, ai_name, 1) ||
WordMatchExact(G, NUCLEIC_NORMAL2, ai_name, 1))))) {
// check for gap
if(ndata->a2 >= 0 &&
// six bonds between phosphates
!ObjectMoleculeCheckBondSep(obj, a1, ndata->a2, 6)) {
/* 3' cap */
trailing_O3p.cap();
ndata->a2 = -1;
}
last_ai = ai;
trailing_O3p.set(-1, -1, NULL);
/* 5' cap */
if(leading_O5p.active()
&& ndata->a2 < 0
&& !AtomInfoSameResidueP(G, ai, leading_O5p.ai)
&& ObjectMoleculeCheckBondSep(obj, a1, leading_O5p.atm, 5)) {
leading_O5p.cap();
}
leading_O5p.set(-1, -1, NULL);
/* this is the main nucleic acid cartoon section... */
nuc_acid(G, ndata, a, a1, ai, cs, obj, true);
} else if(
// P -> O3* bond
AtomInfoSameResidueP(G, last_ai, ai)
&& ndata->a2 >= 0
&& last_ai
&& cAN_P == last_ai->protons
&& cAN_O == ai->protons
&& trailing_O3p.enabled
&& (WordMatchExact(G, "O3'", ai_name, 1) ||
WordMatchExact(G, "O3*", ai_name, 1))
&& ObjectMoleculeCheckBondSep(obj, a1, ndata->a2, 5)) {
// remember trailing O3*
trailing_O3p.set(a, a1, ai);
} else if(
// O5* atom
ai->protons == cAN_O
&& leading_O5p.enabled
&& (WordMatchExact(G, "O5'", ai_name, 1) ||
WordMatchExact(G, "O5*", ai_name, 1))) {
leading_O5p.set(a, a1, ai);
}
}
/* BEGIN 3' cap */
if(trailing_O3p.cap()) {
ndata->a2 = -1;
}
}