mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
1046 lines
30 KiB
C++
1046 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_predef.h"
|
|
#include"os_std.h"
|
|
#include"os_gl.h"
|
|
|
|
#include"ObjectGadgetRamp.h"
|
|
#include"GadgetSet.h"
|
|
#include"Base.h"
|
|
#include"MemoryDebug.h"
|
|
#include"CGO.h"
|
|
#include"Scene.h"
|
|
#include"Setting.h"
|
|
#include"PConv.h"
|
|
#include"main.h"
|
|
#include"Color.h"
|
|
#include"VFont.h"
|
|
#include"ObjectMolecule.h"
|
|
#include"Executive.h"
|
|
#include"Util.h"
|
|
#include"P.h"
|
|
#include"PyMOLObject.h"
|
|
#include "Feedback.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 = NULL;
|
|
|
|
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(NULL));
|
|
}
|
|
if(I->Color && I->NLevel) {
|
|
PyList_SetItem(result, 4, PConvFloatVLAToPyList(I->Color));
|
|
} else {
|
|
PyList_SetItem(result, 4, PConvAutoNone(NULL));
|
|
}
|
|
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 = NULL;
|
|
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(NULL));
|
|
VLAFreeP(special);
|
|
|
|
PyList_SetItem(result, 10, PConvAutoNone(NULL) /* I->Extreme, removed in PyMOL 1.8 */);
|
|
return (PConvAutoNone(result));
|
|
}
|
|
|
|
int ObjectGadgetRampNewFromPyList(PyMOLGlobals * G, PyObject * list,
|
|
ObjectGadgetRamp ** result, int version)
|
|
{
|
|
ObjectGadgetRamp *I = NULL;
|
|
int ok = true;
|
|
int ll = 0;
|
|
|
|
if(ok)
|
|
I = new ObjectGadgetRamp(G);
|
|
if(ok)
|
|
ok = (I != NULL);
|
|
if(ok)
|
|
ok = (list != NULL);
|
|
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 = NULL;
|
|
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 != NULL);
|
|
if(ok)
|
|
ok = ObjectMapInterpolate(I->Map, src_state, pos, &level, NULL, 1);
|
|
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 != NULL);
|
|
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(), NULL,
|
|
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 = NULL;
|
|
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 = NULL;
|
|
|
|
// was a memory leak in < 1.8.3.1
|
|
for (int i = 0; i < og->NGSet; ++i) {
|
|
delete og->GSet[i];
|
|
og->GSet[i] = NULL;
|
|
}
|
|
|
|
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;
|
|
}
|