mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
1172 lines
36 KiB
C++
1172 lines
36 KiB
C++
|
|
/*
|
|
A* -------------------------------------------------------------------
|
|
B* This file contains source code for the PyMOL computer program
|
|
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
|
|
D* -------------------------------------------------------------------
|
|
E* It is unlawful to modify or remove this copyright notice.
|
|
F* -------------------------------------------------------------------
|
|
G* Please see the accompanying LICENSE file for further information.
|
|
H* -------------------------------------------------------------------
|
|
I* Additional authors of this source file include:
|
|
-*
|
|
-*
|
|
-*
|
|
Z* -------------------------------------------------------------------
|
|
*/
|
|
#include"os_python.h"
|
|
|
|
#include"os_predef.h"
|
|
#include"os_std.h"
|
|
#include"os_gl.h"
|
|
|
|
#include"Base.h"
|
|
#include"Err.h"
|
|
#include"Vector.h"
|
|
#include"ObjectMolecule.h"
|
|
#include"RepCylBond.h"
|
|
#include"SideChainHelper.h"
|
|
#include"Color.h"
|
|
#include"Setting.h"
|
|
#include"Feedback.h"
|
|
#include"ShaderMgr.h"
|
|
#include"Scene.h"
|
|
#include"CGO.h"
|
|
#include "Lex.h"
|
|
|
|
#include <iostream>
|
|
|
|
struct RepCylBond : Rep {
|
|
using Rep::Rep;
|
|
|
|
~RepCylBond() override;
|
|
|
|
cRep_t type() const override { return cRepCyl; }
|
|
void render(RenderInfo* info) override;
|
|
|
|
CGO* primitiveCGO = nullptr;
|
|
CGO* renderCGO = nullptr;
|
|
};
|
|
|
|
/* RepCylinder -- This function is a helper function that generates a cylinder for RepCylBond.
|
|
* Depending on the s1 and s2 arguments, it will either draw a bond or a half bond. Half bonds
|
|
* always have flat caps on the inside. The isRamped argument specifies whether the colors are
|
|
* interpolated only when s1 and s2 are set.
|
|
*
|
|
* PARAMS
|
|
*
|
|
* cgo - CGO that cylinder operation is added
|
|
* s1, s2 - whether first(s1)/second(s2) half of cylinder is rendered
|
|
* isRamped - only when s1 and s2, whether color interpolation is used
|
|
* v1, v2 - x/y/z of both vertices of cylinder
|
|
* frontCap/endCap - whether front/end of cylinder should be round or not
|
|
* tube_size - radius of cylinder
|
|
* v2color - second color specified as 3 floats (optional)
|
|
* v2pickcolor - second pick color info (ptr to structure Pickable) specified (optional)
|
|
*
|
|
* RETURN VALUE: returns ok (whether adding operation(s) were successful
|
|
*
|
|
*/
|
|
static int RepCylinder(CGO* cgo, bool s1, bool s2, bool isRamped,
|
|
float const* v1, float const* v2, bool frontCap, bool endCap,
|
|
float tube_size, float const* v2color = nullptr,
|
|
Pickable* v2pickcolor = nullptr)
|
|
{
|
|
float axis[3];
|
|
int ok = true;
|
|
subtract3f(v2, v1, axis);
|
|
if (s1 && s2){
|
|
short cap = (frontCap ? cCylShaderCap1Round : 0) |
|
|
(endCap ? cCylShaderCap2Round : 0) |
|
|
(isRamped ? cCylShaderInterpColor : 0);
|
|
if (v2color){
|
|
ok &= (bool)cgo->add<cgo::draw::shadercylinder2ndcolor>(cgo, v1, axis, tube_size, cap, v2color, v2pickcolor);
|
|
} else {
|
|
ok &= (bool)cgo->add<cgo::draw::shadercylinder>(v1, axis, tube_size, cap);
|
|
}
|
|
} else {
|
|
// if either s1 or s2 is 0, then draw half bond
|
|
mult3f(axis, .5f, axis);
|
|
if (s1) {
|
|
short cap = (frontCap ? cCylShaderCap1Round : 0) | cCylShaderCap2Flat;
|
|
ok &= (bool)cgo->add<cgo::draw::shadercylinder>(v1, axis, tube_size, cap);
|
|
} else if (s2){
|
|
short cap = (endCap ? cCylShaderCap2Round : 0) | cCylShaderCap1Flat;
|
|
float v1new[3];
|
|
add3f(v1, axis, v1new);
|
|
if (v2color){
|
|
ok &= CGOColorv(cgo, v2color);
|
|
}
|
|
if (v2pickcolor){
|
|
ok &= CGOPickColor(cgo, v2pickcolor->index, v2pickcolor->bond);
|
|
}
|
|
ok &= (bool)cgo->add<cgo::draw::shadercylinder>(v1new, axis, tube_size, cap);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
RepCylBond::~RepCylBond()
|
|
{
|
|
auto I = this;
|
|
CGOFree(I->primitiveCGO);
|
|
CGOFree(I->renderCGO);
|
|
}
|
|
|
|
static int RepCylBondCGOGenerate(RepCylBond * I, RenderInfo * info)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
|
|
const CGO* input = I->primitiveCGO;
|
|
assert(input);
|
|
|
|
bool const use_shader = info->use_shaders && //
|
|
SettingGet<bool>(*I->cs, cSetting_stick_use_shader);
|
|
bool const as_cylinders =
|
|
use_shader && //
|
|
SettingGet<bool>(*I->cs, cSetting_stick_as_cylinders) &&
|
|
SettingGet<bool>(*I->cs, cSetting_render_as_cylinders) &&
|
|
G->ShaderMgr->ShaderPrgExists("cylinder");
|
|
|
|
std::unique_ptr<CGO> convertcgo;
|
|
|
|
if (as_cylinders) {
|
|
convertcgo.reset(CGONew(G));
|
|
|
|
CGOEnable(convertcgo.get(), GL_CYLINDER_SHADER);
|
|
std::unique_ptr<CGO> cylindercgo(
|
|
CGOConvertShaderCylindersToCylinderShader(input, convertcgo.get()));
|
|
convertcgo->move_append(std::move(*cylindercgo));
|
|
CGODisable(convertcgo.get(), GL_CYLINDER_SHADER);
|
|
|
|
std::unique_ptr<CGO> spherescgo(
|
|
CGOOptimizeSpheresToVBONonIndexed(input, 0, true));
|
|
if (spherescgo) {
|
|
convertcgo->move_append(std::move(*spherescgo));
|
|
}
|
|
} else {
|
|
std::unique_ptr<CGO> simplified(CGOSimplify(input, 0, //
|
|
SettingGet<int>(G, cSetting_cgo_sphere_quality),
|
|
SettingGet<int>(G, cSetting_stick_round_nub)));
|
|
p_return_val_if_fail(simplified, false);
|
|
if (use_shader) {
|
|
convertcgo.reset(CGOOptimizeToVBONotIndexed(simplified.get()));
|
|
} else {
|
|
convertcgo.reset(CGOCombineBeginEnd(simplified.get()));
|
|
}
|
|
}
|
|
|
|
p_return_val_if_fail(convertcgo, false);
|
|
|
|
assert(!I->renderCGO);
|
|
I->renderCGO = convertcgo.release();
|
|
CGOSetUseShader(I->renderCGO, use_shader);
|
|
|
|
return true;
|
|
}
|
|
|
|
void RepCylBond::render(RenderInfo * info)
|
|
{
|
|
auto I = this;
|
|
CRay *ray = info->ray;
|
|
auto pick = info->pick;
|
|
PyMOLGlobals *G = I->G;
|
|
int ok = true;
|
|
|
|
if(ray) {
|
|
#ifndef _PYMOL_NO_RAY
|
|
CGORenderRay(I->primitiveCGO, ray, info, nullptr, nullptr, I->cs->Setting.get(), I->obj->Setting.get());
|
|
ray->transparentf(0.0);
|
|
#endif
|
|
} else if(G->HaveGUI && G->ValidContext) {
|
|
bool use_shader = SettingGetGlobal_b(G, cSetting_stick_use_shader)
|
|
&& SettingGetGlobal_b(G, cSetting_use_shaders);
|
|
|
|
if (I->renderCGO && (CGOCheckWhetherToFree(G, I->renderCGO) || ((bool)I->renderCGO->use_shader) != use_shader)){
|
|
CGOFree(I->renderCGO);
|
|
I->renderCGO = 0;
|
|
}
|
|
|
|
if(pick) {
|
|
PRINTFD(G, FB_RepCylBond)
|
|
" RepCylBondRender: rendering pickable...\n" ENDFD;
|
|
|
|
if (I->renderCGO){
|
|
CGORenderPicking(I->renderCGO, info, &I->context, I->cs->Setting.get(), I->obj->Setting.get());
|
|
}
|
|
} else { /* else not pick, i.e., when rendering */
|
|
if (!I->renderCGO){
|
|
ok &= RepCylBondCGOGenerate(I, info);
|
|
assert(I->renderCGO);
|
|
}
|
|
const float *color = ColorGet(G, I->obj->Color);
|
|
I->renderCGO->debug = SettingGetGlobal_i(G, cSetting_stick_debug);
|
|
CGORender(I->renderCGO, color, nullptr, nullptr, info, I);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int RepZeroOrderBond(RepCylBond* I, CGO* cgo, bool s1, bool s2,
|
|
const float* vv1, const float* vv2, float zradius, const float* rgb1,
|
|
const float* rgb2, unsigned int b1, unsigned int b2, int a, bool b1masked,
|
|
bool b2masked)
|
|
{
|
|
float axis[3], naxis[3];
|
|
subtract3f(vv2, vv1, axis);
|
|
copy3f(axis, naxis);
|
|
normalize3f(naxis);
|
|
float blen = length3f(axis);
|
|
float dgap = zradius*6.f, dlen = zradius*3.f; // dlen - dash length, dgap - gap length
|
|
float placep[3], placep2[3], adddlen[3], adddtot[3];
|
|
float dplace;
|
|
int ndashes = blen / (dlen + dgap);
|
|
|
|
// only do even number of dashes
|
|
if (ndashes < 2) {
|
|
ndashes = 2;
|
|
} else if (ndashes % 2) {
|
|
--ndashes;
|
|
}
|
|
|
|
float remspace = blen - (ndashes * dlen); // remaining space for first gaps
|
|
dgap = remspace / (ndashes + 1.f);
|
|
|
|
int ok = true;
|
|
mult3f(naxis, dlen, adddlen); // adddlen - length of dash as x/y/z vector
|
|
mult3f(naxis, dlen + dgap, adddtot); // adddtot - length of dash plus gap as x/y/z vector
|
|
mult3f(naxis, dgap, placep);
|
|
add3f(vv1, placep, placep);
|
|
if (s1){
|
|
ok &= CGOColorv(I->primitiveCGO, rgb1);
|
|
ok &= CGOPickColor(I->primitiveCGO, b1, b1masked ? cPickableNoPick : a);
|
|
for (dplace = dgap; (dplace+dlen) < blen / 2.f; ){
|
|
add3f(placep, adddlen, placep2);
|
|
ok &= RepCylinder(I->primitiveCGO, true, true, false, placep, placep2, true, true, zradius);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dlen + dgap;
|
|
}
|
|
if (!s2){
|
|
if (dplace < blen / 2.f){
|
|
// if we are behind the mid-point, only s1, so draw a half-bond
|
|
add3f(placep, adddlen, placep2);
|
|
ok &= RepCylinder(I->primitiveCGO, s1, s2, false, placep, placep2, true, false, zradius);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dlen + dgap;
|
|
}
|
|
}
|
|
} else {
|
|
float tmpp[3];
|
|
dplace = dgap + (ndashes/2) * (dlen + dgap);
|
|
mult3f(naxis, dplace, tmpp);
|
|
add3f(vv1, tmpp, placep);
|
|
// if !s1, then definitely s2, so draw half-bond
|
|
if (dplace < blen / 2.f){
|
|
// if no s1, and we are behind the mid-point, draw half-bond with only s2
|
|
add3f(placep, adddlen, placep2);
|
|
ok &= CGOColorv(I->primitiveCGO, rgb2);
|
|
ok &= CGOPickColor(I->primitiveCGO, b2, b2masked ? cPickableNoPick : a);
|
|
ok &= RepCylinder(I->primitiveCGO, s1, s2, false, placep, placep2, false, true, zradius);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dlen + dgap;
|
|
}
|
|
}
|
|
if (s2){
|
|
if (dplace < blen / 2.f){
|
|
// if we are behind the mid-point, draw a split cylinder with both colors
|
|
add3f(placep, adddlen, placep2);
|
|
Pickable pickcolor2 = { b2, b2masked ? cPickableNoPick : a };
|
|
ok &= RepCylinder(I->primitiveCGO, true, true, false, placep, placep2, true, true, zradius, rgb2, &pickcolor2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dlen + dgap;
|
|
}
|
|
ok &= CGOColorv(I->primitiveCGO, rgb2);
|
|
ok &= CGOPickColor(I->primitiveCGO, b2, b2masked ? cPickableNoPick : a);
|
|
for (; (dplace + dlen) < blen; ){
|
|
add3f(placep, adddlen, placep2);
|
|
ok &= RepCylinder(I->primitiveCGO, true, true, false, placep, placep2, true, true, zradius);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dlen + dgap;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static int RepValence(RepCylBond *I, CGO *cgo, bool s1, bool s2, bool isRamped,
|
|
const float *v1, const float *v2, const int *other,
|
|
int a1, int a2, const float *coord,
|
|
const float *color1, const float *color2, int ord,
|
|
float tube_size,
|
|
bool fixed_r, float scale_r,
|
|
Pickable pickdata[] = nullptr)
|
|
{
|
|
float d[3], t[3], p0[3], p1[3], p2[3];
|
|
const float* vv;
|
|
float v1t[3], v2t[3];
|
|
int a3;
|
|
int double_sided;
|
|
int ok = true;
|
|
|
|
/* First, we need to construct a coordinate system */
|
|
|
|
/* get direction vector */
|
|
|
|
p0[0] = (v2[0] - v1[0]);
|
|
p0[1] = (v2[1] - v1[1]);
|
|
p0[2] = (v2[2] - v1[2]);
|
|
|
|
copy3f(p0, d);
|
|
normalize3f(p0);
|
|
|
|
/* need a third atom to get planarity */
|
|
a3 = ObjectMoleculeGetPrioritizedOther(other, a1, a2, &double_sided);
|
|
|
|
if(a3 < 0) {
|
|
t[0] = p0[0];
|
|
t[1] = p0[1];
|
|
t[2] = -p0[2];
|
|
} else {
|
|
vv = coord + 3 * a3;
|
|
t[0] = *(vv++) - v1[0];
|
|
t[1] = *(vv++) - v1[1];
|
|
t[2] = *(vv++) - v1[2];
|
|
normalize3f(t);
|
|
}
|
|
|
|
cross_product3f(d, t, p1);
|
|
|
|
normalize3f(p1);
|
|
|
|
if(length3f(p1) == 0.0) {
|
|
p1[0] = p0[1];
|
|
p1[1] = p0[2];
|
|
p1[2] = p0[0];
|
|
cross_product3f(p0, p1, p2);
|
|
normalize3f(p2);
|
|
} else {
|
|
cross_product3f(d, p1, p2);
|
|
|
|
normalize3f(p2);
|
|
}
|
|
|
|
/* we have a coordinate system */
|
|
|
|
/* Next, we need to determine how many cylinders */
|
|
switch (ord) {
|
|
case 2:
|
|
{
|
|
float radius = tube_size;
|
|
if(!fixed_r) {
|
|
radius *= scale_r;
|
|
radius /= 2.5;
|
|
}
|
|
|
|
t[0] = p2[0] * 1.5F * radius;
|
|
t[1] = p2[1] * 1.5F * radius;
|
|
t[2] = p2[2] * 1.5F * radius;
|
|
|
|
add3f(v1, t, v1t);
|
|
add3f(v2, t, v2t);
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color1);
|
|
if (ok && pickdata)
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
Pickable *pickdataptr = nullptr;
|
|
if (pickdata)
|
|
pickdataptr = &pickdata[1];
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1t, v2t, true, true, radius, color2, pickdataptr);
|
|
subtract3f(v1, t, v1t);
|
|
subtract3f(v2, t, v2t);
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color1);
|
|
if (ok && pickdata)
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1t, v2t, true, true, radius, color2, pickdataptr);
|
|
}
|
|
break;
|
|
case 3:
|
|
{
|
|
float radius = tube_size;
|
|
if(!fixed_r) {
|
|
radius *= scale_r;
|
|
radius /= 3.5;
|
|
}
|
|
|
|
t[0] = p2[0] * 2.5F * radius;
|
|
t[1] = p2[1] * 2.5F * radius;
|
|
t[2] = p2[2] * 2.5F * radius;
|
|
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color1);
|
|
if (ok && pickdata)
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
|
|
copy3f(v1, v1t);
|
|
copy3f(v2, v2t);
|
|
Pickable *pickdataptr = nullptr;
|
|
if (pickdata)
|
|
pickdataptr = &pickdata[1];
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1t, v2t, true, true, radius, color2, pickdataptr);
|
|
add3f(v1, t, v1t);
|
|
add3f(v2, t, v2t);
|
|
if (ok && pickdata){
|
|
ok &= CGOColorv(cgo, color1);
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
}
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1t, v2t, true, true, radius, color2, pickdataptr);
|
|
subtract3f(v1, t, v1t);
|
|
subtract3f(v2, t, v2t);
|
|
if (ok && pickdata){
|
|
ok &= CGOColorv(cgo, color1);
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
}
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1t, v2t, true, true, radius, color2, pickdataptr);
|
|
}
|
|
break;
|
|
case 4:
|
|
{
|
|
float radius = tube_size;
|
|
float radius2 = tube_size;
|
|
float along[3], adj[3], v1tt[3], v2tt[3];
|
|
float inner1a = 0.24F;
|
|
float inner1b = 0.44F;
|
|
float inner2a = 0.5F + (0.5F - inner1b);
|
|
float inner2b = 1.0F - inner1a;
|
|
|
|
if(!fixed_r) {
|
|
radius *= scale_r;
|
|
radius2 = radius / 2.5F;
|
|
t[0] = p2[0] * 1.5F * radius;
|
|
t[1] = p2[1] * 1.5F * radius;
|
|
t[2] = p2[2] * 1.5F * radius;
|
|
} else {
|
|
inner1a -= 0.04F;
|
|
inner2b = 1.0F - inner1a;
|
|
t[0] = p2[0] * 3 * radius;
|
|
t[1] = p2[1] * 3 * radius;
|
|
t[2] = p2[2] * 3 * radius;
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color1);
|
|
if (ok && pickdata)
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
|
|
subtract3f(v1, t, v1t);
|
|
subtract3f(v2, t, v2t);
|
|
subtract3f(v2t, v1t, along);
|
|
scale3f(along, inner1a, adj);
|
|
add3f(adj, v1t, v1tt);
|
|
scale3f(along, inner1b, adj);
|
|
add3f(adj, v1t, v2tt);
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1tt, v2tt, true, true, radius2);
|
|
if(double_sided) {
|
|
add3f(v1, t, v1t);
|
|
add3f(v2, t, v2t);
|
|
subtract3f(v2t, v1t, along);
|
|
scale3f(along, inner1a, adj);
|
|
add3f(adj, v1t, v1tt);
|
|
scale3f(along, inner1b, adj);
|
|
add3f(adj, v1t, v2tt);
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1tt, v2tt, true, true, radius2);
|
|
}
|
|
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color2);
|
|
if (ok && pickdata)
|
|
ok &= CGOPickColor(cgo, pickdata[1].index, pickdata[1].bond);
|
|
subtract3f(v1, t, v1t);
|
|
subtract3f(v2, t, v2t);
|
|
subtract3f(v2t, v1t, along);
|
|
scale3f(along, inner2a, adj);
|
|
add3f(adj, v1t, v1tt);
|
|
scale3f(along, inner2b, adj);
|
|
add3f(adj, v1t, v2tt);
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1tt, v2tt, true, true, radius2);
|
|
if(double_sided) {
|
|
add3f(v1, t, v1t);
|
|
add3f(v2, t, v2t);
|
|
subtract3f(v2t, v1t, along);
|
|
scale3f(along, inner2a, adj);
|
|
add3f(adj, v1t, v1tt);
|
|
scale3f(along, inner2b, adj);
|
|
add3f(adj, v1t, v2tt);
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1tt, v2tt, true, true, radius2);
|
|
}
|
|
|
|
Pickable *pickdataptr = nullptr;
|
|
if (ok)
|
|
ok &= CGOColorv(cgo, color1);
|
|
if (ok && pickdata){
|
|
ok &= CGOPickColor(cgo, pickdata[0].index, pickdata[0].bond);
|
|
pickdataptr = &pickdata[1];
|
|
}
|
|
if (ok)
|
|
ok &= RepCylinder(cgo, s1, s2, isRamped, v1, v2, true, true, radius, color2, pickdataptr);
|
|
}
|
|
break;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
Rep *RepCylBondNew(CoordSet * cs, int state)
|
|
{
|
|
PyMOLGlobals *G = cs->G;
|
|
ObjectMolecule *obj;
|
|
int a1, a2; // can be -1 for missing atoms
|
|
int a;
|
|
int c1, c2, s1, s2;
|
|
unsigned int b1, b2;
|
|
const BondType *b;
|
|
float radius;
|
|
float valence;
|
|
int half_bonds, *other = nullptr;
|
|
int visFlag;
|
|
int ord;
|
|
int stick_ball, stick_ball_color = -1;
|
|
float stick_ball_ratio = 1.0F;
|
|
const AtomInfoType *ai1;
|
|
bool fixed_radius = false;
|
|
int valence_flag = false;
|
|
int hide_long = false;
|
|
int stick_color = 0;
|
|
int cartoon_side_chain_helper = 0;
|
|
int ribbon_side_chain_helper = 1;
|
|
int na_mode;
|
|
bool *marked = nullptr;
|
|
float *capdrawn = nullptr;
|
|
float scale_r = 1.0F;
|
|
float transp, h_scale;
|
|
float prev_transp = -1;
|
|
int valence_found = false;
|
|
const float _0p9 = 0.9F;
|
|
short shader_mode = 0;
|
|
int ok = true;
|
|
|
|
PRINTFD(G, FB_RepCylBond)
|
|
" RepCylBondNew-Debug: entered.\n" ENDFD;
|
|
obj = cs->Obj;
|
|
visFlag = false;
|
|
b = obj->Bond;
|
|
ai1 = obj->AtomInfo;
|
|
if(obj->RepVisCache & cRepCylBit)
|
|
for(a = 0; a < obj->NBond; a++) {
|
|
b1 = b->index[0];
|
|
b2 = b->index[1];
|
|
if((cRepCylBit & ai1[b1].visRep & ai1[b2].visRep)) {
|
|
visFlag = true;
|
|
break;
|
|
}
|
|
b++;
|
|
}
|
|
if(!visFlag) {
|
|
return (nullptr); /* skip if no sticks are visible */
|
|
}
|
|
|
|
capdrawn = pymol::calloc<float>(obj->NAtom); // max radius of caps
|
|
marked = pymol::calloc<bool>(obj->NAtom);
|
|
CHECKOK(ok, marked);
|
|
if (!ok){
|
|
return nullptr;
|
|
}
|
|
|
|
valence = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence);
|
|
valence_flag = (valence != 0.0F);
|
|
|
|
stick_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_color);
|
|
cartoon_side_chain_helper = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(),
|
|
cSetting_cartoon_side_chain_helper);
|
|
ribbon_side_chain_helper = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(),
|
|
cSetting_ribbon_side_chain_helper);
|
|
|
|
transp = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_transparency);
|
|
hide_long = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_hide_long_bonds);
|
|
|
|
std::set<int> all_zero_order_bond_atoms;
|
|
b = obj->Bond;
|
|
for(a = 0; ok && a < obj->NBond; a++) {
|
|
b1 = b->index[0];
|
|
b2 = b->index[1];
|
|
ord = b->order;
|
|
a1 = cs->atmToIdx(b1);
|
|
a2 = cs->atmToIdx(b2);
|
|
|
|
if((a1 >= 0) && (a2 >= 0)) {
|
|
AtomInfoType *ati1 = obj->AtomInfo + b1;
|
|
AtomInfoType *ati2 = obj->AtomInfo + b2;
|
|
s1 = GET_BIT(ati1->visRep, cRepCyl);
|
|
s2 = GET_BIT(ati2->visRep, cRepCyl);
|
|
|
|
if (s1 && s2){
|
|
if (!valence_found)
|
|
valence_found = BondSettingGetWD(G, b, cSetting_valence, valence_flag);
|
|
|
|
if (!ord){
|
|
all_zero_order_bond_atoms.insert(b1);
|
|
all_zero_order_bond_atoms.insert(b2);
|
|
}
|
|
}
|
|
}
|
|
b++;
|
|
ok &= !G->Interrupt;
|
|
}
|
|
radius = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_radius);
|
|
half_bonds = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_half_bonds);
|
|
na_mode =
|
|
SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_cartoon_nucleic_acid_mode);
|
|
int na_mode_ribbon =
|
|
SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_ribbon_nucleic_acid_mode);
|
|
h_scale = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_h_scale);
|
|
auto valence_zero_scale =
|
|
SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence_zero_scale);
|
|
auto valence_zero_mode =
|
|
SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence_zero_mode);
|
|
|
|
auto I = new RepCylBond(cs, state);
|
|
|
|
I->primitiveCGO = CGONew(G);
|
|
if(ok && obj->NBond) {
|
|
stick_ball = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_ball);
|
|
|
|
shader_mode = SettingGetGlobal_b(G, cSetting_use_shaders) && SettingGetGlobal_b(G, cSetting_stick_as_cylinders)
|
|
&& SettingGetGlobal_b(G, cSetting_render_as_cylinders) && SettingGetGlobal_b(G, cSetting_stick_use_shader);
|
|
|
|
if(cartoon_side_chain_helper || ribbon_side_chain_helper) {
|
|
SideChainHelperMarkNonCartoonBonded(marked, obj, cs,
|
|
cartoon_side_chain_helper,
|
|
ribbon_side_chain_helper);
|
|
}
|
|
|
|
if(valence_found) { /* build list of up to 2 connected atoms for each atom */
|
|
other = ObjectMoleculeGetPrioritizedOtherIndexList(obj, cs);
|
|
CHECKOK(ok, other);
|
|
if (ok){
|
|
fixed_radius = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_fixed_radius);
|
|
scale_r = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_valence_scale);
|
|
}
|
|
}
|
|
|
|
/* spheres for stick & balls */
|
|
stick_ball_ratio = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_ball_ratio);
|
|
stick_ball_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_ball_color);
|
|
|
|
b = obj->Bond;
|
|
for(a = 0; ok && a < obj->NBond; ++a, ++b) {
|
|
b1 = b->index[0];
|
|
b2 = b->index[1];
|
|
ord = b->order;
|
|
|
|
if (ord == 0 && valence_zero_mode == 0)
|
|
continue;
|
|
|
|
a1 = cs->atmToIdx(b1);
|
|
a2 = cs->atmToIdx(b2);
|
|
|
|
if((a1 >= 0) && (a2 >= 0)) {
|
|
AtomInfoType *ati1 = obj->AtomInfo + b1;
|
|
AtomInfoType *ati2 = obj->AtomInfo + b2;
|
|
float bd_radius_full;
|
|
|
|
auto bd_stick_color = BondSettingGetWD(G, b, cSetting_stick_color, stick_color);
|
|
auto bd_radius = BondSettingGetWD(G, b, cSetting_stick_radius, radius);
|
|
|
|
// version <=1.8.2 used negative stick_radius to turn on
|
|
// stick_h_scale, which had a default of 0.4 (now: 1.0)
|
|
if(bd_radius < 0.0F) {
|
|
bd_radius = -bd_radius;
|
|
|
|
// legacy behavior
|
|
if (h_scale == 1.0F)
|
|
h_scale = 0.4F;
|
|
}
|
|
|
|
// before H scaling
|
|
bd_radius_full = bd_radius;
|
|
|
|
// scaling for bonds involving hydrogen
|
|
if (ati1->isHydrogen() || ati2->isHydrogen())
|
|
bd_radius *= h_scale;
|
|
|
|
if(bd_stick_color < 0) {
|
|
if(bd_stick_color == cColorObject) {
|
|
c1 = (c2 = obj->Color);
|
|
} else if(ColorCheckRamped(G, bd_stick_color)) {
|
|
c1 = (c2 = bd_stick_color);
|
|
} else {
|
|
c1 = ati1->color;
|
|
c2 = ati2->color;
|
|
}
|
|
} else {
|
|
c1 = (c2 = bd_stick_color);
|
|
}
|
|
|
|
s1 = GET_BIT(ati1->visRep, cRepCyl);
|
|
s2 = GET_BIT(ati2->visRep, cRepCyl);
|
|
|
|
if (!(s1 || s2)) {
|
|
continue;
|
|
}
|
|
|
|
if (!(s1 && s2) && !half_bonds) {
|
|
continue;
|
|
}
|
|
|
|
auto const s1_before_symop = s1;
|
|
auto const s2_before_symop = s2;
|
|
int symop_pass = 0;
|
|
|
|
pymol::SymOp symop[2] = {pymol::SymOp(), b->symop_2};
|
|
assert(!symop[0]);
|
|
float vv_buf[2][3];
|
|
float const *vv1, *vv2;
|
|
|
|
inv_sym_bond:
|
|
|
|
vv1 = cs->coordPtrSym(a1, symop[0], vv_buf[0], symop_pass);
|
|
vv2 = cs->coordPtrSym(a2, symop[1], vv_buf[1], symop_pass);
|
|
|
|
if (!vv1 || !vv2) {
|
|
PRINTFB(G, FB_RepCylBond, FB_Warnings)
|
|
" %s-Warning: Failed to get symmetry coordiantes\n",
|
|
__func__ ENDFB(G);
|
|
continue;
|
|
}
|
|
|
|
// show half-bond for atom which connects to a symmetry mate
|
|
s1 = s1_before_symop && !symop[0];
|
|
s2 = s2_before_symop && !symop[1];
|
|
|
|
if(hide_long && (s1 || s2)) {
|
|
float cutoff = (ati1->vdw + ati2->vdw) * _0p9;
|
|
if(!within3f(vv1, vv2, cutoff)) /* atoms separated by more than 90% of the sum of their vdw radii */
|
|
s1 = s2 = 0;
|
|
}
|
|
|
|
// side chain helpers
|
|
if ((s1 || s2) && (ati1->flags & ati2->flags & cAtomFlag_polymer)) {
|
|
if ((cRepCartoonBit & ati1->visRep & ati2->visRep)) {
|
|
bool sc_helper =
|
|
AtomSettingGetWD(G, ati1,
|
|
cSetting_cartoon_side_chain_helper, cartoon_side_chain_helper) ||
|
|
AtomSettingGetWD(G, ati2,
|
|
cSetting_cartoon_side_chain_helper, cartoon_side_chain_helper);
|
|
if (sc_helper &&
|
|
SideChainHelperFilterBond(G, marked, ati1, ati2, b1, b2, na_mode, &c1, &c2))
|
|
s1 = s2 = 0;
|
|
}
|
|
|
|
if ((s1 || s2) && (cRepRibbonBit & ati1->visRep & ati2->visRep)) {
|
|
bool sc_helper =
|
|
AtomSettingGetWD(G, ati1,
|
|
cSetting_ribbon_side_chain_helper, ribbon_side_chain_helper) ||
|
|
AtomSettingGetWD(G, ati2,
|
|
cSetting_ribbon_side_chain_helper, ribbon_side_chain_helper);
|
|
if (sc_helper &&
|
|
SideChainHelperFilterBond(G, marked, ati1, ati2, b1, b2, na_mode_ribbon, &c1, &c2))
|
|
s1 = s2 = 0;
|
|
}
|
|
}
|
|
|
|
/* This means that if stick_ball gets changed, the RepCylBond needs to be completely invalidated */
|
|
|
|
auto stick_ball_impl = [&](AtomInfoType * ati1, int b1, int c1, float const* vv1) {
|
|
int stick_ball_1 = AtomSettingGetWD(G, ati1, cSetting_stick_ball, stick_ball);
|
|
if(stick_ball_1) {
|
|
float vdw = stick_ball_ratio * ((ati1->protons == cAN_H) ? bd_radius : bd_radius_full);
|
|
float vdw1 = (vdw >= 0) ? vdw : -ati1->vdw * vdw;
|
|
|
|
// only draw cap if larger than any previously drawn (PYMOL-2527)
|
|
if (vdw1 < capdrawn[b1])
|
|
return;
|
|
|
|
int sbc1 = (stick_ball_color == cColorDefault) ? c1 : stick_ball_color;
|
|
float rgb1[3];
|
|
if(sbc1 == cColorAtomic)
|
|
sbc1 = ati1->color;
|
|
capdrawn[b1] = vdw1;
|
|
ColorGetCheckRamped(G, sbc1, vv1, rgb1, state);
|
|
CGOColorv(I->primitiveCGO, rgb1);
|
|
CGOPickColor(I->primitiveCGO, b1, ati1->masked ? cPickableNoPick : a);
|
|
CGOSphere(I->primitiveCGO, vv1, vdw1);
|
|
}
|
|
};
|
|
|
|
if(s1 || s2) {
|
|
auto const bd_transp =
|
|
BondSettingGetWD(G, b, cSetting_stick_transparency, transp);
|
|
|
|
if (prev_transp != bd_transp) {
|
|
prev_transp = bd_transp;
|
|
CGOAlpha(I->primitiveCGO, 1.0F - bd_transp);
|
|
|
|
if (bd_transp > 0) {
|
|
I->setHasTransparency();
|
|
}
|
|
}
|
|
|
|
if (s1) stick_ball_impl(ati1, b1, c1, vv1);
|
|
if (s2) stick_ball_impl(ati2, b2, c2, vv2);
|
|
|
|
float rgb1[3], rgb2[3];
|
|
bool isRamped = false;
|
|
isRamped = ColorGetCheckRamped(G, c1, vv1, rgb1, state);
|
|
isRamped = ColorGetCheckRamped(G, c2, vv2, rgb2, state) | isRamped;
|
|
|
|
if (ord == 0) {
|
|
bd_radius *= valence_zero_scale;
|
|
if (valence_zero_mode == 2) {
|
|
ord = 1;
|
|
}
|
|
}
|
|
|
|
if (!ord){
|
|
// zero order bonds
|
|
ok &= RepZeroOrderBond(I, I->primitiveCGO, s1, s2, vv1, vv2, bd_radius, rgb1, rgb2, b1, b2, a, ati1->masked, ati2->masked);
|
|
} else {
|
|
all_zero_order_bond_atoms.erase(b1);
|
|
all_zero_order_bond_atoms.erase(b2);
|
|
|
|
bool bd_valence_flag = (ord > 1) && (ord < 5) &&
|
|
BondSettingGetWD(G, b, cSetting_valence, valence_flag);
|
|
|
|
if(bd_valence_flag) {
|
|
Pickable pickdata[] = { { b1, ati1->masked ? cPickableNoPick : a },
|
|
{ b2, ati2->masked ? cPickableNoPick : a } };
|
|
ok &= RepValence(I, I->primitiveCGO, s1, s2, isRamped, vv1, vv2, other,
|
|
a1, a2, cs->Coord, rgb1, rgb2, ord,
|
|
bd_radius, fixed_radius, scale_r, pickdata);
|
|
} else {
|
|
ok &= CGOColorv(I->primitiveCGO, rgb1);
|
|
ok &= CGOPickColor(I->primitiveCGO, b1, ati1->masked ? cPickableNoPick : a);
|
|
/* generate a cylinder */
|
|
if (ok){
|
|
Pickable pickdata = { b2, ati2->masked ? cPickableNoPick : a };
|
|
|
|
bool drawcap1 = bd_radius > capdrawn[b1] && s1;
|
|
bool drawcap2 = bd_radius > capdrawn[b2] && s2;
|
|
|
|
ok &= RepCylinder(I->primitiveCGO, s1, s2, isRamped, vv1, vv2,
|
|
drawcap1, drawcap2, bd_radius, rgb2, &pickdata);
|
|
|
|
if (shader_mode) {
|
|
// don't render caps twice with the cylinder shader
|
|
if (drawcap1) capdrawn[b1] = bd_radius;
|
|
if (drawcap2) capdrawn[b2] = bd_radius;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this was a half-bond to a symmetry mate, do another pass and
|
|
// render the other half.
|
|
if (symop_pass == 0 && ati1 != ati2 && symop[1]) {
|
|
symop_pass = 1;
|
|
std::swap(symop[0], symop[1]);
|
|
goto inv_sym_bond;
|
|
}
|
|
}
|
|
}
|
|
/* for all zero order bond atoms that do not have any other ordered bonds,
|
|
a sphere should be rendered for it (a cylinder with vertices that are almost
|
|
exactly the same is used to render a sphere so that we won't need to use
|
|
the sphere shader excessively. */
|
|
for (auto at : all_zero_order_bond_atoms){
|
|
ai1 = obj->AtomInfo + at;
|
|
c1 = ai1->color;
|
|
float *v1 = cs->coordPtr(cs->atmToIdx(at));
|
|
float v2[3];
|
|
float rgb1[3];
|
|
float v[3] = {R_SMALL4,0.,0.};
|
|
// instead of using the sphere shader, we use a cylinder with 0 length (or very close to zero,
|
|
// since the shader uses the axis for many computations/checks
|
|
add3f(v1, v, v2);
|
|
|
|
ColorGetCheckRamped(G, c1, v1, rgb1, state);
|
|
ok &= CGOColorv(I->primitiveCGO, rgb1);
|
|
ok &= CGOPickColor(I->primitiveCGO, at, ai1->masked ? cPickableNoPick : cPickableAtom);
|
|
ok &= RepCylinder(I->primitiveCGO, true, true, false, v1, v2, true, true, radius);
|
|
}
|
|
}
|
|
FreeP(other);
|
|
FreeP(marked);
|
|
FreeP(capdrawn);
|
|
|
|
CGOStop(I->primitiveCGO);
|
|
if (!ok){
|
|
delete I;
|
|
I = nullptr;
|
|
}
|
|
|
|
return (Rep *) I;
|
|
}
|
|
|
|
#ifndef PURE_OPENGL_ES_2
|
|
static void RepCylinderImmediate(const float *v1arg, const float *v2arg, int nEdge,
|
|
int frontCapArg, int endCapArg,
|
|
float overlap, float nub, float radius, float **dir)
|
|
{
|
|
float d[3], t[3], p0[3], p1[3], p2[3], v1ptr[3], v2ptr[3], *v1, *v2;
|
|
float v[3], vv[3], vvv[3];
|
|
float x, y;
|
|
int c, frontCap = frontCapArg, endCap = endCapArg, tmpCap;
|
|
|
|
p0[0] = (v2arg[0] - v1arg[0]);
|
|
p0[1] = (v2arg[1] - v1arg[1]);
|
|
p0[2] = (v2arg[2] - v1arg[2]);
|
|
|
|
normalize3f(p0);
|
|
|
|
v1ptr[0] = v1arg[0]; v1ptr[1] = v1arg[1]; v1ptr[2] = v1arg[2];
|
|
v2ptr[0] = v2arg[0]; v2ptr[1] = v2arg[1]; v2ptr[2] = v2arg[2];
|
|
|
|
v1ptr[0] -= p0[0] * overlap;
|
|
v1ptr[1] -= p0[1] * overlap;
|
|
v1ptr[2] -= p0[2] * overlap;
|
|
|
|
if(endCap) {
|
|
v2ptr[0] += p0[0] * overlap;
|
|
v2ptr[1] += p0[1] * overlap;
|
|
v2ptr[2] += p0[2] * overlap;
|
|
}
|
|
|
|
v1 = v1ptr;
|
|
v2 = v2ptr;
|
|
|
|
d[0] = (v2[0] - v1[0]);
|
|
d[1] = (v2[1] - v1[1]);
|
|
d[2] = (v2[2] - v1[2]);
|
|
|
|
if (dir){
|
|
if (!*dir){
|
|
*dir = pymol::malloc<float>(3);
|
|
(*dir)[0] = d[0]; (*dir)[1] = d[1]; (*dir)[2] = d[2];
|
|
} else {
|
|
if (get_angle3f(d, *dir)>=(cPI/2.)){
|
|
v1 = v2ptr;
|
|
v2 = v1ptr;
|
|
d[0] = -d[0]; d[1] = -d[1]; d[2] = -d[2];
|
|
tmpCap = frontCap;
|
|
frontCap = endCap;
|
|
endCap = tmpCap;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* direction vector */
|
|
|
|
p0[0] = (v2[0] - v1[0]);
|
|
p0[1] = (v2[1] - v1[1]);
|
|
p0[2] = (v2[2] - v1[2]);
|
|
|
|
normalize3f(p0);
|
|
get_divergent3f(d, t);
|
|
cross_product3f(d, t, p1);
|
|
normalize3f(p1);
|
|
cross_product3f(d, p1, p2);
|
|
normalize3f(p2);
|
|
|
|
/* now we have a coordinate system */
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
|
|
for(c = nEdge; c >= 0; c--) {
|
|
x = (float) radius * cos(c * 2 * PI / nEdge);
|
|
y = (float) radius * sin(c * 2 * PI / nEdge);
|
|
v[0] = p1[0] * x + p2[0] * y;
|
|
v[1] = p1[1] * x + p2[1] * y;
|
|
v[2] = p1[2] * x + p2[2] * y;
|
|
|
|
vv[0] = v1[0] + v[0];
|
|
vv[1] = v1[1] + v[1];
|
|
vv[2] = v1[2] + v[2];
|
|
|
|
glNormal3fv(v);
|
|
|
|
vvv[0] = vv[0] + d[0];
|
|
vvv[1] = vv[1] + d[1];
|
|
vvv[2] = vv[2] + d[2];
|
|
|
|
glVertex3fv(vv);
|
|
glVertex3fv(vvv);
|
|
}
|
|
glEnd();
|
|
|
|
if(frontCap) {
|
|
v[0] = -p0[0];
|
|
v[1] = -p0[1];
|
|
v[2] = -p0[2];
|
|
|
|
vv[0] = v1[0] - p0[0] * nub;
|
|
vv[1] = v1[1] - p0[1] * nub;
|
|
vv[2] = v1[2] - p0[2] * nub;
|
|
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
|
|
glNormal3fv(v);
|
|
glVertex3fv(vv);
|
|
|
|
for(c = nEdge; c >= 0; c--) {
|
|
x = (float) radius * cos(c * 2 * PI / nEdge);
|
|
y = (float) radius * sin(c * 2 * PI / nEdge);
|
|
v[0] = p1[0] * x + p2[0] * y;
|
|
v[1] = p1[1] * x + p2[1] * y;
|
|
v[2] = p1[2] * x + p2[2] * y;
|
|
|
|
vv[0] = v1[0] + v[0];
|
|
vv[1] = v1[1] + v[1];
|
|
vv[2] = v1[2] + v[2];
|
|
|
|
glNormal3fv(v);
|
|
glVertex3fv(vv);
|
|
}
|
|
|
|
glEnd();
|
|
}
|
|
|
|
if(endCap) {
|
|
|
|
v[0] = p0[0];
|
|
v[1] = p0[1];
|
|
v[2] = p0[2];
|
|
|
|
vv[0] = v2[0] + p0[0] * nub;
|
|
vv[1] = v2[1] + p0[1] * nub;
|
|
vv[2] = v2[2] + p0[2] * nub;
|
|
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
|
|
glNormal3fv(v);
|
|
glVertex3fv(vv);
|
|
|
|
for(c = 0; c <= nEdge; c++) {
|
|
x = (float) radius * cos(c * 2 * PI / nEdge);
|
|
y = (float) radius * sin(c * 2 * PI / nEdge);
|
|
v[0] = p1[0] * x + p2[0] * y;
|
|
v[1] = p1[1] * x + p2[1] * y;
|
|
v[2] = p1[2] * x + p2[2] * y;
|
|
|
|
vv[0] = v2[0] + v[0];
|
|
vv[1] = v2[1] + v[1];
|
|
vv[2] = v2[2] + v[2];
|
|
glNormal3fv(v);
|
|
glVertex3fv(vv);
|
|
}
|
|
glEnd();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void RepCylBondRenderImmediate(CoordSet * cs, RenderInfo * info)
|
|
{
|
|
#ifndef PURE_OPENGL_ES_2
|
|
/* performance optimized, so it does not support the following:
|
|
|
|
- anything other than opengl
|
|
- display of bond valences
|
|
- per-bond & per-atom properties
|
|
- half-bonds
|
|
- helper settings such as cartoon_side_chain_helper
|
|
- suppression of long bonds
|
|
- color ramps
|
|
- atom picking
|
|
- display lists
|
|
- transparency
|
|
|
|
*/
|
|
|
|
PyMOLGlobals *G = cs->G;
|
|
if(info->ray || info->pick || (!(G->HaveGUI && G->ValidContext)))
|
|
return;
|
|
else {
|
|
int active = false;
|
|
ObjectMolecule *obj = cs->Obj;
|
|
int nEdge = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_quality);
|
|
float radius =
|
|
fabs(SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_radius));
|
|
float overlap =
|
|
SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_overlap);
|
|
float nub = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_stick_nub);
|
|
float overlap_r = radius * overlap;
|
|
float nub_r = radius * nub;
|
|
|
|
{
|
|
int a;
|
|
int nBond = obj->NBond;
|
|
const BondType *bd = obj->Bond.data();
|
|
const AtomInfoType *ai = obj->AtomInfo.data();
|
|
int last_color = -9;
|
|
const float *coord = cs->Coord.data();
|
|
const float _pt5 = 0.5F;
|
|
|
|
for(a = 0; a < nBond; a++) {
|
|
int b1 = bd->index[0];
|
|
int b2 = bd->index[1];
|
|
const AtomInfoType *ai1, *ai2;
|
|
bd++;
|
|
|
|
if( ((ai1 = ai + b1)->visRep & cRepCylBit) &&
|
|
((ai2 = ai + b2)->visRep & cRepCylBit)) {
|
|
int a1, a2;
|
|
active = true;
|
|
a1 = cs->atmToIdx(b1);
|
|
a2 = cs->atmToIdx(b2);
|
|
|
|
if((a1 >= 0) && (a2 >= 0)) {
|
|
int c1 = ai1->color;
|
|
int c2 = ai2->color;
|
|
|
|
const float *v1 = coord + 3 * a1;
|
|
const float *v2 = coord + 3 * a2;
|
|
|
|
if(c1 == c2) { /* same colors -> one cylinder */
|
|
if(c1 != last_color) {
|
|
last_color = c1;
|
|
glColor3fv(ColorGet(G, c1));
|
|
}
|
|
|
|
/* overlap is half since it is one cylinder representing both halfs of a bond */
|
|
RepCylinderImmediate(v1, v2, nEdge, 1, 1, overlap_r, nub_r, radius, nullptr);
|
|
|
|
} else { /* different colors -> two cylinders, no interior */
|
|
float avg[3], *dir = nullptr;
|
|
|
|
avg[0] = (v1[0] + v2[0]) * _pt5;
|
|
avg[1] = (v1[1] + v2[1]) * _pt5;
|
|
avg[2] = (v1[2] + v2[2]) * _pt5;
|
|
|
|
if(c1 != last_color) {
|
|
last_color = c1;
|
|
glColor3fv(ColorGet(G, c1));
|
|
}
|
|
|
|
RepCylinderImmediate(v1, avg, nEdge, 1, 0, overlap_r, nub_r, radius, &dir);
|
|
|
|
if(c2 != last_color) {
|
|
last_color = c2;
|
|
glColor3fv(ColorGet(G, c2));
|
|
}
|
|
|
|
RepCylinderImmediate(v2, avg, nEdge, 1, 0, overlap_r, nub_r, radius, &dir);
|
|
if (dir){
|
|
FreeP(dir);
|
|
dir = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!active)
|
|
cs->Active[cRepCyl] = false;
|
|
}
|
|
#endif
|
|
}
|