/* A* ------------------------------------------------------------------- B* This file contains source code for the PyMOL computer program C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. D* ------------------------------------------------------------------- E* It is unlawful to modify or remove this copyright notice. F* ------------------------------------------------------------------- G* Please see the accompanying LICENSE file for further information. H* ------------------------------------------------------------------- I* Additional authors of this source file include: -* -* -* Z* ------------------------------------------------------------------- */ #include "os_gl.h" #include "os_predef.h" #include "os_python.h" #include "os_std.h" #include "Base.h" #include "CGO.h" #include "Color.h" #include "CoordSet.h" #include "Err.h" #include "Feedback.h" #include "Map.h" #include "MemoryDebug.h" #include "ObjectMolecule.h" #include "P.h" #include "PConv.h" #include "Rep.h" #include "RepSurface.h" #include "Scene.h" #include "Selector.h" #include "Setting.h" #include "ShaderMgr.h" #include "Sphere.h" #include "Triangle.h" #include "Util.h" #include "Vector.h" #include "main.h" #ifdef NT #undef NT #endif enum class SurfaceType { Solid = 0, DotDefault = 1, Triangle = 2, SolidDeterministic = 3, SolidWeightings = 4, SolidScribed = 5, SolidEmpirical = 6 }; struct RepSurface : Rep { using Rep::Rep; ~RepSurface() override; cRep_t type() const override { return cRepSurface; } void render(RenderInfo* info) override; void invalidate(cRepInv_t level) override; Rep* recolor() override; bool sameVis() const override; bool sameColor() const override; int N = 0; int NT = 0; //!< number of triangles (?) bool proximity = false; //!< cSetting_surface_proximity std::vector V; //!< Verts std::vector VN; //!< Normals std::vector VC; //!< Colors std::vector VA; //!< Alpha std::vector VAO; //!< Ambient Occlusion per vertex std::vector RC; std::vector Vis; std::vector T; //!< vertices (of triangles?) std::vector S; //!< strips std::vector AT; //!< closest atom for vertices bool oneColorFlag = false; int oneColor; bool allVisibleFlag = false; char* LastVisib = nullptr; int* LastColor = nullptr; bool ColorInvalidated = false; SurfaceType Type; float max_vdw; /* These variables are for using the shader. All of them */ /* are allocated/set when generate_shader_cgo to minimize */ /* allocation during the rendering loop. */ CGO* shaderCGO = nullptr; CGO* pickingCGO = nullptr; bool dot_as_spheres = false; int surface_mode = cRepSurface_by_flags; }; static void RepSurfaceSmoothEdges(RepSurface* I); static void setShaderCGO(RepSurface* I, CGO* cgo) { if (I->shaderCGO != I->pickingCGO) { CGOFree(I->shaderCGO); } I->shaderCGO = cgo; } static void setPickingCGO(RepSurface* I, CGO* cgo) { if (I->shaderCGO != I->pickingCGO) { CGOFree(I->pickingCGO); } I->pickingCGO = cgo; } RepSurface::~RepSurface() { setPickingCGO(this, nullptr); setShaderCGO(this, nullptr); } struct SolventDot { int nDot{}; float* dot{}; float* dotNormal{}; int* dotCode{}; }; struct SurfaceJobAtomInfo { float vdw; int flags; }; static SolventDot* SolventDotNew(PyMOLGlobals* G, float* coord, std::vector& atom_info, float probe_radius, SphereRec* sp, int* present, int circumscribe, int surface_mode, int surface_solvent, int cavity_cull, int all_visible_flag, float max_vdw, int cavity_mode, float cavity_radius, float cavity_cutoff); static void SolventDotFree(SolventDot* I) { if (I) { VLAFreeP(I->dot); VLAFreeP(I->dotNormal); VLAFreeP(I->dotCode); } DeleteP(I); } #ifndef PURE_OPENGL_ES_2 static void immediate_draw_masked_vertices(const float* vc, // colors const float* vn, // normals const float* v, // vertices const int* mask, // mask int count) { for (int i = 0; i < count; ++i) { if (!mask[i]) continue; int i3 = i * 3; if (vc) glColor3fv(vc + i3); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } static void immediate_draw_indexed_vertices(const float* vc, // colors const float* vn, // normals const float* v, // vertices const int* indices, // indices int count) { for (int i = 0; i < count; ++i) { int i3 = indices[i] * 3; if (vc) glColor3fv(vc + i3); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } static void immediate_draw_indexed_vertices_alpha(const float* vc, // colors const float* va, // alpha array float alpha, // alpha value if va is nullptr const float* vn, // normals const float* v, // vertices const int* indices, // indices int count) { for (int i = 0; i < count; ++i) { int i3 = indices[i] * 3; if (vc) glColor4f(vc[i3], vc[i3 + 1], vc[i3 + 2], va ? va[indices[i]] : alpha); if (vn) glNormal3fv(vn + i3); glVertex3fv(v + i3); } } #endif static bool visibility_test(bool proximityFlag, const int* vi, // visibility array const int* t) // indices { if (proximityFlag) return (vi[t[0]] || vi[t[1]] || vi[t[2]]); return (vi[t[0]] && vi[t[1]] && vi[t[2]]); } static int check_and_add(int* cache, int spacing, int t0, int t1) { int* rec; int cnt; t0++; t1++; rec = cache + spacing * t0; cnt = spacing; while (cnt > 0) { if (*rec == t1) return 1; if (!*rec) { *rec = t1; break; } rec++; cnt--; } rec = cache + spacing * t1; cnt = spacing; while (cnt > 0) { if (*rec == t0) return 1; if (!*rec) { *rec = t0; break; } rec++; cnt--; } return 0; } #define CLAMP_VALUE(v) ((v > 1.f) ? 1.f : (v < 0.f) ? 0.f : v) static int AtomInfoIsMasked(ObjectMolecule* obj, int atm) { AtomInfoType* ait; if (atm < 0) return cPickableNoPick; ait = &obj->AtomInfo[atm]; return (ait->masked ? cPickableNoPick : cPickableAtom); } static int RepSurfaceCGOGenerate(RepSurface* I, RenderInfo* info) { PyMOLGlobals* G = I->G; float* v = I->V.data(); float* vn = I->VN.data(); float* vc = I->VC.data(); float* va = I->VA.data(); int* t = I->T.data(); int* s = I->S.data(); int c = I->N; int* vi = I->Vis.data(); int* at = I->AT.data(); int ok = true; float alpha; int t_mode; CGO* convertcgo = nullptr; auto* const cs = I->cs; auto* const obj = I->cs->Obj; bool pick_surface = SettingGet_b( G, cs->Setting.get(), obj->Setting.get(), cSetting_pick_surface); short dot_as_spheres = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_as_spheres); alpha = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_transparency); alpha = 1.0F - alpha; if (fabs(alpha - 1.0) < R_SMALL4) alpha = 1.0F; setPickingCGO(I, nullptr); setShaderCGO(I, CGONew(G)); if (!I->shaderCGO) return false; I->shaderCGO->use_shader = true; I->dot_as_spheres = dot_as_spheres; if (alpha < 1) { I->setHasTransparency(); } if (I->Type == SurfaceType::DotDefault) { /* no triangle information, so we're rendering dots only */ int normals = SettingGet( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_normals); if (!normals) { CGOResetNormal(I->shaderCGO, true); } if ((alpha != 1.0)) { CGOAlpha(I->shaderCGO, alpha); } if (dot_as_spheres) { if (c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); if (ok && I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); } while (ok && c--) { if (*vi) { if (!I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, vc); } if (ok && normals) ok &= CGONormalv(I->shaderCGO, vn); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, *at, AtomInfoIsMasked(obj, *at)); if (ok) ok &= CGOSphere(I->shaderCGO, v, 1.f); } vi++; vc += 3; vn += 3; v += 3; at++; } } } else { ok &= CGODotwidth( I->shaderCGO, SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_width)); if (ok && c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); ok &= CGOBegin(I->shaderCGO, GL_POINTS); if (ok && I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); } while (ok && c--) { if (*vi) { if (!I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, vc); } if (normals) { CGONormalv(I->shaderCGO, vn); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, *at, AtomInfoIsMasked(obj, *at)); if (ok) ok &= CGOVertexv(I->shaderCGO, v); } vi++; vc += 3; vn += 3; v += 3; at++; } if (ok) ok &= CGOEnd(I->shaderCGO); } } } else if (I->Type == SurfaceType::Triangle) { int normals = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_normals); if (ok && !normals) { ok &= CGOResetNormal(I->shaderCGO, true); } if (ok) { float line_width = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_width); line_width = SceneGetDynamicLineWidth(info, line_width); ok &= CGOSpecial(I->shaderCGO, LINEWIDTH_DYNAMIC_MESH); c = I->NT; if (ok && c) { if (I->oneColorFlag) { ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { if (normals) { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } else { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = *t; if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } } else t += 3; } } else { /* not oneColorFlag */ while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { if (normals) { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } else { int idx; ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); idx = (*(t + 2)); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; idx = (*t); if (ok) ok &= CGOColorv(I->shaderCGO, vc + idx * 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + idx * 3); t++; if (ok) ok &= CGOEnd(I->shaderCGO); } } else t += 3; } } } } } else { /* we're rendering triangles */ if ((alpha != 1.0) || va) { t_mode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_transparency_mode); if (info && info->alpha_cgo) { t_mode = 0; } if (t_mode) { float **t_buf = nullptr, **tb; float *z_value = nullptr, *zv; int* ix = nullptr; int n_tri = 0; float sum[3]; float matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, matrix); if (I->oneColorFlag) { t_buf = pymol::malloc(I->NT * 6); } else { t_buf = pymol::malloc(I->NT * 12); } CHECKOK(ok, t_buf); if (ok) { z_value = pymol::malloc(I->NT); CHECKOK(ok, z_value); } if (ok) { ix = pymol::malloc(I->NT); CHECKOK(ok, ix); } zv = z_value; tb = t_buf; c = I->NT; if (ok) { if (I->oneColorFlag) { while (c--) { if (visibility_test(I->proximity, vi, t)) { *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-3], sum); add3f(sum, tb[-5], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } else { while (c--) { if (visibility_test(I->proximity, vi, t)) { if (va) *(tb++) = va + (*t); else *(tb++) = α *(tb++) = vc + (*t) * 3; *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; if (va) *(tb++) = va + (*(t + 1)); else *(tb++) = α *(tb++) = vc + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; if (va) *(tb++) = va + (*(t + 2)); else *(tb++) = α *(tb++) = vc + (*(t + 2)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-5], sum); add3f(sum, tb[-9], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } } switch (t_mode) { case 1: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, true); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */ break; default: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, false); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */ break; } c = n_tri; if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok) { CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); for (c = 0; ok && c < n_tri; c++) { int idx; tb = t_buf + 6 * c; // tb = t_buf + 6 * ix[c]; if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); } if (ok) ok &= CGOEnd(I->shaderCGO); } } else { /* else I->oneColorFlag */ if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); for (c = 0; ok && c < n_tri; c++) { float *vv, *v_alpha; int idx; tb = t_buf + 12 * c; /* need to update index every frame */ // tb = t_buf + 12 * ix[c]; v_alpha = *(tb++); vv = *(tb++); ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); v_alpha = *(tb++); vv = *(tb++); if (ok) ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); v_alpha = *(tb++); vv = *(tb++); if (ok) ok &= CGOAlpha(I->shaderCGO, *v_alpha); if (ok) ok &= CGOColor(I->shaderCGO, vv[0], vv[1], vv[2]); if (ok) ok &= CGONormalv(I->shaderCGO, *(tb++)); idx = ((*tb - v) / 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[idx], AtomInfoIsMasked(obj, I->AT[idx])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[idx]); } if (ok) ok &= CGOVertexv(I->shaderCGO, *(tb++)); } if (ok) ok &= CGOEnd(I->shaderCGO); } FreeP(ix); FreeP(z_value); FreeP(t_buf); } else if (ok) { if (info->alpha_cgo) { /* global transparency sort */ if (I->allVisibleFlag) { if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); c = *(s++); while (c) { int parity = 0; s += 2; while (ok && c--) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col, col, col, alpha, alpha, alpha, parity); s++; parity = !parity; } c = *(s++); } } else { c = *(s++); while (ok && c) { float *col0, *col1, *col2; int parity = 0; float alpha0, alpha1, alpha2; col0 = vc + (*s) * 3; if (va) { alpha0 = va[(*s)]; } else { alpha0 = alpha; } s++; col1 = vc + (*s) * 3; if (va) { alpha1 = va[(*s)]; } else { alpha1 = alpha; } s++; while (ok && c--) { col2 = vc + (*s) * 3; if (va) { alpha2 = va[(*s)]; } else { alpha2 = alpha; } ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col0, col1, col2, alpha0, alpha1, alpha2, parity); alpha0 = alpha1; alpha1 = alpha2; col0 = col1; col1 = col2; s++; parity = !parity; } c = *(s++); } } } else if (ok) { /* subset s */ c = I->NT; if (c) { if (I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, color, color, color, alpha, alpha, alpha, 0); } t += 3; } } else { while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { if (va) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, va[t[0]], va[t[1]], va[t[2]], 0); } else { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, alpha, alpha, alpha, 0); } } t += 3; } } if (ok) CGOEnd(info->alpha_cgo); } } } else if (ok) { /* fast and ugly */ /* glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glDepthMask(GL_FALSE); */ if (I->allVisibleFlag) { if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); c = *(s++); while (ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while (ok && c--) { ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } else { /* I->oneColorFlag */ c = *(s++); while (ok && c) { float* col; if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); col = vc + (*s) * 3; if (va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; col = vc + (*s) * 3; if (va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while (ok && c--) { col = vc + (*s) * 3; if (va) { ok &= CGOAlpha(I->shaderCGO, va[(*s)]); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } else { ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, col[0], col[1], col[2]); } if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } } else { /* subset s */ c = I->NT; if (ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); if (I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); if (ok) ok &= CGOColor(I->shaderCGO, color[0], color[1], color[2]); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } else { float* col; while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { col = vc + (*t) * 3; if (va) { ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; col = vc + (*t) * 3; if (va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; col = vc + (*t) * 3; if (va) { if (ok) ok &= CGOAlpha(I->shaderCGO, va[(*t)]); } else { if (ok) ok &= CGOAlpha(I->shaderCGO, alpha); } if (ok) ok &= CGOColorv(I->shaderCGO, col); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } } } /* glDisable(GL_CULL_FACE); glDepthMask(GL_TRUE); */ } } else if (ok) { /* opaque */ if (I->allVisibleFlag) { if (I->oneColorFlag) { if (ok) { CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); c = *(s++); while (ok && c) { if (ok) ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while (ok && c--) { ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } } else { /* not one color */ { c = *(s++); while (ok && c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP); if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; while (ok && c--) { ok &= CGOColorv(I->shaderCGO, vc + (*s) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*s) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*s]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*s], AtomInfoIsMasked(obj, I->AT[*s])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*s) * 3); s++; } if (ok) ok &= CGOEnd(I->shaderCGO); c = *(s++); } } } /* one color */ } else if (ok) { /* subsets */ c = I->NT; if (ok && c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); if (I->oneColorFlag) { if (ok) ok &= CGOColorv(I->shaderCGO, ColorGet(G, I->oneColor)); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } else { while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGOColorv(I->shaderCGO, vc + (*t) * 3); if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor(I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } } /* if (use_shader) { CShaderPrg_Disable(shaderPrg); }*/ } } if (ok && SettingGetGlobal_i(G, cSetting_surface_debug)) { t = I->T.data(); c = I->NT; if (c) { ok &= CGOBegin(I->shaderCGO, GL_TRIANGLES); while (ok && c--) { if (I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && !I->VAO.empty()) { ok &= CGOAccessibility(I->shaderCGO, I->VAO[*t]); } if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else { t += 3; } } if (ok) ok &= CGOEnd(I->shaderCGO); } t = I->T.data(); c = I->NT; if (ok && c) { ok &= CGOColor(I->shaderCGO, 0.0, 1.0, 0.0); if (ok) ok &= CGODotwidth(I->shaderCGO, 1.0F); while (ok && c--) { ok &= CGOBegin(I->shaderCGO, GL_LINE_STRIP); if (I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; if (ok) ok &= CGONormalv(I->shaderCGO, vn + (*t) * 3); if (ok && pick_surface) ok &= CGOPickColor( I->shaderCGO, I->AT[*t], AtomInfoIsMasked(obj, I->AT[*t])); if (ok) ok &= CGOVertexv(I->shaderCGO, v + (*t) * 3); t++; } else { t += 3; } if (ok) ok &= CGOEnd(I->shaderCGO); } } c = I->N; if (ok && c) { ok &= CGOColor(I->shaderCGO, 1.0, 0.0, 0.0); if (ok) ok &= CGOResetNormal(I->shaderCGO, true); if (ok) ok &= CGOBegin(I->shaderCGO, GL_LINES); while (ok && c--) { ok &= CGOVertexv(I->shaderCGO, v); if (ok) ok &= CGOVertex(I->shaderCGO, v[0] + vn[0] / 2, v[1] + vn[1] / 2, v[2] + vn[2] / 2); v += 3; vn += 3; } if (ok) ok &= CGOEnd(I->shaderCGO); } } if (ok) ok &= CGOStop(I->shaderCGO); if (I->Type != SurfaceType::Triangle) { CGOCombineBeginEnd(&I->shaderCGO); } if (I->Type == SurfaceType::DotDefault) { if (dot_as_spheres) { CGO* tmpCGO = CGONew(G); CHECKOK(ok, tmpCGO); ok &= CGOEnable(tmpCGO, GL_SPHERE_SHADER); ok &= CGOEnable(tmpCGO, GL_DOT_LIGHTING); // TODO: this needs normals in // sphere shader PYMOL-1870 ok &= CGOSpecial(tmpCGO, DOTSIZE_WITH_SPHERESCALE); convertcgo = CGOOptimizeSpheresToVBONonIndexedNoShader(I->shaderCGO, CGO_BOUNDING_BOX_SZ + fsizeof() + 2); ok &= CGOAppendNoStop(tmpCGO, convertcgo); CGOFreeWithoutVBOs(convertcgo); ok &= CGODisable(tmpCGO, GL_SPHERE_SHADER); CGOStop(tmpCGO); convertcgo = tmpCGO; } else { convertcgo = CGOOptimizeToVBONotIndexedNoShader(I->shaderCGO); } CHECKOK(ok, convertcgo); if (ok) convertcgo->use_shader = true; } else if (I->Type == SurfaceType::Triangle) { CGO* tmpCGO = CGONew(G); CHECKOK(ok, tmpCGO); auto convertcgo2 = CGOConvertLinesToShaderCylinders(I->shaderCGO, 0); CHECKOK(ok, convertcgo2); CGOEnable(tmpCGO, GL_CYLINDER_SHADER); CGOSpecial(tmpCGO, MESH_WIDTH_FOR_SURFACES); convertcgo = CGOConvertShaderCylindersToCylinderShader(convertcgo2, tmpCGO); CGOAppendNoStop(tmpCGO, convertcgo); CGOFreeWithoutVBOs(convertcgo); CGODisable(tmpCGO, GL_CYLINDER_SHADER); CGOStop(tmpCGO); convertcgo = tmpCGO; convertcgo->use_shader = true; CGOFree(convertcgo2); } else { if ((alpha != 1.0) || va) { // semi-transparent if (ok) convertcgo = CGOOptimizeToVBOIndexedWithColorEmbedTransparentInfo( I->shaderCGO, 0, 0, 0); CHECKOK(ok, convertcgo); } else { if (ok) convertcgo = CGOOptimizeToVBONotIndexed(I->shaderCGO, 0, false); CHECKOK(ok, convertcgo); } if (ok) convertcgo->use_shader = true; { CGO* tmpCGO = nullptr; tmpCGO = CGONew(G); CGOEnable(tmpCGO, GL_SURFACE_SHADER); // CGOEnable(tmpCGO, CGO_GL_LIGHTING); // do we need this? CGOSpecial(tmpCGO, SET_SURFACE_UNIFORMS); CGOAppendNoStop(tmpCGO, convertcgo); CGODisable(tmpCGO, GL_SURFACE_SHADER); CGOStop(tmpCGO); CGOFreeWithoutVBOs(convertcgo); convertcgo = tmpCGO; convertcgo->use_shader = true; } } if (ok && I->Type == SurfaceType::DotDefault && !dot_as_spheres) { setShaderCGO(I, CGONew(G)); CHECKOK(ok, I->shaderCGO); if (ok) { I->shaderCGO->use_shader = true; if (ok) ok &= CGOResetNormal(I->shaderCGO, true); if (ok) ok &= CGOEnable(I->shaderCGO, GL_SURFACE_SHADER); if (ok) ok &= CGOSpecial(I->shaderCGO, POINTSIZE_DYNAMIC_DOT_WIDTH); if (ok) CGOAppendNoStop(I->shaderCGO, convertcgo); if (ok) ok &= CGODisable(I->shaderCGO, GL_SURFACE_SHADER); if (ok) ok &= CGOStop(I->shaderCGO); CGOFreeWithoutVBOs(convertcgo); convertcgo = nullptr; } } else { setShaderCGO(I, convertcgo); convertcgo = nullptr; } if (I->shaderCGO) { I->shaderCGO->no_pick = !pick_surface; setPickingCGO(I, I->shaderCGO); } return ok; } void RepSurface::render(RenderInfo* info) { auto* I = this; CRay* ray = info->ray; auto pick = info->pick; float* v = I->V.data(); float* vn = I->VN.data(); float* vc = I->VC.data(); float* va = I->VA.data(); int* rc = I->RC.data(); int* t = I->T.data(); int* s = I->S.data(); int c = I->N; int* vi = I->Vis.data(); int ok = true; float ambient_occlusion_scale = 0.f; int ambient_occlusion_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_ambient_occlusion_mode); int ambient_occlusion_mode_div_4 = 0; if (ambient_occlusion_mode) { ambient_occlusion_scale = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_ambient_occlusion_scale); ambient_occlusion_mode_div_4 = ambient_occlusion_mode / 4; } if ((I->Type != SurfaceType::DotDefault) && (!s)) { return; } auto alpha = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_transparency); alpha = 1.0F - alpha; if (fabs(alpha - 1.0) < R_SMALL4) alpha = 1.0F; if (ray) { #ifndef _PYMOL_NO_RAY ray->transparentf(1.0F - alpha); if (I->Type == SurfaceType::DotDefault) { /* dot surface */ float radius; radius = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_radius); if (radius == 0.0F) { radius = ray->PixelRadius * SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_width) / 1.4142F; } if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); ray->color3fv(col); } if (c) while (ok && c--) { if (*vi) { if (!I->oneColorFlag) { ray->color3fv(vc); } ok &= ray->sphere3fv(v, radius); } vi++; vc += 3; v += 3; } } else if ((I->Type == SurfaceType::Solid) || (I->Type == SurfaceType::SolidDeterministic) || (I->Type == SurfaceType::SolidWeightings) || (I->Type == SurfaceType::SolidScribed)) { /* solid surface */ c = I->NT; if (I->oneColorFlag) { float col[3], col1[3], col2[3], col3[3]; ColorGetEncoded(G, I->oneColor, col); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { copy3f(col, col1); copy3f(col, col2); copy3f(col, col3); if (!I->VAO.empty()) { float ao1, ao2, ao3; switch (ambient_occlusion_mode_div_4) { case 1: ao1 = 1.f - ambient_occlusion_scale * (I->VAO[*t]); ao2 = 1.f - ambient_occlusion_scale * (I->VAO[*(t + 1)]); ao3 = 1.f - ambient_occlusion_scale * (I->VAO[*(t + 2)]); break; case 2: ao1 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale * (I->VAO[*t]))); ao2 = cos( .5f * PI * CLAMP_VALUE(ambient_occlusion_scale * (I->VAO[*(t + 1)]))); ao3 = cos( .5f * PI * CLAMP_VALUE(ambient_occlusion_scale * (I->VAO[*(t + 2)]))); break; default: ao1 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*t])) - 10.f)))); ao2 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*(t + 1)])) - 10.f)))); ao3 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*(t + 2)])) - 10.f)))); } mult3f(col1, ao1, col1); mult3f(col2, ao2, col2); mult3f(col3, ao3, col3); } ok &= ray->triangle3fv(v + (*t) * 3, v + (*(t + 1)) * 3, v + (*(t + 2)) * 3, vn + (*t) * 3, vn + (*(t + 1)) * 3, vn + (*(t + 2)) * 3, col1, col2, col3); } t += 3; } } else { while (ok && c--) { int ttA = *t, ttB = *(t + 1), ttC = *(t + 2); if (visibility_test(I->proximity, vi, t)) { int ttA3 = ttA * 3, ttB3 = ttB * 3, ttC3 = ttC * 3; float cA[3], cB[3], cC[3]; copy3f(vc + ttA3, cA); copy3f(vc + ttB3, cB); copy3f(vc + ttC3, cC); // register float *cA = vc + ttA3, *cB = vc + ttB3, *cC = // vc + ttC3; if (rc) { if (rc[ttA] < -1) ColorGetEncoded(G, rc[ttA], cA); // ColorGetEncoded(G, rc[ttA], (cA = colA)); if (rc[ttB] < -1) ColorGetEncoded(G, rc[ttB], cB); // ColorGetEncoded(G, rc[ttB], (cB = colB)); if (rc[ttC] < -1) ColorGetEncoded(G, rc[ttC], cC); // ColorGetEncoded(G, rc[ttC], (cC = colC)); } if ((*(vi + ttA)) || (*(vi + ttB)) || (*(vi + ttC))) { if (!I->VAO.empty()) { float ao1, ao2, ao3; switch (ambient_occlusion_mode_div_4) { case 1: ao1 = 1.f - ambient_occlusion_scale * (I->VAO[*t]); ao2 = 1.f - ambient_occlusion_scale * (I->VAO[*(t + 1)]); ao3 = 1.f - ambient_occlusion_scale * (I->VAO[*(t + 2)]); break; case 2: ao1 = cos(.5f * PI * CLAMP_VALUE(ambient_occlusion_scale * (I->VAO[*t]))); ao2 = cos(.5f * PI * CLAMP_VALUE( ambient_occlusion_scale * (I->VAO[*(t + 1)]))); ao3 = cos(.5f * PI * CLAMP_VALUE( ambient_occlusion_scale * (I->VAO[*(t + 2)]))); break; default: ao1 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*t])) - 10.f)))); ao2 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*(t + 1)])) - 10.f)))); ao3 = CLAMP_VALUE( 1.f / (1.f + exp(.5f * ((ambient_occlusion_scale * (I->VAO[*(t + 2)])) - 10.f)))); } mult3f(cA, ao1, cA); mult3f(cB, ao2, cB); mult3f(cC, ao3, cC); } if (va) { ok &= ray->triangleTrans3fv(v + ttA3, v + ttB3, v + ttC3, vn + ttA3, vn + ttB3, vn + ttC3, cA, cB, cC, 1.0F - va[ttA], 1.0F - va[ttB], 1.0F - va[ttC]); } else { ok &= ray->triangle3fv(v + ttA3, v + ttB3, v + ttC3, vn + ttA3, vn + ttB3, vn + ttC3, cA, cB, cC); } } } t += 3; } } } else if (I->Type == SurfaceType::Triangle) { float radius; int t0, t1, t2; int spacing = 10; int* cache = pymol::calloc(spacing * (I->N + 1)); CHECKOK(ok, cache); radius = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_radius); if (ok && radius == 0.0F) { float line_width = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_width); line_width = SceneGetDynamicLineWidth(info, line_width); radius = ray->PixelRadius * line_width / 2.0F; } if (ok) { float col[3], *col0, *col1, *col2; c = I->NT; if (I->oneColorFlag) { ColorGetEncoded(G, I->oneColor, col); col0 = col1 = col2 = col; } while (ok && c--) { t0 = (*t); t1 = (*(t + 1)); t2 = (*(t + 2)); if (visibility_test(I->proximity, vi, t)) { if (!I->oneColorFlag) { col0 = vc + t0 * 3; col1 = vc + t1 * 3; col2 = vc + t2 * 3; } if (!check_and_add(cache, spacing, t0, t1)) ok &= ray->sausage3fv(v + t0 * 3, v + t1 * 3, radius, col0, col1); if (!check_and_add(cache, spacing, t1, t2)) ok &= ray->sausage3fv(v + t1 * 3, v + t2 * 3, radius, col1, col2); if (!check_and_add(cache, spacing, t2, t0)) ok &= ray->sausage3fv(v + t2 * 3, v + t0 * 3, radius, col2, col0); } t += 3; } FreeP(cache); } } if (ok) { ray->transparentf(0.0); } else { /* If not ok, then Clear Entire RepSurface, not just the ray object */ } #endif } else if (G->HaveGUI && G->ValidContext) { /* Not ray tracing, but rendering */ if (pick) { // Don't render transparent unpickable surfaces. Do render solid but // unpickable surfaces to write the depth buffer and prevent // through-picking. if (I->pickingCGO && !(I->pickingCGO->no_pick && alpha < 1.F)) { CGORenderPicking(I->pickingCGO, info, &I->context, cs->Setting.get(), obj->Setting.get()); } } else { #ifndef PURE_OPENGL_ES_2 bool use_shader = SettingGetGlobal_b(G, cSetting_surface_use_shader) && SettingGetGlobal_b(G, cSetting_use_shaders); if (use_shader && !info->alpha_cgo) #endif { bool dot_as_spheres = SettingGet_b( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_as_spheres); if (!I->shaderCGO || CGOCheckWhetherToFree(G, I->shaderCGO) || (I->Type == SurfaceType::DotDefault && I->dot_as_spheres != dot_as_spheres)) { ok &= RepSurfaceCGOGenerate(I, info); } if (ok && I->shaderCGO) { const float* color = ColorGet(G, obj->Color); CGORender(I->shaderCGO, color, nullptr, nullptr, info, I); return; } PRINTFB(G, FB_RepSurface, FB_Errors) " RepSurfaceCGOGenerate failed\n" ENDFB(G); } setShaderCGO(I, nullptr); #ifndef PURE_OPENGL_ES_2 bool two_sided_lighting = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_two_sided_lighting) > 0; if (two_sided_lighting) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); } if (I->Type == SurfaceType::DotDefault) { /* no triangle information, so we're rendering dots only */ { int normals = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_normals); int lighting = info->line_lighting || SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_lighting); if (!normals) { SceneResetNormal(G, true); vn = nullptr; } if (!lighting) glDisable(GL_LIGHTING); glPointSize(SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_dot_width)); if (c) { glBegin(GL_POINTS); if (I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = nullptr; } else { glColor3f(1.0, 0.0, 0.0); } immediate_draw_masked_vertices(vc, vn, v, vi, c); glEnd(); } if (!lighting) glEnable(GL_LIGHTING); } /* else use shader */ } else if (I->Type == SurfaceType::Triangle) { if (ok) { c = I->NT; if (ok && c) { int normals = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_normals); int lighting = info->line_lighting || SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_lighting); if (!normals) { SceneResetNormal(G, true); vn = nullptr; } if (!lighting) glDisable(GL_LIGHTING); float line_width = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_mesh_width); glLineWidth(SceneGetDynamicLineWidth(info, line_width)); if (I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = nullptr; } glBegin(GL_LINES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { int indices[] = {t[0], t[1], t[1], t[2], t[2], t[0]}; immediate_draw_indexed_vertices(vc, vn, v, indices, 6); } } glEnd(); if (!lighting) glEnable(GL_LIGHTING); } } } else { /* we're rendering triangles */ if ((alpha != 1.0) || va) { auto t_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_transparency_mode); if (info && info->alpha_cgo) { t_mode = 0; } if (t_mode) { float **t_buf = nullptr, **tb; float *z_value = nullptr, *zv; int* ix = nullptr; int n_tri = 0; float sum[3]; float matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, matrix); if (I->oneColorFlag) { t_buf = pymol::malloc(I->NT * 6); } else { t_buf = pymol::malloc(I->NT * 12); } CHECKOK(ok, t_buf); if (ok) { z_value = pymol::malloc(I->NT); CHECKOK(ok, z_value); } if (ok) { ix = pymol::malloc(I->NT); CHECKOK(ok, ix); } zv = z_value; tb = t_buf; c = I->NT; if (ok) { if (I->oneColorFlag) { while (c--) { if (visibility_test(I->proximity, vi, t)) { *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-3], sum); add3f(sum, tb[-5], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } else { while (c--) { if (visibility_test(I->proximity, vi, t)) { if (va) *(tb++) = va + (*t); else *(tb++) = α *(tb++) = vc + (*t) * 3; *(tb++) = vn + (*t) * 3; *(tb++) = v + (*t) * 3; if (va) *(tb++) = va + (*(t + 1)); else *(tb++) = α *(tb++) = vc + (*(t + 1)) * 3; *(tb++) = vn + (*(t + 1)) * 3; *(tb++) = v + (*(t + 1)) * 3; if (va) *(tb++) = va + (*(t + 2)); else *(tb++) = α *(tb++) = vc + (*(t + 2)) * 3; *(tb++) = vn + (*(t + 2)) * 3; *(tb++) = v + (*(t + 2)) * 3; add3f(tb[-1], tb[-5], sum); add3f(sum, tb[-9], sum); *(zv++) = matrix[2] * sum[0] + matrix[6] * sum[1] + matrix[10] * sum[2]; n_tri++; } t += 3; } } } switch (t_mode) { case 1: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, true); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZOrderFn); */ break; default: ok &= UtilSemiSortFloatIndex(n_tri, z_value, ix, false); /* UtilSortIndex(n_tri,z_value,ix,(UtilOrderFn*)ZRevOrderFn); */ break; } c = n_tri; if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); if (ok) { glColor4f(col[0], col[1], col[2], alpha); glBegin(GL_TRIANGLES); for (c = 0; c < n_tri; c++) { tb = t_buf + 6 * ix[c]; glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); } glEnd(); } } else { /* else I->oneColorFlag */ glBegin(GL_TRIANGLES); for (c = 0; c < n_tri; c++) { float *vv, *v_alpha; tb = t_buf + 12 * ix[c]; v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); v_alpha = *(tb++); vv = *(tb++); glColor4f(vv[0], vv[1], vv[2], *v_alpha); glNormal3fv(*(tb++)); glVertex3fv(*(tb++)); } glEnd(); } { FreeP(ix); FreeP(z_value); FreeP(t_buf); } } else if (ok) { if (info->alpha_cgo) { /* global transparency sort */ if (I->allVisibleFlag) { if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); glColor4f(col[0], col[1], col[2], alpha); c = *(s++); while (c) { int parity = 0; s += 2; while (ok && c--) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col, col, col, alpha, alpha, alpha, parity); s++; parity = !parity; } c = *(s++); } } else { c = *(s++); while (ok && c) { float *col0, *col1, *col2; int parity = 0; float alpha0, alpha1, alpha2; col0 = vc + (*s) * 3; if (va) { alpha0 = va[(*s)]; } else { alpha0 = alpha; } s++; col1 = vc + (*s) * 3; if (va) { alpha1 = va[(*s)]; } else { alpha1 = alpha; } s++; while (ok && c--) { col2 = vc + (*s) * 3; if (va) { alpha2 = va[(*s)]; } else { alpha2 = alpha; } ok &= CGOAlphaTriangle(info->alpha_cgo, v + s[-2] * 3, v + s[-1] * 3, v + (*s) * 3, vn + s[-2] * 3, vn + s[-1] * 3, vn + (*s) * 3, col0, col1, col2, alpha0, alpha1, alpha2, parity); alpha0 = alpha1; alpha1 = alpha2; col0 = col1; col1 = col2; s++; parity = !parity; } c = *(s++); } } } else if (ok) { /* subset s */ c = I->NT; if (c) { if (I->oneColorFlag) { float color[3]; ColorGetEncoded(G, I->oneColor, color); while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, color, color, color, alpha, alpha, alpha, 0); } t += 3; } } else { while (ok && c--) { if (visibility_test(I->proximity, vi, t)) { if (va) { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, va[t[0]], va[t[1]], va[t[2]], 0); } else { ok &= CGOAlphaTriangle(info->alpha_cgo, v + t[0] * 3, v + t[1] * 3, v + t[2] * 3, vn + t[0] * 3, vn + t[1] * 3, vn + t[2] * 3, vc + t[0] * 3, vc + t[1] * 3, vc + t[2] * 3, alpha, alpha, alpha, 0); } } t += 3; } } if (ok) CGOEnd(info->alpha_cgo); } } } else if (ok) { /* fast and ugly */ if (I->oneColorFlag) { float col[3]; ColorGetEncoded(G, I->oneColor, col); glColor4f(col[0], col[1], col[2], alpha); vc = nullptr; } if (I->allVisibleFlag) { for (; (c = *(s++)); s += c + 2) { glBegin(GL_TRIANGLE_STRIP); immediate_draw_indexed_vertices_alpha( vc, va, alpha, vn, v, s, c + 2); glEnd(); } } else { c = I->NT; if (c) { glBegin(GL_TRIANGLES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices_alpha( vc, va, alpha, vn, v, t, 3); } } glEnd(); } } } } } else if (ok) { /* opaque */ if (I->oneColorFlag) { glColor3fv(ColorGet(G, I->oneColor)); vc = nullptr; } if (I->allVisibleFlag) { for (; (c = *(s++)); s += c + 2) { glBegin(GL_TRIANGLE_STRIP); immediate_draw_indexed_vertices(vc, vn, v, s, c + 2); glEnd(); } } else { c = I->NT; if (c) { glBegin(GL_TRIANGLES); for (; c--; t += 3) { if (visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices(vc, vn, v, t, 3); } } glEnd(); } } } } if (ok && SettingGetGlobal_i(G, cSetting_surface_debug)) { t = I->T.data(); c = I->NT; glLineWidth(1.0F); // draw green triangles, either filled or as line edges, // depending on surface_type if (c) { glColor3f(0.0, 1.0, 0.0); if (I->Type == SurfaceType::Triangle) { // filled green triangles for surface as mesh glBegin(GL_TRIANGLES); for (; c--; t += 3) { if (I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { immediate_draw_indexed_vertices(NULL, vn, v, t, 3); } } glEnd(); } else { // line edges as green lines glBegin(GL_LINES); for (; c--; t += 3) { if (I->allVisibleFlag || visibility_test(I->proximity, vi, t)) { int indices[] = {t[0], t[1], t[1], t[2], t[2], t[0]}; immediate_draw_indexed_vertices(NULL, vn, v, indices, 6); } } glEnd(); } } // draw normals as red lines c = I->N; if (c) { SceneResetNormal(G, true); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINES); while (c--) { glVertex3fv(v); glVertex3f(v[0] + vn[0] / 2, v[1] + vn[1] / 2, v[2] + vn[2] / 2); v += 3; vn += 3; } glEnd(); } } if (two_sided_lighting) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); } #endif } } if (!ok) { I->invalidate(cRepInvPurge); I->cs->Active[cRepSurface] = false; } } bool RepSurface::sameVis() const { for (int idx = 0; idx < cs->NIndex; ++idx) { auto* ai = cs->getAtomInfo(idx); if (LastVisib[idx] != GET_BIT(ai->visRep, cRepSurface)) { return false; } } return true; } void RepSurface::invalidate(cRepInv_t level) { Rep::invalidate(level); if (level >= cRepInvColor) ColorInvalidated = true; } bool RepSurface::sameColor() const { if (ColorInvalidated) return false; const auto* lc = LastColor; for (int idx = 0; idx < cs->NIndex; idx++) { auto* ai = cs->getAtomInfo(idx); if (ai->visRep & cRepSurfaceBit) { if (*(lc++) != ai->color) { return false; } } } return true; } Rep* RepSurface::recolor() { auto const I = this; assert(cs == this->cs); MapType *map = nullptr, *ambient_occlusion_map = nullptr; int a, i0, i, j, c1; float *v0, *vc, *va; const float* c0; float* n0; int* lc; char* lv; int first_color; float *v_pos, v_above[3]; int ramp_above; ObjectMolecule* obj; float probe_radius; float dist; float cutoff; int inclH; int inclInvis; int cullByFlag = false; int surface_mode, ambient_occlusion_mode; int surface_color; int* present = nullptr; int* rc; int ramped_flag = false; int carve_state = 0; int carve_flag = false; float carve_cutoff; float carve_normal_cutoff; int carve_normal_flag; const char* carve_selection = nullptr; float* carve_vla = nullptr; MapType* carve_map = nullptr; int clear_state = 0; int clear_flag = false; float clear_cutoff; const char* clear_selection = nullptr; float* clear_vla = nullptr; int state = getState(); float transp; int variable_alpha = false; MapType* clear_map = nullptr; AtomInfoType *ai2 = nullptr, *ai1; obj = cs->Obj; ambient_occlusion_mode = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_ambient_occlusion_mode); surface_mode = I->surface_mode; ramp_above = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_ramp_above_mode); surface_color = SettingGet_color( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_color); cullByFlag = (surface_mode == cRepSurface_by_flags); inclH = !((surface_mode == cRepSurface_heavy_atoms) || (surface_mode == cRepSurface_vis_heavy_only)); inclInvis = !((surface_mode == cRepSurface_vis_only) || (surface_mode == cRepSurface_vis_heavy_only)); probe_radius = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_solvent_radius); I->proximity = SettingGet_b( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_proximity); carve_cutoff = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_cutoff); clear_cutoff = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_clear_cutoff); transp = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_transparency); carve_normal_cutoff = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_normal_cutoff); carve_normal_flag = carve_normal_cutoff > (-1.0F); cutoff = I->max_vdw + 2 * probe_radius; if (!I->LastVisib) I->LastVisib = pymol::malloc(cs->NIndex); if (!I->LastColor) I->LastColor = pymol::malloc(cs->NIndex); lv = I->LastVisib; lc = I->LastColor; for (a = 0; a < cs->NIndex; a++) { ai2 = cs->getAtomInfo(a); *(lv++) = GET_BIT(ai2->visRep, cRepSurface); *(lc++) = ai2->color; } if (I->N) { if (carve_cutoff > 0.0F) { carve_state = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_state) - 1; carve_selection = SettingGet_s(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_selection); if (carve_selection) carve_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, carve_selection), carve_state, carve_cutoff, &carve_vla); if (carve_map) MapSetupExpress(carve_map); carve_flag = true; } if (clear_cutoff > 0.0F) { clear_state = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_clear_state) - 1; clear_selection = SettingGet_s(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_clear_selection); if (clear_selection) clear_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, clear_selection), clear_state, clear_cutoff, &clear_vla); if (clear_map) MapSetupExpress(clear_map); clear_flag = true; } if (I->VC.empty()) I->VC.resize(3 * I->N); vc = I->VC.data(); if (I->VA.empty()) I->VA.resize(I->N); va = I->VA.data(); if (I->RC.empty()) I->RC.resize(I->N); rc = I->RC.data(); if (I->Vis.empty()) I->Vis.resize(I->N); if (ColorCheckRamped(G, surface_color)) { I->oneColorFlag = false; } else { I->oneColorFlag = true; } first_color = -1; present = pymol::malloc(cs->NIndex); { int* ap = present; for (a = 0; a < cs->NIndex; a++) { ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if ((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *ap = 2; else *ap = 0; ap++; } } if (inclInvis) { float probe_radiusX2 = probe_radius * 2; map = new MapType(G, 2 * I->max_vdw + probe_radius, cs->Coord, cs->NIndex, nullptr, present); MapSetupExpress(map); /* add in nearby invisibles */ for (a = 0; a < cs->NIndex; a++) { if (!present[a]) { ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if ((!cullByFlag) || !(ai1->flags & cAtomFlag_ignore)) { v0 = cs->coordPtr(a); i = *(MapLocusEStart(map, v0)); if (i && !map->EList.empty()) { j = map->EList[i++]; while (j >= 0) { if (present[j] > 1) { ai2 = obj->AtomInfo + cs->IdxToAtm[j]; if (within3f(cs->coordPtr(j), v0, ai1->vdw + ai2->vdw + probe_radiusX2)) { present[a] = 1; break; } } j = map->EList[i++]; } } } } } MapFree(map); map = nullptr; } /** * Update the ambient occlusion accessibility array (VAO) */ auto update_VAO = [&]() { if (!ambient_occlusion_mode) { I->VAO.clear(); return; } float maxDist = 0.f, maxDistA = 0.f; int level_min = 64, level_max = 0; double start_time, cur_time; short vertex_map = 0; /* vertex or atom map */ float map_cutoff = cutoff; switch (ambient_occlusion_mode % 4) { case 1: case 3: vertex_map = 0; /* ambient_occlusion_mode - 1 atoms in map (default), 2 - vertices in map */ break; case 2: vertex_map = 1; } I->VAO.resize(I->N); start_time = UtilGetSeconds(G); if (vertex_map) { ambient_occlusion_map = new MapType(G, map_cutoff, I->V.data(), I->N, nullptr, nullptr); } else { ambient_occlusion_map = new MapType(G, map_cutoff, cs->Coord, cs->NIndex, nullptr, present); } MapSetupExpress(ambient_occlusion_map); if (ambient_occlusion_mode == 3) { /* per atom */ float* VAO = pymol::malloc(cs->NIndex); short* nVAO = pymol::malloc(cs->NIndex); memset(VAO, 0, sizeof(float) * cs->NIndex); memset(nVAO, 0, sizeof(short) * cs->NIndex); for (a = 0; a < I->N; a++) { int nbits = 0; short level1, level2, has; unsigned long bits = 0L, bit; float d[3], *vn0, v0mod[3]; int closeA = -1; float closeDist = FLT_MAX; has = 0; v0 = I->V.data() + 3 * a; vn0 = I->VN.data() + 3 * a; mult3f(vn0, .01f, v0mod); add3f(v0, v0mod, v0mod); i = *(MapLocusEStart(ambient_occlusion_map, v0)); if (i && !map->EList.empty()) { j = ambient_occlusion_map->EList[i++]; while (j >= 0) { subtract3f(cs->coordPtr(j), v0, d); dist = (float) length3f(d); if (dist < closeDist) { closeA = j; closeDist = dist; } j = ambient_occlusion_map->EList[i++]; } } if (closeA >= 0) { if (nVAO[closeA]) { I->VAO[a] = VAO[closeA]; } else { v0 = cs->coordPtr(closeA); i = *(MapLocusEStart(ambient_occlusion_map, v0)); if (i) { j = ambient_occlusion_map->EList[i++]; while (j >= 0) { if (closeA == j) { j = ambient_occlusion_map->EList[i++]; continue; } subtract3f(cs->coordPtr(j), v0, d); dist = (float) length3f(d); if (dist > 12.f) { j = ambient_occlusion_map->EList[i++]; continue; } has = 1; level1 = (d[2] < 0.f) ? 4 : 0; level1 |= (d[1] < 0.f) ? 2 : 0; level1 |= (d[0] < 0.f) ? 1 : 0; d[0] = fabs(d[0]); d[1] = fabs(d[1]); d[2] = fabs(d[2]); level2 = (d[0] <= d[1]) ? 4 : 0; level2 |= (d[1] <= d[2]) ? 2 : 0; level2 |= (d[0] <= d[2]) ? 1 : 0; bit = level1 * 8 + level2; bits |= (1L << bit); j = ambient_occlusion_map->EList[i++]; } } if (has) { nbits = countBits(bits); if (nbits > level_max) level_max = nbits; if (nbits < level_min) level_min = nbits; I->VAO[a] = nbits; } else { level_min = 0; I->VAO[a] = 0.f; } VAO[closeA] = I->VAO[a]; nVAO[closeA] = 1; } } } FreeP(VAO); FreeP(nVAO); } else { float natomsL = 0; for (a = 0; a < I->N; a++) { int natoms = 0, nbits = 0; short level1, level2, has; unsigned long bits = 0L, bit; float d[3], *vn0, pt[3], v0mod[3]; if (a % 1000 == 0) { PRINTFB(G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): Ambient Occlusion computing mode=%d " "#vertices=%d done=%d\n", ambient_occlusion_mode, I->N, a ENDFB(G); } v0 = I->V.data() + 3 * a; vn0 = I->VN.data() + 3 * a; mult3f(vn0, .01f, v0mod); add3f(v0, v0mod, v0mod); i = *(MapLocusEStart(ambient_occlusion_map, v0)); if (i && !ambient_occlusion_map->EList.empty()) { j = ambient_occlusion_map->EList[i++]; maxDistA = 0.f; has = 0; while (j >= 0) { natomsL++; if (vertex_map && a == j) { j = ambient_occlusion_map->EList[i++]; continue; } if (vertex_map) { copy3f(I->V.data() + j * 3, pt); subtract3f(I->V.data() + j * 3, v0mod, d); } else { copy3f(cs->coordPtr(j), pt); subtract3f(cs->coordPtr(j), v0mod, d); } dist = (float) length3f(d); normalize3f(d); if (get_angle3f(vn0, d) >= (PI / 2.f)) { j = ambient_occlusion_map->EList[i++]; continue; } if (dist <= .0001f || dist > 12.f) { j = ambient_occlusion_map->EList[i++]; continue; } has = 1; (dist > maxDistA) ? maxDistA = dist : 0; level1 = (d[2] < 0.f) ? 4 : 0; level1 |= (d[1] < 0.f) ? 2 : 0; level1 |= (d[0] < 0.f) ? 1 : 0; d[0] = fabs(d[0]); d[1] = fabs(d[1]); d[2] = fabs(d[2]); level2 = (d[0] <= d[1]) ? 4 : 0; level2 |= (d[1] <= d[2]) ? 2 : 0; level2 |= (d[0] <= d[2]) ? 1 : 0; bit = level1 * 8 + level2; bits |= (1L << bit); j = ambient_occlusion_map->EList[i++]; natoms++; } if (has) { nbits = countBits(bits); if (nbits > level_max) level_max = nbits; if (nbits < level_min) level_min = nbits; (maxDistA > maxDist) ? maxDist = maxDistA : 0; I->VAO[a] = nbits; } else { level_min = 0; I->VAO[a] = 0.f; } maxDistA = 0.f; } } PRINTFB(G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): #vertices=%d Ambient Occlusion average #atoms " "looked at per vertex = %f\n", I->N, (natomsL / I->N) ENDFB(G); } { int ambient_occlusion_smooth = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_ambient_occlusion_smooth); int surface_quality = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_quality); float min_max_diff = level_max - level_min; if (surface_quality > 0) ambient_occlusion_smooth *= surface_quality; /* Now we should set VAO from min/max */ if (min_max_diff) { for (a = 0; a < I->N; a++) { I->VAO[a] = ((I->VAO[a] - level_min) / min_max_diff); } } else { for (a = 0; a < I->N; a++) { I->VAO[a] = 0.f; } } /* SMOOTH Accessibility VALUES */ if (ambient_occlusion_smooth && !I->T.empty()) { int i, j, pt1, pt2, pt3; float ave; float* tmpVAO = pymol::malloc(I->N); int *nVAO = pymol::malloc(I->N), c, *t; for (j = 0; j < ambient_occlusion_smooth; j++) { memset(nVAO, 0, sizeof(int) * I->N); memset(tmpVAO, 0, sizeof(float) * I->N); t = I->T.data(); c = I->NT; while (c--) { if (I->allVisibleFlag || visibility_test(I->proximity, I->Vis.data(), t)) { pt1 = *t; pt2 = *(t + 1); pt3 = *(t + 2); nVAO[pt1] += 1; nVAO[pt2] += 1; nVAO[pt3] += 1; ave = ave3(I->VAO[pt1], I->VAO[pt2], I->VAO[pt3]); tmpVAO[pt1] += ave; tmpVAO[pt2] += ave; tmpVAO[pt3] += ave; } t += 3; } for (i = 0; i < I->N; i++) { if (nVAO[i]) { /* only update if added and greater than original */ // if ((tmpVAO[i]/(float)nVAO[i]) > I->VAO[i]) I->VAO[i] = tmpVAO[i] / (float) nVAO[i]; } } } FreeP(tmpVAO); FreeP(nVAO); } } MapFree(ambient_occlusion_map); cur_time = UtilGetSeconds(G); PRINTFB(G, FB_RepSurface, FB_Debugging) "RepSurfaceColor(): Ambient Occlusion computed #atoms=%d #vertices=%d " "time=%lf seconds\n", cs->NIndex, I->N, (cur_time - start_time) ENDFB(G); ambient_occlusion_map = nullptr; }; // update_VAO /* now, assign colors to each point */ map = new MapType(G, cutoff, cs->Coord, cs->NIndex, nullptr, present); if (map) { short color_smoothing = SettingGetGlobal_i(G, cSetting_surface_color_smoothing); float color_smoothing_threshold = SettingGetGlobal_f(G, cSetting_surface_color_smoothing_threshold); int atm, ok = true; MapSetupExpress(map); ok &= !G->Interrupt; if (ok && I->AT.empty()) I->AT.resize(I->N); for (a = 0; ok && a < I->N; a++) { float at_transp = transp; AtomInfoType* ai0 = nullptr; float minDist = FLT_MAX, minDist2 = FLT_MAX, distDiff = FLT_MAX; int pi = -1, catm = -1; /* variables for color smoothing */ AtomInfoType *pai = nullptr, *pai2 = nullptr; /* variables for color smoothing */ c1 = 1; i0 = -1; v0 = I->V.data() + 3 * a; n0 = I->VN.data() + 3 * a; auto vi = &I->Vis[a]; /* colors */ i = *(MapLocusEStart(map, v0)); if (i && !map->EList.empty()) { j = map->EList[i++]; while (j >= 0) { atm = cs->IdxToAtm[j]; ai2 = obj->AtomInfo + atm; if ((inclH || (!ai2->isHydrogen())) && ((!cullByFlag) || (!(ai2->flags & cAtomFlag_ignore)))) { dist = (float) diff3f(v0, cs->coordPtr(j)) - ai2->vdw; if (color_smoothing) { if (dist < minDist) { /* switching closest to 2nd closest */ pai2 = pai; minDist2 = minDist; pi = j; catm = atm; pai = ai2; minDist = dist; } else if (dist < minDist2) { /* just setting second closest */ pai2 = ai2; minDist2 = dist; } } else if (dist < minDist) { i0 = j; catm = atm; ai0 = ai2; minDist = dist; } } j = map->EList[i++]; } } I->AT[a] = catm; if (color_smoothing) { i0 = pi; ai0 = pai; /* TODO: should find point closest to v0 between atoms points (cs->coordPtr(pi)) and (cs->coordPtr(pi2)) (including vdw), then set this distance to the distance between the vertex v0 and that point. We might want to use the normal to compute this distance. */ distDiff = fabs(minDist2 - minDist); } if (i0 >= 0) { int at_surface_color = AtomSettingGetWD(G, ai0, cSetting_surface_color, surface_color); at_transp = AtomSettingGetWD(G, ai0, cSetting_transparency, transp); if (at_surface_color != -1) { c1 = at_surface_color; distDiff = FLT_MAX; } else { c1 = ai0->color; } if (I->oneColorFlag) { if (first_color >= 0) { if (first_color != c1) I->oneColorFlag = false; } else first_color = c1; } if (I->allVisibleFlag) *vi = 1; else { ai2 = obj->AtomInfo + cs->IdxToAtm[i0]; if ((ai2->visRep & cRepSurfaceBit) && (inclH || (!ai2->isHydrogen())) && ((!cullByFlag) || (!(ai2->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *vi = 1; else *vi = 0; } } else { *vi = 0; } if (carve_flag && (*vi)) { /* is point visible, and are we carving? */ *vi = 0; if (carve_map) { i = *(MapLocusEStart(carve_map, v0)); if (i && !carve_map->EList.empty()) { j = carve_map->EList[i++]; while (j >= 0) { float* v_targ = carve_vla + 3 * j; if (within3f(v_targ, v0, carve_cutoff)) { if (!carve_normal_flag) { *vi = 1; break; } else { float v_to[3]; subtract3f(v_targ, v0, v_to); if (dot_product3f(v_to, n0) >= carve_normal_cutoff) { *vi = 1; break; } } } j = carve_map->EList[i++]; } } } } if (clear_flag && (*vi)) { /* is point visible, and are we clearing? */ if (clear_map) { i = *(MapLocusEStart(clear_map, v0)); if (i && !clear_map->EList.empty()) { j = clear_map->EList[i++]; while (j >= 0) { if (within3f(clear_vla + 3 * j, v0, clear_cutoff)) { *vi = 0; break; } j = clear_map->EList[i++]; } } } } /* if(ColorCheckRamped(G,surface_color)) { c1 = surface_color; } */ if (ColorCheckRamped(G, c1)) { I->oneColorFlag = false; switch (ramp_above) { case 1: copy3f(n0, v_above); scale3f(v_above, probe_radius, v_above); add3f(v0, v_above, v_above); v_pos = v_above; rc[0] = -1; break; default: v_pos = v0; rc[0] = c1; ramped_flag = true; break; } ColorGetRamped(G, c1, v_pos, vc, state); vc += 3; rc++; } else { if (color_smoothing && distDiff < color_smoothing_threshold && pai2) { const float* c2; float weight, weight2; if (color_smoothing == 1) { weight = 1.f + sin(.5f * PI * (distDiff / color_smoothing_threshold)); } else { weight = 1.f + (distDiff / color_smoothing_threshold); } weight2 = 2.f - weight; c0 = ColorGet(G, c1); c2 = ColorGet(G, pai2->color); *(rc++) = c1; *(vc++) = ((weight * (*(c0++))) + (weight2 * (*(c2++)))) / 2.f; *(vc++) = ((weight * (*(c0++))) + (weight2 * (*(c2++)))) / 2.f; *(vc++) = ((weight * (*(c0++))) + (weight2 * (*(c2++)))) / 2.f; } else { c0 = ColorGet(G, c1); *(rc++) = c1; *(vc++) = *(c0++); *(vc++) = *(c0++); *(vc++) = *(c0++); } } if (at_transp != transp) variable_alpha = true; *(va++) = 1.0F - at_transp; if (at_transp > 0) { I->setHasTransparency(); } if (!*vi) I->allVisibleFlag = false; } MapFree(map); } if (variable_alpha) I->oneColorFlag = false; if (I->oneColorFlag) { I->oneColor = first_color; } // ambient occlusion update_VAO(); } /* if(surface_color>=0) { I->oneColorFlag=true; I->oneColor=surface_color; } */ if (G->HaveGUI) { setShaderCGO(I, nullptr); } if (!I->VA.empty() && (!variable_alpha)) { I->VA.clear(); } if (carve_map) MapFree(carve_map); VLAFreeP(carve_vla); if (clear_map) MapFree(clear_map); VLAFreeP(clear_vla); if ((!ramped_flag) || (!SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_ray_color_ramps))) { I->RC.clear(); } I->ColorInvalidated = false; FreeP(present); return this; } struct SurfaceJob { /* input */ std::vector coord; std::vector atomInfo{}; float maxVdw{}; int allVisibleFlag{}; int nPresent{}; std::vector presentVla{}; int solventSphereIndex{}; int sphereIndex{}; SurfaceType surfaceType{}; int circumscribe{}; float probeRadius{}; float carveCutoff{}; std::vector carveVla{}; int surfaceMode{}; int surfaceSolvent{}; int cavityCull{}; float pointSep{}; float trimCutoff{}; float trimFactor{}; int cavityMode{}; float cavityRadius{}; float cavityCutoff{}; /* results */ std::vector V{}; std::vector VN{}; int N{}; std::vector T{}; std::vector S{}; int NT{}; }; static void SurfaceJobPurgeResult(PyMOLGlobals* G, SurfaceJob* I) { I->N = 0; I->NT = 0; I->V.clear(); I->VN.clear(); I->T.clear(); I->S.clear(); } #ifndef _PYMOL_NOPY static PyObject* SurfaceJobAtomInfoAsPyTuple( std::vector& atom_info) { PyObject* result = nullptr; if (!atom_info.empty()) { auto size = 2 * atom_info.size() + 1; result = PyTuple_New(size); if (result) { PyTuple_SetItem(result, 0, PyInt_FromLong(2)); /* width of array */ auto ai = atom_info.data(); for (std::size_t i = 1; i < size; i += 2) { PyTuple_SetItem(result, i, PyFloat_FromDouble(ai->vdw)); PyTuple_SetItem(result, i + 1, PyInt_FromLong(ai->flags)); ai++; } } } return (PConvAutoNone(result)); } #if 0 static SurfaceJobAtomInfo *SurfaceJobAtomInfoVLAFromPyTuple(PyObject * tuple) { SurfaceJobAtomInfo *result = nullptr; if(tuple && PyTuple_Check(tuple)) { ov_size size = PyTuple_Size(tuple); if(size) { ov_size width = PyInt_AsLong(PyTuple_GetItem(tuple, 0)); if(width == 2) { ov_size vla_size = (size - 1) / 2; result = VLAlloc(SurfaceJobAtomInfo, vla_size); if(result) { SurfaceJobAtomInfo *atom_info = result; ov_size i; for(i = 1; i < size; i += 2) { atom_info->vdw = (float) PyFloat_AsDouble(PyTuple_GetItem(tuple, i)); atom_info->flags = PyInt_AsLong(PyTuple_GetItem(tuple, i + 1)); atom_info++; } } } } } return (result); } #endif static PyObject* SurfaceJobInputAsTuple(PyMOLGlobals* G, SurfaceJob* I) { PyObject* result = PyTuple_New(24); if (result) { PyTuple_SetItem(result, 0, PyString_FromString("SurfaceJob")); PyTuple_SetItem(result, 1, PyInt_FromLong(1)); /* version */ PyTuple_SetItem(result, 2, PConvToPyObject(I->coord)); PyTuple_SetItem(result, 3, SurfaceJobAtomInfoAsPyTuple(I->atomInfo)); PyTuple_SetItem(result, 4, PyFloat_FromDouble(I->maxVdw)); PyTuple_SetItem(result, 5, PyInt_FromLong(I->allVisibleFlag)); PyTuple_SetItem(result, 6, PyInt_FromLong(I->nPresent)); PyTuple_SetItem(result, 7, PConvToPyObject(I->presentVla)); PyTuple_SetItem(result, 8, PyInt_FromLong(I->solventSphereIndex)); PyTuple_SetItem(result, 9, PyInt_FromLong(I->sphereIndex)); PyTuple_SetItem( result, 10, PyInt_FromLong(static_cast(I->surfaceType))); PyTuple_SetItem(result, 11, PyInt_FromLong(I->circumscribe)); PyTuple_SetItem(result, 12, PyFloat_FromDouble(I->probeRadius)); PyTuple_SetItem(result, 13, PyFloat_FromDouble(I->carveCutoff)); PyTuple_SetItem(result, 14, PConvToPyObject(I->carveVla)); PyTuple_SetItem(result, 15, PyInt_FromLong(I->surfaceMode)); PyTuple_SetItem(result, 16, PyInt_FromLong(I->surfaceSolvent)); PyTuple_SetItem(result, 17, PyInt_FromLong(I->cavityCull)); PyTuple_SetItem(result, 18, PyFloat_FromDouble(I->pointSep)); PyTuple_SetItem(result, 19, PyFloat_FromDouble(I->trimCutoff)); PyTuple_SetItem(result, 20, PyFloat_FromDouble(I->trimFactor)); PyTuple_SetItem(result, 21, PyInt_FromLong(I->cavityMode)); PyTuple_SetItem(result, 22, PyFloat_FromDouble(I->cavityRadius)); PyTuple_SetItem(result, 23, PyFloat_FromDouble(I->cavityCutoff)); } return result; } static PyObject* SurfaceJobResultAsTuple(PyMOLGlobals* G, SurfaceJob* I) { PyObject* result = PyTuple_New(6); if (result) { PyTuple_SetItem(result, 0, PyInt_FromLong(I->N)); PyTuple_SetItem(result, 1, PConvToPyObject(I->V)); // WAS TO TUPLE! PyTuple_SetItem(result, 2, PConvToPyObject(I->VN)); PyTuple_SetItem(result, 3, PyInt_FromLong(I->NT)); PyTuple_SetItem(result, 4, PConvToPyObject(I->T)); PyTuple_SetItem(result, 5, PConvToPyObject(I->S)); } return result; } static ov_status SurfaceJobResultFromTuple( PyMOLGlobals* G, SurfaceJob* I, PyObject* tuple) { ov_status status = OV_STATUS_FAILURE; SurfaceJobPurgeResult(G, I); if (tuple && PyTuple_Check(tuple)) { ov_size size = PyTuple_Size(tuple); if (size >= 6) { status = OV_STATUS_SUCCESS; I->N = PyInt_AsLong(PyTuple_GetItem(tuple, 0)); if (OV_OK(status)) status = PConvFromPyObject(G, PyTuple_GetItem(tuple, 1), I->V); if (OV_OK(status)) status = PConvFromPyObject(G, PyTuple_GetItem(tuple, 2), I->VN); I->NT = PyInt_AsLong(PyTuple_GetItem(tuple, 3)); if (OV_OK(status)) status = PConvFromPyObject(G, PyTuple_GetItem(tuple, 4), I->T); if (OV_OK(status)) status = PConvFromPyObject(G, PyTuple_GetItem(tuple, 5), I->S); } if (OV_ERR(status)) SurfaceJobPurgeResult(G, I); } return status; } #endif static int SurfaceJobEliminateCloseDotsType3orMore( PyMOLGlobals* G, SurfaceJob* I, int* repeat_flag, int* dot_flag) { int ok = true; int jj; float dist; float nearest; float point_sep = I->pointSep; float min_sep2 = point_sep * point_sep; float diff[3]; { int a; for (a = 0; a < I->N; a++) dot_flag[a] = 1; } { MapType* map = new MapType(G, point_sep + 0.05F, I->V.data(), I->N, nullptr); int a; float* v = I->V.data(); float* vn = I->VN.data(); float min_dot = 0.1F; CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); for (a = 0; ok && a < I->N; a++) { if (dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; jj = I->N; nearest = point_sep + 1.0F; while (j >= 0) { if (j > a) { if (dot_flag[j]) { if (dot_product3f(I->VN.data() + (3 * j), vn) > min_dot) { if (within3fret(I->V.data() + (3 * j), v, point_sep, min_sep2, diff, &dist)) { *repeat_flag = true; if (dist < nearest) { /* try to be as determinstic as possible in terms of how we collapse points */ jj = j; nearest = dist; } else if ((j < jj) && (fabs(dist - nearest) < R_SMALL4)) { jj = j; nearest = dist; } } } } } j = map->EList[i++]; } if (jj < I->N) { dot_flag[jj] = 0; add3f(vn, I->VN.data() + (3 * jj), vn); average3f(I->V.data() + (3 * jj), v, v); *repeat_flag = true; } } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); } return ok; } static int SurfaceJobEliminateCloseDotsTypeLessThan3( PyMOLGlobals* G, SurfaceJob* I, int* repeat_flag, int* dot_flag) { int ok = true; int a; float point_sep = I->pointSep; MapType* map = new MapType(G, -point_sep, I->V.data(), I->N, nullptr); float* v = I->V.data(); float* vn = I->VN.data(); CHECKOK(ok, map); if (ok) { for (a = 0; a < I->N; a++) dot_flag[a] = 1; ok &= MapSetupExpress(map); } for (a = 0; ok && a < I->N; a++) { if (dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { if (j != a) { if (dot_flag[j]) { if (within3f(I->V.data() + (3 * j), v, point_sep)) { dot_flag[j] = 0; add3f(vn, I->VN.data() + (3 * j), vn); average3f(I->V.data() + (3 * j), v, v); *repeat_flag = true; } } } j = map->EList[i++]; } } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); return ok; } static void SurfaceJobEliminateFromVArrays( PyMOLGlobals* G, SurfaceJob* I, int* dot_flag, short normalize) { float* v0 = I->V.data(); float* vn0 = I->VN.data(); int* p = dot_flag; int c = I->N; int a; float* v = I->V.data(); float* vn = I->VN.data(); I->N = 0; for (a = 0; a < c; a++) { if (*(p++)) { *(v0++) = *(v++); *(v0++) = *(v++); *(v0++) = *(v++); if (normalize) normalize3f(vn); *(vn0++) = *(vn++); *(vn0++) = *(vn++); *(vn0++) = *(vn++); I->N++; } else { v += 3; vn += 3; } } } static int SurfaceJobEliminateCloseDots(PyMOLGlobals* G, SurfaceJob* I) { int ok = true; if (I->N) { int repeat_flag = true; int* dot_flag = pymol::malloc(I->N); CHECKOK(ok, dot_flag); while (ok && repeat_flag) { repeat_flag = false; if (static_cast(I->surfaceType) >= 3) { ok = SurfaceJobEliminateCloseDotsType3orMore( G, I, &repeat_flag, dot_flag); } else { /* surface types < 3 */ ok = SurfaceJobEliminateCloseDotsTypeLessThan3( G, I, &repeat_flag, dot_flag); } if (ok) { SurfaceJobEliminateFromVArrays(G, I, dot_flag, true); } ok &= !G->Interrupt; } FreeP(dot_flag); } return ok; } /* For each vertex, lookup all vertices within the neighborhood, and sum the dot_product of the normals. If the average of the dot_products of the normals is less than the trim_cutoff, then the middle vertex is eliminated. */ static int SurfaceJobEliminateTroublesomeVerticesMark(PyMOLGlobals* G, SurfaceJob* I, int* repeat_flag, MapType* map, int* dot_flag, float neighborhood, float trim_cutoff) { int ok = true; int a; float* v = I->V.data(); float* vn = I->VN.data(); for (a = 0; ok && a < I->N; a++) { if (dot_flag[a]) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; int n_nbr = 0; float dot_sum = 0.0F; while (j >= 0) { if (j != a) { if (dot_flag[j]) { float* v0 = I->V.data() + 3 * j; if (within3f(v0, v, neighborhood)) { float* n0 = I->VN.data() + 3 * j; dot_sum += dot_product3f(n0, vn); n_nbr++; } } } j = map->EList[i++]; } if (n_nbr) { dot_sum /= n_nbr; if (dot_sum < trim_cutoff) { dot_flag[a] = false; *repeat_flag = true; } } } } v += 3; vn += 3; ok &= !G->Interrupt; } return ok; } static int SurfaceJobEliminateTroublesomeVertices( PyMOLGlobals* G, SurfaceJob* I) { int ok = true; if ((I->surfaceType != SurfaceType::SolidDeterministic) && I->N && (I->trimCutoff > 0.0F) && (I->trimFactor > 0.0F)) { float trim_cutoff = I->trimCutoff; float trim_factor = I->trimFactor; int repeat_flag = true; float point_sep = I->pointSep; float neighborhood = trim_factor * point_sep; int* dot_flag = pymol::malloc(I->N); CHECKOK(ok, dot_flag); if (ok && I->surfaceType == SurfaceType::SolidEmpirical) { /* emprical tweaks */ trim_factor *= 2.5; trim_cutoff *= 1.5; } while (ok && repeat_flag) { MapType* map = new MapType(G, neighborhood, I->V.data(), I->N, nullptr); CHECKOK(ok, map); if (ok) { int a; for (a = 0; a < I->N; a++) dot_flag[a] = 1; ok &= MapSetupExpress(map); } repeat_flag = false; ok &= SurfaceJobEliminateTroublesomeVerticesMark( G, I, &repeat_flag, map, dot_flag, neighborhood, trim_cutoff); if (ok) { SurfaceJobEliminateFromVArrays(G, I, dot_flag, true); } MapFree(map); ok &= !G->Interrupt; } FreeP(dot_flag); } return ok; } static int SurfaceJobAtomProximityCleanupPass(PyMOLGlobals* G, SurfaceJob* I, int* dot_flag, int* present_vla, float probe_radius) { int ok = true; float cutoff = 0.5 * probe_radius; float* I_coord = I->coord.data(); SurfaceJobAtomInfo* I_atom_info = I->atomInfo.data(); int n_index = I->atomInfo.size(); MapType* map = new MapType( G, I->maxVdw + probe_radius, I_coord, n_index, nullptr, present_vla); int a; float* v; CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); v = I->V.data(); for (a = 0; ok && a < I->N; a++) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { SurfaceJobAtomInfo* atom_info = I_atom_info + j; if ((!present_vla) || present_vla[j]) { if (within3f(I_coord + 3 * j, v, atom_info->vdw + cutoff)) { dot_flag[a] = true; } } j = map->EList[i++]; } } v += 3; ok &= !G->Interrupt; } MapFree(map); return ok; } static int SurfaceJobRefineCopyNewPoints( SurfaceJob* I, float* new_dot, int n_new) { int ok = true; float* n1 = new_dot + 3; float* v1 = new_dot; float *v, *vn; I->V.resize(3 * (I->N + n_new)); CHECKOK(ok, !I->V.empty()); if (ok) { I->VN.resize(3 * (I->N + n_new)); } CHECKOK(ok, !I->VN.empty()); if (ok) { v = I->V.data() + 3 * I->N; vn = I->VN.data() + 3 * I->N; I->N += n_new; } while (ok && n_new--) { copy3f(v1, v); copy3f(n1, vn); v += 3; vn += 3; v1 += 6; n1 += 6; } return ok; } static int SurfaceJobRefineAddNewVerticesCheckPoint(SurfaceJob* I, MapType* map, int* n_new, float** new_dot, int j, float* v, float* vn, float map_cutoff, float neighborhood, float insert_cutoff) { int ok = true; float* v0 = I->V.data() + 3 * j; if (within3f(v0, v, map_cutoff)) { int add_new = false; float* n0 = I->VN.data() + 3 * j; VLACheck(*new_dot, float, (*n_new) * 6 + 5); CHECKOK(ok, *new_dot); if (ok) { float* v1 = (*new_dot) + (*n_new) * 6; average3f(v, v0, v1); if ((dot_product3f(n0, vn) < 0.666 /* dot_cutoff, was hardcoded as a variable */) && (within3f(v0, v, neighborhood))) { // if the normals are further than dot_cutoff apart // and the related points are close to each other, than add new add_new = true; } else { /* if points are too far apart, insert a new one, i.e., search for any point within insert_cutoff, if not, add */ int ii = *(MapLocusEStart(map, v1)); if (ii) { int found = false; int jj = map->EList[ii++]; while (jj >= 0) { if (jj != j) { float* vv0 = I->V.data() + 3 * jj; if (within3f(vv0, v1, insert_cutoff)) { found = true; break; } } jj = map->EList[ii++]; } if (!found) add_new = true; } } if (add_new) { /* highly divergent, add dot in-between v and v0 (averaged, v1 set above, compute the normal (averaged below)) and n_new incremented */ float* n1 = v1 + 3; (*n_new)++; average3f(vn, n0, n1); normalize3f(n1); } } } return ok; } static int SurfaceJobRefineAddNewVertices(PyMOLGlobals* G, SurfaceJob* I) { int ok = true; float point_sep = I->pointSep; int n_new = 0; float neighborhood = 2.6 * point_sep; /* these constants need more tuning... */ float insert_cutoff = 1.1 * point_sep; float map_cutoff = neighborhood; float* new_dot = VLAlloc(float, 1000); float *v, *vn; if (map_cutoff < (2.9 * point_sep)) { /* these constants need more tuning... */ map_cutoff = 2.9 * point_sep; } { MapType* map = nullptr; int a; map = new MapType(G, map_cutoff, I->V.data(), I->N, nullptr); CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); v = I->V.data(); vn = I->VN.data(); for (a = 0; ok && a < I->N; a++) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (ok && j >= 0) { if (j > a) { SurfaceJobRefineAddNewVerticesCheckPoint(I, map, &n_new, &new_dot, j, v, vn, map_cutoff, neighborhood, insert_cutoff); } j = map->EList[i++]; ok &= !G->Interrupt; } } v += 3; vn += 3; ok &= !G->Interrupt; } MapFree(map); } if (ok && n_new) { ok = SurfaceJobRefineCopyNewPoints(I, new_dot, n_new); } VLAFreeP(new_dot); return ok; } static void SurfaceJobCheckInteriorSolventSurface(MapType* solv_map, float* v, SolventDot* sol_dot, float probe_rad_less, float dist2, int a, int* flag) { int ii; ii = *(MapLocusEStart(solv_map, v)); if (ii && !solv_map->EList.empty()) { float* i_dot = sol_dot->dot; float dist = probe_rad_less; int* elist_ii = solv_map->EList.data() + ii; float v_0 = v[0]; int jj_next, jj = *(elist_ii++); float v_1 = v[1]; float* v1 = i_dot + 3 * jj; float v_2 = v[2]; while (jj >= 0) { /* huge bottleneck -- optimized for superscaler processors */ float dx = v1[0], dy, dz; jj_next = *(elist_ii++); dx -= v_0; if (jj != a) { dx = (dx < 0.0F) ? -dx : dx; dy = v1[1] - v_1; if (!(dx > dist)) { dy = (dy < 0.0F) ? -dy : dy; dz = v1[2] - v_2; if (!(dy > dist)) { dx = dx * dx; dz = (dz < 0.0F) ? -dz : dz; dy = dy * dy; if (!(dz > dist)) { dx = dx + dy; dz = dz * dz; if (!(dx > dist2)) if ((dx + dz) <= dist2) { *flag = false; break; } } } } } v1 = i_dot + 3 * jj_next; jj = jj_next; } } } static void SurfaceJobCheckPresentAndWithin(MapType* map, SurfaceJob* I, int* present_vla, float* v, float probe_rad_more, int* flag) { SurfaceJobAtomInfo* I_atom_info = I->atomInfo.data(); float* I_coord = I->coord.data(); int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { SurfaceJobAtomInfo* atom_info = I_atom_info + j; if ((!present_vla) || present_vla[j]) { if (within3f(I_coord + 3 * j, v, atom_info->vdw + probe_rad_more)) { *flag = false; break; } } j = map->EList[i++]; } } } static void SurfaceJobSetProbeRadius(SurfaceType surface_type, float point_sep, float* probe_radius, float* probe_rad_more, float* probe_rad_less, float* probe_rad_less2) { float solv_tole = point_sep * 0.04F; if (*probe_radius < (2.5F * point_sep)) { /* minimum probe radius allowed */ *probe_radius = 2.5F * point_sep; } *probe_rad_more = *probe_radius * (1.0F + solv_tole); switch (surface_type) { case SurfaceType::Solid: case SurfaceType::SolidDeterministic: case SurfaceType::SolidWeightings: case SurfaceType::SolidScribed: case SurfaceType::SolidEmpirical: *probe_rad_less = *probe_radius; break; default: *probe_rad_less = *probe_radius * (1.0F - solv_tole); break; } *probe_rad_less2 = (*probe_rad_less) * (*probe_rad_less); } static int SurfaceJobRun(PyMOLGlobals* G, SurfaceJob* I) { int ok = true; int MaxN; int n_present = I->nPresent; SphereRec* sp = G->Sphere->Sphere[I->sphereIndex]; SphereRec* ssp = G->Sphere->Sphere[I->solventSphereIndex]; SurfaceJobPurgeResult(G, I); { /* compute limiting storage requirements */ int tmp = n_present; if (tmp < 1) tmp = 1; if (sp->nDot < ssp->nDot) MaxN = tmp * ssp->nDot; else MaxN = tmp * sp->nDot; } MaxN = n_present; // MaxN = 0; I->V.resize((MaxN + 1) * 3); CHECKOK(ok, !I->V.empty()); if (ok) { I->VN.resize((MaxN + 1) * 3); } CHECKOK(ok, !I->VN.empty()); ok &= !G->Interrupt; if (!(!I->V.empty() && !I->VN.empty()) || (!ok)) { /* bail out point -- try to reduce crashes */ PRINTFB(G, FB_RepSurface, FB_Errors) "Error-RepSurface: insufficient memory to calculate surface at this " "quality.\n" ENDFB(G); I->V.clear(); I->VN.clear(); ok = false; } else { SolventDot* sol_dot = nullptr; float* v = I->V.data(); float* vn = I->VN.data(); MapType* carve_map = nullptr; float probe_radius = I->probeRadius; int circumscribe = I->circumscribe; SurfaceType surface_type = I->surfaceType; float point_sep = I->pointSep; int* present_vla = I->presentVla.data(); I->N = 0; sol_dot = SolventDotNew(G, I->coord.data(), I->atomInfo, probe_radius, ssp, present_vla, circumscribe, I->surfaceMode, I->surfaceSolvent, I->cavityCull, I->allVisibleFlag, I->maxVdw, I->cavityMode, I->cavityRadius, I->cavityCutoff); CHECKOK(ok, sol_dot); ok &= !G->Interrupt; if (ok) { if (!I->surfaceSolvent) { float probe_rad_more, probe_rad_less, probe_rad_less2; SurfaceJobSetProbeRadius(surface_type, point_sep, &probe_radius, &probe_rad_more, &probe_rad_less, &probe_rad_less2); if (surface_type == SurfaceType::SolidScribed || surface_type == SurfaceType::SolidEmpirical) { /* effectively double-weights atom points */ if (sol_dot->nDot) { if (sol_dot->nDot > MaxN) { MaxN = sol_dot->nDot; VecCheck(I->V, 3 * (MaxN + 1)); VecCheck(I->VN, 3 * (MaxN + 1)); v = I->V.data(); vn = I->VN.data(); } int a; float* v0 = sol_dot->dot; float* n0 = sol_dot->dotNormal; for (a = 0; a < sol_dot->nDot; a++) { scale3f(n0, -probe_radius, v); add3f(v0, v, v); copy3f(n0, vn); v += 3; vn += 3; n0 += 3; v0 += 3; I->N++; } } } ok &= !G->Interrupt; if (ok) { MapType* solv_map{}; auto map = new MapType(G, I->maxVdw + probe_rad_more, I->coord.data(), I->coord.size() / 3, nullptr, nullptr); CHECKOK(ok, map); if (ok) solv_map = new MapType( G, probe_rad_less, sol_dot->dot, sol_dot->nDot, nullptr); CHECKOK(ok, solv_map); if (ok) { ok &= MapSetupExpress(solv_map); if (ok) ok &= MapSetupExpress(map); ok &= !G->Interrupt; ok &= !map->EList.empty() && !solv_map->EList.empty(); if (sol_dot->nDot && ok) { Vector3f* dot = pymol::malloc(sp->nDot); float *v0, *n0; CHECKOK(ok, dot); if (ok) { int b; for (b = 0; b < sp->nDot; b++) { scale3f(sp->dot[b], probe_radius, dot[b]); } } v0 = sol_dot->dot; n0 = sol_dot->dotNormal; if (ok) { int a, b; int sp_nDot = sp->nDot; for (a = 0; ok && a < sol_dot->nDot; a++) { if (sol_dot->dotCode[a] || (surface_type != SurfaceType::SolidEmpirical)) { OrthoBusyFast(G, a + sol_dot->nDot * 2, sol_dot->nDot * 5); /* 2/5 to 3/5 */ for (b = 0; ok && b < sp_nDot; b++) { float* dot_b = dot[b]; v[0] = v0[0] + dot_b[0]; v[1] = v0[1] + dot_b[1]; v[2] = v0[2] + dot_b[2]; { int flag = true; SurfaceJobCheckInteriorSolventSurface(solv_map, v, sol_dot, probe_rad_less, probe_rad_less2, a, &flag); /* at this point, we have points on the interior of the solvent surface, so now we need to further trim that surface to cover atoms that are present */ if (flag) { SurfaceJobCheckPresentAndWithin( map, I, present_vla, v, probe_rad_more, &flag); if (!flag) { /* compute the normals */ vn[0] = -sp->dot[b][0]; vn[1] = -sp->dot[b][1]; vn[2] = -sp->dot[b][2]; I->N++; VecCheck(I->V, 3 * (I->N + 1)); VecCheck(I->VN, 3 * (I->N + 1)); CHECKOK(ok, !I->V.empty()); CHECKOK(ok, !I->VN.empty()); v = I->V.data() + I->N * 3; vn = I->VN.data() + I->N * 3; } } } ok &= !G->Interrupt; } } v0 += 3; n0 += 3; ok &= !G->Interrupt; } } FreeP(dot); } } MapFree(solv_map); MapFree(map); } } else { float* v0 = sol_dot->dot; float* n0 = sol_dot->dotNormal; int a; circumscribe = 0; if (sol_dot->nDot) { VecCheck(I->V, 3 * (I->N + sol_dot->nDot)); VecCheck(I->VN, 3 * (I->N + sol_dot->nDot)); v = I->V.data(); vn = I->VN.data(); for (a = 0; a < sol_dot->nDot; a++) { *(v++) = *(v0++); *(vn++) = *(n0++); *(v++) = *(v0++); *(vn++) = *(n0++); *(v++) = *(v0++); *(vn++) = *(n0++); I->N++; } } } } SolventDotFree(sol_dot); sol_dot = nullptr; ok &= !G->Interrupt; if (ok) { int refine, ref_count = 1; if ((surface_type == SurfaceType::Solid) && (circumscribe)) { ref_count = 2; /* these constants need more tuning... */ } for (refine = 0; ok && refine < ref_count; refine++) { /* add new vertices in regions where curvature is very high or where there are gaps with no points */ if (I->N && (surface_type == SurfaceType::Solid) && (circumscribe)) { ok = SurfaceJobRefineAddNewVertices(G, I); } if (ok && I->N && (surface_type == SurfaceType::Solid) && (circumscribe)) { /* combine scribing with an atom proximity cleanup pass */ int* dot_flag = pymol::calloc(I->N); CHECKOK(ok, dot_flag); ok &= SurfaceJobAtomProximityCleanupPass( G, I, dot_flag, present_vla, probe_radius); /* purge unused dots */ if (ok) SurfaceJobEliminateFromVArrays( G, I, dot_flag, false); // not normalize? FreeP(dot_flag); } /* now, eliminate dots that are too close to each other */ ok &= SurfaceJobEliminateCloseDots(G, I); /* now eliminate troublesome vertices in regions of extremely high * curvature */ ok &= SurfaceJobEliminateTroublesomeVertices(G, I); ok &= !G->Interrupt; } } if (ok && I->N && !I->V.empty() && !I->VN.empty()) { // VLASizeForSure(I->V, float, 3 * I->N); I->V.resize(3 * I->N); CHECKOK(ok, !I->V.empty()); if (ok) { // VLASizeForSure(I->VN, float, 3 * I->N); I->VN.resize(3 * I->N); } CHECKOK(ok, !I->VN.empty()); } PRINTFB(G, FB_RepSurface, FB_Blather) " RepSurface: %i surface points.\n", I->N ENDFB(G); ok &= !G->Interrupt; OrthoBusyFast(G, 3, 5); if (I->N) { if (ok && surface_type != SurfaceType::DotDefault) { float cutoff = point_sep * 5.0F; if ((cutoff > probe_radius) && (!I->surfaceSolvent)) cutoff = probe_radius; I->T = TrianglePointsToSurface(G, I->V.data(), I->VN.data(), I->N, cutoff, &I->NT, I->S, nullptr, I->cavityMode); CHECKOK(ok, !I->T.empty()); PRINTFB(G, FB_RepSurface, FB_Blather) " RepSurface: %i triangles.\n", I->NT ENDFB(G); } } else { if (ok) I->V.resize(1); CHECKOK(ok, !I->V.empty()); if (ok) I->VN.resize(1); CHECKOK(ok, !I->VN.empty()); } if (carve_map) MapFree(carve_map); } return ok; } static void RepSurfaceSetSettings(PyMOLGlobals* G, CoordSet* cs, ObjectMolecule* obj, int surface_quality, SurfaceType surface_type, float* point_sep, int* sphere_idx, int* solv_sph_idx, int* circumscribe) { if (surface_quality >= 4) { /* totally impractical */ *point_sep = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_best) / 4.f; *sphere_idx = 4; *solv_sph_idx = 4; if (*circumscribe < 0) *circumscribe = 91; } else { switch (surface_quality) { case 3: /* nearly impractical */ *point_sep = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_best) / 3.f; *sphere_idx = 4; *solv_sph_idx = 3; if (*circumscribe < 0) *circumscribe = 71; break; case 2: /* nearly perfect */ *point_sep = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_best) / 2.f; *sphere_idx = 3; *solv_sph_idx = 3; if (*circumscribe < 0) *circumscribe = 41; break; case 1: /* good */ *point_sep = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_best); *sphere_idx = 2; *solv_sph_idx = 3; if ((*circumscribe < 0) && (surface_type == SurfaceType::SolidEmpirical)) *circumscribe = 40; break; case 0: /* 0 - normal */ *point_sep = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_normal); *sphere_idx = 1; *solv_sph_idx = 2; if ((*circumscribe < 0) && (surface_type == SurfaceType::SolidEmpirical)) *circumscribe = 30; break; case -1: /* -1 */ *point_sep = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_poor); *sphere_idx = 1; *solv_sph_idx = 2; if ((*circumscribe < 0) && (surface_type == SurfaceType::SolidEmpirical)) *circumscribe = 10; break; case -2: /* -2 god awful */ *point_sep = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_poor) * 1.5F; *sphere_idx = 1; *solv_sph_idx = 1; break; case -3: /* -3 miserable */ *point_sep = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_miserable); *sphere_idx = 1; *solv_sph_idx = 1; break; default: *point_sep = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_miserable) * 1.18F; *sphere_idx = 0; *solv_sph_idx = 1; } } /* Fixed problem with surface holes when surface_quality>2, it seems like circumscribe can only be used with surface_solvent */ if ((*circumscribe < 0) || (!SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_solvent))) *circumscribe = 0; } static int RepSurfacePrepareSurfaceJob(PyMOLGlobals* G, SurfaceJob* surf_job, RepSurface* I, CoordSet* cs, ObjectMolecule* obj, std::vector& atom_info, float* carve_vla, int n_present, std::vector& present_vla, int optimize, int sphere_idx, int solv_sph_idx, SurfaceType surface_type, int circumscribe, float probe_radius, float point_sep, float carve_cutoff) { int ok = true; surf_job->maxVdw = I->max_vdw; surf_job->allVisibleFlag = I->allVisibleFlag; surf_job->atomInfo = std::move(atom_info); surf_job->nPresent = n_present; if (!present_vla.empty() && optimize) { /* implies that n_present < cs->NIndex, so eliminate irrelevant atoms & coordinates if we are optimizing subsets */ surf_job->coord.resize(n_present * 3); CHECKOK(ok, !surf_job->coord.empty()); if (ok) { int* p = present_vla.data(); SurfaceJobAtomInfo* ai_src = surf_job->atomInfo.data(); SurfaceJobAtomInfo* ai_dst = surf_job->atomInfo.data(); const float* v_src = cs->Coord.data(); float* v_dst = surf_job->coord.data(); int a; for (a = 0; a < cs->NIndex; a++) { if (*(p++)) { copy3f(v_src, v_dst); v_dst += 3; if (ai_dst != ai_src) *ai_dst = *ai_src; ai_dst++; } v_src += 3; ai_src++; } } surf_job->atomInfo.resize(n_present); CHECKOK(ok, !surf_job->atomInfo.empty()); } else { surf_job->presentVla = std::move(present_vla); surf_job->coord.resize(cs->NIndex * 3); CHECKOK(ok, !surf_job->coord.empty()); if (ok) std::copy_n(cs->Coord.data(), 3 * cs->NIndex, surf_job->coord.data()); } if (ok) { surf_job->sphereIndex = sphere_idx; surf_job->solventSphereIndex = solv_sph_idx; surf_job->surfaceType = surface_type; surf_job->circumscribe = circumscribe; surf_job->probeRadius = probe_radius; surf_job->pointSep = point_sep; surf_job->trimCutoff = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_trim_cutoff); surf_job->trimFactor = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_trim_factor); surf_job->cavityMode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_cavity_mode); surf_job->cavityRadius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_cavity_radius); surf_job->cavityCutoff = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_cavity_cutoff); if (carve_vla) { surf_job->carveVla = std::vector(VLAGetSize(carve_vla)); std::copy_n( carve_vla, surf_job->carveVla.size(), surf_job->carveVla.data()); } surf_job->carveCutoff = carve_cutoff; surf_job->surfaceMode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_mode); surf_job->surfaceSolvent = SettingGet_b( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_solvent); surf_job->cavityCull = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_cavity_cull); } return ok; } #ifndef _PYMOL_NOPY static void RepSurfaceConvertSurfaceJobToPyObject(PyMOLGlobals* G, SurfaceJob* surf_job, CoordSet* cs, ObjectMolecule* obj, PyObject** entry, PyObject** input, PyObject** output, int* found) { int cache_mode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_cache_mode); if (cache_mode > 0) { int blocked = PAutoBlock(G); *input = SurfaceJobInputAsTuple(G, surf_job); if (PCacheGet(G, output, entry, *input) == OV_STATUS_YES) { if (OV_OK(SurfaceJobResultFromTuple(G, surf_job, *output))) { *found = true; PXDecRef(*input); *input = nullptr; PXDecRef(*entry); *entry = nullptr; } PXDecRef(*output); *output = nullptr; } if (PyErr_Occurred()) PyErr_Print(); PAutoUnblock(G, blocked); } } #endif static void RepSurfaceFindAllPresentAtoms(ObjectMolecule* obj, CoordSet* cs, int* present_vla, int inclH, int cullByFlag) { int* ap = present_vla; const int* idx_to_atm = cs->IdxToAtm.data(); const AtomInfoType* obj_AtomInfo = obj->AtomInfo.data(); int a, cs_NIndex = cs->NIndex; for (a = 0; a < cs_NIndex; a++) { const AtomInfoType* ai1 = obj_AtomInfo + *(idx_to_atm++); if ((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate))))) *ap = 2; else *ap = 0; ap++; } } static int RepSurfaceAddNearByAtomsIfNotSurfaced(PyMOLGlobals* G, MapType* map, ObjectMolecule* obj, CoordSet* cs, int* present_vla, int inclH, int cullByFlag, float probe_radius, int optimize) { int ok = true; float probe_radiusX2 = probe_radius * 2; int a; for (a = 0; ok && a < cs->NIndex; a++) { if (!present_vla[a]) { AtomInfoType* ai1 = obj->AtomInfo + cs->IdxToAtm[a]; if ((inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || !(ai1->flags & cAtomFlag_ignore))) { const float* v0 = cs->coordPtr(a); int i = *(MapLocusEStart(map, v0)); if (optimize) { if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { if (present_vla[j] > 1) { AtomInfoType* ai2 = obj->AtomInfo + cs->IdxToAtm[j]; if (within3f(cs->coordPtr(j), v0, ai1->vdw + ai2->vdw + probe_radiusX2)) { present_vla[a] = 1; break; } } j = map->EList[i++]; } } } else present_vla[a] = 1; } } ok &= !G->Interrupt; } return ok; } static int RepSurfaceRemoveAtomsNotWithinCutoff(PyMOLGlobals* G, MapType* carve_map, float* carve_vla, CoordSet* cs, int* present_vla, float carve_cutoff) { int ok = true; int a; for (a = 0; ok && a < cs->NIndex; a++) { int include_flag = false; if (carve_map) { const float* v0 = cs->coordPtr(a); int i = *(MapLocusEStart(carve_map, v0)); if (i && !carve_map->EList.empty()) { int j = carve_map->EList[i++]; while (j >= 0) { if (within3f(carve_vla + 3 * j, v0, carve_cutoff)) { include_flag = true; break; } j = carve_map->EList[i++]; } } } if (!include_flag) present_vla[a] = 0; ok &= !G->Interrupt; } return ok; } Rep* RepSurfaceNew(CoordSet* cs, int state) { int ok = true; PyMOLGlobals* G = cs->G; ObjectMolecule* obj = cs->Obj; int surface_mode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_mode); int cullByFlag = (surface_mode == cRepSurface_by_flags); int inclH = !((surface_mode == cRepSurface_heavy_atoms) || (surface_mode == cRepSurface_vis_heavy_only)); int inclInvis = !((surface_mode == cRepSurface_vis_only) || (surface_mode == cRepSurface_vis_heavy_only)); int visFlag = false; if (GET_BIT(obj->RepVisCache, cRepSurface)) { const int* idx_to_atm = cs->IdxToAtm.data(); const AtomInfoType* obj_AtomInfo = obj->AtomInfo.data(); int a, cs_NIndex = cs->NIndex; // If all atoms have "flag ignore", then don't cull by flags if (cullByFlag) { bool all_ignore = true; for (int idx = 0; idx < cs_NIndex; ++idx) { if (!(obj_AtomInfo[idx_to_atm[idx]].flags & cAtomFlag_ignore)) { all_ignore = false; break; } } if (all_ignore) { cullByFlag = false; surface_mode = cRepSurface_all; } } for (a = 0; a < cs_NIndex; a++) { const AtomInfoType* ai1 = obj_AtomInfo + *(idx_to_atm++); if ((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_exfoliate | cAtomFlag_ignore))))) { visFlag = true; break; } } } if (!visFlag) { return (nullptr); /* skip if no thing visible */ } auto I = new RepSurface(cs, state); I->surface_mode = surface_mode; { int surface_flag = false; auto surface_type = SettingGet( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_type); int surface_quality = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_quality); float probe_radius = SettingGet_f( G, cs->Setting.get(), obj->Setting.get(), cSetting_solvent_radius); int optimize = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_optimize_subsets); int circumscribe = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_circumscribe); int sphere_idx = 0, solv_sph_idx = 0; MapType* map = nullptr; float point_sep; int n_present = 0; int carve_state = 0; int carve_flag = false; float carve_cutoff; const char* carve_selection = nullptr; float* carve_vla = nullptr; MapType* carve_map = nullptr; bool smooth_edges = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_smooth_edges); I->Type = surface_type; I->max_vdw = ObjectMoleculeGetMaxVDW(obj); RepSurfaceSetSettings(G, cs, obj, surface_quality, surface_type, &point_sep, &sphere_idx, &solv_sph_idx, &circumscribe); I->allVisibleFlag = true; obj = cs->Obj; /* don't waist time computing a Surface unless we need it!! */ { const int* idx_to_atm = cs->IdxToAtm.data(); const AtomInfoType* obj_AtomInfo = obj->AtomInfo.data(); int a, cs_NIndex = cs->NIndex; for (a = 0; a < cs_NIndex; a++) { const AtomInfoType* ai1 = obj_AtomInfo + *(idx_to_atm++); if ((ai1->visRep & cRepSurfaceBit) && (inclH || (!ai1->isHydrogen())) && ((!cullByFlag) || (!(ai1->flags & (cAtomFlag_exfoliate | cAtomFlag_ignore))))) surface_flag = true; else { I->allVisibleFlag = false; if (surface_flag) break; } } } if (surface_flag) { std::vector atom_info(cs->NIndex); CHECKOK(ok, !atom_info.empty()); if (ok && !atom_info.empty()) { const AtomInfoType *i_ai, *obj_atom_info = obj->AtomInfo.data(); const int* idx_to_atm = cs->IdxToAtm.data(); int n_index = cs->NIndex; SurfaceJobAtomInfo* i_atom_info = atom_info.data(); int i; /* fill in surfacing flags into SurfaceJobAtomInfo array */ for (i = 0; i < n_index; i++) { i_ai = obj_atom_info + idx_to_atm[i]; i_atom_info->flags = i_ai->flags & (cAtomFlag_ignore | cAtomFlag_exfoliate); i_atom_info->vdw = i_ai->vdw; i_atom_info++; } } OrthoBusyFast(G, 0, 1); if (ok) { n_present = cs->NIndex; carve_selection = SettingGet_s(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_selection); carve_cutoff = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_cutoff); if ((!carve_selection) || (!carve_selection[0])) carve_cutoff = 0.0F; if (carve_cutoff > 0.0F) { carve_state = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_surface_carve_state) - 1; carve_cutoff += 2 * I->max_vdw + probe_radius; if (carve_selection) carve_map = SelectorGetSpacialMapFromSeleCoord(G, SelectorIndexByName(G, carve_selection), carve_state, carve_cutoff, &carve_vla); if (carve_map) ok &= MapSetupExpress(carve_map); carve_flag = true; I->allVisibleFlag = false; } } std::vector present_vla; if (ok && !I->allVisibleFlag) { /* optimize the space over which we calculate a surface */ /* first find out which atoms are actually to be surfaced */ present_vla.resize(cs->NIndex); CHECKOK(ok, !present_vla.empty()); if (ok) { RepSurfaceFindAllPresentAtoms( obj, cs, present_vla.data(), inclH, cullByFlag); } if (ok) map = new MapType(G, 2 * I->max_vdw + probe_radius, cs->Coord, cs->NIndex, nullptr, present_vla.data()); CHECKOK(ok, map); if (ok) ok &= MapSetupExpress(map); if (ok && inclInvis) { /* then add in the nearby atoms which are not surfaced and not ignored */ ok &= RepSurfaceAddNearByAtomsIfNotSurfaced(G, map, obj, cs, present_vla.data(), inclH, cullByFlag, probe_radius, optimize); } if (ok && carve_flag && (!optimize)) { /* and optimize for carved region */ ok &= RepSurfaceRemoveAtomsNotWithinCutoff( G, carve_map, carve_vla, cs, present_vla.data(), carve_cutoff); } MapFree(map); map = nullptr; /* now count how many atoms we actually need to think about */ n_present = 0; if (ok) { int a; for (a = 0; a < cs->NIndex; a++) { if (present_vla[a]) { n_present++; } } } } if (ok) { auto surf_jobU = std::make_unique(); auto surf_job = surf_jobU.get(); CHECKOK(ok, surf_job); ok &= RepSurfacePrepareSurfaceJob(G, surf_job, I, cs, obj, atom_info, carve_vla, n_present, present_vla, optimize, sphere_idx, solv_sph_idx, surface_type, circumscribe, probe_radius, point_sep, carve_cutoff); ok &= !G->Interrupt; if (ok) { int found = false; #ifndef _PYMOL_NOPY PyObject* entry = nullptr; PyObject* output = nullptr; PyObject* input = nullptr; int cache_mode = SettingGet_i( G, cs->Setting.get(), obj->Setting.get(), cSetting_cache_mode); RepSurfaceConvertSurfaceJobToPyObject( G, surf_job, cs, obj, &entry, &input, &output, &found); #endif if (ok && !found) { ok &= SurfaceJobRun(G, surf_job); #ifndef _PYMOL_NOPY if (cache_mode > 1) { int blocked = PAutoBlock(G); output = SurfaceJobResultAsTuple(G, surf_job); PCacheSet(G, entry, output); PXDecRef(entry); entry = nullptr; PXDecRef(output); output = nullptr; PXDecRef(input); input = nullptr; PAutoUnblock(G, blocked); } #endif } #ifndef _PYMOL_NOPY if (entry || input || output) { int blocked = PAutoBlock(G); PXDecRef(entry); PXDecRef(input); PXDecRef(output); PAutoUnblock(G, blocked); } #endif } /* surf_job must be valid at this point */ if (ok) { I->N = std::move(surf_job->N); I->V = std::move(surf_job->V); I->VN = std::move(surf_job->VN); I->NT = surf_job->NT; I->T = std::move(surf_job->T); I->S = std::move(surf_job->S); } } ok &= !G->Interrupt; if (ok) I->recolor(); if (ok && smooth_edges) RepSurfaceSmoothEdges(I); } if (carve_map) MapFree(carve_map); VLAFreeP(carve_vla); OrthoBusyFast(G, 4, 4); } if (!ok) { delete I; I = nullptr; } return (Rep*) I; } void RepSurfaceSmoothEdges(RepSurface* I) { if (I->allVisibleFlag) return; std::vector> edges(I->N); // Find all edges. // Uses the following edge structure: // edge[v0] -> { v1, t0, t1 } // where (v0, v1) defines an edge and (t0, t1) // are triangles the edge belongs to. // The edge is unique when t0 == t1. int* t = I->T.data(); int* vi = I->Vis.data(); for (auto i = 0; i < I->NT; i++) { if (visibility_test(I->proximity, vi, t)) { int v0 = t[0]; int v1 = t[1]; int v2 = t[2]; if (v0 < v1) { edges[v0].push_back(v1); edges[v0].push_back(i); edges[v0].push_back(i); } else { edges[v1].push_back(v0); edges[v1].push_back(i); edges[v1].push_back(i); } if (v1 < v2) { edges[v1].push_back(v2); edges[v1].push_back(i); edges[v1].push_back(i); } else { edges[v2].push_back(v1); edges[v2].push_back(i); edges[v2].push_back(i); } if (v2 < v0) { edges[v2].push_back(v0); edges[v2].push_back(i); edges[v2].push_back(i); } else { edges[v0].push_back(v2); edges[v0].push_back(i); edges[v0].push_back(i); } } t += 3; } // Assign triangles to every edge for (auto i = 0; i < I->N; i++) { for (auto j = 0; j < edges[i].size(); j += 3) { for (auto k = 0; k < j; k += 3) { if ((j != k) && (edges[i][j] == edges[i][k])) { edges[i][j + 2] = edges[i][k + 1]; edges[i][k + 2] = edges[i][j + 1]; } } } } std::vector unique_edges; // Build a list of unique edges for (auto i = 0; i < I->N; i++) { for (auto j = 0; j < edges[i].size(); j += 3) { if (edges[i][j + 1] == edges[i][j + 2]) { unique_edges.push_back(i); unique_edges.push_back(edges[i][j]); } } } if (unique_edges.size() > 0) { auto new_vertices = I->V; // Average positions of vertices shared by consecutive unique edges for (auto i = 0; i < unique_edges.size(); i += 2) { for (auto j = 0; j < i; j += 2) { auto v0 = -1, v1 = -1, v2 = -1; if (unique_edges[i] == unique_edges[j]) { v0 = unique_edges[i]; v1 = unique_edges[i + 1]; v2 = unique_edges[j + 1]; } else if (unique_edges[i + 1] == unique_edges[j]) { v0 = unique_edges[i + 1]; v1 = unique_edges[i]; v2 = unique_edges[j + 1]; } else if (unique_edges[i] == unique_edges[j + 1]) { v0 = unique_edges[i]; v1 = unique_edges[i + 1]; v2 = unique_edges[j]; } else if (unique_edges[i + 1] == unique_edges[j + 1]) { v0 = unique_edges[i + 1]; v1 = unique_edges[i]; v2 = unique_edges[j]; } if (v0 >= 0) { for (auto k = 0; k < 3; k++) { new_vertices[3 * v0 + k] = (1. / 3.) * (I->V[3 * v0 + k] + I->V[3 * v1 + k] + I->V[3 * v2 + k]); } } } } I->V = std::move(new_vertices); } } static int SolventDotFilterOutSameXYZ(PyMOLGlobals* G, MapType* map, SurfaceJobAtomInfo* atom_info, SurfaceJobAtomInfo* a_atom_info, float* coord, int a, int* present, int* skip_flag) { int ok = true; float* v0 = coord + 3 * a; int i = *(MapLocusEStart(map, v0)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (ok && j >= 0) { SurfaceJobAtomInfo* j_atom_info = atom_info + j; if (j > a) /* only check if this is atom trails */ if ((!present) || present[j]) { if (j_atom_info->vdw == a_atom_info->vdw) { /* handle singularities */ float* v1 = coord + 3 * j; if ((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])) { // is this necessary? if some different atoms have exact same // x/y/z *skip_flag = true; } } } j = map->EList[i++]; ok &= !G->Interrupt; } } return ok; } static int SolventDotGetDotsAroundVertexInSphere(PyMOLGlobals* G, SolventDot* I, MapType* map, SurfaceJobAtomInfo* atom_info, SurfaceJobAtomInfo* a_atom_info, float* coord, int a, int* present, SphereRec* sp, float radius, int* dotCnt, int stopDot, float* dotPtr, float* dotNormal, int* nDot) { float vdw = a_atom_info->vdw + radius; float* v0 = coord + 3 * a; int b, ok = true; float* v = dotPtr + (*nDot) * 3; float* n = nullptr; Vector3f* sp_dot = sp->dot; if (dotNormal) { n = dotNormal + (*nDot) * 3; } for (b = 0; ok && b < sp->nDot; b++) { float* sp_dot_b = (float*) (sp_dot + b); int i; int flag = true; if (n) { n[0] = sp_dot_b[0]; n[1] = sp_dot_b[1]; n[2] = sp_dot_b[2]; } v[0] = v0[0] + vdw * sp_dot_b[0]; v[1] = v0[1] + vdw * sp_dot_b[1]; v[2] = v0[2] + vdw * sp_dot_b[2]; i = *(MapLocusEStart(map, v)); if (i) { int j = map->EList[i++]; while (ok && j >= 0) { SurfaceJobAtomInfo* j_atom_info = atom_info + j; if ((!present) || present[j]) { if (j != a) { int skip_flag = false; if (j_atom_info->vdw == a_atom_info->vdw) { /* handle singularities */ float* v1 = coord + 3 * j; if ((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])) { skip_flag = true; } } if (!skip_flag) if (within3f(coord + 3 * j, v, j_atom_info->vdw + radius)) { flag = false; break; } } } j = map->EList[i++]; ok &= !G->Interrupt; } } if (ok && flag && (*dotCnt < stopDot)) { (*dotCnt)++; v += 3; if (n) n += 3; (*nDot)++; } } return ok; } static int SolventDotCircumscribeAroundVertex(PyMOLGlobals* G, SolventDot* I, MapType* map, float* vdw, float dist, float* v0, float* v2, int circumscribe, SurfaceJobAtomInfo* atom_info, SurfaceJobAtomInfo* a_atom_info, SurfaceJobAtomInfo* jj_atom_info, int* present, int a, int jj, float* coord, float probe_radius, int* dotCnt, int stopDot) { int ok = true; float vz[3], vx[3], vy[3], vp[3]; float tri_a = vdw[0], tri_b = vdw[2], tri_c = dist; float tri_s = (tri_a + tri_b + tri_c) * 0.5F; float area = (float) sqrt1f( tri_s * (tri_s - tri_a) * (tri_s - tri_b) * (tri_s - tri_c)); float radius = (2 * area) / dist; float adj = (float) sqrt1f(vdw[1] - radius * radius); int b; float* v = I->dot + I->nDot * 3; float* n = I->dotNormal + I->nDot * 3; subtract3f(v2, v0, vz); get_system1f3f(vz, vx, vy); copy3f(vz, vp); scale3f(vp, adj, vp); add3f(v0, vp, vp); for (b = 0; ok && b <= circumscribe; b++) { float xcos = (float) cos((b * 2 * cPI) / circumscribe); float ysin = (float) sin((b * 2 * cPI) / circumscribe); float xcosr = xcos * radius; float ysinr = ysin * radius; int flag = true, i; v[0] = vp[0] + vx[0] * xcosr + vy[0] * ysinr; v[1] = vp[1] + vx[1] * xcosr + vy[1] * ysinr; v[2] = vp[2] + vx[2] * xcosr + vy[2] * ysinr; i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (ok && j >= 0) { SurfaceJobAtomInfo* j_atom_info = atom_info + j; if ((!present) || present[j]) if ((j != a) && (j != jj)) { int skip_flag = false; if (a_atom_info->vdw == j_atom_info->vdw) { /* handle singularities */ float* v1 = coord + 3 * j; if ((v0[0] == v1[0]) && (v0[1] == v1[1]) && (v0[2] == v1[2])) skip_flag = true; } if (jj_atom_info->vdw == j_atom_info->vdw) { /* handle singularities */ float* v1 = coord + 3 * j; if ((v2[0] == v1[0]) && (v2[1] == v1[1]) && (v2[2] == v1[2])) skip_flag = true; } if (!skip_flag) if (within3f(coord + 3 * j, v, j_atom_info->vdw + probe_radius)) { flag = false; break; } } j = map->EList[i++]; ok &= !G->Interrupt; } } if (ok && flag && (*dotCnt < stopDot)) { float vt0[3], vt2[3]; subtract3f(v0, v, vt0); subtract3f(v2, v, vt2); normalize3f(vt0); normalize3f(vt2); add3f(vt0, vt2, n); invert3f(n); normalize3f(n); /* n[0] = vx[0] * xcos + vy[0] * ysin; n[1] = vx[1] * xcos + vy[1] * ysin; n[2] = vx[2] * xcos + vy[2] * ysin; */ I->dotCode[I->nDot] = 1; /* mark as exempt */ (*dotCnt)++; v += 3; n += 3; I->nDot++; } } return ok; } static int SolventDotMarkDotsWithinCutoff(PyMOLGlobals* G, SolventDot* I, MapType* map, float* I_dot, int nDot, float* cavityDot, int* dot_flag, float cutoff) { int ok = true, *p = dot_flag; float* v = I->dot; int a; for (a = 0; ok && a < I->nDot; a++) { int i = *(MapLocusEStart(map, v)); if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { if (within3f(cavityDot + (3 * j), v, cutoff)) { *p = true; break; } j = map->EList[i++]; } } v += 3; p++; if (G->Interrupt) { ok = false; } } return ok; } static void SolventDotSlideDotsAndInfo( PyMOLGlobals* G, SolventDot* I, int* dot_flag, int flag_value) { float* v0 = I->dot; float* n0 = I->dotNormal; int* dc0 = I->dotCode; int* p = dot_flag; int c = I->nDot; float* n = n0; float* v = v0; int* dc = dc0; int a; I->nDot = 0; for (a = 0; a < c; a++) { if (flag_value ? *(p++) : !*(p++)) { *(v0++) = *(v++); *(n0++) = *(n++); *(v0++) = *(v++); *(n0++) = *(n++); *(v0++) = *(v++); *(n0++) = *(n++); *(dc0++) = *(dc++); I->nDot++; } else { v += 3; n += 3; } } PRINTFD(G, FB_RepSurface) " SolventDotNew-DEBUG: %d->%d\n", c, I->nDot ENDFD; } static int SolventDotMarkDotsWithinProbeRadius(PyMOLGlobals* G, SolventDot* I, MapType* map, int cavity_cull, float probe_radius_plus, int* dot_flag, int* flag) { int ok = true; int* p = dot_flag; float* v = I->dot; int a; for (a = 0; ok && a < I->nDot; a++) { if (!dot_flag[a]) { int i = *(MapLocusEStart(map, v)); int cnt = 0; if (i && !map->EList.empty()) { int j = map->EList[i++]; while (j >= 0) { if (j != a) { if (within3f(I->dot + (3 * j), v, probe_radius_plus)) { if (dot_flag[j]) { *p = true; (*flag) = true; break; } cnt++; if (cnt > cavity_cull) { *p = true; (*flag) = true; break; } } } j = map->EList[i++]; } } } v += 3; p++; ok &= !G->Interrupt; } ok &= !G->Interrupt; return ok; } static SolventDot* SolventDotNew(PyMOLGlobals* G, float* coord, std::vector& atom_info, float probe_radius, SphereRec* sp, int* present, int circumscribe, int surface_mode, int surface_solvent, int cavity_cull, int all_visible_flag, float max_vdw, int cavity_mode, float cavity_radius, float cavity_cutoff) { int ok = true; int stopDot; int n_coord = atom_info.size(); auto I = new SolventDot(); CHECKOK(ok, I); /* printf("%p %p %p %f %p %p %p %d %d %d %d %d %f\n", G, coord, atom_info, probe_radius,sp, extent,present, circumscribe, surface_mode, surface_solvent, cavity_cull, all_visible_flag, max_vdw); */ stopDot = n_coord * sp->nDot + 2 * circumscribe; if (ok) I->dot = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, I->dot); if (ok) { I->dotNormal = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, I->dotNormal); } if (ok) { I->dotCode = VLACalloc(int, stopDot + 1); CHECKOK(ok, I->dotCode); } I->nDot = 0; if (ok) { int dotCnt = 0; MapType* map = new MapType( G, max_vdw + probe_radius, coord, n_coord, nullptr, present); CHECKOK(ok, map); ok &= !G->Interrupt; if (map && ok) { ok &= MapSetupExpress(map); if (ok) { int a; int skip_flag; SurfaceJobAtomInfo* a_atom_info = atom_info.data(); for (a = 0; ok && a < n_coord; a++) { OrthoBusyFast(G, a, n_coord * 5); if ((!present) || (present[a])) { skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map, atom_info.data(), a_atom_info, coord, a, present, &skip_flag); if (ok && !skip_flag) { ok = SolventDotGetDotsAroundVertexInSphere(G, I, map, atom_info.data(), a_atom_info, coord, a, present, sp, probe_radius, &dotCnt, stopDot, I->dot, I->dotNormal, &I->nDot); } } a_atom_info++; } } /* for each pair of proximal atoms, circumscribe a circle for their * intersection */ if (ok) { MapType* map2 = nullptr; if (circumscribe && (!surface_solvent)) { map2 = new MapType(G, 2 * (max_vdw + probe_radius), coord, n_coord, nullptr, present); CHECKOK(ok, map2); } ok &= !G->Interrupt; if (ok && map2) { int a; int skip_flag; SurfaceJobAtomInfo* a_atom_info = atom_info.data(); ok &= MapSetupExpress(map2); for (a = 0; ok && a < n_coord; a++) { if ((!present) || present[a]) { float* v0 = coord + 3 * a; skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map2, atom_info.data(), a_atom_info, coord, a, present, &skip_flag); if (ok && !skip_flag) { int ii = *(MapLocusEStart(map2, v0)); if (ii) { int jj = map2->EList[ii++]; float vdw[3]; vdw[0] = a_atom_info->vdw + probe_radius; vdw[1] = vdw[0] * vdw[0]; while (ok && jj >= 0) { auto* jj_atom_info = &atom_info[jj]; float dist; if (jj > a) /* only check if this is atom trails */ if ((!present) || present[jj]) { float* v2 = coord + 3 * jj; vdw[2] = jj_atom_info->vdw + probe_radius; dist = (float) diff3f(v0, v2); if ((dist > R_SMALL4) && (dist < (vdw[0] + vdw[2]))) { ok = SolventDotCircumscribeAroundVertex(G, I, map, vdw, dist, v0, v2, circumscribe, atom_info.data(), a_atom_info, jj_atom_info, present, a, jj, coord, probe_radius, &dotCnt, stopDot); } } jj = map2->EList[ii++]; } } } } a_atom_info++; ok &= !G->Interrupt; } } MapFree(map2); } } MapFree(map); } if (ok && cavity_mode) { int nCavityDot = 0; int dotCnt = 0; float* cavityDot = VLAlloc(float, (stopDot + 1) * 3); CHECKOK(ok, cavityDot); if (cavity_radius < 0.0F) { cavity_radius = -probe_radius * cavity_radius; } if (cavity_cutoff < 0.0F) { cavity_cutoff = cavity_radius - cavity_cutoff * probe_radius; } { MapType* map = new MapType( G, max_vdw + cavity_radius, coord, n_coord, nullptr, present); CHECKOK(ok, map); if (G->Interrupt) ok = false; if (ok && map) { ok &= MapSetupExpress(map); if (ok) { int a; int skip_flag; SurfaceJobAtomInfo* a_atom_info = atom_info.data(); for (a = 0; a < n_coord; a++) { if ((!present) || (present[a])) { skip_flag = false; ok = SolventDotFilterOutSameXYZ(G, map, atom_info.data(), a_atom_info, coord, a, present, &skip_flag); if (ok && !skip_flag) { ok = SolventDotGetDotsAroundVertexInSphere(G, I, map, atom_info.data(), a_atom_info, coord, a, present, sp, cavity_radius, &dotCnt, stopDot, cavityDot, nullptr, &nCavityDot); } } a_atom_info++; } } } MapFree(map); } { int* dot_flag = pymol::calloc(I->nDot); ErrChkPtr(G, dot_flag); { MapType* map = new MapType(G, cavity_cutoff, cavityDot, nCavityDot, nullptr); if (map) { MapSetupExpress(map); ok = SolventDotMarkDotsWithinCutoff( G, I, map, I->dot, I->nDot, cavityDot, dot_flag, cavity_cutoff); } MapFree(map); } SolventDotSlideDotsAndInfo(G, I, dot_flag, false); FreeP(dot_flag); } VLAFreeP(cavityDot); } if (ok && (cavity_mode != 1) && (cavity_cull > 0) && (probe_radius > 0.75F) && (!surface_solvent)) { int* dot_flag = pymol::calloc(I->nDot); float probe_radius_plus; probe_radius_plus = probe_radius * 1.5F; ErrChkPtr(G, dot_flag); { MapType* map = new MapType(G, probe_radius_plus, I->dot, I->nDot, nullptr); if (map) { int flag = true; MapSetupExpress(map); while (ok && flag) { flag = false; ok = SolventDotMarkDotsWithinProbeRadius( G, I, map, cavity_cull, probe_radius_plus, dot_flag, &flag); } } MapFree(map); } if (ok) { SolventDotSlideDotsAndInfo(G, I, dot_flag, true); } FreeP(dot_flag); } if (!ok) { SolventDotFree(I); I = nullptr; } return I; }