Files
pymol-open-source/layer2/ObjectGadgetRamp.cpp
2026-02-10 15:18:00 -05:00

1052 lines
30 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_gl.h"
#include "os_predef.h"
#include "os_std.h"
#include "Base.h"
#include "CGO.h"
#include "Color.h"
#include "Executive.h"
#include "Feedback.h"
#include "GadgetSet.h"
#include "MemoryDebug.h"
#include "ObjectGadgetRamp.h"
#include "ObjectMolecule.h"
#include "P.h"
#include "PConv.h"
#include "PyMOLObject.h"
#include "Scene.h"
#include "Setting.h"
#include "Util.h"
#include "VFont.h"
#include "main.h"
static void ObjectGadgetRampBuild(ObjectGadgetRamp*);
static int ObjectGadgetRampHandleInputColors(ObjectGadgetRamp*);
ObjectGadgetRamp::~ObjectGadgetRamp()
{
auto I = this;
ColorForgetExt(I->G, I->Name);
}
static void ObjectGadgetRampCalculate(
ObjectGadgetRamp* I, float v, float* result)
{
const float _1 = 1.0F;
const float _0 = 0.0F;
/* from Filipe Maia */
/* All of this functions are taken right of the gnuplot manual */
if (v > _1)
v = _1;
else if (v < _0)
v = _0;
switch (I->CalcMode) {
case cRAMP_TRADITIONAL:
result[0] = (float) sqrt(v);
result[1] = v * v * v;
result[2] = (float) sin(v * 2 * cPI);
break;
case cRAMP_SLUDGE:
result[0] = v;
result[1] = (float) fabs(v - 0.5F);
result[2] = v * v * v * v;
break;
case cRAMP_OCEAN:
result[0] = 3 * v - 2;
result[1] = (float) fabs((3 * v - 1) / 2);
result[2] = v;
break;
case cRAMP_HOT:
result[0] = 3 * v;
result[1] = 3 * v - 1;
result[2] = 3 * v - 2;
break;
case cRAMP_GRAYABLE:
result[0] = v / 0.32F - 0.78125F;
result[1] = 2 * v - 0.84F;
result[2] = v / 0.08F - 11.5F; /* I'm not so sure about this one */
break;
case cRAMP_RAINBOW:
result[0] = (float) fabs(2 * v - 0.5F);
result[1] = (float) sin(v * cPI);
result[2] = (float) cos(v * cPI / 2.0F);
break;
case cRAMP_AFMHOT:
result[0] = 2 * v;
result[1] = 2 * v - 0.5F;
result[2] = 2 * v - 1.0F;
break;
case cRAMP_GRAYSCALE:
result[0] = v;
result[1] = v;
result[2] = v;
break;
default: /* default is simply white */
result[0] = 1.0F;
result[1] = 1.0F;
result[2] = 1.0F;
break;
}
clamp3f(result);
}
/**
* Get the "Level" array, eventually spaced out to match number of colors.
*/
static const float* ObjectGadgetRampGetLevel(ObjectGadgetRamp* I)
{
if (!I->Level || !I->Color)
return I->Level;
int n_color = VLAGetSize(I->Color) / 3;
if (n_color == I->NLevel || n_color < 2)
return I->Level;
if (!I->LevelTmp) {
float level0 = I->Level[0], level1 = I->Level[I->NLevel - 1];
I->LevelTmp = pymol::vla<float>(n_color);
for (int i = 0; i < n_color; ++i) {
float a = i / (float) (n_color - 1);
I->LevelTmp[i] = level0 * (1.f - a) + level1 * a;
}
}
return I->LevelTmp;
}
/**
* We support storing special color indices as negative R in the RGB color
*/
inline int GetSpecial(const float* rgb)
{
if (rgb[0] < 0.f)
return (int) rgb[0];
return 0;
}
static int _ObjectGadgetRampInterpolate(
ObjectGadgetRamp* I, float level, float* color, const float* table)
{
const float* i_level = ObjectGadgetRampGetLevel(I);
int n_level = VLAGetSize(i_level);
const float _0 = 0.0F;
const float _1 = 1.0F;
int ok = true;
if (i_level && table) {
int level_is_ge = -1;
int level_is_le = n_level;
int i = 0;
i = n_level - 1;
while (i >= 0) {
float f = i_level[i];
if (level >= f) {
level_is_ge = i;
break;
}
i--;
}
i = 0;
while (i < n_level) {
float f = i_level[i];
if (level <= f) {
level_is_le = i;
break;
} else
i++;
}
if (level_is_ge != level_is_le) {
if (level_is_le == 0) { /* lower extreme */
copy3f(table, color);
} else if (level_is_ge == (n_level - 1)) { /* upper extreme */
copy3f(table + 3 * (n_level - 1), color);
} else {
float d, x0, x1;
d = i_level[level_is_ge] - i_level[level_is_le];
if (fabs(d) > R_SMALL8) {
x0 = (level - i_level[level_is_le]) / d;
x1 = 1.0F - x0;
for (i = 0; i < 3; i++) {
color[i] = x0 * table[3 * level_is_ge + i] +
x1 * table[3 * level_is_le + i];
}
clamp3f(color);
} else {
copy3f(table + 3 * level_is_ge, color);
}
}
} else { /* dead on the specified level */
copy3f(table + 3 * level_is_ge, color);
clamp3f(color);
}
} else {
float base, range;
if (n_level && i_level) {
base = i_level[0];
range = i_level[n_level - 1] - base;
if (fabs(range) < R_SMALL8)
range = _1;
} else {
base = _0;
range = _1;
}
level = (level - base) / range;
ObjectGadgetRampCalculate(I, level, color);
}
return (ok);
}
static int _ObjectGadgetRampBlend(
ObjectGadgetRamp* I, float* color, const float* table, int mode)
{
/* this capability needs to be re-thought */
const float* i_level = ObjectGadgetRampGetLevel(I);
int n_level = VLAGetSize(i_level);
const float _1 = 1.0F;
float avg[3];
int ok = true;
zero3f(avg);
{
int cnt = 0;
switch (mode) {
case 1:
case 2:
break;
default:
if (i_level && table) {
int i;
for (i = 0; i < n_level; i++) {
add3f(table + 3 * i, avg, avg);
cnt++;
}
if (cnt) {
float fact = _1 / cnt;
scale3f(avg, fact, avg);
}
clamp3f(avg);
}
copy3f(avg, color);
}
}
switch (mode) {
case 1: /* min components */
case 3:
ones3f(color);
if (i_level && table) {
int i, j;
for (i = 0; i < n_level; i++) {
for (j = 0; j < 3; j++) {
color[j] =
(color[j] < table[3 * i + j]) ? color[j] : table[3 * i + j];
}
}
clamp3f(color);
}
if (mode == 3) { /* average serves as a minimum */
int j;
for (j = 0; j < 3; j++) {
color[j] = (color[j] > avg[j]) ? color[j] : avg[j];
}
}
break;
case 2: /* max components */
zero3f(color);
if (i_level && table) {
int i, j;
for (i = 0; i < n_level; i++) {
for (j = 0; j < 3; j++) {
color[j] =
(color[j] > table[3 * i + j]) ? color[j] : table[3 * i + j];
}
}
clamp3f(color);
}
break;
default: /* simple average of all colors */
copy3f(avg, color);
break;
}
return (ok);
}
#define MAX_COLORS 64
static int ObjectGadgetRampInterpolateWithSpecial(ObjectGadgetRamp* I,
float level, float* color, const float* atomic, const float* object,
const float* vertex, int state, int blend_all)
{
/* now thread-safe...via stack copy of colors */
float stack_color[MAX_COLORS * 3];
const float* i_level = ObjectGadgetRampGetLevel(I);
const float* i_color = I->Color;
if (i_level && i_color) {
int i = 0;
int n_level = VLAGetSize(i_level);
/* mix special coloring into the table */
const float* src = i_color;
float* dst = stack_color;
if ((n_level + 2) > MAX_COLORS)
n_level = MAX_COLORS - 2;
while (i < n_level) {
int index = GetSpecial(src);
switch (index) {
case 0:
copy3f(src, dst);
break;
case cColorDefault:
case cColorAtomic:
copy3f(atomic, dst);
break;
case cColorObject:
copy3f(object, dst);
break;
default: /* allow nested ramps */
ColorGetRamped(I->G, index, vertex, dst, state);
break;
}
dst += 3;
src += 3;
i++;
}
i_color = stack_color;
}
/* interpolate using static tables */
if (blend_all)
return _ObjectGadgetRampBlend(I, color, i_color, I->SrcState);
return _ObjectGadgetRampInterpolate(I, level, color, i_color);
}
int ObjectGadgetRampInterpolate(ObjectGadgetRamp* I, float level, float* color)
{
int result = _ObjectGadgetRampInterpolate(I, level, color, I->Color);
return result;
}
PyObject* ObjectGadgetRampAsPyList(ObjectGadgetRamp* I)
{
PyObject* result = nullptr;
result = PyList_New(11);
PyList_SetItem(result, 0, ObjectGadgetPlainAsPyList(I, false));
PyList_SetItem(result, 1, PyInt_FromLong(I->RampType));
PyList_SetItem(result, 2, PyInt_FromLong(I->NLevel));
if (I->Level && I->NLevel) {
PyList_SetItem(result, 3, PConvFloatVLAToPyList(I->Level));
} else {
PyList_SetItem(result, 3, PConvAutoNone(nullptr));
}
if (I->Color && I->NLevel) {
PyList_SetItem(result, 4, PConvFloatVLAToPyList(I->Color));
} else {
PyList_SetItem(result, 4, PConvAutoNone(nullptr));
}
PyList_SetItem(result, 5, PyInt_FromLong(I->var_index));
PyList_SetItem(result, 6, PyString_FromString(I->SrcName));
PyList_SetItem(result, 7, PyInt_FromLong(I->SrcState));
PyList_SetItem(result, 8, PyInt_FromLong(I->CalcMode));
// I->Special, removed in PyMOL 1.8
bool any = false;
int* special = nullptr;
int pse_export_version =
SettingGetGlobal_f(I->G, cSetting_pse_export_version) * 1000;
if (I->Color && pse_export_version < 1800) {
int n_color = VLAGetSize(I->Color) / 3;
special = VLAlloc(int, n_color);
for (int a = 0; a < n_color; ++a) {
any = (special[a] = GetSpecial(I->Color + a * 3)) || any;
}
}
PyList_SetItem(
result, 9, any ? PConvIntVLAToPyList(special) : PConvAutoNone(nullptr));
VLAFreeP(special);
PyList_SetItem(result, 10,
PConvAutoNone(nullptr) /* I->Extreme, removed in PyMOL 1.8 */);
return (PConvAutoNone(result));
}
int ObjectGadgetRampNewFromPyList(
PyMOLGlobals* G, PyObject* list, ObjectGadgetRamp** result, int version)
{
ObjectGadgetRamp* I = nullptr;
int ok = true;
int ll = 0;
if (ok)
I = new ObjectGadgetRamp(G);
if (ok)
ok = (I != nullptr);
if (ok)
ok = (list != nullptr);
if (ok)
ok = PyList_Check(list);
if (ok)
ll = PyList_Size(list);
/* TO SUPPORT BACKWARDS COMPATIBILITY...
Always check ll when adding new PyList_GetItem's */
if (ok) {
auto val = PyList_GetItem(list, 0);
ok = ObjectGadgetInitFromPyList(G, val, I, version);
}
if (ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->RampType);
if (ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->NLevel);
if (ok && I->NLevel)
ok = PConvPyListToFloatVLA(PyList_GetItem(list, 3), &I->Level);
if (ok && I->NLevel) {
PyObject* item = PyList_GetItem(list, 4);
if (item != Py_None) {
ok = PConvPyListToFloatVLA(item, &I->Color);
}
}
if (ok)
ok = CPythonVal_PConvPyStrToStr_From_List(
G, list, 6, I->SrcName, WordLength);
if (ok)
ok = PConvPyIntToInt(PyList_GetItem(list, 7), &I->SrcState);
if (ok && (ll > 8))
ok = CPythonVal_PConvPyIntToInt_From_List(G, list, 8, &I->CalcMode);
if (ok && I->NLevel && (ll > 10)) {
CPythonVal* item = CPythonVal_PyList_GetItem(G, list, 10);
if (!CPythonVal_IsNone(item)) {
// ObjectGadgetRamp::Extreme removed in PyMOL 1.8
// Copy extreme colors to the beginning and end of the Color array
// and repeat the first and last level value
float* extreme = nullptr;
PConvPyListToFloatVLA(item, &extreme);
if (extreme) {
I->NLevel += 2;
VLASize(I->Level, float, I->NLevel);
for (int i = I->NLevel - 2; i >= 1; --i)
I->Level[i] = I->Level[i - 1];
I->Level[I->NLevel - 1] = I->Level[I->NLevel - 2];
if (I->Color) {
VLASize(I->Color, float, I->NLevel * 3);
for (int i = (I->NLevel - 1) * 3 - 1; i >= 3; --i)
I->Color[i] = I->Color[i - 3];
copy3f(extreme, I->Color.data());
copy3f(extreme + 3, I->Color + (I->NLevel - 1) * 3);
}
VLAFreeP(extreme);
}
}
CPythonVal_Free(item);
}
ObjectGadgetRampHandleInputColors(I);
ObjectGadgetRampBuild(I);
if (ok)
(*result) = I;
return (ok);
}
int ObjectGadgetRampInterVertex(
ObjectGadgetRamp* I, const float* pos, float* color, int state)
{
float level;
int ok = true;
switch (I->RampType) {
case cRampMap:
if (!I->Map)
I->Map = ExecutiveFindObjectMapByName(I->G, I->SrcName);
if (!ExecutiveValidateObjectPtr(I->G, I->Map, cObjectMap))
ok = false;
else {
int src_state;
if (I->SrcState >= 0)
src_state = I->SrcState;
else
src_state = state;
if (src_state < 0)
src_state = SceneGetState(I->G);
if (ok)
ok = (I->Map != nullptr);
if (ok) {
std::uint8_t inside{};
ok = ObjectMapInterpolate(I->Map, src_state, pos, &level, &inside, 1);
if (!inside) {
ok = false;
}
}
if (ok)
ok = ObjectGadgetRampInterpolate(I, level, color);
}
break;
case cRampMol:
if (!I->Mol)
I->Mol = ExecutiveFindObjectMoleculeByName(I->G, I->SrcName);
if (!ExecutiveValidateObjectPtr(I->G, I->Mol, cObjectMolecule))
ok = false;
else {
float cutoff = 1.0F;
float dist;
int sub_vdw = false;
if (state < 0)
state = SceneGetState(I->G);
if (I->Level && I->NLevel) {
cutoff = I->Level[I->NLevel - 1];
if (I->Level[0] < 0.0F) {
sub_vdw = true;
cutoff += MAX_VDW;
}
}
if (ok)
ok = (I->Mol != nullptr);
if (ok) {
if (I->Mol->NCSet == 1) // if only one state, then set state to 0
state = 0;
if (SettingGet_b(I->G, I->Setting.get(), nullptr,
cSetting_ramp_blend_nearby_colors)) {
float atomic[3];
int index = ObjectMoleculeGetNearestBlendedColor(
I->Mol, pos, cutoff, state, &dist, atomic, sub_vdw);
if (index >= 0) {
const float* object = ColorGetRaw(I->G, I->Mol->Color);
if (!ObjectGadgetRampInterpolateWithSpecial(
I, dist, color, atomic, object, pos, state, false)) {
copy3f(I->Color.data(), color);
}
} else {
float white[3] = {1.0F, 1.0F, 1.0F};
if (!ObjectGadgetRampInterpolateWithSpecial(
I, cutoff + 1.0F, color, white, white, pos, state, false)) {
copy3f(I->Color.data(), color);
}
}
} else {
int index = ObjectMoleculeGetNearestAtomIndex(
I->Mol, pos, cutoff, state, &dist);
if (index >= 0) {
const float* atomic =
ColorGetRaw(I->G, I->Mol->AtomInfo[index].color);
const float* object = ColorGetRaw(I->G, I->Mol->Color);
if (sub_vdw) {
dist -= I->Mol->AtomInfo[index].vdw;
if (dist < 0.0F)
dist = 0.0F;
}
if (!ObjectGadgetRampInterpolateWithSpecial(
I, dist, color, atomic, object, pos, state, false)) {
copy3f(I->Color.data(), color);
}
} else {
float white[3] = {1.0F, 1.0F, 1.0F};
if (!ObjectGadgetRampInterpolateWithSpecial(
I, cutoff + 1.0F, color, white, white, pos, state, false)) {
copy3f(I->Color.data(), color);
}
}
}
}
}
break;
case cRampNone: {
float white[3] = {1.0F, 1.0F, 1.0F};
if (!ObjectGadgetRampInterpolateWithSpecial(I, 0.0F, color, white, white,
pos, state, true)) { /* simple blend */
copy3f(I->Color.data(), color);
}
} break;
default:
ok = false;
break;
}
return (ok);
}
static void ObjectGadgetRampUpdateCGO(ObjectGadgetRamp* I, GadgetSet* gs)
{
CGO* cgo;
int a;
char buffer[255];
int blocked = false;
int font_id = 0;
int n_color = I->Color ? VLAGetSize(I->Color) / 3 : 0;
blocked = PAutoBlock(I->G);
font_id = VFontLoad(I->G, 1.0, 1, 1, true);
if (blocked)
PUnblock(I->G);
cgo = CGONewSized(I->G, 100);
/* behind text */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOColor(cgo, 0.05F, 0.05F, 0.05F);
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
CGOVertex(cgo, I->border, -(I->border + I->bar_height), I->border); // 9
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOVertex(
cgo, I->width + I->border, -(I->border + I->bar_height), I->border); // 10
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGOEnd(cgo);
CGOColor(cgo, 1.0F, 1.0F, 1.0F);
CGONormal(cgo, 0.f, 0.f,
1.f); // text/characters need z-normal to include normals in vbo
if (I->Level && I->NLevel) {
float exindent = (n_color > 0) ? I->bar_height : 0.f;
float pos[] = {I->border + I->text_border + exindent,
I->text_border - (I->border + I->height), I->border + I->text_raise};
float scale[] = {I->text_scale_h, I->text_scale_v};
float axes[] = {1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F};
float color[] = {1.f, 1.f, 1.f};
/* left text for ramp */
sprintf(buffer, "%0.3f", I->Level[0]);
VFontWriteToCGO(I->G, font_id, cgo, buffer, pos, scale, axes, color);
/* right text, right justified for ramp */
pos[0] = I->width + I->border - exindent;
pos[1] = I->text_border - (I->border + I->height);
pos[2] = I->border + I->text_raise;
sprintf(buffer, "%0.3f", I->Level[I->NLevel - 1]);
/* indent for right justification */
VFontIndent(I->G, font_id, buffer, pos, scale, axes, -1.f);
VFontWriteToCGO(I->G, font_id, cgo, buffer, pos, scale, axes, color);
}
/* center */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
if (n_color > 0) {
const float* i_level = ObjectGadgetRampGetLevel(I);
const float* src = I->Color;
float stack_color[6], stack_level[2] = {0.f, 1.f};
if (n_color == 1) {
n_color = 2;
copy3f(src, stack_color);
copy3f(src, stack_color + 3);
src = stack_color;
i_level = stack_level;
}
// can this ever happen?
if (!i_level) {
i_level = stack_level;
n_color = 2;
}
float range = i_level[n_color - 1] - i_level[0];
// avoid devide by zero
if (fabs(range) < R_SMALL8) {
range = 1.f;
i_level = stack_level;
n_color = 2;
}
// repeat first and last color in a `bar_height` wide square
for (a = -1; a <= n_color; a++) {
float tmp[3] = {1.f, 1.f, 1.f};
float v1 = I->border;
if (!GetSpecial(src)) {
copy3f(src, tmp);
ColorLookupColor(I->G, tmp);
}
if (a == n_color) {
v1 += I->width;
} else if (a != -1) {
v1 += I->bar_height + (I->width - 2 * I->bar_height) *
(i_level[a] - i_level[0]) / range;
if (a != n_color - 1) {
src += 3;
}
}
CGOColorv(cgo, tmp);
CGOVertex(cgo, v1, -I->border, I->border);
CGOVertex(cgo, v1, -I->border - I->bar_height, I->border);
}
} else {
int samples = 20;
float fxn;
float color[3];
for (a = 0; a < samples; a++) {
fxn = a / (samples - 1.0F);
ObjectGadgetRampCalculate(I, fxn, color);
CGOColorv(cgo, color);
CGOVertex(cgo, I->border + (I->width * fxn), -I->border, I->border);
CGOVertex(cgo, I->border + (I->width * fxn), -(I->border + I->bar_height),
I->border);
}
}
/* center */
CGOEnd(cgo);
CGOColor(cgo, 1.F, 1.F, 1.F);
/* top */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
CGOVertex(cgo, I->border, -I->border, I->border); // 5
CGOVertex(cgo, I->width + I->border, -I->border, I->border); // 6
CGONormal(cgo, 0.f, 1.f, .1f); // normal 1
CGOVertex(cgo, 0.0, 0.0, 0.0); // 1
CGOVertex(cgo, I->width + I->border * 2, 0.0, 0.0); // 2
CGOEnd(cgo);
/* bottom */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGONormal(cgo, 0.f, -1.f, .1f); // normal 4
CGOVertex(cgo, 0.f, -(I->height + I->border * 2), 0.f); // 3
CGOVertex(
cgo, I->width + I->border * 2, -(I->height + I->border * 2), 0.f); // 4
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGOEnd(cgo);
/* left */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGONormal(cgo, -1.f, 0.f, 0.1f); // normal 3
CGOVertex(cgo, 0.f, 0.f, 0.f); // 1
CGOVertex(cgo, 0.f, -(I->height + I->border * 2), 0.f); // 3
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
CGOVertex(cgo, I->border, -I->border, I->border); // 5
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOEnd(cgo);
/* right */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGONormal(cgo, 0.f, 0.f, 1.f); // normal 2
CGOVertex(cgo, I->width + I->border, -I->border, I->border); // 6
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGONormal(cgo, 1.f, 0.f, .1f); // normal 0
CGOVertex(cgo, I->width + I->border * 2, 0.f, 0.f); // 2
CGOVertex(
cgo, I->width + I->border * 2, -(I->height + I->border * 2), 0.f); // 4
CGOEnd(cgo);
CGOStop(cgo);
CGOFree(gs->ShapeCGO);
gs->ShapeCGO = cgo;
// #ifndef _PYMOL_NOPY
CGOPreloadFonts(gs->ShapeCGO);
// #endif
cgo = CGONewSized(I->G, 100);
CGODotwidth(cgo, 5);
CGOPickColor(cgo, 0, cPickableGadget);
/* top */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOVertex(cgo, 0.f, 0.f, 0.f); // 1
CGOVertex(cgo, I->width + I->border * 2, 0.f, 0.f); // 2
CGOVertex(cgo, I->border, -I->border, I->border); // 5
CGOVertex(cgo, I->width + I->border, -I->border, I->border); // 6
CGOEnd(cgo);
/* bottom */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOVertex(cgo, 0.f, -(I->height + I->border * 2), 0.f); // 3
CGOVertex(
cgo, I->width + I->border * 2, -(I->height + I->border * 2), 0.f); // 4
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGOEnd(cgo);
/* left */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOVertex(cgo, 0.f, 0.f, 0.f); // 1
CGOVertex(cgo, 0.f, -(I->height + I->border * 2), 0.f); // 3
CGOVertex(cgo, I->border, -I->border, I->border); // 5
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOEnd(cgo);
/* right */
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOVertex(cgo, I->width + I->border, -I->border, I->border); // 6
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGOVertex(cgo, I->width + I->border * 2, 0.f, 0.f); // 2
CGOVertex(
cgo, I->width + I->border * 2, -(I->height + I->border * 2), 0.f); // 4
CGOEnd(cgo);
/* band */
CGOPickColor(cgo, 1, cPickableGadget);
CGOBegin(cgo, GL_TRIANGLE_STRIP);
CGOVertex(cgo, I->border, -I->border, I->border); // 5
CGOVertex(cgo, I->width + I->border, -I->border, I->border); // 6
CGOVertex(cgo, I->border, -(I->height + I->border), I->border); // 7
CGOVertex(
cgo, I->width + I->border, -(I->height + I->border), I->border); // 8
CGOEnd(cgo);
CGOStop(cgo);
CGOFree(gs->PickShapeCGO);
gs->PickShapeCGO = cgo;
}
static void ObjectGadgetRampBuild(ObjectGadgetRamp* I)
{
GadgetSet* gs = nullptr;
ObjectGadget* og;
OrthoBusyPrime(I->G);
og = I;
gs = GadgetSetNew(I->G);
gs->NCoord = 2;
I->var_index = gs->NCoord;
gs->Coord = VLAlloc(float, gs->NCoord * 3);
gs->Coord[0] = I->x;
gs->Coord[1] = I->y;
gs->Coord[2] = .3f;
gs->Coord[3] = gs->Coord[4] = gs->Coord[5] = 0.f;
gs->NNormal = 0;
gs->Normal = nullptr;
// was a memory leak in < 1.8.3.1
for (int i = 0; i < og->NGSet; ++i) {
delete og->GSet[i];
og->GSet[i] = nullptr;
}
og->GSet[0] = gs;
og->NGSet = 1;
gs->Obj = (ObjectGadget*) I;
gs->State = 0;
ObjectGadgetRampUpdateCGO(I, gs);
gs->update();
}
/*========================================================================*/
void ObjectGadgetRamp::update()
{
auto I = this;
float scale;
if (I->Changed) {
scale = (1.0F + 5 * I->GSet[0]->Coord[1 * 3]);
I->GSet[0]->Coord[1 * 3] = 0.0;
switch (I->RampType) {
case cRampMol: {
int a;
for (a = 0; a < I->NLevel; a++) {
I->Level[a] = I->Level[a] * scale;
}
ExecutiveInvalidateRep(I->G, cKeywordAll, cRepAll, cRepInvColor);
} break;
default:
if (I->NLevel == 2) {
{
float mean = (I->Level[0] + I->Level[1]) / 2.0F;
I->Level[0] = (I->Level[0] - mean) * scale + mean;
I->Level[1] = (I->Level[1] - mean) * scale + mean;
ExecutiveInvalidateRep(I->G, cKeywordAll, cRepAll, cRepInvColor);
}
break;
} else if (I->NLevel == 3) {
I->Level[0] = (I->Level[0] - I->Level[1]) * scale + I->Level[1];
I->Level[2] = (I->Level[2] - I->Level[1]) * scale + I->Level[1];
ExecutiveInvalidateRep(I->G, cKeywordAll, cRepAll, cRepInvColor);
}
}
VLAFreeP(I->LevelTmp);
if (I->NGSet)
if (I->GSet[0]) {
ObjectGadgetRampUpdateCGO(I, I->GSet[0]);
ObjectGadgetUpdateStates(I);
}
ObjectGadgetUpdateExtents(I);
I->Changed = false;
SceneChanged(I->G);
}
}
static int ObjectGadgetRampHandleInputColors(ObjectGadgetRamp* I)
{
VLAFreeP(I->LevelTmp);
if (I->NLevel < 1) {
VLASize(I->Level, float, 1);
I->NLevel = 1;
I->Level[0] = 0.0F;
}
if (I->Color) {
int n_color = VLAGetSize(I->Color) / 3;
/* handle cases where number of colors doesn't make expectation */
if (!n_color) {
VLASize(I->Color, float, 3);
I->Color[0] = I->Color[1] = I->Color[2] = 1.0F;
n_color++;
}
if (n_color != I->NLevel && I->NLevel != 2) {
PRINTFB(I->G, FB_ObjectGadget, FB_Warnings)
" GadgetRamp-Warning: number of colors (%d) and number of levels (%d) "
"don't\n"
" match and n_level != 2. Support for trailing extreme colors dropped in "
"1.8.",
n_color, I->NLevel ENDFB(I->G);
}
if (n_color < I->NLevel) {
// repeat last color until n_color == NLevel
VLASize(I->Color, float, 3 * I->NLevel);
while (n_color < I->NLevel) {
copy3f(I->Color + 3 * (n_color - 1), I->Color + 3 * n_color);
n_color++;
}
}
}
return true;
}
/*========================================================================*/
ObjectGadgetRamp* ObjectGadgetRampMapNewAsDefined(PyMOLGlobals* G,
ObjectGadgetRamp* I, ObjectMap* map, pymol::vla<float>&& level_vla,
pymol::vla<float>&& color_vla, int map_state, float* vert_vla, float beyond,
float within, float sigma, int zero, int calc_mode)
{
if (!I)
I = new ObjectGadgetRamp(G);
I->RampType = cRampMap;
if (color_vla || calc_mode > 0) {
I->Color = std::move(color_vla);
I->CalcMode = calc_mode;
}
{
ObjectMapState* ms;
float tmp_level[3];
if (map_state < 0)
map_state = 0;
if (vert_vla && map && (ms = ObjectMapGetState(map, map_state))) {
if (ObjectMapStateGetExcludedStats(
G, ms, vert_vla, beyond, within, tmp_level)) {
tmp_level[0] = tmp_level[1] + (tmp_level[0] - tmp_level[1]) * sigma;
tmp_level[2] = tmp_level[1] + (tmp_level[2] - tmp_level[1]) * sigma;
if (zero) {
if (tmp_level[1] < 0.0F) {
tmp_level[1] = 0.0F;
tmp_level[2] = -tmp_level[0];
} else if (tmp_level[1] > 0.0F) {
tmp_level[1] = 0.0F;
tmp_level[0] = -tmp_level[2];
}
}
}
I->Level = pymol::vla<float>(3);
copy3f(tmp_level, I->Level.data());
VLAFreeP(level_vla);
} else if (level_vla) {
I->Level = std::move(level_vla);
}
}
I->NLevel = VLAGetSize(I->Level);
ObjectGadgetRampHandleInputColors(I);
ObjectGadgetRampBuild(I);
if (map) {
I->Map = map;
I->SrcState = map_state;
UtilNCopy(I->SrcName, map->Name, WordLength);
}
return (I);
}
/*========================================================================*/
ObjectGadgetRamp* ObjectGadgetRampMolNewAsDefined(PyMOLGlobals* G,
ObjectGadgetRamp* I, ObjectMolecule* mol, pymol::vla<float>&& level_vla,
pymol::vla<float>&& color_vla, int mol_state, int calc_mode)
{
if (!I) {
I = new ObjectGadgetRamp(G);
I->RampType = cRampNone;
UtilNCopy(I->SrcName, "none", WordLength);
}
if (mol) {
I->RampType = cRampMol;
I->Mol = mol;
I->SrcState = mol_state;
UtilNCopy(I->SrcName, mol->Name, WordLength);
}
if (color_vla || calc_mode > 0) {
I->Color = std::move(color_vla);
I->CalcMode = calc_mode;
}
if (level_vla) {
I->Level = std::move(level_vla);
I->NLevel = VLAGetSize(I->Level);
}
ObjectGadgetRampHandleInputColors(I);
ObjectGadgetRampBuild(I);
return (I);
}
void ObjectGadgetRamp::invalidate(cRep_t rep, cRepInv_t level, int state) {}
/*========================================================================*/
ObjectGadgetRamp::ObjectGadgetRamp(PyMOLGlobals* G)
: ObjectGadget(G)
{
auto I = this;
I->GadgetType = cGadgetRamp;
I->SrcName[0] = 0;
}