/* 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 #include"os_predef.h" #include"os_std.h" #include"os_gl.h" #include"Base.h" #include"Err.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 "CoordSet.h" #include "AtomIterators.h" enum { CARTOON_CYLINDRICAL_HELICES_CURVED = 1, CARTOON_CYLINDRICAL_HELICES_STRAIGHT = 2 }; class CCInOut { signed char cc_in { cCartoon_auto /* 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; } }; struct RepCartoon : Rep { using Rep::Rep; ~RepCartoon() override; cRep_t type() const override { return cRepCartoon; } void render(RenderInfo* info) override; void invalidate(cRepInv_t level) override; bool sameVis() const override; CGO* ray = nullptr; CGO* std = nullptr; CGO* preshader = nullptr; /** * Free the preshader CGO or move to another owner. * @post preshader == nullptr */ void disposePreshaderCGO() { if (!ray) { std::swap(ray, preshader); } else { CGOFree(preshader); } } char* LastVisib = nullptr; }; #include"ObjectMolecule.h" #define ESCAPE_MAX 500 RepCartoon::~RepCartoon() { auto I = this; assert(I->ray != I->preshader); CGOFree(I->preshader); CGOFree(I->ray); CGOFree(I->std); FreeP(I->LastVisib); } /** * 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(CGO* cgo) { if (!CGOHasOperations(cgo)) { return cgo; } auto G = cgo->G; CGO *tmpCGO = CGONew(G); CGOSpecial(tmpCGO, ENABLE_BACK_FACES_IF_NOT_TWO_SIDED); tmpCGO->free_append(cgo); CGOSpecial(tmpCGO, DISABLE_BACK_FACES_IF_NOT_TWO_SIDED); CGOStop(tmpCGO); return tmpCGO; } static int RepCartoonCGOGenerate(RepCartoon * I, RenderInfo * info) { PyMOLGlobals *G = I->G; int ok = true; int use_shaders, has_cylinders_to_optimize; float alpha = 1.0F - SettingGet_f(G, I->cs->Setting.get(), I->obj->Setting.get(), cSetting_cartoon_transparency); bool const hasAlpha = alpha < 1 || [](RepCartoon * I){ for(CoordSetAtomIterator iter(I->cs); iter.next();){ auto ai = iter.getAtomInfo(); if ((ai->visRep & cRepCartoonBit) && AtomSettingGetWD(I->G, ai, cSetting_cartoon_transparency, 0.0f) > 0) { return true; } } return false; }(I); I->setHasTransparency(hasAlpha); 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); assert(!I->std); if (use_shaders){ if (hasAlpha && (SettingGetGlobal_i(G, cSetting_transparency_mode) != 3)){ // some transparency std::unique_ptr simplified(CGOSimplify(I->preshader)); std::unique_ptr optimized( CGOOptimizeToVBOIndexedWithColorEmbedTransparentInfo( simplified.get(), 0, nullptr, true)); CGO* tmp2CGO = CGONew(G); CGOEnable(tmp2CGO, GL_BACK_FACE_CULLING); tmp2CGO->move_append(std::move(*optimized)); CGODisable(tmp2CGO, GL_BACK_FACE_CULLING); CGOStop(tmp2CGO); I->std = tmp2CGO; } else { CGO* leftOverCGOBorrowed = nullptr; std::unique_ptr leftOverCGOManaged; std::unique_ptr convertcgo(CGONew(G)); if (has_cylinders_to_optimize && G->ShaderMgr->Get_CylinderShader(info->pass, 0)){ /* Optimize Cylinders into Shader operation */ leftOverCGOManaged.reset(CGONew(G)); leftOverCGOBorrowed = leftOverCGOManaged.get(); CGOEnable(convertcgo.get(), GL_CYLINDER_SHADER); CGOFilterOutCylinderOperationsInto(I->preshader, leftOverCGOBorrowed); convertcgo->free_append(CGOConvertShaderCylindersToCylinderShader( I->preshader, convertcgo.get())); CGODisable(convertcgo.get(), GL_CYLINDER_SHADER); CGOStop(convertcgo.get()); assert(convertcgo->use_shader); } else { leftOverCGOBorrowed = I->preshader; } bool has_spheres_to_optimize = CGOHasSphereOperations(leftOverCGOBorrowed); if (has_spheres_to_optimize){ /* Optimize spheres and putting them into convertcgo as a shader CGO operation */ std::unique_ptr leftOverAfterSpheresCGO(CGONew(G)); std::unique_ptr sphereVBOs(CGOOptimizeSpheresToVBONonIndexed( leftOverCGOBorrowed, 0, true, leftOverAfterSpheresCGO.get())); if (sphereVBOs) { convertcgo->move_append(std::move(*sphereVBOs)); leftOverCGOManaged = std::move(leftOverAfterSpheresCGO); leftOverCGOBorrowed = leftOverCGOManaged.get(); } } /* For the rest of the primitives that exist, simplify them into Geometry * (should probably be no more, but do this anyway) */ std::unique_ptr leftOverCGOSimplified(CGOSimplify(leftOverCGOBorrowed)); if (leftOverCGOSimplified) { std::unique_ptr optimized( CGOOptimizeToVBONotIndexed(leftOverCGOSimplified.get())); if (optimized) { convertcgo->move_append(std::move(*optimized)); } } I->std = CGOAddTwoSidedBackfaceSpecialOps(convertcgo.release()); } I->std->use_shader = true; } else { if (ok){ auto simplifiedCGO = CGOSimplify(I->preshader, 0); if (hasAlpha){ 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(I->std); } } } I->disposePreshaderCGO(); return ok; } void RepCartoon::render(RenderInfo* info) { auto I = this; if (info->ray) { #ifndef _PYMOL_NO_RAY CGO* raycgo = I->ray ? I->ray : I->preshader; if (raycgo && !CGORenderRay(raycgo, info->ray, info, nullptr, nullptr, I->cs->Setting.get(), I->obj->Setting.get())) { PRINTFB(G, FB_RepCartoon, FB_Warnings) " %s-Warning: ray rendering failed\n", __func__ ENDFB(G); CGOFree(I->ray); } #endif return; } if (G->HaveGUI && G->ValidContext) { if (I->preshader) { assert(!I->std); bool ok = RepCartoonCGOGenerate(I, info); if (!ok) { I->disposePreshaderCGO(); I->invalidate(cRepInvPurge); I->cs->Active[cRepCartoon] = false; } } if (I->std && CGOHasOperations(I->std)) { assert(!I->preshader); if (info->pick) { CGORenderPicking(I->std, info, &I->context, I->cs->Setting.get(), I->obj->Setting.get()); } else { CGORender(I->std, nullptr, I->cs->Setting.get(), I->obj->Setting.get(), info, I); } } } } #define NUCLEIC_NORMAL0 "C2" #define NUCLEIC_NORMAL1 "C3*" #define NUCLEIC_NORMAL2 "C3'" #define MAX_RING_ATOM 10 enum class ss_t { NONE = 0, HELIX = 1, SHEET = 2, NUCLEIC = 3, }; 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 const float *v_o_last; // defaults to nullptr int *sptr; int *iptr; CCInOut * cc; int nAt; ss_t *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 * * @param nuc_flag NAtom-length boolean nucleic acid flags array * @param obj Molecule * @param atm atom index * @param max_depth maximum search depth * @param 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 ObjectMolecule* obj, const int atm, const int max_depth, std::set &seen) { for (auto const& neighbor : AtomNeighbors(obj, atm)) { auto const atm_neighbor = neighbor.atm; 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, obj, 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) { const 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]; const AtomInfoType *ai_i[MAX_RING_ATOM]; int have_all = true; int all_marked = true; const 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->coordPtr(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; const AtomInfoType* ai2; const AtomInfoType* atomInfo = obj->AtomInfo.data(); int mem0, mem1, mem2, mem3, mem4, mem5, mem6, mem7; auto* const neighbor = obj->getNeighborArray(); 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)) { const AtomInfoType *g1_ai = atomInfo + g1; const AtomInfoType *g2_ai = atomInfo + g2; if (ring_connector_visible(G, g1_ai, g2_ai, sc_helper)) { float avg[3]; int g1_x = cs->atmToIdx(g1); int g2_x = cs->atmToIdx(g2); const float* g1p = cs->coordPtr(g1_x); const float* g2p = cs->coordPtr(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 = { static_cast(g2), g2_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(g2p, g1p, axis); CGOColorv(cgo, color1); float ladder_alpha = 1.0f - AtomSettingGetWD(G, ai_i[i], cSetting_cartoon_transparency, 1.0f - alpha); cgo->add(cgo, g1p, axis, glyco_radius, 0x1f, color2, &pickcolor2, ladder_alpha); } } } } } } } /* see if any of the neighbors are confirmed nucleic acids... */ if(sugar_at >= 0) { if(!nf) { auto seen = std::set(); nf = has_nuc_neighbor(nuc_flag, obj, 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; { const AtomInfoType *sug_ai = atomInfo + sugar_at; const AtomInfoType *bas_ai = atomInfo + base_at; if (ring_connector_visible(G, bas_ai, sug_ai, sc_helper)) { int sug = cs->atmToIdx(sugar_at); int 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 = { static_cast(base_at), bas_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(cs->coordPtr(bas), cs->coordPtr(sug), axis); CGOColorv(cgo, color1); float ladder_alpha = 1.0f - AtomSettingGetWD(G, ai_i[i], cSetting_cartoon_transparency, 1.0f - alpha); cgo->add(cgo, cs->coordPtr(sug), axis, ladder_radius, 0x1f, color2, &pickcolor2, ladder_alpha); } } } } base_at = save_at; sugar_at = save_at; } } if((base_at >= 0) && (sugar_at >= 0)) { const AtomInfoType *sug_ai = atomInfo + sugar_at; const AtomInfoType *bas_ai = atomInfo + base_at; if (ring_connector_visible(G, bas_ai, sug_ai, sc_helper)) { int sug = cs->atmToIdx(sugar_at); int bas = cs->atmToIdx(base_at); if((sug >= 0) && (bas >= 0)) { float tmp[3], outer[3]; const float* v_outer = cs->coordPtr(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)) { int p3 = cs->atmToIdx(phos3_at); int p5 = cs->atmToIdx(phos5_at); if((p3 >= 0) && (p5 >= 0)) { if(ring_mode) { scale3f(cs->coordPtr(p5), 0.333333F, outer); scale3f(cs->coordPtr(p3), 0.666667F, tmp); } else { scale3f(cs->coordPtr(p3), 0.5F, outer); scale3f(cs->coordPtr(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 = { static_cast(base_at), bas_ai->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(cs->coordPtr(bas), v_outer, axis); CGOColorv(cgo, color1); float ladder_alpha = 1.0f - AtomSettingGetWD(G, sug_ai, cSetting_cartoon_transparency, 1.0f - alpha); cgo->add(cgo, v_outer, axis, ladder_radius, 0x1f, color2, &pickcolor2, ladder_alpha); } } } } } } } 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(); nf = has_nuc_neighbor(nuc_flag, obj, 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 = nullptr; /* 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)))) { auto atom_alpha = 1.0f - AtomSettingGetWD(G, ai_i[i], cSetting_cartoon_transparency, 1.0f - ring_alpha); if((alpha != 1.0F) || (ring_alpha != alpha) || atom_alpha != 1.0){ if(atom_alpha != ring_alpha){ CGOAlpha(cgo, atom_alpha); } else { 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 = { static_cast(atix[ii]), ai_i[ii]->masked ? cPickableNoPick : cPickableAtom }; float axis[3]; subtract3f(v_i[ii], v_i[i], axis); CGOColorv(cgo, color1); float ladder_alpha = 1.0f - AtomSettingGetWD(G, ai_i[i], cSetting_cartoon_transparency, 1.0f - alpha); cgo->add(cgo, v_i[i], axis, ring_width_for_mode, 0x1f, color2, &pickcolor2, ladder_alpha); } } } } } } } } } /** * 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, const AtomInfoType* ai, const CoordSet* cs, const ObjectMolecule* obj, int set_flags) { int a3, a4, st, nd; const float *v_o, *v_c; const float *v1; int cur_car; const auto& nuc_flag = ndata->nuc_flag; if(ndata->a2 < 0) { ndata->nSeg++; ndata->v_o_last = nullptr; } *(ndata->sptr++) = ndata->nSeg; *(ndata->iptr++) = a; cur_car = ai->cartoon; if(cur_car == cCartoon_auto) cur_car = cCartoon_tube; (*ndata->ss) = ss_t::NUCLEIC; /* DNA/RNA */ if(cur_car == cCartoon_putty) ndata->putty_flag = true; *(ndata->cc++) = cur_car; v1 = cs->coordPtr(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 = nullptr; 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 = nullptr; v_o = nullptr; AtomInfoBracketResidueFast(G, obj->AtomInfo, obj->NAtom, a1, &st, &nd); { int *nf = nullptr; 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->coordPtr(a4); } } else if(a3 == a1) { v_c = cs->coordPtr(a4); } if(WordMatchExact(G, NUCLEIC_NORMAL0, LexStr(G, obj->AtomInfo[a3].name), 1)) { v_o = cs->coordPtr(a4); } } } } if(!(v_c && v_o)) { zero3f(ndata->voptr); ndata->v_o_last = nullptr; } 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 = nullptr; v2 = nullptr; v3 = nullptr; v4 = nullptr; 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 = nullptr; int atom_index1, atom_index2, *s, *atp, a, cur_car; unsigned *vi; int last_color, uniform_color; bool hasAtomLevelTrans = false; int contFlag, extrudeFlag; int b, c1, c2; float *h_start = nullptr, *h_end = nullptr; 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.get(), obj->Setting.get(), 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 = 1.0f - AtomSettingGetWD(G, ai1, cSetting_cartoon_transparency, 1.0f - objAlpha); float alpha2 = 1.0f - AtomSettingGetWD(G, ai2, cSetting_cartoon_transparency, 1.0f - objAlpha); if (!hasAtomLevelTrans && (alpha1 != objAlpha || alpha2 != objAlpha)) { hasAtomLevelTrans = true; } if (c1 < 0) c1 = ai1->color; if (c2 < 0) c2 = ai2->color; if((*(cc) == *(cc + 1)) && (c1 != c2)) 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 = 1.0f - AtomSettingGetWD(G, ai1, cSetting_cartoon_transparency, 1.0f - objAlpha); float alpha2 = 1.0f - AtomSettingGetWD(G, ai2, cSetting_cartoon_transparency, 1.0f - objAlpha); if(!hasAtomLevelTrans && (alpha1 != objAlpha || alpha2 != objAlpha)){ hasAtomLevelTrans = true; } 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 && !hasAtomLevelTrans) { cgo->add(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++) { scale3f(t0, ((float) b) / n_pm1, t1); scale3f(t0, ((float) b + 1) / n_pm1, t2); add3f(t3, t1, t1); add3f(t3, t2, t2); if(hasAtomLevelTrans){ cgo->add(t1, t2, helix_radius, ex->c + (b * 3), ex->c + (b + 1) * 3, ex->alpha[b], ex->alpha[b + 1], (float) (b ? cCylCap::None : cCylCap::Flat), (float) (b == n_pm2 ? cCylCap::Flat : cCylCap::None)); } else { cgo->add(t1, t2, helix_radius, ex->c + (b * 3), ex->c + (b + 1) * 3, (float) (b ? cCylCap::None : cCylCap::Flat), (float) (b == n_pm2 ? cCylCap::Flat : cCylCap::None)); } } } } 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 *marked = pymol::calloc(obj->NAtom); float *moved = pymol::calloc(obj->NAtom * 3); int ring_color; int ok = true; int escape_count; 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.get(), obj->Setting.get(), 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.get(), obj->Setting.get(), cSetting_cartoon_side_chain_helper); ladder_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ladder_mode); ladder_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ladder_radius); ladder_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ladder_color); ring_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ring_radius); if(ladder_color == -1) ladder_color = cartoon_color; ring_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ring_color); if(ring_color == -1) ring_color = cartoon_color; const int* atmToIdx = obj->DiscreteFlag ? nullptr : cs->AtmToIdx.data(); const auto* const neighbor = obj->getNeighborArray(); 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, cCylCap 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, nullptr, 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.get(), obj->Setting.get(), cSetting_cartoon_putty_transform), putty_vals[0], putty_vals[1], putty_vals[2], putty_vals[3], SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_putty_scale_power), SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_putty_range), SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_putty_scale_min), SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_putty_scale_max), sampling / 2); if (ok) ok &= ExtrudeCGOSurfaceVariableTube(ex, cgo, cCylCap::Flat); return ok; } static int CartoonExtrudeCircle(CExtrude *ex, CGO *cgo, short use_cylinders_for_strands, int loop_quality, float loop_radius, cCylCap 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, nullptr, use_cylinders_for_strands, dash); return ok; } /** * Modify `ex` to approximate a curved helix axis and render it as a tube. * * @param[in,out] ex Helix trace which will be converted to helix center trace * @param[in,out] cgo CGO to render to * @param quality Number of circular sampling points * @param radius Cylindrical helix radius * @param sampling Samples per residue */ static void CartoonExtrudeCurvedCylindricalHelix( CExtrude* ex, CGO* cgo, int quality, float radius, int sampling) { ExtrudeShiftToAxis(ex, radius, sampling); ExtrudeCircle(ex, quality, radius); ExtrudeCGOSurfaceTube(ex, cgo, cCylCap::Flat, nullptr, false); } 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, cCylCap::Flat, nullptr); } else { ok = ExtrudeRectangle(ex, width, length, 1); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygon(ex, cgo, cCylCap::None, nullptr); if (ok){ ok &= ExtrudeRectangle(ex, width, length, 2); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygon(ex, cgo, cCylCap::Flat, 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, cCylCap::Flat, nullptr, use_cylinders_for_strands); else ok &= ExtrudeCGOSurfaceTube(ex, cgo, cCylCap::Flat, 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, nullptr); 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, nullptr); } else { ok = ExtrudeDumbbell1(ex, dumbbell_width, dumbbell_length, 1); if (ok) ExtrudeBuildNormals2f(ex); if (ok) ok &= ExtrudeCGOSurfacePolygonTaper(ex, cgo, sampling, nullptr); 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, cCylCap::Flat, nullptr, 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, cCylCap::Flat, nullptr, 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(cs->G, cs->Setting.get(), cs->Obj->Setting.get(), 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->G; int ok = true; CGO *cgo; int contigFlag, contFlag, extrudeFlag, n_p; CExtrude *ex = nullptr; float dev; unsigned int *vi; int atom_index1, atom_index2; float *v, *v1, *v2, *vo; float *d, *vc = nullptr, *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; 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.get(), obj->Setting.get(), cSetting_cartoon_color); ring_width = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ring_width); if(ring_width < 0.0F) { ring_width = fabs(SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_radius)) * 0.5F; } length = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_rect_length); width = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_rect_width); oval_length = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_oval_length); dumbbell_length = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_dumbbell_length); oval_width = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_oval_width); dumbbell_width = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_dumbbell_width); dumbbell_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_dumbbell_radius); if(dumbbell_radius < 0.01F) dumbbell_radius = 0.01F; auto tube_cap = static_cast(SettingGet(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_tube_cap)); auto loop_cap = static_cast(SettingGet(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_loop_cap)); tube_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_tube_radius); if(tube_radius < 0.01F) tube_radius = 0.01F; putty_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), 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.get(), obj->Setting.get(), cSetting_cartoon_refine); power_a = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_power); power_b = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_power_b); throw_ = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_throw); loop_radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_loop_radius); if(loop_radius < 0.01F) loop_radius = 0.01F; discrete_colors = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_discrete_colors); nucleic_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_nucleic_acid_color); if(nucleic_color == -1) nucleic_color = cartoon_color; highlight_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_highlight_color); cylindrical_helices = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_cylindrical_helices); int const sampling_cylindrical_helices = sampling / 8 + 1; sampling_tmp = pymol::malloc(sampling * 3); cartoon_debug = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), 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 == CARTOON_CYLINDRICAL_HELICES_STRAIGHT) { 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; const auto helix_radius = SettingGet( G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_helix_radius); 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); auto const cur_sampling = (cur_car == cCartoon_cylinder) ? sampling_cylindrical_helices : sampling; CartoonGenerateSample(G, cur_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, cur_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; case cCartoon_cylinder: CartoonExtrudeCurvedCylindricalHelix(ex, cgo, loop_quality * 2, helix_radius, sampling_cylindrical_helices); 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); } bool RepCartoon::sameVis() const { if (!LastVisib) return false; for (int idx = 0; idx < cs->NIndex; idx++) { const auto* ai = cs->getAtomInfo(idx); if (LastVisib[idx] != GET_BIT(ai->visRep, cRepCartoon)) { return false; } } return true; } void RepCartoon::invalidate(cRepInv_t level) { if (level >= cRepInvColor){ FreeP(LastVisib); } Rep::invalidate(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(nullptr) { 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, nullptr); 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 = nullptr; 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.get(), obj->Setting.get(), cSetting_cartoon_fancy_sheets); fancy_helices = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_fancy_helices); cylindrical_helices = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_cylindrical_helices); cartoon_side_chain_helper = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_side_chain_helper); int trace_ostate = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_trace_atoms); trace_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_trace_atoms_mode); auto gap_cutoff = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_gap_cutoff); /** * JJ: I think this for loop is a bit ill conceived. Nuc_acid_data * skips over consideration of invisible cartoons, which has historically * worked since it's compensated for later, but this approach doesn't consider * the difference between removed vs hidden residues which can have different * representations. For now, use this flag to track hidden (but valid atoms) cartoons, * which should be skipped and not dashed. Removed residues should be dashed. * See PYMOL-3168 */ bool invisFound = false; // 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))) { invisFound = true; 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 { auto gapRep = invisFound ? cCartoon_skip : cCartoon_dash; (ndata->cc - 1)->setCCOut(gapRep); invisFound = false; } } 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) { if (cylindrical_helices == CARTOON_CYLINDRICAL_HELICES_STRAIGHT) { cur_car = cCartoon_skip_helix; } else if (cylindrical_helices == CARTOON_CYLINDRICAL_HELICES_CURVED) { cur_car = cCartoon_cylinder; } else if (fancy_helices) { cur_car = cCartoon_dumbbell; } else { cur_car = cCartoon_oval; } } (*ndata->ss) = ss_t::HELIX; parity = 0; break; case 'S': case 's': if(cur_car == cCartoon_auto) { cur_car = fancy_sheets ? cCartoon_arrow : cCartoon_rect; } (*ndata->ss) = ss_t::SHEET; parity = !parity; break; default: /* 'L', 'T', 0, etc. */ if(cur_car == cCartoon_auto) { cur_car = cCartoon_loop; } parity = 0; (*ndata->ss) = ss_t::NONE; break; } if(cur_car == cCartoon_putty) ndata->putty_flag = true; // coordinates copy3f(cs->coordPtr(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 == obj->NAtom || (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 = nullptr; v_n = nullptr; v_o = nullptr; // 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->coordPtr(a4); } else if(WordMatchExact(G, "N", a3name, true)) { v_n = cs->coordPtr(a4); } else if(WordMatchExact(G, "O", a3name, true)) { v_o = cs->coordPtr(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, nullptr); /* 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, nullptr); /* 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, const int nAt, const int* sptr, const ss_t* ss, const float* v0, const float* vptr) { const float *v1 = nullptr, *v2 = nullptr, *v3 = nullptr, *v4 = nullptr, *v5 = nullptr; int last, a; float t0[3], t1[3], t2[3]; last = 0; if(nAt > 1) { for(a = 0; a < nAt; a++) { if(a) { if(*sptr != *(sptr - 1)) { /* contiguous helices in disconnected segments */ v1 = nullptr; v2 = nullptr; v3 = nullptr; v4 = nullptr; v5 = nullptr; last = 0; } } v5 = v4; v4 = v3; v3 = v2; v2 = v1; if(*ss == ss_t::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 = nullptr; v2 = nullptr; v3 = nullptr; v4 = nullptr; v5 = nullptr; 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, const int nAt, const int* seg, const float* tv, float *pvo, float *pva, const ss_t* ss, const float* nv) { int refine_normals = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), 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 *va, max_dot; int a; float t0[3], t1[3], t2[3], t3[3], o0[12], o1[12]; float dp; const float *v0, *v1 = tv + 3; ndata->voptr = pvo + 3; const int* 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; for(a = 0; a < nAt; a++) { /* original */ copy3f(ndata->voptr, va); va += 3; /* inverse */ copy3f(ndata->voptr, va); if(*ss != ss_t::HELIX) { 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; auto 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 ss_t* 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.get(), obj->Setting.get(), 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 != ss_t::SHEET) { 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 == ss_t::SHEET) { 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, const ss_t* ss, float *tv) { int *sptr, a; float *v2; float refine_tips; float t0[3]; refine_tips = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_refine_tips); sptr = seg + 1; ++ss; v2 = tv + 3; /* normal */ for(a = 1; a < (nAt - 1); a++) { if((*ss == ss_t::SHEET) && (*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, const int nAt, const int* seg, float* pv, const ss_t* ss, float* pvo, const float* /* tv */, float* tmp, const int* flag_tmp) { int 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.get(), obj->Setting.get(), cSetting_cartoon_smooth_first); smooth_last = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_smooth_last); smooth_cycles = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_smooth_cycles); auto* sptr = seg; 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 != ss_t::NONE) { 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 == ss_t::NONE) { if(first < 0) first = a; last = a; } ss++; sptr++; } } } Rep *RepCartoonNew(CoordSet * cs, int state) { PyMOLGlobals *G = cs->G; ObjectMolecule *obj; int *i, *sptr, *at, *seg, nAt; CCInOut *car, *cc; float *pv = nullptr; float *pvo = nullptr, *pva = nullptr; float *dv = nullptr; float *nv = nullptr; float *tv = nullptr; float *tmp = nullptr; float *dl = nullptr; 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 = nullptr; int *nuc_flag = nullptr; 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 nullptr; /* THIS IS BY FAR THE WORST ROUTINE IN PYMOL! * DEVELOP ON IT ONLY AT EXTREME RISK TO YOUR MENTAL HEALTH */ auto I = new RepCartoon(cs, state); PRINTFD(G, FB_RepCartoon) " RepCartoonNew-Debug: entered.\n" ENDFD; obj = cs->Obj; alpha = 1.0F - SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_transparency); round_helices = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_round_helices); na_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_nucleic_acid_mode); ladder_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_ladder_mode); /* find all of the CA points */ auto const nAtIndex = obj->NAtom; // was NAtIndex at = pymol::malloc(nAtIndex); /* cs index pointers */ pv = pymol::malloc(nAtIndex * 3); tmp = pymol::malloc(nAtIndex * 3); pvo = pymol::malloc(nAtIndex * 3); /* orientation vector */ pva = pymol::malloc(nAtIndex * 6); /* alternative orientation vectors, two per atom */ seg = pymol::malloc(nAtIndex); car = pymol::calloc(nAtIndex); /* cartoon type for each atom */ auto sstype = pymol::malloc(nAtIndex); flag_tmp = pymol::calloc(nAtIndex); nuc_flag = pymol::calloc(nAtIndex); I->LastVisib = pymol::calloc(nAtIndex); auto cartoon_all_alt = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_all_alt); ndata.next_alt = 0; do { ndata.alt = ndata.next_alt; ndata.next_alt = 0; memset(car, 0, sizeof(*car) * nAtIndex); memset(sstype, 0, sizeof(*sstype) * nAtIndex); memset(flag_tmp, 0, sizeof(*flag_tmp) * nAtIndex); memset(nuc_flag, 0, sizeof(*nuc_flag) * nAtIndex); i = at; sptr = seg; cc = car; auto* ss = sstype; nAt = 0; ndata.na_mode = na_mode; ndata.nuc_flag = nuc_flag; ndata.a2 = -1; ndata.nSeg = 0; ndata.v_o_last = nullptr; 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.get(), obj->Setting.get(), cSetting_cartoon_ring_mode); ndata.ring_finder = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), 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, 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 = pymol::malloc(nAt * 3); /* differences between next and current 3f */ nv = pymol::malloc(nAt * 3); /* normal */ dl = pymol::malloc(nAt); /* length (i.e., normal * length = difference) */ RepCartoonComputeDifferencesAndNormals(G, nAt, seg, pv, dv, nv, dl, true); /* compute tangents */ tv = pymol::malloc(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.get(), obj->Setting.get(), cSetting_cartoon_smooth_loops); bool cartoon_flat_sheets = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), 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); } } } } CGO* preshadercgo = GenerateRepCartoonCGO(cs, obj, &ndata, na_strands_as_cylinders, pv, nAt, tv, pvo, dl, car, seg, at, nuc_flag, putty_vals, alpha); if (preshadercgo && preshadercgo->has_begin_end) { CGOCombineBeginEnd(&preshadercgo); } if (I->preshader) { I->preshader->free_append(preshadercgo); } else { I->preshader = preshadercgo; } } while (ndata.next_alt && cartoon_all_alt); CHECKOK(ok, I->preshader); ok &= !G->Interrupt; if (!ok || !CGOHasOperations(I->preshader)) { /* cannot generate RepCartoon */ delete I; I = nullptr; } 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; }