mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
868 lines
27 KiB
C++
868 lines
27 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_gl.h"
|
|
|
|
#include"Rep.h"
|
|
#include"Err.h"
|
|
#include"RepWireBond.h"
|
|
#include"SideChainHelper.h"
|
|
#include"Color.h"
|
|
#include"Scene.h"
|
|
#include"main.h"
|
|
#include"Setting.h"
|
|
#include"ShaderMgr.h"
|
|
#include"CGO.h"
|
|
#include "Feedback.h"
|
|
#include"CoordSet.h"
|
|
|
|
struct RepWireBond : Rep {
|
|
using Rep::Rep;
|
|
|
|
~RepWireBond() override;
|
|
|
|
cRep_t type() const override { return cRepLine; }
|
|
void render(RenderInfo* info) override;
|
|
|
|
CGO *shaderCGO = nullptr;
|
|
CGO *primitiveCGO = nullptr;
|
|
bool shaderCGO_has_cylinders = false;
|
|
};
|
|
|
|
#include"ObjectMolecule.h"
|
|
|
|
static int RepLine(CGO *cgo, bool s1, bool s2, bool isRamped,
|
|
const float *v1,
|
|
const float *v2,
|
|
const float *v1color,
|
|
unsigned int b1, unsigned int b2, int a,
|
|
const float *v2color,
|
|
bool b1masked, bool b2masked)
|
|
{
|
|
int ok = true;
|
|
if (s1 && s2){
|
|
CGOColorv(cgo, v1color);
|
|
CGOPickColor(cgo, b1, b1masked ? cPickableNoPick : a);
|
|
{
|
|
// if not ramped, then insert vertices so colors are not interpolated since lines interpolate by default
|
|
bool eq = equal3f(v1color, v2color);
|
|
bool split = !eq || b1 != b2;
|
|
if (split){
|
|
cgo->add<cgo::draw::splitline>(v1, v2, v2color, b2, b2masked ? cPickableNoPick : a, isRamped, b1==b2, eq);
|
|
cgo->current_pick_color_index = b2;
|
|
cgo->current_pick_color_bond = b2masked ? cPickableNoPick : a;
|
|
} else {
|
|
cgo->add<cgo::draw::line>(v1, v2);
|
|
}
|
|
}
|
|
} else {
|
|
// if half bond, then split for either s1 or s2
|
|
float h[3];
|
|
average3f(v1, v2, h);
|
|
if (s1){
|
|
CGOColorv(cgo, v1color);
|
|
CGOPickColor(cgo, b1, b1masked ? cPickableNoPick : a);
|
|
cgo->add<cgo::draw::line>(v1, h);
|
|
} else {
|
|
if (v2color)
|
|
CGOColorv(cgo, v2color);
|
|
if (b2)
|
|
CGOPickColor(cgo, b2, b2masked ? cPickableNoPick : a);
|
|
cgo->add<cgo::draw::line>(h, v2);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
static void RepValence(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, int fancy, unsigned int b1, unsigned int b2, int a, bool b1masked, bool b2masked)
|
|
{
|
|
|
|
float d[3], t[3], p0[3], p1[3], p2[3];
|
|
int a3;
|
|
const float indent = tube_size;
|
|
/* direction vector */
|
|
subtract3f(v2, v1, p0);
|
|
copy3f(p0, d);
|
|
normalize3f(p0);
|
|
/* need a prioritized third atom to get planarity */
|
|
a3 = ObjectMoleculeGetPrioritizedOther(other, a1, a2, nullptr);
|
|
if(a3 < 0) {
|
|
t[0] = p0[0];
|
|
t[1] = p0[1];
|
|
t[2] = -p0[2];
|
|
} else {
|
|
subtract3f(coord + 3 * a3, v1, t);
|
|
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);
|
|
}
|
|
/* now we have a coordinate system */
|
|
mult3f(p2, tube_size, t);
|
|
|
|
bool ord3 = (ord == 3);
|
|
if (ord3)
|
|
mult3f(t, 2.f, t);
|
|
if (fancy || ord3){
|
|
RepLine(cgo, s1, s2, isRamped, v1, v2, color1, b1, b2, a, color2, b1masked, b2masked);
|
|
}
|
|
if(fancy) {
|
|
float f[] = { indent, 1.f - indent };
|
|
float f_1[] = { 1.f - f[0], 1.f - f[1] };
|
|
float vv1[] = { (f_1[0] * v1[0] + f[0] * v2[0]) - 2 * t[0],
|
|
(f_1[0] * v1[1] + f[0] * v2[1]) - 2 * t[1],
|
|
(f_1[0] * v1[2] + f[0] * v2[2]) - 2 * t[2] };
|
|
float vv2[] = { (f_1[1] * v1[0] + f[1] * v2[0]) - 2 * t[0],
|
|
(f_1[1] * v1[1] + f[1] * v2[1]) - 2 * t[1],
|
|
(f_1[1] * v1[2] + f[1] * v2[2]) - 2 * t[2] };
|
|
RepLine(cgo, s1, s2, isRamped, vv1, vv2, color1, b1, b2, a, color2, b1masked, b2masked);
|
|
} else {
|
|
float vv1[][3] = { { v1[0] - t[0], v1[1] - t[1], v1[2] - t[2] },
|
|
{ v1[0] + t[0], v1[1] + t[1], v1[2] + t[2] } };
|
|
float vv2[][3] = { { v2[0] - t[0], v2[1] - t[1], v2[2] - t[2] },
|
|
{ v2[0] + t[0], v2[1] + t[1], v2[2] + t[2] } };
|
|
RepLine(cgo, s1, s2, isRamped, vv1[0], vv2[0], color1, b1, b2, a, color2, b1masked, b2masked);
|
|
RepLine(cgo, s1, s2, isRamped, vv1[1], vv2[1], color1, b1, b2, a, color2, b1masked, b2masked);
|
|
}
|
|
}
|
|
|
|
static void RepAromatic(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,
|
|
float tube_size, int half_state, unsigned int b1, unsigned int b2, int a, bool b1masked, bool b2masked)
|
|
{
|
|
float d[3], t[3], p0[3], p1[3], p2[3];
|
|
int a3;
|
|
int double_sided;
|
|
|
|
/* 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 prioritized 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 {
|
|
const float* 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);
|
|
}
|
|
|
|
t[0] = p2[0] * tube_size * 2;
|
|
t[1] = p2[1] * tube_size * 2;
|
|
t[2] = p2[2] * tube_size * 2;
|
|
RepLine(cgo, s1, s2, isRamped, v1, v2, color1, b1, b2, a, color2, b1masked, b2masked);
|
|
if (s1){
|
|
CGOColorv(cgo, color1);
|
|
CGOPickColor(cgo, b1, b1masked ? cPickableNoPick : a);
|
|
float f[] = { 0.14F, 0.4F } ;
|
|
float f_1[] = { 1.0F - f[0], 1.0F - f[1] };
|
|
float pt1[] = { (f_1[0] * v1[0] + f[0] * v2[0]),
|
|
(f_1[0] * v1[1] + f[0] * v2[1]),
|
|
(f_1[0] * v1[2] + f[0] * v2[2]) };
|
|
float pt2[] = { (f_1[1] * v1[0] + f[1] * v2[0]),
|
|
(f_1[1] * v1[1] + f[1] * v2[1]),
|
|
(f_1[1] * v1[2] + f[1] * v2[2]) };
|
|
float p1[3], p2[3];
|
|
subtract3f(pt1, t, p1);
|
|
subtract3f(pt2, t, p2);
|
|
cgo->add<cgo::draw::line>(p1, p2);
|
|
if(double_sided) {
|
|
add3f(pt1, t, p1);
|
|
add3f(pt2, t, p2);
|
|
cgo->add<cgo::draw::line>(p1, p2);
|
|
}
|
|
}
|
|
if (s2){
|
|
CGOColorv(cgo, color2);
|
|
CGOPickColor(cgo, b2, b2masked ? cPickableNoPick : a);
|
|
float f[] = { 0.6F, 0.86F } ;
|
|
float f_1[] = { 1.0F - f[0], 1.0F - f[1] };
|
|
float pt1[] = { (f_1[0] * v1[0] + f[0] * v2[0]),
|
|
(f_1[0] * v1[1] + f[0] * v2[1]),
|
|
(f_1[0] * v1[2] + f[0] * v2[2]) };
|
|
float pt2[] = { (f_1[1] * v1[0] + f[1] * v2[0]),
|
|
(f_1[1] * v1[1] + f[1] * v2[1]),
|
|
(f_1[1] * v1[2] + f[1] * v2[2]) };
|
|
float p1[3], p2[3];
|
|
subtract3f(pt1, t, p1);
|
|
subtract3f(pt2, t, p2);
|
|
cgo->add<cgo::draw::line>(p1, p2);
|
|
if(double_sided) {
|
|
add3f(pt1, t, p1);
|
|
add3f(pt2, t, p2);
|
|
cgo->add<cgo::draw::line>(p1, p2);
|
|
}
|
|
}
|
|
}
|
|
|
|
RepWireBond::~RepWireBond()
|
|
{
|
|
CGOFree(shaderCGO);
|
|
CGOFree(primitiveCGO);
|
|
}
|
|
|
|
|
|
/* lower memory use and higher performance for
|
|
display of large trajectories, etc. */
|
|
|
|
void RepWireBondRenderImmediate(CoordSet * cs, RenderInfo * info)
|
|
{
|
|
/* performance optimized, so it does not support the following:
|
|
|
|
- anything other than opengl
|
|
- display of bond valences
|
|
- per-bond & per-atom properties, including color and line-width
|
|
- 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;
|
|
float line_width, line_width_setting =
|
|
SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_line_width);
|
|
line_width = SceneGetDynamicLineWidth(info, line_width_setting);
|
|
|
|
if(info->width_scale_flag)
|
|
glLineWidth(line_width * info->width_scale);
|
|
else
|
|
glLineWidth(line_width);
|
|
|
|
SceneResetNormal(G, true);
|
|
if(!info->line_lighting)
|
|
glDisable(GL_LIGHTING);
|
|
glBegin(GL_LINES);
|
|
{
|
|
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();
|
|
|
|
for(a = 0; a < nBond; a++) {
|
|
int b1 = bd->index[0];
|
|
int b2 = bd->index[1];
|
|
const AtomInfoType *ai1, *ai2;
|
|
bd++;
|
|
if(GET_BIT((ai1 = ai + b1)->visRep,cRepLine) && GET_BIT((ai2 = ai + b2)->visRep,cRepLine)) {
|
|
active = true;
|
|
int a1 = cs->atmToIdx(b1);
|
|
int 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 line */
|
|
if(c1 != last_color) {
|
|
last_color = c1;
|
|
glColor3fv(ColorGet(G, c1));
|
|
}
|
|
glVertex3fv(v1);
|
|
glVertex3fv(v2); /* we done */
|
|
} else { /* different colors -> two lines */
|
|
float avg[3];
|
|
average3f(v1, v2, avg);
|
|
|
|
if(c1 != last_color) {
|
|
last_color = c1;
|
|
glColor3fv(ColorGet(G, c1));
|
|
}
|
|
glVertex3fv(v1);
|
|
glVertex3fv(avg);
|
|
|
|
if(c2 != last_color) {
|
|
last_color = c2;
|
|
glColor3fv(ColorGet(G, c2));
|
|
}
|
|
glVertex3fv(avg);
|
|
glVertex3fv(v2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
glEnd();
|
|
glEnable(GL_LIGHTING);
|
|
if(!active)
|
|
cs->Active[cRepLine] = false;
|
|
}
|
|
}
|
|
|
|
static int RepWireBondCGOGenerate(RepWireBond * I, RenderInfo * info)
|
|
{
|
|
PyMOLGlobals *G = I->G;
|
|
CGO *convertcgo = nullptr;
|
|
int ok = true;
|
|
short line_as_cylinders = 0;
|
|
line_as_cylinders = SettingGetGlobal_b(G, cSetting_use_shaders) && SettingGetGlobal_b(G, cSetting_render_as_cylinders) && SettingGetGlobal_b(G, cSetting_line_as_cylinders);
|
|
|
|
{
|
|
if (ok && I->primitiveCGO){
|
|
if (line_as_cylinders){
|
|
CGO *tmpCGO = CGONew(G);
|
|
|
|
if (ok) ok &= CGOEnable(tmpCGO, GL_CYLINDER_SHADER);
|
|
if (ok) ok &= CGOSpecial(tmpCGO, CYLINDER_WIDTH_FOR_REPWIRE);
|
|
convertcgo = CGOConvertLinesToCylinderShader(I->primitiveCGO, tmpCGO);
|
|
I->shaderCGO_has_cylinders = true;
|
|
|
|
if (ok) ok &= CGOAppendNoStop(tmpCGO, convertcgo);
|
|
|
|
if (ok) ok &= CGODisable(tmpCGO, GL_CYLINDER_SHADER);
|
|
if (ok) ok &= CGOStop(tmpCGO);
|
|
CGOFreeWithoutVBOs(convertcgo);
|
|
convertcgo = tmpCGO;
|
|
} else {
|
|
bool trilines = SettingGetGlobal_b(G, cSetting_trilines);
|
|
CGO *tmpCGO = CGONew(G), *tmp2CGO;
|
|
int shader = trilines ? GL_TRILINES_SHADER : GL_LINE_SHADER;
|
|
|
|
if (ok) ok &= CGOEnable(tmpCGO, shader);
|
|
if (ok) ok &= CGODisable(tmpCGO, CGO_GL_LIGHTING);
|
|
if (trilines) {
|
|
if (ok) ok &= CGOSpecial(tmpCGO, LINEWIDTH_DYNAMIC_WITH_SCALE);
|
|
tmp2CGO = CGOConvertToTrilinesShader(I->primitiveCGO, tmpCGO);
|
|
} else {
|
|
tmp2CGO = CGOConvertToLinesShader(I->primitiveCGO, tmpCGO);
|
|
}
|
|
if (ok) ok &= CGOAppendNoStop(tmpCGO, tmp2CGO);
|
|
if (ok) ok &= CGODisable(tmpCGO, shader);
|
|
if (ok) ok &= CGOStop(tmpCGO);
|
|
CGOFreeWithoutVBOs(tmp2CGO);
|
|
convertcgo = tmpCGO;
|
|
}
|
|
convertcgo->use_shader = true;
|
|
}
|
|
CGOFree(I->shaderCGO);
|
|
I->shaderCGO = convertcgo;
|
|
CHECKOK(ok, I->shaderCGO);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void RepWireBond::render(RenderInfo* info)
|
|
{
|
|
auto I = this;
|
|
CRay *ray = info->ray;
|
|
auto pick = info->pick;
|
|
int ok = true;
|
|
|
|
if(ray) {
|
|
#ifndef _PYMOL_NO_RAY
|
|
CGORenderRay(primitiveCGO, ray, info, nullptr, nullptr, cs->Setting.get(), cs->Obj->Setting.get());
|
|
ray->transparentf(0.0);
|
|
#endif
|
|
} else if(G->HaveGUI && G->ValidContext) {
|
|
bool use_shader = SettingGetGlobal_b(G, cSetting_line_use_shader) &&
|
|
SettingGetGlobal_b(G, cSetting_use_shaders);
|
|
if(pick) {
|
|
CGORenderPicking(use_shader ? I->shaderCGO : I->primitiveCGO, info, &I->context, nullptr, nullptr, I);
|
|
} else { /* else not pick i.e., when rendering */
|
|
short line_as_cylinders ;
|
|
line_as_cylinders = SettingGetGlobal_b(G, cSetting_render_as_cylinders) && SettingGetGlobal_b(G, cSetting_line_as_cylinders);
|
|
if (I->shaderCGO && (!use_shader || (line_as_cylinders ^ I->shaderCGO_has_cylinders))){
|
|
CGOFree(I->shaderCGO);
|
|
I->shaderCGO_has_cylinders = 0;
|
|
}
|
|
if (ok){
|
|
if (use_shader) {
|
|
if (!I->shaderCGO)
|
|
ok &= RepWireBondCGOGenerate(I, info);
|
|
CGORender(I->shaderCGO, nullptr, nullptr, nullptr, info, I);
|
|
} else {
|
|
CGORender(I->primitiveCGO, nullptr, nullptr, nullptr, info, I);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ok){
|
|
CGOFree(I->shaderCGO);
|
|
I->invalidate(cRepInvPurge);
|
|
I->cs->Active[cRepLine] = false;
|
|
}
|
|
}
|
|
|
|
static
|
|
bool IsBondTerminal(ObjectMolecule *obj, int b1, int b2){
|
|
auto const has_heavy_neighbors = [obj](int atm, int atm_other) {
|
|
for (auto const& neighbor : AtomNeighbors(obj, atm)) {
|
|
if (neighbor.atm != atm_other &&
|
|
obj->AtomInfo[neighbor.atm].protons > 1) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
return !has_heavy_neighbors(b1, b2) || !has_heavy_neighbors(b2, b1);
|
|
}
|
|
|
|
static int RepWireZeroOrderBond(CGO *cgo, bool s1, bool s2,
|
|
const float *v1,
|
|
const float *v2,
|
|
const float *rgb1,
|
|
const float *rgb2,
|
|
unsigned int b1, unsigned int b2, int a, float dash_gap, float dash_length, bool b1masked, bool b2masked)
|
|
{
|
|
int ok = true;
|
|
float axis[3], naxis[3];
|
|
subtract3f(v2, v1, axis);
|
|
copy3f(axis, naxis);
|
|
normalize3f(naxis);
|
|
float blen = length3f(axis);
|
|
float dash_tot = dash_gap + dash_length;
|
|
int ndashes = blen / dash_tot;
|
|
|
|
// only do even number of dashes
|
|
if (ndashes < 2) {
|
|
ndashes = 2;
|
|
} else if (ndashes % 2) {
|
|
--ndashes;
|
|
}
|
|
|
|
float remspace = blen - (ndashes * dash_length); // remaining space for first gaps
|
|
float dgap = remspace / (ndashes - 1.f); // endpoints at each vertex, therefore only account for ndashes-1 spaces
|
|
float placep[3], placep2[3], adddlen[3], adddtot[3];
|
|
float dplace;
|
|
int ndashes_drawn = 0;
|
|
bool color2_set = false;
|
|
mult3f(naxis, dash_length, adddlen); // adddlen - length of dash as x/y/z vector
|
|
mult3f(naxis, dash_length + dgap, adddtot); // adddtot - length of dash plus gap as x/y/z vector
|
|
|
|
copy3f(v1, placep);
|
|
if (s1){
|
|
ok &= CGOColorv(cgo, rgb1);
|
|
ok &= CGOPickColor(cgo, b1, b1masked ? cPickableNoPick : a);
|
|
for (dplace = 0.f; (dplace+dash_length) < blen / 2.f; ){
|
|
add3f(placep, adddlen, placep2);
|
|
cgo->add<cgo::draw::line>(placep, placep2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dash_length + dgap;
|
|
++ndashes_drawn;
|
|
}
|
|
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);
|
|
cgo->add<cgo::draw::line>(placep, placep2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dash_length + dgap;
|
|
++ndashes_drawn;
|
|
}
|
|
}
|
|
} else {
|
|
float tmpp[3];
|
|
dplace = (ndashes/2) * (dash_length + dgap);
|
|
mult3f(naxis, dplace, tmpp);
|
|
add3f(v1, tmpp, placep);
|
|
ndashes_drawn = ndashes/2;
|
|
// 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(cgo, rgb2);
|
|
ok &= CGOPickColor(cgo, b2, b2masked ? cPickableNoPick : a);
|
|
color2_set = true;
|
|
cgo->add<cgo::draw::line>(placep, placep2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dash_length + dgap;
|
|
++ndashes_drawn;
|
|
}
|
|
}
|
|
if (s2){
|
|
if (dplace < blen / 2.f){
|
|
// if we are behind the mid-point, draw a split cylinder with both colors
|
|
float tmpp[3];
|
|
mult3f(axis, .5f, tmpp);
|
|
add3f(v1, tmpp, tmpp);
|
|
cgo->add<cgo::draw::line>(placep, tmpp);
|
|
add3f(placep, adddlen, placep2);
|
|
if (!color2_set){
|
|
ok &= CGOColorv(cgo, rgb2);
|
|
ok &= CGOPickColor(cgo, b2, b2masked ? cPickableNoPick : a);
|
|
}
|
|
cgo->add<cgo::draw::line>(tmpp, placep2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dash_length + dgap;
|
|
++ndashes_drawn;
|
|
} else if (!color2_set){
|
|
ok &= CGOColorv(cgo, rgb2);
|
|
ok &= CGOPickColor(cgo, b2, b2masked ? cPickableNoPick : a);
|
|
}
|
|
while (ndashes_drawn < ndashes){
|
|
add3f(placep, adddlen, placep2);
|
|
cgo->add<cgo::draw::line>(placep, placep2);
|
|
add3f(placep, adddtot, placep);
|
|
dplace += dash_length + dgap;
|
|
++ndashes_drawn;
|
|
}
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
Rep *RepWireBondNew(CoordSet * cs, int state)
|
|
{
|
|
PyMOLGlobals *G = cs->G;
|
|
ObjectMolecule *obj = cs->Obj;
|
|
int a1, a2;
|
|
unsigned int b1, b2;
|
|
int a, c1, c2, ord;
|
|
bool s1, s2;
|
|
const BondType *b;
|
|
int half_bonds, *other = nullptr;
|
|
float valence;
|
|
const float *v1, *v2;
|
|
int visFlag;
|
|
float line_width;
|
|
int valence_flag = false;
|
|
const AtomInfoType *ai1;
|
|
int cartoon_side_chain_helper = 0;
|
|
int ribbon_side_chain_helper = 0;
|
|
int line_stick_helper = 0;
|
|
int na_mode;
|
|
bool *marked = nullptr;
|
|
int valence_found = false;
|
|
int variable_width = false;
|
|
float last_line_width = -1.f;
|
|
int line_color;
|
|
int hide_long = false;
|
|
int fancy;
|
|
const float _0p9 = 0.9F;
|
|
int ok = true;
|
|
unsigned int line_counter = 0;
|
|
PRINTFD(G, FB_RepWireBond)
|
|
" RepWireBondNew-Debug: entered.\n" ENDFD;
|
|
|
|
visFlag = false;
|
|
b = obj->Bond;
|
|
ai1 = obj->AtomInfo;
|
|
if(ok && GET_BIT(obj->RepVisCache,cRepLine)){
|
|
for(a = 0; a < obj->NBond; a++) {
|
|
b1 = b->index[0];
|
|
b2 = b->index[1];
|
|
if((cRepLineBit & ai1[b1].visRep & ai1[b2].visRep)) {
|
|
visFlag = true;
|
|
break;
|
|
}
|
|
b++;
|
|
}
|
|
}
|
|
if(!visFlag) {
|
|
return (nullptr); /* skip if no dots are visible */
|
|
}
|
|
marked = pymol::calloc<bool>(obj->NAtom);
|
|
CHECKOK(ok, marked);
|
|
if (!ok){
|
|
return (nullptr);
|
|
}
|
|
|
|
valence_flag = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence);
|
|
valence = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence_size);
|
|
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);
|
|
line_stick_helper = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(),
|
|
cSetting_line_stick_helper);
|
|
line_color = SettingGet_color(G, cs->Setting.get(), obj->Setting.get(), cSetting_line_color);
|
|
line_width = SettingGet_f(G, cs->Setting.get(), obj->Setting.get(), cSetting_line_width);
|
|
|
|
if(line_stick_helper && (SettingGet_f(G, cs->Setting.get(), obj->Setting.get(),
|
|
cSetting_stick_transparency) > R_SMALL4))
|
|
line_stick_helper = false;
|
|
half_bonds = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_half_bonds);
|
|
hide_long = SettingGet_b(G, cs->Setting.get(), obj->Setting.get(), cSetting_hide_long_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);
|
|
fancy = SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence_mode) == 1;
|
|
auto valence_zero_mode =
|
|
SettingGet_i(G, cs->Setting.get(), obj->Setting.get(), cSetting_valence_zero_mode);
|
|
|
|
b = obj->Bond;
|
|
|
|
for(a = 0; a < obj->NBond; a++) {
|
|
b1 = b->index[0];
|
|
b2 = b->index[1];
|
|
|
|
a1 = cs->atmToIdx(b1);
|
|
a2 = cs->atmToIdx(b2);
|
|
if((a1 >= 0) && (a2 >= 0)) {
|
|
if(!variable_width)
|
|
if (AtomInfoCheckBondSetting(G, b, cSetting_line_width)){
|
|
variable_width = true;
|
|
if (valence_found) break;
|
|
}
|
|
auto bd_valence_flag = BondSettingGetWD(G, b, cSetting_valence, valence_flag);
|
|
if(bd_valence_flag) {
|
|
valence_found = true;
|
|
if (variable_width) break;
|
|
}
|
|
}
|
|
b++;
|
|
}
|
|
|
|
auto I = new RepWireBond(cs, state);
|
|
|
|
I->primitiveCGO = CGONew(G);
|
|
|
|
CGOSpecialWithArg(I->primitiveCGO, LINE_LIGHTING, 0.f);
|
|
|
|
CGOSpecial(I->primitiveCGO, LINEWIDTH_FOR_LINES);
|
|
CGOBegin(I->primitiveCGO, GL_LINES);
|
|
|
|
if(obj->NBond) {
|
|
|
|
if(valence_found) /* build list of up to 2 connected atoms for each atom */
|
|
other = ObjectMoleculeGetPrioritizedOtherIndexList(obj, cs);
|
|
|
|
if(ok && (cartoon_side_chain_helper || ribbon_side_chain_helper)) {
|
|
SideChainHelperMarkNonCartoonBonded(marked, obj, cs,
|
|
cartoon_side_chain_helper,
|
|
ribbon_side_chain_helper);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
s1 = (ati1->visRep & cRepLineBit);
|
|
s2 = (ati2->visRep & cRepLineBit);
|
|
|
|
if(s1 ^ s2){
|
|
if(!half_bonds) {
|
|
if(line_stick_helper &&
|
|
(((!s1) && (cRepCylBit & ati1->visRep) && !(cRepCylBit & ati2->visRep)) ||
|
|
((!s2) && (cRepCylBit & ati2->visRep) && !(cRepCylBit & ati1->visRep))))
|
|
s1 = s2 = 1; /* turn on line when both stick and line are alternately shown */
|
|
else {
|
|
s1 = 0;
|
|
s2 = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
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];
|
|
|
|
inv_sym_bond:
|
|
|
|
v1 = cs->coordPtrSym(a1, symop[0], vv_buf[0], symop_pass);
|
|
v2 = cs->coordPtrSym(a2, symop[1], vv_buf[1], symop_pass);
|
|
|
|
if (!v1 || !v2) {
|
|
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(v1, v2, cutoff)) /* atoms separated by more than 90% of the sum of their vdw radii */
|
|
s1 = s2 = 0;
|
|
}
|
|
|
|
if(s1 || s2) {
|
|
float rgb1[3], rgb2[3];
|
|
int terminal = false;
|
|
|
|
auto bd_valence_flag = BondSettingGetWD(G, b, cSetting_valence, valence_flag);
|
|
auto bd_line_color = BondSettingGetWD(G, b, cSetting_line_color, line_color);
|
|
|
|
if(fancy && bd_valence_flag && (b->order > 1)) {
|
|
terminal = IsBondTerminal(obj, b1, b2);
|
|
}
|
|
|
|
if(variable_width) {
|
|
auto bd_line_width = BondSettingGetWD(G, b, cSetting_line_width, line_width);
|
|
if (last_line_width!=bd_line_width){
|
|
CGOSpecialWithArg(I->primitiveCGO, LINEWIDTH_FOR_LINES, bd_line_width);
|
|
last_line_width = bd_line_width;
|
|
}
|
|
}
|
|
|
|
if(bd_line_color < 0) {
|
|
if(bd_line_color == cColorObject) {
|
|
c1 = (c2 = obj->Color);
|
|
} else if(ColorCheckRamped(G, bd_line_color)) {
|
|
c1 = (c2 = bd_line_color);
|
|
} else {
|
|
c1 = ati1->color;
|
|
c2 = ati2->color;
|
|
}
|
|
} else {
|
|
c1 = (c2 = bd_line_color);
|
|
}
|
|
|
|
if (line_stick_helper && (ati1->visRep & ati2->visRep & cRepCylBit)) {
|
|
s1 = s2 = 0;
|
|
} else if ((ati1->flags & ati2->flags & cAtomFlag_polymer)) {
|
|
// side chain helpers
|
|
if ((cRepCartoonBit & ati1->visRep & ati2->visRep)) {
|
|
bool sc_helper = AtomSettingGetWD(G, ati1,
|
|
cSetting_cartoon_side_chain_helper, cartoon_side_chain_helper);
|
|
|
|
if (!sc_helper)
|
|
AtomSettingGetIfDefined(G, ati2, cSetting_cartoon_side_chain_helper, &sc_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);
|
|
|
|
if (!sc_helper)
|
|
AtomSettingGetIfDefined(G, ati2, cSetting_ribbon_side_chain_helper, &sc_helper);
|
|
|
|
if (sc_helper &&
|
|
SideChainHelperFilterBond(G, marked, ati1, ati2, b1, b2, na_mode_ribbon, &c1, &c2))
|
|
s1 = s2 = 0;
|
|
}
|
|
}
|
|
|
|
bool isRamped = false;
|
|
isRamped = ColorGetCheckRamped(G, c1, v1, rgb1, state);
|
|
isRamped = ColorGetCheckRamped(G, c2, v2, rgb2, state) | isRamped;
|
|
if (s1 || s2){
|
|
if (ord == 0 && valence_zero_mode == 2) {
|
|
ord = 1;
|
|
}
|
|
|
|
if (!ord){
|
|
RepWireZeroOrderBond(I->primitiveCGO, s1, s2, v1, v2, rgb1, rgb2, b1, b2, a, .15f, .15f, ati1->masked, ati2->masked);
|
|
} else if (!bd_valence_flag || ord <= 1){
|
|
RepLine(I->primitiveCGO, s1, s2, isRamped, v1, v2, rgb1, b1, b2, a, rgb2, ati1->masked, ati2->masked);
|
|
} else {
|
|
if (ord == 4){
|
|
RepAromatic(I->primitiveCGO, s1, s2, isRamped, v1, v2, other, a1, a2, cs->Coord, rgb1, rgb2, valence, 0, b1, b2, a, ati1->masked, ati2->masked);
|
|
} else {
|
|
RepValence(I->primitiveCGO, s1, s2, isRamped, v1, v2, other, a1, a2, cs->Coord, rgb1, rgb2, ord, valence, fancy && !terminal, b1, b2, a, ati1->masked, ati2->masked);
|
|
}
|
|
}
|
|
line_counter++;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
ok &= !G->Interrupt;
|
|
}
|
|
}
|
|
CGOEnd(I->primitiveCGO);
|
|
CGOSpecialWithArg(I->primitiveCGO, LINE_LIGHTING, 1.f);
|
|
CGOStop(I->primitiveCGO);
|
|
FreeP(marked);
|
|
FreeP(other);
|
|
if (!ok || !line_counter){
|
|
delete I;
|
|
I = nullptr;
|
|
}
|
|
return (Rep *) I;
|
|
}
|
|
|