layer2/RepCartoon.cpp (3,359 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: -* Cameron Mura -* -* Z* ------------------------------------------------------------------- */ #include <set> #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"Base.h" #include"OOMac.h" #include"RepCartoon.h" #include"Color.h" #include"Setting.h" #include"Word.h" #include"Feedback.h" #include"CGO.h" #include"Extrude.h" #include"ShaderMgr.h" #include "Lex.h" #include "AtomIterators.h" class CCInOut { // skip c++11 initialization, this is Calloc'd signed char cc_in; // { 0 }; signed char cc_out; // { 0 }; public: signed char getCCIn() const { return cc_in; } signed char getCCOut() const { return cc_out ? cc_out : getCCIn(); } void setCCOut(int c) { cc_out = c; } // legacy void operator= (int c) { cc_in = c; } operator int() const { return cc_in; } bool operator== (const CCInOut &other) const { return cc_in == other.cc_in; } }; typedef struct RepCartoon { Rep R; /* must be first! */ CGO *ray, *std, *preshader; char *LastVisib; unsigned char renderWithShaders, hasTransparency; } RepCartoon; #include"ObjectMolecule.h" #define ESCAPE_MAX 500 void RepCartoonFree(RepCartoon * I); void RepCartoonFree(RepCartoon * I) { if (I->ray != I->preshader) CGOFree(I->preshader); CGOFree(I->ray); CGOFree(I->std); FreeP(I->LastVisib); RepPurge(&I->R); OOFreeP(I); } /* * CGOAddTwoSidedBackfaceSpecialOps: this function takes in a CGO, * and outputs a CGO with that CGO wrapped with the two operations: * enabling and disabling back faces if two_sided_lighting is not set * (i.e., the CGOSpecial operations, see below) * */ static CGO *CGOAddTwoSidedBackfaceSpecialOps(PyMOLGlobals *G, CGO *cgo){ CGO *tmpCGO = CGONew(G); CGOSpecial(tmpCGO, ENABLE_BACK_FACES_IF_NOT_TWO_SIDED); CGOAppendNoStop(tmpCGO, cgo); CGOSpecial(tmpCGO, DISABLE_BACK_FACES_IF_NOT_TWO_SIDED); CGOStop(tmpCGO); tmpCGO->render_alpha = cgo->render_alpha; CGOFreeWithoutVBOs(cgo); return tmpCGO; } static int RepCartoonCGOGenerate(RepCartoon * I, RenderInfo * info) { PyMOLGlobals *G = I->R.G; int ok = true; int use_shaders, has_cylinders_to_optimize; float alpha = 1.0F - SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_cartoon_transparency); auto hasAtomLevelAlpha = [](RepCartoon * I){ for(CoordSetAtomIterator iter(I->R.cs); iter.next();){ auto ai = iter.getAtomInfo(); if(AtomSettingGetWD(I->R.G, ai, cSetting_cartoon_transparency, 0.0f)){ return true; } } return false; }(I); use_shaders = SettingGetGlobal_b(G, cSetting_use_shaders) && SettingGetGlobal_b(G, cSetting_cartoon_use_shader); has_cylinders_to_optimize = G->ShaderMgr->Get_CylinderShader(info->pass, 0) && SettingGetGlobal_i(G, cSetting_cartoon_nucleic_acid_as_cylinders) && SettingGetGlobal_b(G, cSetting_render_as_cylinders) && CGOHasCylinderOperations(I->preshader); if (I->std && (use_shaders ^ I->renderWithShaders)){ // if renderWithShaders doesn't match use_shader, clear CGO and re-generate CGOFree(I->std); I->std = 0; } I->hasTransparency = 0; if (use_shaders){ CGO *convertcgo = NULL, *tmpCGO = NULL, *tmp2CGO = NULL; if (((alpha < 1.f) || hasAtomLevelAlpha) && (SettingGetGlobal_i(G, cSetting_transparency_mode) != 3)){ // some transparency const float *color; float colorWithA[4]; CGO *convertcgo2; convertcgo2 = CGOSimplify(I->preshader, 0) ; convertcgo = CGOCombineBeginEnd(convertcgo2, 0); CGOFree(convertcgo2); CHECKOK(ok, convertcgo); color = ColorGet(G, I->R.obj->Color); copy3f(color, colorWithA); colorWithA[3] = alpha; tmpCGO = CGOOptimizeToVBOIndexedWithColorEmbedTransparentInfo(convertcgo, 0, NULL, true); CGOStop(tmpCGO); CGOFree(convertcgo); tmp2CGO = CGONew(G); CGOEnable(tmp2CGO, GL_BACK_FACE_CULLING); CGOAppendNoStop(tmp2CGO, tmpCGO); CGODisable(tmp2CGO, GL_BACK_FACE_CULLING); CGOStop(tmp2CGO); CGOFreeWithoutVBOs(tmpCGO); I->std = tmp2CGO; I->hasTransparency = 1; } else { CGO *leftOverCGO = NULL; if (has_cylinders_to_optimize && G->ShaderMgr->Get_CylinderShader(info->pass, 0)){ /* Optimize Cylinders into Shader operation */ CGO *tmpCGO = CGONew(G); leftOverCGO = CGONew(G); CGOEnable(tmpCGO, GL_CYLINDER_SHADER); CGOFilterOutCylinderOperationsInto(I->preshader, leftOverCGO); convertcgo = CGOConvertShaderCylindersToCylinderShader(I->preshader, tmpCGO); CGOAppendNoStop(tmpCGO, convertcgo); CGODisable(tmpCGO, GL_CYLINDER_SHADER); CGOStop(tmpCGO); CGOFreeWithoutVBOs(convertcgo); convertcgo = tmpCGO; convertcgo->use_shader = true; } if (!leftOverCGO){ leftOverCGO = I->preshader; convertcgo = CGONew(G); } bool has_spheres_to_optimize = CGOHasSphereOperations(leftOverCGO); if (has_spheres_to_optimize){ /* Optimize spheres and putting them into convertcgo as a shader CGO operation */ CGO *sphereVBOs = NULL, *leftOverAfterSpheresCGO = NULL; leftOverAfterSpheresCGO = CGONew(G); sphereVBOs = CGOOptimizeSpheresToVBONonIndexed(leftOverCGO, 0, true, leftOverAfterSpheresCGO); if (sphereVBOs){ ok &= CGOAppendNoStop(convertcgo, sphereVBOs); CGOFreeWithoutVBOs(sphereVBOs); } else { CGOFree(leftOverAfterSpheresCGO); } if (leftOverCGO!=I->ray && leftOverCGO!=I->preshader){ CGOFree(leftOverCGO); } if (leftOverAfterSpheresCGO) leftOverCGO = leftOverAfterSpheresCGO; } /* For the rest of the primitives that exist, simplify them into Geometry * (should probably be no more, but do this anyway) */ CGO *leftOverCGOSimplified = NULL, *leftOverCGOCombined; leftOverCGOSimplified = CGOSimplify(leftOverCGO, 0); CHECKOK(ok, leftOverCGOSimplified); leftOverCGOCombined = CGOCombineBeginEnd(leftOverCGOSimplified, 0); CGOFree(leftOverCGOSimplified); if (leftOverCGO!=I->ray && leftOverCGO!=I->preshader){ CGOFree(leftOverCGO); } // Convert all DrawArrays and Geometry to VBOs if (ok) tmpCGO = CGOOptimizeToVBONotIndexed(leftOverCGOCombined, 0); CHECKOK(ok, tmpCGO); CGOFree(leftOverCGOCombined); if (ok) ok &= CGOAppend(convertcgo, tmpCGO); CGOFreeWithoutVBOs(tmpCGO); tmpCGO = NULL; I->std = CGOAddTwoSidedBackfaceSpecialOps(G, convertcgo); } } else { if (ok){ auto simplifiedCGO = CGOSimplify(I->preshader, 0); if (alpha < 1.f || hasAtomLevelAlpha){ auto convertedCGO = CGOConvertTrianglesToAlpha(simplifiedCGO); CGOFree(simplifiedCGO); I->std = convertedCGO; if(I->std){ I->std->render_alpha = 1; // CGORenderGL should call CGOSetZVector/CGORenderGLAlpha only } } else { I->std = simplifiedCGO; CHECKOK(ok, I->std); } if(I->std) { I->std = CGOAddTwoSidedBackfaceSpecialOps(G, I->std); } } } if(I->preshader && (I->ray!=I->preshader)){ CGOFree(I->preshader); } I->preshader = NULL; I->renderWithShaders = use_shaders; return ok; } static void RepCartoonRender(RepCartoon * I, RenderInfo * info) { CRay *ray = info->ray; auto pick = info->pick; PyMOLGlobals *G = I->R.G; int ok = true; if (!ray && I->preshader) { ok &= RepCartoonCGOGenerate(I, info); } if(ray) { #ifndef _PYMOL_NO_RAY int try_std = false; PRINTFD(G, FB_RepCartoon) " RepCartoonRender: rendering raytracable...\n" ENDFD; if(I->ray){ int rok = CGORenderRay(I->ray, ray, info, NULL, NULL, I->R.cs->Setting, I->R.obj->Setting); if (!rok){ if (I->ray == I->preshader) I->preshader = NULL; CGOFree(I->ray); try_std = true; } } else { try_std = true; } if(try_std && I->std){ ok &= CGORenderRay(I->std, ray, info, NULL, NULL, I->R.cs->Setting, I->R.obj->Setting); if (!ok){ CGOFree(I->std); } } #endif } else if(G->HaveGUI && G->ValidContext) { int use_shader; use_shader = SettingGetGlobal_b(G, cSetting_cartoon_use_shader) && SettingGetGlobal_b(G, cSetting_use_shaders); if(pick) { if(I->std) { I->std->use_shader = use_shader; CGORenderGLPicking(I->std, info, &I->R.context, I->R.cs->Setting, I->R.obj->Setting); } } else { PRINTFD(G, FB_RepCartoon) " RepCartoonRender: rendering GL...\n" ENDFD; if(ok && I->std){ I->std->use_shader = use_shader; CGORenderGL(I->std, NULL, I->R.cs->Setting, I->R.obj->Setting, info, &I->R); } } } if (!ok || !I->ray || !CGOHasOperations(I->ray)){ if (I->ray == I->preshader) I->preshader = NULL; CGOFree(I->ray); CGOFree(I->std); I->R.fInvalidate(&I->R, I->R.cs, cRepInvPurge); I->R.cs->Active[cRepCartoon] = false; } } #define NUCLEIC_NORMAL0 "C2" #define NUCLEIC_NORMAL1 "C3*" #define NUCLEIC_NORMAL2 "C3'" #define MAX_RING_ATOM 10 typedef struct nuc_acid_data { int na_mode; int *nuc_flag; // whether atom is part of nucleotide int a2; // defaults to -1 int nSeg; // defaults to 0 float *v_o_last; // defaults to NULL int *sptr; int *iptr; CCInOut * cc; int nAt; int *ss; int putty_flag; int *fp; float *vptr, *voptr; int *ring_anchor; int ring_mode, ring_finder, ring_finder_eff; int n_ring; char alt; char next_alt; } nuc_acid_data; /* * Return true if a connector between the two atoms should be drawn. * * TODO: should RepSphere and side_chain_helper check really be part of this? * TODO: should line|cyl|sphere also be checked for at2? */ static bool ring_connector_visible(PyMOLGlobals * G, const AtomInfoType * ai1, const AtomInfoType * ai2, bool sc_helper) { if (!(ai1->visRep & ai2->visRep & cRepCartoonBit)) return false; if (!(ai1->visRep & (cRepLineBit | cRepCylBit | cRepSphereBit))) return true; return !( AtomSettingGetWD(G, ai1, cSetting_cartoon_side_chain_helper, sc_helper) || AtomSettingGetWD(G, ai2, cSetting_cartoon_side_chain_helper, sc_helper)); } /* * depth-first neighbor search for nucleic acid atom * * nuc_flag: NAtom-length boolean nucleic acid flags array * neighbor: Neighbor data structure * atm: atom index * max_depth: maximum search depth * seen: set of visited atom indices (read/write) * * Return: true if any nucleic acid atom found within max_depth radius */ static bool has_nuc_neighbor( const int * nuc_flag, const int * neighbor, const int atm, const int max_depth, std::set<int> &seen) { int atm_neighbor, tmp; ITERNEIGHBORATOMS(neighbor, atm, atm_neighbor, tmp) { if (nuc_flag[atm_neighbor]) return true; if (seen.count(atm_neighbor)) continue; seen.insert(atm_neighbor); if (max_depth > 1 && has_nuc_neighbor(nuc_flag, neighbor, atm_neighbor, max_depth - 1, seen)) return true; } return false; } /* atix must contain n_atom + 1 elements, with the first atom repeated at the end */ static void do_ring(PyMOLGlobals * G, nuc_acid_data *ndata, int n_atom, int *atix, ObjectMolecule * obj, CoordSet * cs, float ring_width, CGO * cgo, int ring_color, float ladder_radius, int ladder_color, int ladder_mode, int sc_helper, float ring_alpha, float alpha, int *marked, float *moved, float ring_radius) { float *v_i[MAX_RING_ATOM]; const float *col[MAX_RING_ATOM]; float n_up[MAX_RING_ATOM][3]; float n_dn[MAX_RING_ATOM][3]; AtomInfoType *ai_i[MAX_RING_ATOM]; int have_all = true; int all_marked = true; AtomInfoType *ai; int have_C4 = -1; int have_C4_prime = -1; int have_C_number = -1; int nf = false; int ring_mode = ndata->ring_mode; int finder = ndata->ring_finder; const auto& nuc_flag = ndata->nuc_flag; /* first, make sure all atoms have known coordinates */ { int a, i; for(i = 0; i <= n_atom; i++) { int a1 = atix[i]; int have_atom = false; if(nuc_flag[a1]) nf = true; a = cs->atmToIdx(a1); if(a >= 0) { ai = obj->AtomInfo + a1; if(ai->visRep & cRepCartoonBit) { ai_i[i] = ai; // take atom level settings from any ring atom (effectifly from the // last one with settings) AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ring_mode, &ring_mode); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ring_color, &ring_color); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ring_radius, &ring_radius); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ring_width, &ring_width); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ladder_mode, &ladder_mode); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ladder_color, &ladder_color); AtomSettingGetIfDefined(G, ai, cSetting_cartoon_ladder_radius, &ladder_radius); col[i] = ColorGet(G, ai->color); v_i[i] = cs->Coord + 3 * a; have_atom = true; const char * ai_name = LexStr(G, ai->name); if(WordMatchExact(G, "C4", ai_name, 1)) have_C4 = a1; else if( WordMatchExact(G, "C4'", ai_name, 1) || WordMatchExact(G, "C4*", ai_name, 1)) have_C4_prime = a1; if(((ai_name[0] == 'C') || (ai_name[0] == 'c')) && isdigit(ai_name[1])) have_C_number = a1; } if(!marked[a1]) all_marked = false; } if(!have_atom) { have_all = false; break; } } } if(!ring_mode) finder = 0; ring_width *= 0.5F; if(n_atom && have_all && (!all_marked)) { if(ladder_mode) { int i; int a1; AtomInfoType *ai2; AtomInfoType *atomInfo = obj->AtomInfo; int mem0, mem1, mem2, mem3, mem4, mem5, mem6, mem7; int *neighbor = obj->Neighbor; int nbr[7]; int sugar_at = -1, base_at = -1; int phos3_at = -1, phos5_at = -1; int o3_at = -1, o5_at = -1, c1_at = -1; int purine_flag = false; int c1 = -1, c1_linked = -1; int c2 = -1, c2_linked = -1; int c3 = -1, c3_linked = -1; int c4 = -1, c4_linked = -1; int c5 = -1, c5_linked = -1; if(n_atom == 5) { /* going from the sugar to the base */ for(i = 0; i < n_atom; i++) { a1 = atix[i]; if(!nf) nf = nuc_flag[a1]; ai = atomInfo + a1; if((ai->protons == cAN_C) && (!marked[a1]) && (WordMatchExact(G, "C3*", LexStr(G, ai->name), 1) || WordMatchExact(G, "C3'", LexStr(G, ai->name), 1))) { sugar_at = a1; mem0 = a1; nbr[0] = neighbor[mem0] + 1; while((mem1 = neighbor[nbr[0]]) >= 0) { if((atomInfo[mem1].protons == cAN_O) && (!marked[mem1])) { ai = atomInfo + mem1; if(WordMatchExact(G, "O3*", LexStr(G, ai->name), 1) || WordMatchExact(G, "O3'", LexStr(G, ai->name), 1)) o3_at = mem1; nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_P)) { phos3_at = mem2; } nbr[1] += 2; } } if((atomInfo[mem1].protons == cAN_C) && (!marked[mem1])) { ai2 = atomInfo + mem1; if(WordMatchExact(G, NUCLEIC_NORMAL1, LexStr(G, ai2->name), 1) || WordMatchExact(G, NUCLEIC_NORMAL2, LexStr(G, ai2->name), 1)) sugar_at = mem1; nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_C)) { ai = atomInfo + mem2; if(WordMatchExact(G, "C1*", LexStr(G, ai->name), 1) || WordMatchExact(G, "C1'", LexStr(G, ai->name), 1)) c1_at = mem2; nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0)) { if((atomInfo[mem3].protons == cAN_O) && (!marked[mem3])) { ai = atomInfo + mem3; if(WordMatchExact(G, "O5*", LexStr(G, ai->name), 1) || WordMatchExact(G, "O5'", LexStr(G, ai->name), 1)) o5_at = mem3; nbr[3] = neighbor[mem3] + 1; while((mem4 = neighbor[nbr[3]]) >= 0) { if((mem4 != mem2) && (!marked[mem4]) && (atomInfo[mem4].protons == cAN_P)) { phos5_at = mem4; } nbr[3] += 2; } } if(atomInfo[mem3].protons == cAN_N) { if(ring_mode) { ai2 = atomInfo + mem3; if((!marked[mem3]) && (WordMatchExact(G, "N1", LexStr(G, ai2->name), 1) || WordMatchExact(G, "N9", LexStr(G, ai2->name), 1))) { base_at = mem3; if(ring_mode != 3) { ladder_radius = ring_width * 1.5; } else { ladder_radius = ring_width * 3; } } } else { nbr[3] = neighbor[mem3] + 1; while((mem4 = neighbor[nbr[3]]) >= 0) { if((mem4 != mem2) && (mem4 != mem1) && (mem4 != mem0) && (atomInfo[mem4].protons == cAN_C)) { nbr[4] = neighbor[mem4] + 1; while((mem5 = neighbor[nbr[4]]) >= 0) { if((mem5 != mem3) && (mem5 != mem2) && (mem5 != mem1) && (mem5 != mem0) && (atomInfo[mem5].protons == cAN_N) && (marked[mem5])) { /* must be in a mapped ring */ /* clear flag here */ purine_flag = false; nbr[5] = neighbor[mem5] + 1; while((mem6 = neighbor[nbr[5]]) >= 0) { if((mem6 != mem4) && (mem6 != mem3) && (mem6 != mem2) && (mem6 != mem1) && (mem6 != mem0) && (atomInfo[mem6].protons == cAN_C) && (marked[mem6])) { nbr[6] = neighbor[mem6] + 1; while((mem7 = neighbor[nbr[6]]) >= 0) { ai2 = atomInfo + mem7; if((mem7 != mem5) && (mem7 != mem4) && (mem7 != mem3) && (mem7 != mem2) && (mem7 != mem2) && (mem7 != mem1) && (mem7 != mem0) && (ai2->protons == cAN_N) && (marked[mem7])) { if(WordMatchExact(G, "N1", LexStr(G, ai2->name), 1)) { /* and set flag */ base_at = mem7; purine_flag = true; } } nbr[6] += 2; } } nbr[5] += 2; } if(!purine_flag) { ai2 = atomInfo + mem5; if(marked[mem5] && WordMatchExact(G, "N3", LexStr(G, ai2->name), 1)) { base_at = mem5; } } } nbr[4] += 2; } } nbr[3] += 2; } } } } nbr[2] += 2; } } nbr[1] += 2; } } nbr[0] += 2; } } } } else if((!ring_mode) && (n_atom == 6)) { /* going from the base to the sugar */ for(i = 0; i < n_atom; i++) { a1 = atix[i]; if(!nf) nf = nuc_flag[a1]; ai = atomInfo + a1; /* base-hunting */ if((ai->protons == cAN_N) && (!marked[a1]) && (WordMatchExact(G, "N1", LexStr(G, ai->name), 1) || WordMatchExact(G, "N3", LexStr(G, ai->name), 1))) { mem0 = a1; nbr[0] = neighbor[mem0] + 1; while((mem1 = neighbor[nbr[0]]) >= 0) { if((atomInfo[mem1].protons == cAN_C) && (!marked[mem1])) { nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_N)) { nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0) && (atomInfo[mem3].protons == cAN_C)) { nbr[3] = neighbor[mem3] + 1; while((mem4 = neighbor[nbr[3]]) >= 0) { if((mem4 != mem2) && (mem4 != mem1) && (mem4 != mem0)) { if((atomInfo[mem4].protons == cAN_N) || WordMatchExact(G, "C5", LexStr(G, atomInfo[mem4].name), 1)) { /* purine case */ nbr[4] = neighbor[mem4] + 1; while((mem5 = neighbor[nbr[4]]) >= 0) { if((mem5 != mem3) && (mem5 != mem2) && (mem5 != mem1) && (mem5 != mem0) && (marked[mem5]) && (atomInfo[mem5].protons == cAN_C)) { nbr[5] = neighbor[mem5] + 1; while((mem6 = neighbor[nbr[5]]) >= 0) { if((mem6 != mem4) && (mem6 != mem3) && (mem6 != mem2) && (mem6 != mem1) && (mem6 != mem0) && ((atomInfo[mem6].protons == cAN_C) || (atomInfo[mem6].protons == cAN_O)) && (marked[mem6])) { nbr[6] = neighbor[mem6] + 1; while((mem7 = neighbor[nbr[6]]) >= 0) { ai2 = atomInfo + mem7; if((mem7 != mem5) && (mem7 != mem4) && (mem7 != mem3) && (mem7 != mem2) && (mem7 != mem2) && (mem7 != mem1) && (mem7 != mem0) && (ai2->protons == cAN_C) && (marked[mem7])) { if(WordMatchExact (G, NUCLEIC_NORMAL1, LexStr(G, ai2->name), 1) || WordMatchExact(G, NUCLEIC_NORMAL2, LexStr(G, ai2->name), 1)) { base_at = a1; sugar_at = mem7; } } nbr[6] += 2; } } nbr[5] += 2; } } nbr[4] += 2; } } else if(((atomInfo[mem4].protons == cAN_C) || (atomInfo[mem4].protons == cAN_O)) && (marked[mem4])) { /* pyrimidine case */ nbr[4] = neighbor[mem4] + 1; while((mem5 = neighbor[nbr[4]]) >= 0) { ai2 = atomInfo + mem5; if((mem5 != mem3) && (mem5 != mem2) && (mem5 != mem1) && (mem5 != mem0) && (ai2->protons == cAN_C) && (marked[mem5])) { if(WordMatchExact(G, NUCLEIC_NORMAL1, LexStr(G, ai2->name), 1) || WordMatchExact(G, NUCLEIC_NORMAL2, LexStr(G, ai2->name), 1)) { base_at = a1; sugar_at = mem5; } } nbr[4] += 2; } } } nbr[3] += 2; } } nbr[2] += 2; } } nbr[1] += 2; } } nbr[0] += 2; } } } } if(n_atom == 6) { for(i = 0; i < n_atom; i++) { a1 = atix[i]; ai = atomInfo + a1; /* glycosylation hunting */ if((ai->protons == cAN_C) && (!marked[a1])) { /* unmarked C in ring */ mem0 = a1; nbr[0] = neighbor[mem0] + 1; while((mem1 = neighbor[nbr[0]]) >= 0) { if((atomInfo[mem1].protons == cAN_C) && /* exocyclic C */ (!marked[mem1])) { nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_O)) { /* exocyclic O */ nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0) && (marked[mem3]) && (atomInfo[mem3].protons == cAN_C)) { /* cyclic C */ if(WordMatchExact(G, "C5", LexStr(G, ai->name), 1) && WordMatchExact(G, "C6", LexStr(G, atomInfo[mem1].name), 1)) { c5_linked = mem3; c5 = mem0; } } nbr[2] += 2; } } nbr[1] += 2; } } else if((atomInfo[mem1].protons == cAN_O) && /* exocyclic O */ (!marked[mem1])) { nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (marked[mem2]) && (atomInfo[mem2].protons == cAN_C)) { /* cyclic C */ const char * ai_name = LexStr(G, ai->name); if(WordMatchExact(G, "C1", ai_name, 1)) { c1_linked = mem2; c1 = mem0; } else if(WordMatchExact(G, "C2", ai_name, 1)) { c2_linked = mem2; c2 = mem0; } else if(WordMatchExact(G, "C3", ai_name, 1)) { c3_linked = mem2; c3 = mem0; } else if(WordMatchExact(G, "C4", ai_name, 1)) { c4_linked = mem2; c4 = mem0; } } else if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_C)) { /* exocyclic C */ nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0) && (marked[mem3]) && (atomInfo[mem3].protons == cAN_C)) { /* cyclic C */ if(WordMatchExact(G, "C5", LexStr(G, atomInfo[mem3].name), 1) && WordMatchExact(G, "C6", LexStr(G, atomInfo[mem2].name), 1)) { c5 = mem0; c5_linked = mem3; } } else if((mem3 != mem1) && (mem3 != mem0) && (!marked[mem3]) && (atomInfo[mem3].protons == cAN_C)) { /* exocyclic */ if(WordMatchExact(G, "C1", LexStr(G, ai->name), 1) && WordMatchExact(G, "CA", LexStr(G, atomInfo[mem3].name), 1)) { c1 = mem0; c1_linked = mem3; } } nbr[2] += 2; } } nbr[1] += 2; } } else if((atomInfo[mem1].protons == cAN_N) && /* exocyclic N */ (!marked[mem1])) { nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_C)) { /* exocyclic C */ nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0) && (!marked[mem3]) && (atomInfo[mem3].protons == cAN_C)) { /* exocyclic */ nbr[3] = neighbor[mem3] + 1; while((mem4 = neighbor[nbr[3]]) >= 0) { if((mem4 != mem2) && (mem3 != mem1) && (!marked[mem4]) && (atomInfo[mem4].protons == cAN_C)) { /* exocyclic */ if(WordMatchExact(G, "C1", LexStr(G, ai->name), 1) && WordMatchExact(G, "CA", LexStr(G, atomInfo[mem4].name), 1)) { c1 = mem0; c1_linked = mem4; } } nbr[3] += 2; } } nbr[2] += 2; } } nbr[1] += 2; } } nbr[0] += 2; } } } } if(sugar_at >= 0) { /* still need to find the phosphates... */ int c3_index = -1; ai = atomInfo + sugar_at; if(WordMatchExact(G, "C3*", LexStr(G, ai->name), 1) || WordMatchExact(G, "C3'", LexStr(G, ai->name), 1)) { c3_index = sugar_at; } else { mem0 = sugar_at; nbr[0] = neighbor[mem0] + 1; while((mem1 = neighbor[nbr[0]]) >= 0) { if((atomInfo[mem1].protons == cAN_C) && (!marked[mem1])) { ai = atomInfo + mem1; if(!(WordMatchExact(G, "C3*", LexStr(G, ai->name), 1) || WordMatchExact(G, "C3'", LexStr(G, ai->name), 1))) { c3_index = mem1; } } nbr[0] += 2; } } if(c3_index >= 0) { /* now we know where we are... */ mem0 = c3_index; nbr[0] = neighbor[mem0] + 1; while((mem1 = neighbor[nbr[0]]) >= 0) { if((atomInfo[mem1].protons == cAN_O) && (!marked[mem1])) { ai = atomInfo + mem1; if(WordMatchExact(G, "O3*", LexStr(G, ai->name), 1) || WordMatchExact(G, "O3'", LexStr(G, ai->name), 1)) o3_at = mem1; nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_P)) { phos3_at = mem2; } nbr[1] += 2; } } if((atomInfo[mem1].protons == cAN_C) && (marked[mem1])) { nbr[1] = neighbor[mem1] + 1; while((mem2 = neighbor[nbr[1]]) >= 0) { if((mem2 != mem0) && (!marked[mem2]) && (atomInfo[mem2].protons == cAN_C)) { ai = atomInfo + mem2; if(WordMatchExact(G, "C1*", LexStr(G, ai->name), 1) || WordMatchExact(G, "C1'", LexStr(G, ai->name), 1)) c1_at = mem2; nbr[2] = neighbor[mem2] + 1; while((mem3 = neighbor[nbr[2]]) >= 0) { if((mem3 != mem1) && (mem3 != mem0) && (atomInfo[mem3].protons == cAN_O) && (!marked[mem3])) { ai = atomInfo + mem3; if(WordMatchExact(G, "O5*", LexStr(G, ai->name), 1) || WordMatchExact(G, "O5'", LexStr(G, ai->name), 1)) o5_at = mem3; nbr[3] = neighbor[mem3] + 1; while((mem4 = neighbor[nbr[3]]) >= 0) { if((mem4 != mem2) && (!marked[mem4]) && (atomInfo[mem4].protons == cAN_P)) { phos5_at = mem4; } nbr[3] += 2; } } nbr[2] += 2; } } nbr[1] += 2; } } nbr[0] += 2; } } } /* glycosylation connectors */ if(1) { if((ring_mode) && (finder >= 3)) { if(finder >= 3) { int b; float glyco_radius; switch (ring_mode) { case 3: case 4: glyco_radius = ring_width * 3; break; case 5: glyco_radius = ladder_radius; break; default: glyco_radius = ring_width * 1.5; break; } if((alpha != 1.0F) || (ring_alpha != alpha)) CGOAlpha(cgo, alpha); for(b = 0; b < 5; b++) { int g1 = -1, g2 = -1; switch (b) { case 0: g1 = c1; g2 = c1_linked; break; case 1: g1 = c2; g2 = c2_linked; break; case 2: g1 = c3; g2 = c3_linked; break; case 3: g1 = c4; g2 = c4_linked; break; case 4: g1 = c5; g2 = c5_linked; break; } if((g1 >= 0) && (g2 >= 0)) { AtomInfoType *g1_ai = atomInfo + g1; AtomInfoType *g2_ai = atomInfo + g2; if (ring_connector_visible(G, g1_ai, g2_ai, sc_helper)) { float *g1p, *g2p; float avg[3]; { int g1_x, g2_x; if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[g1] && cs == obj->DiscreteCSet[g2]) { g1_x = obj->DiscreteAtmToIdx[g1]; g2_x = obj->DiscreteAtmToIdx[g2]; } else { g1_x = -1; g2_x = -1; } } else { g1_x = cs->AtmToIdx[g1]; g2_x = cs->AtmToIdx[g2]; } g1p = cs->Coord + 3 * g1_x; g2p = cs->Coord + 3 * g2_x; } if(!((!((ring_mode == 0) || (ring_mode == 4) || (ring_mode == 5))) || (!marked[g2]))) { g2p = moved + 3 * g2; } if((ring_mode == 0) || (ring_mode == 4) || (ring_mode == 5)) { /* ring center */ int i; /* compute average coordinate and mark atoms so that ring is only drawn once */ zero3f(avg); for(i = 0; i < n_atom; i++) { add3f(avg, v_i[i], avg); } scale3f(avg, 1.0F / n_atom, avg); g1p = avg; } { const float *color1, *color2; if (ladder_color >= 0) { color1 = color2 = ColorGet(G, ladder_color); } else { color1 = ColorGet(G, g1_ai->color); color2 = ColorGet(G, g2_ai->color); } CGOPickColor(cgo, g1, g1_ai->masked ? cPickableNoPick : cPickableAtom); Pickable pickcolor2 = { g2, g2_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(g2p, g1p, axis); CGOColorv(cgo, color1); cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, g1p, axis, glyco_radius, 0x1f, color2, &pickcolor2); } } } } } } } /* see if any of the neighbors are confirmed nucleic acids... */ if(sugar_at >= 0) { if(!nf) { auto seen = std::set<int>(); nf = has_nuc_neighbor(nuc_flag, obj->Neighbor, sugar_at, 5, seen); } if(nf) { if((ring_mode) && ((finder == 1) || (finder >= 3))) { if((c1_at >= 0) && (base_at >= 0)) { int save_at = sugar_at; sugar_at = c1_at; { AtomInfoType *sug_ai = atomInfo + sugar_at; AtomInfoType *bas_ai = atomInfo + base_at; if (ring_connector_visible(G, bas_ai, sug_ai, sc_helper)) { int sug, bas; if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[sugar_at] && cs == obj->DiscreteCSet[base_at]) { sug = obj->DiscreteAtmToIdx[sugar_at]; bas = obj->DiscreteAtmToIdx[base_at]; } else { sug = -1; bas = -1; } } else { sug = cs->AtmToIdx[sugar_at]; bas = cs->AtmToIdx[base_at]; } if((sug >= 0) && (bas >= 0)) { { const float *color1, *color2; if (ladder_color >= 0) { color1 = color2 = ColorGet(G, ladder_color); } else { color1 = ColorGet(G, sug_ai->color); color2 = ColorGet(G, bas_ai->color); } CGOPickColor(cgo, sugar_at, sug_ai->masked ? cPickableNoPick : cPickableAtom); Pickable pickcolor2 = { base_at, bas_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(cs->Coord + 3 * bas, cs->Coord + 3 * sug, axis); CGOColorv(cgo, color1); cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, cs->Coord + 3 * sug, axis, ladder_radius, 0x1f, color2, &pickcolor2); } } } } base_at = save_at; sugar_at = save_at; } } if((base_at >= 0) && (sugar_at >= 0)) { AtomInfoType *sug_ai = atomInfo + sugar_at; AtomInfoType *bas_ai = atomInfo + base_at; if (ring_connector_visible(G, bas_ai, sug_ai, sc_helper)) { int sug, bas; float *v_outer, tmp[3], outer[3]; if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[sugar_at] && cs == obj->DiscreteCSet[base_at]) { sug = obj->DiscreteAtmToIdx[sugar_at]; bas = obj->DiscreteAtmToIdx[base_at]; } else { sug = -1; bas = -1; } } else { sug = cs->AtmToIdx[sugar_at]; bas = cs->AtmToIdx[base_at]; } if((sug >= 0) && (bas >= 0)) { int p3, p5; v_outer = cs->Coord + 3 * sug; if((o3_at >= 0) && (phos3_at < 0)) phos3_at = o3_at; if((o5_at >= 0) && (phos5_at < 0)) phos5_at = o5_at; if((ndata->na_mode != 1) && (phos3_at >= 0) && (phos5_at >= 0)) { if(obj->DiscreteFlag) { if(cs == obj->DiscreteCSet[phos3_at] && cs == obj->DiscreteCSet[phos5_at]) { p3 = obj->DiscreteAtmToIdx[phos3_at]; p5 = obj->DiscreteAtmToIdx[phos5_at]; } else { p3 = -1; p5 = -1; } } else { p3 = cs->AtmToIdx[phos3_at]; p5 = cs->AtmToIdx[phos5_at]; } if((p3 >= 0) && (p5 >= 0)) { if(ring_mode) { scale3f(cs->Coord + 3 * p5, 0.333333F, outer); scale3f(cs->Coord + 3 * p3, 0.666667F, tmp); } else { scale3f(cs->Coord + 3 * p3, 0.5F, outer); scale3f(cs->Coord + 3 * p5, 0.5F, tmp); } add3f(tmp, outer, outer); v_outer = outer; } } { const float *color1, *color2; if (ladder_color >= 0) { color1 = color2 = ColorGet(G, ladder_color); } else { color1 = ColorGet(G, sug_ai->color); color2 = ColorGet(G, bas_ai->color); } CGOPickColor(cgo, sugar_at, sug_ai->masked ? cPickableNoPick : cPickableAtom); Pickable pickcolor2 = { base_at, bas_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(cs->Coord + 3 * bas, v_outer, axis); CGOColorv(cgo, color1); cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, v_outer, axis, ladder_radius, 0x1f, color2, &pickcolor2); } } } } } } } if((!ring_mode) || (finder == 2)) { if(ladder_mode) { /* mark sure all rings traversed are mark */ int i; for(i = 0; i <= n_atom; i++) { marked[atix[i]] = true; } } } if((!nf) && ((have_C4_prime >= 0) || (have_C4 >= 0))) { int mem0; /* see if any of the neighbors are confirmed nucleic acids... */ if(have_C4_prime >= 0) mem0 = have_C4_prime; else if(have_C4 >= 0) mem0 = have_C4; else mem0 = -1; if(mem0 >= 1) { auto seen = std::set<int>(); nf = has_nuc_neighbor(nuc_flag, obj->Neighbor, mem0, 9, seen); } } if(n_atom) { /* store center of ring */ float avg[3]; float avg_col[3]; int i; float up[3], upi[3]; float vc0[3], vc1[3]; const float *color = NULL; /* compute average coordinate and mark atoms so that ring is only drawn once */ zero3f(avg); zero3f(avg_col); for(i = 0; i < n_atom; i++) { add3f(avg, v_i[i], avg); add3f(avg_col, col[i], avg_col); marked[atix[i]] = true; } scale3f(avg, 1.0F / n_atom, avg); scale3f(avg_col, 1.0F / n_atom, avg_col); for(i = 0; i < n_atom; i++) { float *v_moved = moved + atix[i] * 3; copy3f(avg, v_moved); /* store ring center for later use */ } if((nf || (!ladder_mode) || (finder >= 3)) && ring_mode && (((finder == 1) && ((have_C4 >= 0) || (have_C4_prime >= 0))) || ((finder == 2) && ((have_C4 >= 0))) || ((finder == 3) && ((have_C_number >= 0))) || ((finder == 4)))) { if((alpha != 1.0F) || (ring_alpha != alpha)) CGOAlpha(cgo, ring_alpha); if(ring_color >= 0) { color = ColorGet(G, ring_color); } else { color = avg_col; } CGOColorv(cgo, color); if((ring_mode == 4) || (ring_mode == 5)) { /* spherical ring */ float radius = ring_radius; if(radius < 0.0F) { radius = 0.0F; if(ring_mode == 4) { for(i = 0; i < n_atom; i++) { float dist = diff3f(avg, v_i[i]); if(radius < dist) radius = dist; } } else { radius = ladder_radius; } } if(n_atom) { CGOColorv(cgo, avg_col); CGOPickColor(cgo, atix[0], ai_i[0]->masked ? cPickableNoPick : cPickableAtom); CGOSphere(cgo, avg, radius); } } else { /* clear the normals */ for(i = 0; i <= n_atom; i++) { zero3f(n_up[i]); zero3f(n_dn[i]); } /* compute average normals */ { float acc[3]; int ii; zero3f(acc); for(i = 0; i < n_atom; i++) { ii = i + 1; subtract3f(v_i[i], avg, vc0); subtract3f(v_i[ii], avg, vc1); cross_product3f(vc0, vc1, up); add3f(up, n_up[i], n_up[i]); add3f(up, n_up[ii], n_dn[ii]); if(!i) { add3f(up, n_up[n_atom], n_up[n_atom]); } else if(ii == n_atom) { add3f(up, n_up[0], n_up[0]); } add3f(up, acc, acc); } normalize3f(up); scale3f(up, -1.0F, upi); } for(i = 0; i <= n_atom; i++) { normalize3f(n_up[i]); scale3f(n_up[i], -1.0F, n_dn[i]); } { int ii; float mid[3]; float up_add[3]; float ct[3], cb[3]; float v0t[3], v0b[3]; float v1t[3], v1b[3]; float out[3]; CGOBegin(cgo, GL_TRIANGLES); for(i = 0; i < n_atom; i++) { ii = i + 1; average3f(v_i[ii], v_i[i], mid); subtract3f(mid, avg, out); /* compute outward-facing normal */ normalize3f(out); scale3f(up, ring_width, up_add); add3f(avg, up_add, ct); subtract3f(avg, up_add, cb); add3f(v_i[i], up_add, v0t); subtract3f(v_i[i], up_add, v0b); add3f(v_i[ii], up_add, v1t); subtract3f(v_i[ii], up_add, v1b); CGONormalv(cgo, up); if(ring_color < 0) CGOColorv(cgo, color); CGOPickColor(cgo, atix[i], ai_i[i]->masked ? cPickableNoPick : cPickableAtom); CGOVertexv(cgo, ct); CGONormalv(cgo, n_up[i]); if(ring_color < 0) CGOColorv(cgo, col[i]); // CGOPickColor(cgo, atix[i], cPickableAtom); CGOVertexv(cgo, v0t); CGONormalv(cgo, n_up[ii]); if(ring_color < 0) CGOColorv(cgo, col[ii]); CGOPickColor(cgo, atix[ii], ai_i[ii]->masked ? cPickableNoPick : cPickableAtom); // CGOPickColor(cgo, atix[ii], cPickableAtom); CGOVertexv(cgo, v1t); if(ring_mode > 1) { CGONormalv(cgo, out); if(ring_color < 0) CGOColorv(cgo, col[i]); // CGOPickColor(cgo, atix[i], cPickableAtom); CGOVertexv(cgo, v0t); CGOVertexv(cgo, v0b); if(ring_color < 0) CGOColorv(cgo, col[ii]); CGOVertexv(cgo, v1t); CGOVertexv(cgo, v1t); if(ring_color < 0) CGOColorv(cgo, col[i]); CGOVertexv(cgo, v0b); if(ring_color < 0) CGOColorv(cgo, col[ii]); CGOVertexv(cgo, v1b); } CGONormalv(cgo, upi); if(ring_color < 0) CGOColorv(cgo, color); CGOVertexv(cgo, cb); CGONormalv(cgo, n_dn[ii]); if(ring_color < 0) CGOColorv(cgo, col[ii]); CGOVertexv(cgo, v1b); CGONormalv(cgo, n_dn[i]); if(ring_color < 0) CGOColorv(cgo, col[i]); CGOVertexv(cgo, v0b); } CGOEnd(cgo); if((alpha != 1.0F) || (ring_alpha != alpha)) CGOAlpha(cgo, alpha); { float ring_width_for_mode = ring_width; if (ring_mode == 3){ ring_width_for_mode = 3.f * ring_width; } for(i = 0; i < n_atom; i++) { ii = i + 1; { const float *color1, *color2; if (ring_color < 0) { color1 = col[i]; color2 = col[ii]; } else { color1 = color2 = color; } CGOPickColor(cgo, atix[i], ai_i[i]->masked ? cPickableNoPick : cPickableAtom); Pickable pickcolor2 = { atix[ii], ai_i[ii]->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(v_i[ii], v_i[i], axis); CGOColorv(cgo, color1); cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, v_i[i], axis, ring_width_for_mode, 0x1f, color2, &pickcolor2); } } } } } } } } } /* * for nucleic acid polymers, fill "ndata" with: * - cartoon type and secondary structure * - check if single nucleotide or actual polymer */ static void nuc_acid(PyMOLGlobals * G, nuc_acid_data *ndata, int a, int a1, AtomInfoType * ai, CoordSet * cs, ObjectMolecule * obj, int set_flags) { int a3, a4, st, nd; float *v_o, *v_c; float *v1; int cur_car; const auto& nuc_flag = ndata->nuc_flag; if(ndata->a2 < 0) { ndata->nSeg++; ndata->v_o_last = NULL; } *(ndata->sptr++) = ndata->nSeg; *(ndata->iptr++) = a; cur_car = ai->cartoon; if(cur_car == cCartoon_auto) cur_car = cCartoon_tube; (*ndata->ss) = 3; /* DNA/RNA */ if(cur_car == cCartoon_putty) ndata->putty_flag = true; *(ndata->cc++) = cur_car; v1 = cs->Coord + 3 * a; copy3f(v1, ndata->vptr); ndata->vptr += 3; if(ndata->a2 >= 0) { if(set_flags) { if((obj->AtomInfo[ndata->a2].protons == cAN_P) && (!nuc_flag[ndata->a2])) { int *nf = NULL; AtomInfoBracketResidueFast(G, obj->AtomInfo, obj->NAtom, ndata->a2, &st, &nd); nf = nuc_flag + st; for(a3 = st; a3 <= nd; a3++) { *(nf++) = true; } } } else if((ndata->na_mode >= 2) && (!nuc_flag[ndata->a2])) { /* just a single nucleotide -- skip */ cur_car = cCartoon_skip; *(ndata->cc - 2) = cCartoon_skip; *(ndata->cc - 1) = cCartoon_skip; } } ndata->a2 = a1; ndata->ss++; v_c = NULL; v_o = NULL; AtomInfoBracketResidueFast(G, obj->AtomInfo, obj->NAtom, a1, &st, &nd); { int *nf = NULL; if(set_flags && ndata->v_o_last) nf = nuc_flag + st; for(a3 = st; a3 <= nd; a3++) { if(nf) *(nf++) = true; /* mark this residue as being part of a nucleic acid chain */ a4 = cs->atmToIdx(a3); if(a4 >= 0) { if(ndata->na_mode == 1) { if(WordMatchExact(G, NUCLEIC_NORMAL1, LexStr(G, obj->AtomInfo[a3].name), 1) || WordMatchExact(G, NUCLEIC_NORMAL2, LexStr(G, obj->AtomInfo[a3].name), 1)) { v_c = cs->Coord + 3 * a4; } } else if(a3 == a1) { v_c = cs->Coord + 3 * a4; } if(WordMatchExact(G, NUCLEIC_NORMAL0, LexStr(G, obj->AtomInfo[a3].name), 1)) { v_o = cs->Coord + 3 * a4; } } } } if(!(v_c && v_o)) { zero3f(ndata->voptr); ndata->v_o_last = NULL; } else { if(ndata->v_o_last) { float t0[3]; add3f(v_o, ndata->v_o_last, t0); add3f(ndata->v_o_last, t0, t0); scale3f(t0, 0.333333F, t0); subtract3f(v_c, t0, ndata->voptr); } else { subtract3f(v_c, v_o, ndata->voptr); } ndata->v_o_last = v_o; normalize3f(ndata->voptr); } ndata->voptr += 3; ndata->nAt++; return; } static void GenerateRepCartoonDrawDebugLineAlongPath(CGO *cgo, int nAt, float *pv){ float *v, *v1, *v2, *v3, *v4; float t0[3], t1[3]; int a; CGOColor(cgo, 1.0, 1.0, 1.0); #ifndef PURE_OPENGL_ES_2 CGODisable(cgo, GL_LIGHTING); #endif v1 = NULL; v2 = NULL; v3 = NULL; v4 = NULL; v = pv; if(nAt > 1) { CGOBegin(cgo, GL_LINE_STRIP); for(a = 0; a < nAt; a++) { v4 = v3; v3 = v2; v2 = v1; v1 = v; if(v1 && v2 && v3 && v4) { add3f(v1, v4, t0); add3f(v2, v3, t1); /* scale3f(t0,0.2024,t0); scale3f(t1,0.2727,t1); */ scale3f(t0, 0.2130F, t0); scale3f(t1, 0.2870F, t1); add3f(t0, t1, t0); CGOVertexv(cgo, t0); } v += 3; } CGOEnd(cgo); } } static int GenerateRepCartoonDrawDebugNormals(CGO *cgo, CExtrude *ex, int n_p){ int b, ok; float *v, *vn; float t0[3]; ok = CGOColor(cgo, 0.0, 1.0, 0.0); v = ex->p; vn = ex->n + 3; #ifndef PURE_OPENGL_ES_2 if (ok) ok &= CGODisable(cgo, GL_LIGHTING); #endif if (ok) ok &= CGOBegin(cgo, GL_LINES); for(b = 0; ok && b < n_p; b++) { ok &= CGOVertexv(cgo, v); add3f(v, vn, t0); if (ok) ok &= CGOVertexv(cgo, t0); v += 3; vn += 9; } if (ok) ok &= CGOEnd(cgo); #ifndef PURE_OPENGL_ES_2 if (ok) ok &= CGOEnable(cgo, GL_LIGHTING); #endif return ok; } static int GenerateRepCartoonDrawDebugOrient(CGO *cgo, int nAt, float *pv, float *pvo, float *tv){ int ok, a; float *v1, *v2, *v3; float t0[3]; ok = CGOColor(cgo, 1.0, 1.0, 1.0); #ifndef PURE_OPENGL_ES_2 ok &= CGODisable(cgo, GL_LIGHTING); #endif if (ok) ok &= CGOBegin(cgo, GL_LINES); v1 = pv; v2 = pvo; v3 = tv; for(a = 0; ok && a < nAt; a++) { ok &= CGOVertexv(cgo, v1); if (ok){ add3f(v1, v2, t0); add3f(v2, t0, t0); ok &= CGOVertexv(cgo, t0); } if (ok){ subtract3f(v1, v3, t0); ok &= CGOVertexv(cgo, t0); } if (ok){ add3f(v1, v3, t0); ok &= CGOVertexv(cgo, t0); } v1 += 3; v2 += 3; v3 += 3; } if (ok) ok &= CGOEnd(cgo); #ifndef PURE_OPENGL_ES_2 if (ok) ok &= CGOEnable(cgo, GL_LIGHTING); #endif return ok; } static int GenerateRepCartoonProcessCylindricalHelices(PyMOLGlobals * G, ObjectMolecule * obj, CoordSet *cs, CGO *cgo, CExtrude *ex, int nAt, int *seg, float *pv, float *tv, float *pvo, const CCInOut *cc, int *at, float *dl, int cartoon_color, int discrete_colors, float loop_radius, const float objAlpha){ int ok = true; int n_p, n_pm1, n_pm2; const float *v0; float *v, *v1, *v2, *vo, *d; float *valpha; float *vc = NULL; int atom_index1, atom_index2, *s, *atp, a, cur_car; unsigned *vi; int last_color, uniform_color; int contFlag, extrudeFlag; int b, c1, c2; float *h_start = NULL, *h_end = NULL; float t0[3], t1[3], t2[3], t3[3]; float t4[3]; float helix_radius; CGOPickColor(cgo, 0, cPickableNoPick); helix_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_helix_radius); /* this is confusing because we're borrowing Extrude's arrays * for convenient storage, but not actually calling Extrude */ n_p = 0; v = ex->p; vc = ex->c; valpha = ex->alpha; vi = ex->i; last_color = -1; uniform_color = true; v1 = pv; /* points */ v2 = tv; /* tangents */ vo = pvo; d = dl; s = seg; atp = at; /* cs index pointer */ a = 0; contFlag = true; cur_car = cCartoon_skip; extrudeFlag = false; while(contFlag) { if((*cc) != cur_car) { /* new cartoon type */ if(n_p) { /* any cartoon points? */ extrudeFlag = true; } else { cur_car = *(cc); /* now: go ahead and switch cartoons */ n_p = 0; v = ex->p; vc = ex->c; valpha = ex->alpha; vi = ex->i; last_color = -1; uniform_color = true; } } if(a && !extrudeFlag) { if((*s) != *(s - 1)) { /* new segment */ if(n_p) { /* any cartoon points? */ extrudeFlag = true; } else { n_p = 0; v = ex->p; vc = ex->c; valpha = ex->alpha; vi = ex->i; last_color = -1; uniform_color = true; } } } if(!extrudeFlag) { if((a < (nAt - 1)) && (*s == *(s + 1))) { /* working in the same segment... */ AtomInfoType *ai1, *ai2; atom_index1 = cs->IdxToAtm[*atp]; atom_index2 = cs->IdxToAtm[*(atp + 1)]; ai1 = obj->AtomInfo + atom_index1; ai2 = obj->AtomInfo + atom_index2; c1 = AtomSettingGetWD(G, ai1, cSetting_cartoon_color, cartoon_color); c2 = AtomSettingGetWD(G, ai2, cSetting_cartoon_color, cartoon_color); float alpha1 = AtomSettingGetWD(G, ai1, cSetting_cartoon_transparency, 1.0f - objAlpha); float alpha2 = AtomSettingGetWD(G, ai2, cSetting_cartoon_transparency, 1.0f - objAlpha); if (c1 < 0) c1 = ai1->color; if (c2 < 0) c2 = ai2->color; if((*(cc) == *(cc + 1)) && (c1 != c2)) uniform_color = false; if(alpha1 != alpha2){ uniform_color = false; } if(last_color >= 0) { if(c1 != last_color) uniform_color = false; } last_color = c1; v0 = ColorGet(G, c1); *(vc++) = *(v0++); *(vc++) = *(v0++); *(vc++) = *(v0++); *(valpha++) = alpha1; *(vi++) = ai1->masked ? -1 : atom_index1; v0 = ColorGet(G, c2); /* kludge */ *(vc) = *(v0++); *(vc + 1) = *(v0++); *(vc + 2) = *(v0++); *(valpha) = alpha2; *(vi) = ai2->masked ? -1 : atom_index2; } else { vc += 3; /* part of kludge */ valpha++; vi++; } if(cur_car == cCartoon_skip_helix) { if(!n_p) { h_start = v1; h_end = v1; } else { h_end = v1; } copy3f(v1, v); /* just store coordinates until we have a complete cylinder */ v += 3; n_p++; } v1 += 3; v2 += 3; vo += 3; d++; atp++; s++; cc++; } a++; if(a == nAt) { contFlag = false; if(n_p) extrudeFlag = true; } if(extrudeFlag) { /* generate cylinder */ if(n_p > 1) { atom_index1 = cs->IdxToAtm[*(atp - 1)]; c1 = AtomSettingGetWD(G, obj->AtomInfo + atom_index1, cSetting_cartoon_color, cartoon_color); auto ai1 = obj->AtomInfo + atom_index1; auto ai2 = obj->AtomInfo + atom_index2; float alpha1 = AtomSettingGetWD(G, ai1, cSetting_cartoon_transparency, 1.0f - objAlpha); float alpha2 = AtomSettingGetWD(G, ai2, cSetting_cartoon_transparency, 1.0f - objAlpha); if (c1 < 0) c1 = (obj->AtomInfo + atom_index1)->color; if(n_p < 5) { copy3f(ex->p, t3); copy3f(v - 3, t4); } else { add3f(ex->p, ex->p + 9, t0); add3f(ex->p + 3, ex->p + 6, t1); scale3f(t0, 0.2130F, t0); scale3f(t1, 0.2870F, t1); add3f(t0, t1, t3); add3f(v - 3, v - 12, t0); add3f(v - 6, v - 9, t1); scale3f(t0, 0.2130F, t0); scale3f(t1, 0.2870F, t1); add3f(t0, t1, t4); /* extend helix to line up with CA */ subtract3f(t4, t3, t0); normalize3f(t0); subtract3f(v - 3, t3, t1); project3f(t1, t0, t4); add3f(t3, t4, t4); invert3f(t0); subtract3f(ex->p, t4, t1); project3f(t1, t0, t3); add3f(t3, t4, t3); /* relocate terminal CA to touch helix, if necessary */ if(h_start && h_end) { float f0; subtract3f(h_start, t3, t0); f0 = helix_radius - loop_radius * 2; if(length3f(t0) > f0) { normalize3f(t0); scale3f(t0, f0, t1); add3f(t1, t3, h_start); } subtract3f(h_end, t4, t0); if(length3f(t0) > f0) { normalize3f(t0); scale3f(t0, f0, t1); add3f(t1, t4, h_end); } } } /* push helix out a tad to consume loop */ subtract3f(t4, t3, t0); normalize3f(t0); scale3f(t0, loop_radius * 2, t0); add3f(t0, t4, t4); invert3f(t0); add3f(t0, t3, t3); if(uniform_color) { CGOCylinderv(cgo, t3, t4, helix_radius, ex->c, ex->c); } else { subtract3f(t4, t3, t0); n_pm1 = n_p - 1; n_pm2 = n_p - 2; for(b = 0; ok && b < n_pm1; b++) { if(!b) { scale3f(t0, ((float) b - 0.005F) / n_pm1, t1); /* add small overlap */ } else { scale3f(t0, ((float) b) / n_pm1, t1); } if(b < n_pm2) { scale3f(t0, ((float) b + 1.005F) / n_pm1, t2); } else { scale3f(t0, ((float) b + 1) / n_pm1, t2); } add3f(t3, t1, t1); add3f(t3, t2, t2); CGOCustomCylinderv(cgo, t1, t2, helix_radius, ex->c + (b * 3), ex->c + (b + 1) * 3, (float) (b ? 0 : cCylCapFlat), (float) (b == n_pm2 ? cCylCapFlat : 0)); } } } a--; /* undo above... */ extrudeFlag = false; n_p = 0; v = ex->p; vc = ex->c; valpha = ex->alpha; vi = ex->i; uniform_color = true; last_color = -1; } } return ok; } static int GenerateRepCartoonDrawRings(PyMOLGlobals * G, nuc_acid_data *ndata, ObjectMolecule * obj, CoordSet * cs, CGO *cgo, float ring_width, int cartoon_color, float alpha){ int ring_i; int mem[8]; int nbr[7]; int *neighbor; int *marked = Calloc(int, obj->NAtom); float *moved = Calloc(float, obj->NAtom * 3); int ring_color; int ok = true; int escape_count; int *atmToIdx = NULL; int ladder_mode, ladder_color; float ladder_radius, ring_radius; int cartoon_side_chain_helper; float ring_alpha; ring_alpha = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_transparency); if(ring_alpha < 0.0F) ring_alpha = alpha; else ring_alpha = 1.0F - ring_alpha; cartoon_side_chain_helper = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_side_chain_helper); ladder_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ladder_mode); ladder_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ladder_radius); ladder_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ladder_color); ring_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_radius); if(ladder_color == -1) ladder_color = cartoon_color; ring_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_color); if(ring_color == -1) ring_color = cartoon_color; if(!obj->DiscreteFlag) atmToIdx = cs->AtmToIdx; ok &= ObjectMoleculeUpdateNeighbors(obj); neighbor = obj->Neighbor; escape_count = ESCAPE_MAX; /* don't get bogged down with structures that have unreasonable connectivity */ for(ring_i = 0; ok && ring_i < ndata->n_ring; ring_i++) { mem[0] = ndata->ring_anchor[ring_i]; 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 */ /* printf(" 5: %s(%d) %s(%d) %s(%d) %s(%d) %s(%d)\n", obj->AtomInfo[mem[0]].name,mem[0], obj->AtomInfo[mem[1]].name,mem[1], obj->AtomInfo[mem[2]].name,mem[2], obj->AtomInfo[mem[3]].name,mem[3], obj->AtomInfo[mem[4]].name,mem[4]); */ do_ring(G, ndata, 5, mem, obj, cs, ring_width, cgo, ring_color, ladder_radius, ladder_color, ladder_mode, cartoon_side_chain_helper, ring_alpha, alpha, marked, moved, ring_radius); } 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 */ /* printf(" 6: %s %s %s %s %s %s\n", obj->AtomInfo[mem[0]].name, obj->AtomInfo[mem[1]].name, obj->AtomInfo[mem[2]].name, obj->AtomInfo[mem[3]].name, obj->AtomInfo[mem[4]].name, obj->AtomInfo[mem[5]].name ); */ do_ring(G, ndata, 6, mem, obj, cs, ring_width, cgo, ring_color, ladder_radius, ladder_color, ladder_mode, cartoon_side_chain_helper, ring_alpha, alpha, marked, moved, ring_radius); } nbr[6] = neighbor[mem[6]] + 1; while(((mem[7] = neighbor[nbr[6]]) >= 0) && ((!atmToIdx) || (atmToIdx[mem[6]] >= 0))) { if((mem[7] != mem[5]) && (mem[7] != mem[4]) && (mem[7] != mem[3]) && (mem[7] != mem[2]) && (mem[7] != mem[1])) { if(mem[7] == mem[0]) { do_ring(G, ndata, 7, mem, obj, cs, ring_width, cgo, ring_color, ladder_radius, ladder_color, ladder_mode, cartoon_side_chain_helper, ring_alpha, alpha, marked, moved, ring_radius); } } nbr[6] += 2; } } 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 */ } FreeP(marked); FreeP(moved); return ok; } /* * skip > dash > loop */ inline int prioritize(int a, int b) { if (a == b) return a; if (a == cCartoon_skip || b == cCartoon_skip) return cCartoon_skip; if (a == cCartoon_dash || b == cCartoon_dash) return cCartoon_dash; return cCartoon_loop; } /* CheckExtrudeContigFlags: compares current cartoon type and next to figure out whether to extrude, truncate, and/or whether its contiguous */ static int CheckExtrudeContigFlags(int nAt, int n_p, int a, int *cur_car, // in-out const CCInOut *cc, const int *segptr, int * /* contigFlag */, int *extrudeFlag) { int truncate = false; int next_car = cCartoon_skip; if (a < (nAt - 1) && (*segptr) == *(segptr + 1)) { next_car = prioritize(cc[0].getCCOut(), cc[1].getCCIn()); } if (*cur_car != next_car) { /* new cartoon type */ if(n_p) { /* any cartoon points? then extrude, otherwise truncate */ *extrudeFlag = true; } else { *cur_car = next_car; /* no: go ahead and switch cartoons */ truncate = true; } } return truncate; } static void ComputeCartoonAtomColors(PyMOLGlobals *G, ObjectMolecule *obj, CoordSet *cs, int *nuc_flag, int atom_index1, int atom_index2, int *c1a, int *c2a, int *atp, const CCInOut *cc, int cur_car, int cartoon_color, float& alpha1, float& alpha2, int nucleic_color, int discrete_colors, int n_p, int contigFlag){ int c1, c2; if (nucleic_color >= 0 && (nuc_flag[*atp] || nuc_flag[*(atp + 1)])) { c1 = c2 = nucleic_color; } else { c1 = c2 = cartoon_color; } auto ai1 = obj->AtomInfo + atom_index1; auto ai2 = obj->AtomInfo + atom_index2; AtomSettingGetIfDefined(G, ai1, cSetting_cartoon_color, &c1); AtomSettingGetIfDefined(G, ai2, cSetting_cartoon_color, &c2); alpha1 = 1.0f - AtomSettingGetWD(G, ai1, cSetting_cartoon_transparency, 1.0f - alpha1); alpha2 = 1.0f - AtomSettingGetWD(G, ai2, cSetting_cartoon_transparency, 1.0f - alpha2); if (c1 < 0) c1 = ai1->color; if (c2 < 0) c2 = ai2->color; if(discrete_colors) { int next_car = *(cc + 1); // end of loop or dash segment if (cur_car != next_car) { if (cur_car == cCartoon_dash) { c2 = c1; } else if (next_car == cCartoon_dash) { c1 = c2; } else if (cur_car == cCartoon_loop) { c2 = c1; } else if (next_car == cCartoon_loop) { c1 = c2; } } else if (n_p == 0 && contigFlag && (cur_car == cCartoon_dash || cur_car == cCartoon_loop)) { // beginning of loop or dash segment c1 = c2; } } *c1a = c1; *c2a = c2; } static void CartoonGenerateSample(PyMOLGlobals *G, int sampling, int *n_p, float dev, float *vo, float *v1, float *v2, int c1, int c2, float alpha1, float alpha2, int atom_index1, int atom_index2, float power_a, float power_b, float **vc_p, float **valpha_p, unsigned int **vi_p, float **v_p, float **vn_p){ int b, i0; float f0, f1, f2, f3, f4; float a0; const float *v0; unsigned int *vi = *vi_p; float *valpha = *valpha_p; float *vc = *vc_p, *v = *v_p, *vn = *vn_p; for(b = 0; b < sampling; b++) { /* needs optimization */ if(*n_p == 0) { /* provide starting point on first point in segment only... */ f0 = ((float) b) / sampling; /* fraction of completion */ if(f0 <= 0.5) { v0 = ColorGet(G, c1); i0 = atom_index1; a0 = alpha1; } else { v0 = ColorGet(G, c2); i0 = atom_index2; a0 = alpha2; } f0 = smooth(f0, power_a); /* bias sampling towards the center of the curve */ /* store colors and alpha*/ *(vc++) = *(v0++); *(vc++) = *(v0++); *(vc++) = *(v0++); *(valpha++) = a0; *(vi++) = i0; /* start of line/cylinder */ f1 = 1.0F - f0; f2 = smooth(f0, power_b); f3 = smooth(f1, power_b); f4 = dev * f2 * f3; /* displacement magnitude */ *(v++) = f1 * v1[0] + f0 * v1[3] + f4 * (f3 * v2[0] - f2 * v2[3]); *(v++) = f1 * v1[1] + f0 * v1[4] + f4 * (f3 * v2[1] - f2 * v2[4]); *(v++) = f1 * v1[2] + f0 * v1[5] + f4 * (f3 * v2[2] - f2 * v2[5]); vn += 9; copy3f(vo, vn - 6); /* starter... */ (*n_p)++; } f0 = ((float) b + 1) / sampling; if(f0 <= 0.5) { v0 = ColorGet(G, c1); i0 = atom_index1; a0 = alpha1; } else { v0 = ColorGet(G, c2); i0 = atom_index2; a0 = alpha2; } f0 = smooth(f0, power_a); /* bias sampling towards the center of the curve */ /* store colors and alpha*/ *(vc++) = *(v0++); *(vc++) = *(v0++); *(vc++) = *(v0++); *(valpha++) = a0; *(vi++) = i0; /* end of line/cylinder */ f1 = 1.0F - f0; f2 = smooth(f0, power_b); f3 = smooth(f1, power_b); f4 = dev * f2 * f3; /* displacement magnitude */ *(v++) = f1 * v1[0] + f0 * v1[3] + f4 * (f3 * v2[0] - f2 * v2[3]); *(v++) = f1 * v1[1] + f0 * v1[4] + f4 * (f3 * v2[1] - f2 * v2[4]); *(v++) = f1 * v1[2] + f0 * v1[5] + f4 * (f3 * v2[2] - f2 * v2[5]); /* remove_component3f(vo,v2,o0); remove_component3f(vo+3,v2,o0+3); */ vn += 3; *(vn++) = f1 * (vo[0] * f2) + f0 * (vo[3] * f3); *(vn++) = f1 * (vo[1] * f2) + f0 * (vo[4] * f3); *(vn++) = f1 * (vo[2] * f2) + f0 * (vo[5] * f3); vn += 3; if(b == sampling - 1) copy3f(vo + 3, vn - 6); /* starter... */ (*n_p)++; } (*vc_p) = vc; (*valpha_p) = valpha; (*vi_p) = vi; (*v_p) = v; (*vn_p) = vn; } static void CartoonGenerateRefine(int refine, int sampling, float *v, float *vn, float *vo, float *sampling_tmp){ int b, c; float t0[3], t1[3]; float *p0, *p1, *p2, *p3; float f0, f1, f2, f3; c = refine; cross_product3f(vn + 3 - (sampling * 9), vn + 3 - 9, t0); // why is this here? cross_product3f(vo, vo + 3, t0); if((sampling > 1) && length3f(t0) > R_SMALL4) { normalize3f(t0); while(c--) { p0 = v - (sampling * 3) - 3; p1 = v - (sampling * 3); p2 = v - (sampling * 3) + 3; for(b = 0; b < (sampling - 1); b++) { f0 = dot_product3f(t0, p0); f1 = dot_product3f(t0, p1); f2 = dot_product3f(t0, p2); f3 = (f2 + f0) / 2.0F; scale3f(t0, f3 - f1, t1); p3 = sampling_tmp + b * 3; add3f(t1, p1, p3); p0 = p1; p1 = p2; p2 += 3; } p1 = v - (sampling * 3); for(b = 0; b < (sampling - 1); b++) { p3 = sampling_tmp + b * 3; copy3f(p3, p1); p1 += 3; } } } } static int CartoonExtrudeTube(short use_cylinders_for_strands, CExtrude *ex, CGO *cgo, float tube_radius, int tube_quality, int tube_cap){ int ok = true; if (use_cylinders_for_strands){ ok &= ExtrudeCylindersToCGO(ex, cgo, tube_radius); } else { ok &= ExtrudeCircle(ex, tube_quality, tube_radius); if (ok) ExtrudeBuildNormals1f(ex); if (ok) ok &= ExtrudeCGOSurfaceTube(ex, cgo, tube_cap, NULL, use_cylinders_for_strands); } return ok; } static int CartoonExtrudePutty(PyMOLGlobals *G, ObjectMolecule *obj, CoordSet *cs, CGO *cgo, CExtrude *ex, int putty_quality, float putty_radius, float *putty_vals, int sampling){ int ok; ok = ExtrudeCircle(ex, putty_quality, putty_radius); if (ok) ExtrudeBuildNormals1f(ex); if (ok) ok &= ExtrudeComputePuttyScaleFactors(ex, obj, SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_transform), putty_vals[0], putty_vals[1], putty_vals[2], putty_vals[3], SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_scale_power), SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_range), SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_scale_min), SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_scale_max), sampling / 2); if (ok) ok &= ExtrudeCGOSurfaceVariableTube(ex, cgo, 1); return ok; } static int CartoonExtrudeCircle(CExtrude *ex, CGO *cgo, short use_cylinders_for_strands, int loop_quality, float loop_radius, int loop_cap, int dash=0) { int ok; ok = ExtrudeCircle(ex, loop_quality, loop_radius); if (ok) ExtrudeBuildNormals1f(ex); if (ok) ok &= ExtrudeCGOSurfaceTube(ex, cgo, loop_cap, NULL, use_cylinders_for_strands, dash); return ok; } static int CartoonExtrudeRect(PyMOLGlobals *G, CExtrude *ex, CGO *cgo, float width, float length, int highlight_color){ int ok; if(highlight_color < 0) { ok = ExtrudeRectangle(ex, width, length, 0); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygon(ex, cgo, 1, NULL); } else { ok = ExtrudeRectangle(ex, width, length, 1); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygon(ex, cgo, 0, NULL); if (ok){ ok &= ExtrudeRectangle(ex, width, length, 2); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygon(ex, cgo, 1, ColorGet(G, highlight_color)); } } return ok; } static int CartoonExtrudeOval(PyMOLGlobals *G, CExtrude *ex, CGO *cgo, short use_cylinders_for_strands, int oval_quality, float oval_width, float oval_length, int highlight_color){ int ok; ok = ExtrudeOval(ex, oval_quality, oval_width, oval_length); if (ok) ExtrudeBuildNormals2f(ex); if (ok){ if(highlight_color < 0) ok &= ExtrudeCGOSurfaceTube(ex, cgo, 1, NULL, use_cylinders_for_strands); else ok &= ExtrudeCGOSurfaceTube(ex, cgo, 1, ColorGet(G, highlight_color), use_cylinders_for_strands); } return ok; } static int CartoonExtrudeArrow(PyMOLGlobals *G, CExtrude *ex, CGO *cgo, int sampling, float width, float length, int highlight_color){ int ok; ok = ExtrudeRectangle(ex, width, length, 0); // ex->Ns is always 8 for ExtrudeCGOSurfaceStrand if (ok) ExtrudeBuildNormals2f(ex); if (ok){ if(highlight_color < 0) ok &= ExtrudeCGOSurfaceStrand(ex, cgo, sampling, NULL); else ok &= ExtrudeCGOSurfaceStrand(ex, cgo, sampling, ColorGet(G, highlight_color)); } /* for PLY files ExtrudeCircle(ex,loop_quality,loop_radius); ExtrudeBuildNormals1f(ex); ExtrudeCGOSurfaceTube(ex,cgo,loop_cap,NULL); */ return ok; } static int CartoonExtrudeDumbbell(PyMOLGlobals *G, CExtrude *ex, CGO *cgo, int sampling, float dumbbell_width, float dumbbell_length, int highlight_color, int loop_quality, float dumbbell_radius, short use_cylinders_for_strands){ int ok; CExtrude *ex1 = nullptr; if(highlight_color < 0) { ok = ExtrudeDumbbell1(ex, dumbbell_width, dumbbell_length, 0); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygonTaper(ex, cgo, sampling, NULL); } else { ok = ExtrudeDumbbell1(ex, dumbbell_width, dumbbell_length, 1); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygonTaper(ex, cgo, sampling, NULL); if (ok) ok &= ExtrudeDumbbell1(ex, dumbbell_width, dumbbell_length, 2); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygonTaper(ex, cgo, sampling, ColorGet(G, highlight_color)); } /* ExtrudeCGOSurfacePolygonX(ex,cgo,1); */ if (ok){ ex1 = ExtrudeCopyPointsNormalsColors(ex); CHECKOK(ok, ex1); if (ok) ExtrudeDumbbellEdge(ex1, sampling, -1, dumbbell_length); if (ok) ok &= ExtrudeComputeTangents(ex1); } if (ok) ok &= ExtrudeCircle(ex1, loop_quality, dumbbell_radius); if (ok) ExtrudeBuildNormals1f(ex1); if (ok) ok &= ExtrudeCGOSurfaceTube(ex1, cgo, 1, NULL, use_cylinders_for_strands); if (ok){ ExtrudeFree(ex1); ex1 = ExtrudeCopyPointsNormalsColors(ex); CHECKOK(ok, ex1); if (ok) ExtrudeDumbbellEdge(ex1, sampling, 1, dumbbell_length); if (ok) ok &= ExtrudeComputeTangents(ex1); if (ok) ok &= ExtrudeCircle(ex1, loop_quality, dumbbell_radius); if (ok) ExtrudeBuildNormals1f(ex1); if (ok) ok &= ExtrudeCGOSurfaceTube(ex1, cgo, 1, NULL, use_cylinders_for_strands); } if (ex1) ExtrudeFree(ex1); return ok; } /* * Get cartoon quality setting, adapt to number of atoms if -1 */ static int GetCartoonQuality(CoordSet * cs, int setting, int v1, int v2, int v3, int v4, int min_=3) { int quality = SettingGet_i(cs->State.G, cs->Setting, cs->Obj->Obj.Setting, setting); if (quality == -1) { int natom = cs->NIndex; quality = (natom < 100000) ? v1 : (natom < 500000) ? v2 : (natom < 999999) ? v3 : v4; } else if (quality < min_) { quality = min_; } return quality; } static CGO *GenerateRepCartoonCGO(CoordSet *cs, ObjectMolecule *obj, nuc_acid_data *ndata, short use_cylinders_for_strands, float *pv, int nAt, float *tv, float *pvo, float *dl, const CCInOut *car, int *seg, int *at, int *nuc_flag, float *putty_vals, float alpha){ PyMOLGlobals *G = cs->State.G; int ok = true; CGO *cgo; int contigFlag, contFlag, extrudeFlag, n_p; CExtrude *ex = NULL; float dev; unsigned int *vi; int atom_index1, atom_index2; float *v, *v1, *v2, *vo; float *d, *vc = NULL, *vn; float *valpha = nullptr; int *atp; int c1, c2; int a; int sampling; float *sampling_tmp; int *segptr; const CCInOut *cc; int cur_car; float loop_radius; int nucleic_color = 0; float throw_; float power_a = 5; float power_b = 5; int refine; float tube_radius; float putty_radius; int cartoon_debug, cylindrical_helices, cartoon_color, highlight_color, discrete_colors, loop_quality, oval_quality, tube_quality, putty_quality, loop_cap, tube_cap; float length, width; float oval_width, oval_length; float dumbbell_radius, dumbbell_width, dumbbell_length; float ring_width; auto EXTRUDE_TRUNCATE = [&ex, &n_p, &v, &vc, &valpha, &vn, &vi]() { ExtrudeTruncate(ex, 0); n_p = 0; v = ex->p; vc = ex->c; valpha = ex->alpha; vn = ex->n; vi = ex->i; }; cartoon_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_color); ring_width = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_width); if(ring_width < 0.0F) { ring_width = fabs(SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_stick_radius)) * 0.5F; } length = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_rect_length); width = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_rect_width); oval_length = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_oval_length); dumbbell_length = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_dumbbell_length); oval_width = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_oval_width); dumbbell_width = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_dumbbell_width); dumbbell_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_dumbbell_radius); if(dumbbell_radius < 0.01F) dumbbell_radius = 0.01F; tube_cap = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_tube_cap); loop_cap = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_loop_cap); tube_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_tube_radius); if(tube_radius < 0.01F) tube_radius = 0.01F; putty_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_putty_radius); /* WLD removed: if(putty_radius<0.01F) putty_radius=0.01F; -- should not constrain what is effectively a scale factor */ tube_quality = GetCartoonQuality(cs, cSetting_cartoon_tube_quality, 9, 7, 6, 5); oval_quality = GetCartoonQuality(cs, cSetting_cartoon_oval_quality, 10, 8, 7, 6); putty_quality = GetCartoonQuality(cs, cSetting_cartoon_putty_quality, 11, 9, 7, 5); loop_quality = GetCartoonQuality(cs, cSetting_cartoon_loop_quality, 6, 6, 5, 4); sampling = GetCartoonQuality(cs, cSetting_cartoon_sampling, 7, 5, 3, 2, 1); PRINTFB(G, FB_RepCartoon, FB_Blather) " RepCartoon: Use settings tube_quality=%d oval_quality=%d putty_quality=%d loop_quality=%d sampling=%d\n", tube_quality, oval_quality, putty_quality, loop_quality, sampling ENDFB(G); if(SettingGetGlobal_i(G, cSetting_ray_trace_mode) > 0) if(loop_quality < 12) loop_quality *= 2; refine = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_refine); power_a = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_power); power_b = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_power_b); throw_ = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_throw); loop_radius = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_loop_radius); if(loop_radius < 0.01F) loop_radius = 0.01F; discrete_colors = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_discrete_colors); nucleic_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_nucleic_acid_color); if(nucleic_color == -1) nucleic_color = cartoon_color; highlight_color = SettingGet_color(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_highlight_color); cylindrical_helices = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_cylindrical_helices); sampling_tmp = Alloc(float, sampling * 3); cartoon_debug = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_debug); cgo = CGONew(G); if(alpha != 1.0F) CGOAlpha(cgo, alpha); /* debugging output */ if(cartoon_debug > 0.5 && cartoon_debug < 2.5) { GenerateRepCartoonDrawDebugLineAlongPath(cgo, nAt, pv); } PRINTFD(G, FB_RepCartoon) " RepCartoon-Debug: creating 3D scaffold...\n" ENDFD; /* okay, we now have enough info to generate smooth interpolations */ if(nAt > 1) { ex = ExtrudeNew(G); CHECKOK(ok, ex); if (ok) ok &= ExtrudeAllocPointsNormalsColors(ex, cs->NIndex * (3 * sampling + 3)); } /* process cylindrical helices first */ if(ok && (nAt > 1) && cylindrical_helices) { ok = GenerateRepCartoonProcessCylindricalHelices(G, obj, cs, cgo, ex, nAt, seg, pv, tv, pvo, car, at, dl, cartoon_color, discrete_colors, loop_radius, alpha); } if(ok && nAt > 1) { EXTRUDE_TRUNCATE(); v1 = pv; /* points */ v2 = tv; /* tangents */ vo = pvo; d = dl; segptr = seg; cc = car; atp = at; /* cs index pointer */ a = 0; contFlag = true; cur_car = cCartoon_skip; extrudeFlag = false; contigFlag = false; while(contFlag) { if (CheckExtrudeContigFlags(nAt, n_p, a, &cur_car, cc, segptr, &contigFlag, &extrudeFlag)){ EXTRUDE_TRUNCATE(); } if(ok && !extrudeFlag) { if((a < (nAt - 1)) && (*segptr == *(segptr + 1))) { /* working in the same segment... */ AtomInfoType *ai1, *ai2; atom_index1 = cs->IdxToAtm[*atp]; atom_index2 = cs->IdxToAtm[*(atp + 1)]; ai1 = obj->AtomInfo + atom_index1; ai2 = obj->AtomInfo + atom_index2; float alpha1 = alpha; float alpha2 = alpha; ComputeCartoonAtomColors(G, obj, cs, nuc_flag, atom_index1, atom_index2, &c1, &c2, atp, cc, cur_car, cartoon_color, alpha1, alpha2, nucleic_color, discrete_colors, n_p, contigFlag); dev = throw_ * (*d); CartoonGenerateSample(G, sampling, &n_p, dev, vo, v1, v2, c1, c2, alpha1, alpha2, ai1->masked ? -1 : atom_index1, ai2->masked ? -1 : atom_index2, power_a, power_b, &vc, &valpha, &vi, &v, &vn); /* now do a smoothing pass along orientation vector to smooth helices, etc... */ CartoonGenerateRefine(refine, sampling, v, vn, vo, sampling_tmp); } v1 += 3; v2 += 3; vo += 3; d++; atp += 1; segptr++; cc++; } a++; if(a == nAt) { // if at end, don't continue and extrude if needed contFlag = false; if(n_p) extrudeFlag = true; } if(ok && extrudeFlag) { contigFlag = true; if((a < nAt) && extrudeFlag) { if(*(segptr - 1) != *(segptr)) contigFlag = false; } if(ok && (cur_car != cCartoon_skip) && (cur_car != cCartoon_skip_helix)) { if((cartoon_debug > 0.5) && (cartoon_debug < 2.5)) { ok = GenerateRepCartoonDrawDebugNormals(cgo, ex, n_p); } if (ok){ ExtrudeTruncate(ex, n_p); ok &= ExtrudeComputeTangents(ex); } if (ok){ /* set up shape */ switch (cur_car) { case cCartoon_tube: ok = CartoonExtrudeTube(use_cylinders_for_strands, ex, cgo, tube_radius, tube_quality, tube_cap); break; case cCartoon_putty: ok = CartoonExtrudePutty(G, obj, cs, cgo, ex, putty_quality, putty_radius, putty_vals, sampling); if (!ok) contFlag = false; break; case cCartoon_loop: ok = CartoonExtrudeCircle(ex, cgo, use_cylinders_for_strands, loop_quality, loop_radius, loop_cap); break; case cCartoon_dash: ok = CartoonExtrudeCircle(ex, cgo, use_cylinders_for_strands, loop_quality, loop_radius, loop_cap, 2); break; case cCartoon_rect: ok = CartoonExtrudeRect(G, ex, cgo, width, length, highlight_color); break; case cCartoon_oval: ok = CartoonExtrudeOval(G, ex, cgo, use_cylinders_for_strands, oval_quality, oval_width, oval_length, highlight_color); break; case cCartoon_arrow: ok = CartoonExtrudeArrow(G, ex, cgo, sampling, width, length, highlight_color); break; case cCartoon_dumbbell: ok = CartoonExtrudeDumbbell(G, ex, cgo, sampling, dumbbell_width, dumbbell_length, highlight_color, loop_quality, dumbbell_radius, use_cylinders_for_strands); break; } if (!ok) contFlag = false; } } a--; /* undo above... */ extrudeFlag = false; if (ok){ EXTRUDE_TRUNCATE(); // doesn't include vi = ex->i, not used? } /* ExtrudeTruncate(ex, 0); n_p = 0; v = ex->p; vc = ex->c; vn = ex->n;*/ } } } if(ok && nAt > 1) { if((cartoon_debug > 0.5) && (cartoon_debug < 2.5)) { ok = GenerateRepCartoonDrawDebugOrient(cgo, nAt, pv, pvo, tv); } } if(ex) { ExtrudeFree(ex); } /* draw the rings */ if(ok && ndata->ring_anchor && ndata->n_ring) { ok = GenerateRepCartoonDrawRings(G, ndata, obj, cs, cgo, ring_width, cartoon_color, alpha); } if (ok) CGOStop(cgo); FreeP(sampling_tmp); if (!ok){ CGOFree(cgo); } return (cgo); } static int RepCartoonSameVis(RepCartoon * I, CoordSet * cs) { int same = true; char *lv; int a; AtomInfoType *ai; if (!I->LastVisib) return false; ai = cs->Obj->AtomInfo; lv = I->LastVisib; for(a = 0; a < cs->NIndex; a++) { if(*(lv++) != GET_BIT((ai + cs->IdxToAtm[a])->visRep, cRepCartoon)) { same = false; break; } } return (same); } static void RepCartoonInvalidate(struct Rep *I, struct CoordSet *cs, int level) { if (level >= cRepInvColor){ RepCartoon *rc = (RepCartoon*)I; FreeP(rc->LastVisib); rc->LastVisib = NULL; } RepInvalidate(I, cs, level); } /* * nucleic acid cap */ class nuc_acid_cap { PyMOLGlobals * G; nuc_acid_data * ndata; CoordSet * cs; int idx; public: int atm; AtomInfoType * ai; bool enabled; // constructor nuc_acid_cap(PyMOLGlobals * G_, nuc_acid_data * ndata_, CoordSet * cs_, int mode) : G(G_), ndata(ndata_), cs(cs_), idx(0), atm(0), ai(NULL) { enabled = (ndata->na_mode == 4 || ndata->na_mode == mode); } // set atom indices void set(int idx_, int atm_, AtomInfoType * ai_) { idx = idx_; atm = atm_; ai = ai_; } // check if this cap is enabled and has atom information bool active() { return (ai && enabled); } // make the cap, if active, and clear atom indices bool cap() { if(!active()) return false; nuc_acid(G, ndata, idx, atm, ai, cs, cs->Obj, false); set(-1, -1, NULL); return true; } }; /* * compute and fill "ndata" with: * - cartoon trace and segments * - cartoon types and secondary structure * - rings */ static 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; } } static void RepCartoonComputePuttyValues(ObjectMolecule *obj, float *putty_vals){ double sum = 0.0, sumsq = 0.0; float value; int cnt = 0; AtomInfoType *ai; int a; for(a = 0; a < obj->NAtom; a++) { ai = obj->AtomInfo + a; if(ai->visRep & cRepCartoonBit) { value = ai->b; sum += value; sumsq += (value * value); if(value < putty_vals[2]) putty_vals[2] = value; if(value > putty_vals[3]) putty_vals[3] = value; cnt++; } } if(cnt) { putty_vals[0] = (float) (sum / cnt); putty_vals[1] = (float) sqrt1d((sumsq - (sum * sum / cnt)) / (cnt)); } else { /* aren't these assignments unnecessary? */ putty_vals[0] = 10.0F; putty_vals[1] = 10.0F; putty_vals[2] = 0.0F; putty_vals[3] = 10.0F; } } static void RepCartoonComputeDifferencesAndNormals(PyMOLGlobals *G, int nAt, int *seg, float *pv, float *dv, float *nv, float *dl, int quiet){ float *v1, *v2, *vptr, *d; int *sptr, a; /* compute differences and normals */ sptr = seg; vptr = pv; v1 = dv; v2 = nv; d = dl; for(a = 0; a < (nAt - 1); a++) { if (!quiet){ PRINTFD(G, FB_RepCartoon) " RepCartoon: seg %d *s %d , *(s+1) %d\n", a, *sptr, *(sptr + 1) ENDFD; } if(*sptr == *(sptr + 1)) { subtract3f(vptr + 3, vptr, v1); *d = (float) length3f(v1); if(*d > R_SMALL4) { float d_1; d_1 = 1.0F / (*d); scale3f(v1, d_1, v2); } else if(a) { /* if zero, copy previous */ copy3f(v2 - 3, v2); } else { zero3f(v2); } } else { zero3f(v2); } d++; vptr += 3; v1 += 3; v2 += 3; sptr++; } } static void RepCartoonComputeTangents(int nAt, int *seg, float *nv, float *tv){ float *vptr, *v1; int *sptr, a; sptr = seg; vptr = nv; v1 = tv; *(v1++) = *(vptr++); /* first segment */ *(v1++) = *(vptr++); *(v1++) = *(vptr++); sptr++; for(a = 1; a < (nAt - 1); a++) { if((*sptr == *(sptr - 1)) && (*sptr == *(sptr + 1))) { add3f(vptr, (vptr - 3), v1); /* tangent vectors are head-to-tail sums within a segment */ normalize3f(v1); } else if(*sptr == *(sptr - 1)) { *(v1) = *(vptr - 3); /* end a segment */ *(v1 + 1) = *(vptr - 2); *(v1 + 2) = *(vptr - 1); } else if(*sptr == *(sptr + 1)) { *(v1) = *(vptr); /* new segment */ *(v1 + 1) = *(vptr + 1); *(v1 + 2) = *(vptr + 2); } vptr += 3; v1 += 3; sptr++; } *(v1++) = *(vptr - 3); /* last segment */ *(v1++) = *(vptr - 2); *(v1++) = *(vptr - 1); } static void RepCartoonComputeRoundHelices(nuc_acid_data *ndata, int nAt, int *seg, int *sstype, float *tv, float *pv){ float *v0, *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL, *v5 = NULL, *vptr; int last, *sptr, *ss, a; float t0[3], t1[3], t2[3]; sptr = seg; vptr = pv; ss = sstype; v0 = tv; last = 0; if(nAt > 1) { for(a = 0; a < nAt; a++) { if(a) { if(*sptr != *(sptr - 1)) { /* contiguous helices in disconnected segments */ v1 = NULL; v2 = NULL; v3 = NULL; v4 = NULL; v5 = NULL; last = 0; } } v5 = v4; v4 = v3; v3 = v2; v2 = v1; if(*ss == 1) /* helix */ v1 = vptr; else { /* early termination ? */ if(last < 2) { zero3f(t0); if(v2 && v3) { subtract3f(v2, vptr, t0); normalize3f(t0); subtract3f(v3, v2, t1); normalize3f(t1); add3f(t1, t0, t0); if(v4) { subtract3f(v4, v3, t1); normalize3f(t1); add3f(t1, t0, t0); } if(v5) { subtract3f(v5, v4, t1); normalize3f(t1); add3f(t1, t0, t0); } normalize3f(t0); cross_product3f(t0, v0 - 3, ndata->voptr - 3); normalize3f(ndata->voptr - 3); cross_product3f(t0, v0 - 6, ndata->voptr - 6); normalize3f(ndata->voptr - 6); if(v4) { cross_product3f(t0, v0 - 9, ndata->voptr - 9); normalize3f(ndata->voptr - 9); } if(v5) { cross_product3f(t0, v0 - 12, ndata->voptr - 12); normalize3f(ndata->voptr - 12); } if(v4 && v5) { /* now make sure there's no goofy flip on the end... of a short, tight helix */ if(dot_product3f(ndata->voptr - 9, ndata->voptr - 12) < -0.8F) invert3f(ndata->voptr - 12); } } } v1 = NULL; v2 = NULL; v3 = NULL; v4 = NULL; v5 = NULL; last = 0; } if(v1 && v2 && v3 && v4) { add3f(v1, v4, t0); add3f(v2, v3, t1); scale3f(t0, 0.2130F, t0); scale3f(t1, 0.2870F, t1); add3f(t0, t1, t0); if(last) { /* 5th CA or later... */ subtract3f(t2, t0, t1); normalize3f(t1); cross_product3f(t1, v0, ndata->voptr); normalize3f(ndata->voptr); cross_product3f(t1, v0 - 3, ndata->voptr - 3); normalize3f(ndata->voptr - 3); cross_product3f(t1, v0 - 6, ndata->voptr - 6); normalize3f(ndata->voptr - 6); if(last == 1) { /* 5th */ cross_product3f(t1, v0 - 9, ndata->voptr - 9); normalize3f(ndata->voptr - 9); cross_product3f(t1, v0 - 12, ndata->voptr - 12); normalize3f(ndata->voptr - 12); } } last++; copy3f(t0, t2); } vptr += 3; ss++; ndata->voptr += 3; v0 += 3; sptr++; } } } static void RepCartoonRefineNormals(PyMOLGlobals *G, RepCartoon *I, ObjectMolecule *obj, CoordSet * cs, nuc_acid_data *ndata, int nAt, int *seg, float *tv, float *pvo, float *pva, int *sstype, float *nv){ int refine_normals = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_refine_normals); if(refine_normals < 0) { if(obj->NCSet > 1) { int i, n_set = 0; for(i = 0; i < obj->NCSet; i++) if(obj->CSet[i]) { n_set++; if(n_set > 1) refine_normals = 0; /* default behavior is to not refine normals for multi-state objects */ } } } if(refine_normals) { /* first, make sure orientiation vectors are orthogonal to the tangent */ float *v0, *v1, *vptr, *va, max_dot; int *sptr, a, *ss; float t0[3], t1[3], t2[3], t3[3], o0[12], o1[12]; float dp; v1 = tv + 3; ndata->voptr = pvo + 3; sptr = seg + 1; for(a = 1; a < (nAt - 1); a++) { if((*sptr == *(sptr - 1)) && (*sptr == *(sptr + 1))) { /* only operate on vectors within the cartoon itself -- not the end vectors */ remove_component3f(ndata->voptr, v1, t0); normalize23f(t0, ndata->voptr); /* go on to next vertex */ } v1 += 3; ndata->voptr += 3; sptr++; } /* now generate alternative inverted orientation vectors */ va = pva; ndata->voptr = pvo; ss = sstype; for(a = 0; a < nAt; a++) { /* original */ copy3f(ndata->voptr, va); va += 3; /* inverse */ copy3f(ndata->voptr, va); if(*ss != 1) { invert3f(va); /* for helix, don't allow inversion of normals, since that would confuse the inside & outside of the helix */ } va += 3; /* go on to next vertex */ ndata->voptr += 3; ss++; } /* now iterate forward through pairs */ ndata->voptr = pvo + 3; va = pva + 6; vptr = nv + 3; /* normals in direction of chain */ sptr = seg + 1; for(a = 1; a < (nAt - 1); a++) { if((*sptr == *(sptr + 1)) && (*sptr == *(sptr - 1))) { /* only operate within a segment */ remove_component3f(ndata->voptr - 3, vptr - 3, o0); /* previous orientation vector */ normalize3f(o0); /* is now perp to chain direction */ v1 = va; /* candidate orientation vectors for current CA */ remove_component3f(v1, vptr - 3, o1); /* removes chain direction from the two candidates */ remove_component3f(v1 + 3, vptr - 3, o1 + 3); normalize3f(o1); normalize3f(o1 + 3); max_dot = dot_product3f(o0, o1); v0 = v1; dp = dot_product3f(o0, o1 + 3); if(dp > max_dot) { v0 = v1 + 3; max_dot = dp; } copy3f(v0, ndata->voptr); /* updates atom with optimal orientation vector */ } ndata->voptr += 3; va += 6; /* candidate orientation vectors */ vptr += 3; /* normal */ sptr++; } /* now soften up the kinks */ v1 = tv + 3; va = pva + 6; ndata->voptr = pvo + 3; sptr = seg + 1; for(a = 1; a < (nAt - 1); a++) { if((*sptr == *(sptr - 1)) && (*sptr == *(sptr + 1))) { dp = (dot_product3f(ndata->voptr, ndata->voptr + 3) * dot_product3f(ndata->voptr, ndata->voptr - 3)); if(dp < -0.10F) { /* threshold value -- could be a setting */ add3f(ndata->voptr + 3, ndata->voptr - 3, t0); scale3f(ndata->voptr, 0.001, t1); add3f(t1, t0, t0); remove_component3f(t0, v1, t0); normalize3f(t0); if(dot_product3f(ndata->voptr, t0) < 0.0F) { subtract3f(ndata->voptr, t0, t2); } else { add3f(ndata->voptr, t0, t2); } normalize3f(t2); dp = 2 * (-0.10F - dp); if(dp > 1.0F) dp = 1.0F; mix3f(ndata->voptr, t2, dp, t3); copy3f(t3, va); /* store modified vector */ invert3f3f(va, va + 3); } else { copy3f(ndata->voptr, va); /* keep as is */ } } v1 += 3; ndata->voptr += 3; va += 6; sptr++; } /* now update */ va = pva + 6; ndata->voptr = pvo + 3; sptr = seg + 1; for(a = 1; a < (nAt - 1); a++) { if((*sptr == *(sptr - 1)) && (*sptr == *(sptr + 1))) { copy3f(va, ndata->voptr); } ndata->voptr += 3; va += 6; sptr++; } } } static void RepCartoonFlattenSheets(PyMOLGlobals *G, ObjectMolecule *obj, CoordSet * cs, nuc_acid_data *ndata, int nAt, const int *sptr, const CCInOut * cc, float *pv, float *pvo, const int *ss, const float *v0, float *tmp, const int *flag_tmp){ int last, first, cur_car, end_flag, a, b, c, e, f; int flat_cycles; float t0[3]; flat_cycles = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_flat_cycles); last = 0; first = -1; cur_car = *cc; end_flag = false; if(nAt > 1) { for(a = 0; a < nAt; a++) { if(a) { if(*sptr != *(sptr - 1)) { end_flag = true; } else if(*ss != 2) { end_flag = true; } if(a == (nAt - 1)) { end_flag = 1; } } if(end_flag) { if((cur_car != cCartoon_loop) && (cur_car != cCartoon_tube)) { f = 1; for(c = 0; c < flat_cycles; c++) { for(b = first + f; b <= last - f; b++) { /* iterative averaging */ zero3f(t0); for(e = -f; e <= f; e++) { add3f(pv + 3 * (b + e), t0, t0); } scale3f(t0, 1.0F / (f * 2 + 1), tmp + b * 3); } for(b = first + f; b <= last - f; b++) { if(!((*(flag_tmp + b) & cAtomFlag_no_smooth))) { copy3f(tmp + b * 3, pv + b * 3); } } for(b = first + f; b <= last - f; b++) { zero3f(t0); for(e = -f; e <= f; e++) { add3f(pvo + 3 * (b + e), t0, t0); } scale3f(t0, 1.0F / (f * 2 + 1), tmp + b * 3); } for(b = first + f; b <= last - f; b++) { copy3f(tmp + b * 3, pvo + b * 3); /* normalize3f(pvo+b*3); */ } for(b = first + f; b <= last - f; b++) { subtract3f(pv + (b + 1) * 3, pv + (b - 1) * 3, tmp + b * 3); normalize3f(tmp + b * 3); remove_component3f(pvo + b * 3, tmp + b * 3, pvo + b * 3); normalize3f(pvo + b * 3); } } } first = -1; last = -1; end_flag = false; } if(*ss == 2) { if(first < 0) first = a; cur_car = *cc; last = a; } ss++; v0 += 3; sptr++; cc++; } } } static void RepCartoonFlattenSheetsRefineTips(PyMOLGlobals *G, ObjectMolecule *obj, CoordSet * cs, int nAt, int *seg, int *sstype, float *tv){ int *sptr, *ss, a; float *v2; float refine_tips; float t0[3]; refine_tips = SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_refine_tips); sptr = seg + 1; ss = sstype + 1; v2 = tv + 3; /* normal */ for(a = 1; a < (nAt - 1); a++) { if((*ss == 2) && (*sptr == *(sptr + 1)) && (*sptr == *(sptr - 1))) { /* sheet in same segment */ if((*ss == *(ss + 1)) && (*ss != *(ss - 1))) { /* start, bias forwards */ scale3f(v2 + 3, refine_tips, t0); add3f(t0, v2, v2); normalize3f(v2); } else if((*ss != *(ss + 1)) && (*ss == *(ss - 1))) { /* end, bias backwards */ scale3f(v2 - 3, refine_tips, t0); add3f(t0, v2, v2); normalize3f(v2); } } v2 += 3; sptr++; ss++; } } static void RepCartoonSmoothLoops(PyMOLGlobals *G, ObjectMolecule *obj, CoordSet * cs, nuc_acid_data *ndata, int nAt, int *seg, float *pv, int *sstype, float *pvo, float *tv, float *tmp, int *flag_tmp){ int *sptr, *ss, last, first, end_flag, a, b, c, e, f; float t0[3]; int smooth_first, smooth_last, smooth_cycles; smooth_first = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_smooth_first); smooth_last = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_smooth_last); smooth_cycles = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_smooth_cycles); sptr = seg; ss = sstype; last = 0; first = -1; end_flag = false; if(nAt > 1) { for(a = 0; a < nAt; a++) { if(a) { if(*sptr != *(sptr - 1)) { end_flag = true; } else if(*ss != 0) { end_flag = true; } if(a == (nAt - 1)) end_flag = 1; } if(end_flag) { if(a) if(first > 0) /* 011130 WLD */ if(*(seg + first) == *(seg + first - 1)) first--; if(last > 0) if(*sptr == *(sptr - 1)) if(last < (nAt - 1)) last++; for(f = smooth_first; f <= smooth_last; f++) { for(c = 0; c < smooth_cycles; c++) { for(b = first + f; b <= last - f; b++) { /* iterative averaging */ zero3f(t0); for(e = -f; e <= f; e++) { add3f(pv + 3 * (b + e), t0, t0); } scale3f(t0, 1.0F / (f * 2 + 1), tmp + b * 3); } for(b = first + f; b <= last - f; b++) { if(!(*(flag_tmp + b) & cAtomFlag_no_smooth)) { copy3f(tmp + b * 3, pv + b * 3); } } for(b = first + f; b <= last - f; b++) { zero3f(t0); for(e = -f; e <= f; e++) { add3f(pvo + 3 * (b + e), t0, t0); } scale3f(t0, 1.0F / (f * 2 + 1), tmp + b * 3); } for(b = first + f; b <= last - f; b++) { copy3f(tmp + b * 3, pvo + b * 3); normalize3f(pvo + b * 3); } } } first = -1; last = -1; end_flag = false; } if(*ss == 0) { if(first < 0) first = a; last = a; } ss++; sptr++; } } } Rep *RepCartoonNew(CoordSet * cs, int state) { PyMOLGlobals *G = cs->State.G; ObjectMolecule *obj; int *i, *sptr, *at, *seg, nAt, *sstype; CCInOut *car, *cc; float *pv = NULL; float *pvo = NULL, *pva = NULL; float *dv = NULL; float *nv = NULL; float *tv = NULL; float *tmp = NULL; float *dl = NULL; int *ss; int ladder_mode; int round_helices; int na_mode; int *flag_tmp; float putty_vals[4] = { 10.0F, 0.0F, FLT_MAX, -FLT_MAX }; // putty_mean, putty_stdev, putty_min, putty_max int *ring_anchor = NULL; int *nuc_flag = NULL; float alpha; int ok = true; nuc_acid_data ndata; short use_shaders = SettingGetGlobal_b(G, cSetting_use_shaders); short na_strands_as_cylinders = use_shaders && (SettingGetGlobal_i(G, cSetting_cartoon_nucleic_acid_as_cylinders) & 2) && SettingGetGlobal_b(G, cSetting_render_as_cylinders); // skip if not visible if(!cs->hasRep(cRepCartoonBit)) return NULL; /* THIS IS BY FAR THE WORST ROUTINE IN PYMOL! * DEVELOP ON IT ONLY AT EXTREME RISK TO YOUR MENTAL HEALTH */ OOCalloc(G, RepCartoon); PRINTFD(G, FB_RepCartoon) " RepCartoonNew-Debug: entered.\n" ENDFD; obj = cs->Obj; RepInit(G, &I->R); alpha = 1.0F - SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_transparency); round_helices = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_round_helices); na_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_nucleic_acid_mode); ladder_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ladder_mode); I->R.fRender = (void (*)(struct Rep *, RenderInfo *)) RepCartoonRender; I->R.fSameVis = (int (*)(struct Rep *, struct CoordSet *)) RepCartoonSameVis; I->R.fFree = (void (*)(struct Rep *)) RepCartoonFree; I->R.fInvalidate = RepCartoonInvalidate; I->R.fRecolor = NULL; I->R.obj = &obj->Obj; I->R.cs = cs; I->ray = NULL; I->std = NULL; I->preshader = NULL; I->R.context.object = (void *) obj; I->R.context.state = state; /* find all of the CA points */ at = Alloc(int, cs->NAtIndex); /* cs index pointers */ pv = Alloc(float, cs->NAtIndex * 3); tmp = Alloc(float, cs->NAtIndex * 3); pvo = Alloc(float, cs->NAtIndex * 3); /* orientation vector */ pva = Alloc(float, cs->NAtIndex * 6); /* alternative orientation vectors, two per atom */ seg = Alloc(int, cs->NAtIndex); car = Calloc(CCInOut, cs->NAtIndex); /* cartoon type for each atom */ sstype = Alloc(int, cs->NAtIndex); flag_tmp = Calloc(int, cs->NAtIndex); nuc_flag = Calloc(int, cs->NAtIndex); I->LastVisib = Calloc(char, cs->NAtIndex); auto cartoon_all_alt = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_all_alt); ndata.next_alt = 0; do { ndata.alt = ndata.next_alt; ndata.next_alt = 0; memset(car, 0, sizeof(*car) * cs->NAtIndex); memset(sstype, 0, sizeof(*sstype) * cs->NAtIndex); memset(flag_tmp, 0, sizeof(*flag_tmp) * cs->NAtIndex); memset(nuc_flag, 0, sizeof(*nuc_flag) * cs->NAtIndex); i = at; sptr = seg; cc = car; ss = sstype; nAt = 0; ndata.na_mode = na_mode; ndata.nuc_flag = nuc_flag; ndata.a2 = -1; ndata.nSeg = 0; ndata.v_o_last = NULL; ndata.sptr = sptr; ndata.iptr = i; ndata.cc = cc; ndata.nAt = nAt; ndata.ss = ss; ndata.putty_flag = false; ndata.fp = flag_tmp; ndata.vptr = pv; ndata.voptr = pvo; ndata.ring_mode = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_mode); ndata.ring_finder = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_ring_finder); ndata.ring_finder_eff = ndata.ring_finder; if((!ndata.ring_mode) || (ndata.ring_finder == 2)) ndata.ring_finder_eff = 1; if(ndata.ring_mode || ladder_mode) { ring_anchor = VLAlloc(int, cs->NAtIndex / 10 + 1); } ndata.ring_anchor = ring_anchor; ndata.n_ring = 0; RepCartoonGeneratePASS1(G, I, obj, cs, &ndata); nAt = ndata.nAt; if(nAt && ndata.putty_flag) { RepCartoonComputePuttyValues(obj, putty_vals); } PRINTFD(G, FB_RepCartoon) " RepCartoon-Debug: path outlined, interpolating... nAt=%d\n", nAt ENDFD; if(nAt) { dv = Alloc(float, nAt * 3); /* differences between next and current 3f */ nv = Alloc(float, nAt * 3); /* normal */ dl = Alloc(float, nAt); /* length (i.e., normal * length = difference) */ RepCartoonComputeDifferencesAndNormals(G, nAt, seg, pv, dv, nv, dl, true); /* compute tangents */ tv = Alloc(float, nAt * 3 + 6); RepCartoonComputeTangents(nAt, seg, nv, tv); PRINTFD(G, FB_RepCartoon) " RepCartoon-Debug: generating coordinate systems...\n" ENDFD; if(round_helices) { ndata.voptr = pvo; RepCartoonComputeRoundHelices(&ndata, nAt, seg, sstype, tv, pv); } RepCartoonRefineNormals(G, I, obj, cs, &ndata, nAt, seg, tv, pvo, pva, sstype, nv); { int smooth_loops = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_smooth_loops); bool cartoon_flat_sheets = SettingGet_b(G, cs->Setting, obj->Obj.Setting, cSetting_cartoon_flat_sheets); if(smooth_loops || cartoon_flat_sheets) { if(cartoon_flat_sheets) { RepCartoonFlattenSheets(G, obj, cs, &ndata, nAt, seg, car, pv, pvo, sstype, tv, tmp, flag_tmp); } if(smooth_loops) { RepCartoonSmoothLoops(G, obj, cs, &ndata, nAt, seg, pv, sstype, pvo, tv, tmp, flag_tmp); } /* recompute differences and normals */ RepCartoonComputeDifferencesAndNormals(G, nAt, seg, pv, dv, nv, dl, true); /* recompute tangents */ RepCartoonComputeTangents(nAt, seg, nv, tv); if(cartoon_flat_sheets) { RepCartoonFlattenSheetsRefineTips(G, obj, cs, nAt, seg, sstype, tv); } } } } I->ray = GenerateRepCartoonCGO(cs, obj, &ndata, na_strands_as_cylinders, pv, nAt, tv, pvo, dl, car, seg, at, nuc_flag, putty_vals, alpha); if (I->ray && I->ray->has_begin_end){ CGOCombineBeginEnd(&I->ray); } if (I->preshader) { CGOAppend(I->preshader, I->ray); CGOFree(I->ray); } else { I->preshader = I->ray; } } while (ndata.next_alt && cartoon_all_alt); I->ray = I->preshader; CHECKOK(ok, I->ray); ok &= !G->Interrupt; if (!ok){ /* cannot generate RepCartoon */ RepCartoonFree(I); I = NULL; } FreeP(dv); FreeP(dl); FreeP(tv); FreeP(nv); FreeP(at); FreeP(seg); FreeP(pv); FreeP(pvo); FreeP(pva); FreeP(car); FreeP(tmp); FreeP(sstype); FreeP(flag_tmp); FreeP(nuc_flag); VLAFreeP(ndata.ring_anchor); return (Rep *) I; }