Files
pymol-open-source/layer1/Color.cpp
2024-06-02 20:44:43 -04:00

1924 lines
57 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"Base.h"
#include"MemoryDebug.h"
#include"Ortho.h"
#include"Word.h"
#include"Color.h"
#include"PConv.h"
#include"ObjectGadgetRamp.h"
#include"Util.h"
#include"Executive.h"
#include"MyPNG.h"
#include"Scene.h"
#include "Feedback.h"
static int AutoColor[] = {
26, /* carbon */
5, /* cyan */
154, /* lightmagenta */
6, /* yellow */
9, /* salmon */
29, /* hydrogen */
11, /* slate */
13, /* orange */
10, /* lime */
5262, /* deepteal */
12, /* hotpink */
36, /* yelloworange */
5271, /* violetpurple */
124, /* grey70 */
17, /* marine */
18, /* olive */
5270, /* smudge */
20, /* teal */
5272, /* dirtyviolet */
52, /* wheat */
5258, /* deepsalmon */
5274, /* lightpink */
5257, /* aquamarine */
5256, /* paleyellow */
15, /* limegreen */
5277, /* skyblue */
5279, /* warmpink */
5276, /* limon */
53, /* violet */
5278, /* bluewhite */
5275, /* greencyan */
5269, /* sand */
22, /* forest */
5266, /* lightteal */
5280, /* darksalmon */
5267, /* splitpea */
5268, /* raspberry */
104, /* grey50 */
23, /* deepblue */
51, /* brown */
};
static int nAutoColor = 40;
static void lookup_color(CColor * I, const float *in, float *out, int big_endian);
void ColorGetBkrdContColor(PyMOLGlobals * G, float *rgb, int invert_flag)
{
const float *bkrd = ColorGet(G, SettingGet_color(G, nullptr, nullptr, cSetting_bg_rgb));
if(!invert_flag) {
if((bkrd[0] + bkrd[1] + bkrd[2]) > 0.5F) {
rgb[0] = 1.0F;
rgb[1] = 1.0F;
rgb[2] = 1.0F;
} else {
rgb[0] = 0.0F;
rgb[1] = 0.0F;
rgb[2] = 0.0F;
}
}
{
int a;
for(a = 0; a < 3; a++)
if(fabs(bkrd[a] - rgb[a]) < 0.5F) {
rgb[a] = 1.0F - rgb[a];
if(fabs(bkrd[a] - rgb[a]) < 0.5F) {
if(bkrd[a] > 0.5F)
rgb[a] = 0.0F;
else
rgb[a] = 1.0F;
}
}
}
}
unsigned int ColorGet32BitWord(PyMOLGlobals * G, const float *rgba)
{
CColor *I = G->Color;
unsigned int rc, gc, bc, ac;
unsigned int result;
rc = (int) (255 * rgba[0] + 0.49999F);
gc = (int) (255 * rgba[1] + 0.49999F);
bc = (int) (255 * rgba[2] + 0.49999F);
ac = (int) (255 * rgba[3] + 0.49999F);
if(rc > 255)
rc = 255;
if(bc > 255)
bc = 255;
if(gc > 255)
gc = 255;
if(ac > 255)
ac = 255;
if(I->BigEndian) {
result = (rc << 24) | (gc << 16) | (bc << 8) | ac;
} else {
result = (ac << 24) | (bc << 16) | (gc << 8) | rc;
}
return result;
}
int ColorGetNext(PyMOLGlobals * G)
{
int result;
int next;
next = SettingGetGlobal_i(G, cSetting_auto_color_next);
if(next >= nAutoColor)
next = 0;
result = AutoColor[next];
next++;
if(next >= nAutoColor)
next = 0;
SettingSetGlobal_i(G, cSetting_auto_color_next, next);
return (result);
}
int ColorGetCurrent(PyMOLGlobals * G)
{
int result;
int next;
next = SettingGetGlobal_i(G, cSetting_auto_color_next);
next--;
if(next < 0)
next = (nAutoColor - 1);
result = AutoColor[next];
return (result);
}
int ColorCheckRamped(PyMOLGlobals * G, int index)
{
return (index <= (cColorExtCutoff));
}
ObjectGadgetRamp *ColorGetRamp(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
if(index <= cColorExtCutoff) {
index = cColorExtCutoff - index;
if (index < I->Ext.size()) {
auto& ext = I->Ext[index];
if (!ext.Ptr && ext.Name) {
ext.Ptr = ExecutiveFindObject<ObjectGadgetRamp>(G, ext.Name);
}
return ext.Ptr;
}
}
return nullptr;
}
int ColorGetRamped(PyMOLGlobals * G, int index, const float *vertex, float *color, int state)
{
CColor *I = G->Color;
int ok = false;
if (auto* ptr = ColorGetRamp(G, index)) {
ok = ObjectGadgetRampInterVertex(ptr, vertex, color, state);
}
if(!ok) {
color[0] = 1.0;
color[1] = 1.0;
color[2] = 1.0;
} else if(I->LUTActive) {
lookup_color(I, color, color, I->BigEndian);
}
return (ok);
}
/**
* Gets a color as 3 floats from an index and writes it into
* the color argument. If the index is a ramp, then it uses the vertex and state arguments to lookup the
* color value in the ramp.
* NOTES: does not support index values cColorObject(-5) or cColorAtomic(-4) color since the object
* or atom color is not passed in.
*
* @param index - color index value
* @param vertex - x/y/z used for ramp lookup (if color index is a ramp)
* @param[out] color - output color array of 3 floats
* @param state - state lookup if ramp
*
* @return whether the color index is dependent on a ramp.
*/
bool ColorGetCheckRamped(PyMOLGlobals * G, int index, const float *vertex, float *color, int state)
{
bool isRamped = false;
if(ColorCheckRamped(G, index)) {
ColorGetRamped(G, index, vertex, color, state);
isRamped = true;
} else {
copy3f(ColorGet(G, index), color);
}
return isRamped;
}
/**
* Find a record by case-insensitive name
*
* @param seq Indexable container (Color or Ext)
* @param name Color name
* @return seq index or -1 if not found
*/
template <typename Sequence>
static int findByCaseInsensitiveName(
PyMOLGlobals* G, const Sequence& seq, const char* name)
{
for (int a = 0; a < seq.size(); ++a) {
auto* color_name = seq[a].Name;
if (color_name) {
int wm = WordMatch(G, name, color_name, true);
if (wm < 0) {
return a;
}
}
}
return -1;
}
/**
* Find a record by case-insensitive and/or partial name
*
* @param seq Indexable container (Color or Ext)
* @param name Color name
* @param[in,out] best Word match score (0 for perfect match), must not be
* negative
* @return seq index or -1 if not found
*/
template <typename Sequence>
static int findByCaseInsensitivePrefix(
PyMOLGlobals* G, const Sequence& seq, const char* name, int& best)
{
int best_a = -1;
assert(best >= 0);
// search for an imperfect match
for (int a = 0; a < seq.size(); ++a) {
auto* color_name = seq[a].Name;
if (color_name) {
auto wm = WordMatch(G, name, color_name, true);
if (wm < 0) {
// perfect case-insensitive match
best = 0;
return a;
}
if (best < wm) {
// prefix match
best = wm;
best_a = a;
}
}
}
return best_a;
}
/**
* Find a color ramp by case-insensitive name
*
* @param name Color name (ramp name)
* @return Ext index or -1 if not found
*/
static int ColorFindExtByName(PyMOLGlobals* G, const char* name)
{
return findByCaseInsensitiveName(G, G->Color->Ext, name);
}
/**
* Map name to index (idx[name] = index)
*
* If the name is already in use and the index can't be reused, then clear the
* name on the existing color or ext record.
*
* @param index Color index
* @param name Color name
* @param reuse If the name already exists, reuse the existing index if possible
* (not possible to reuse a ramp index for a color or vice versa)
*
* @return pointer to stored name string
*/
static const char* reg_name(CColor* const I, CColor::ColorIdx const index,
const char* name, bool reuse = false)
{
auto handle = I->Idx.emplace(name, index);
auto& handle_name = handle.first->first;
auto& handle_index = handle.first->second;
if (handle_index != index &&
(!reuse || bool(cColorExtCutoff < handle_index) !=
bool(cColorExtCutoff < index))) {
assert(!handle.second);
// if we're stealing a name to a new index, clear the name on the old record
if (handle_index <= cColorExtCutoff) {
auto& ext = I->Ext[cColorExtCutoff - handle_index];
assert(ext.Name == handle_name.c_str());
ext.Name = nullptr;
} else if (handle_index >= 0) {
auto& col = I->Color[handle_index];
assert(col.Name == handle_name.c_str());
col.Name = nullptr;
}
handle_index = index;
}
return handle_name.c_str();
}
void ColorRegisterExt(PyMOLGlobals* G, const char* name, ObjectGadgetRamp* ptr)
{
CColor *I = G->Color;
int a;
a = ColorFindExtByName(G, name);
if(a < 0) {
a = I->Ext.size();
I->Ext.emplace_back();
auto& ext = I->Ext.back();
ext.Name = reg_name(I, cColorExtCutoff - a, name);
assert(I->Idx[ext.Name] == cColorExtCutoff - a);
}
if(a >= 0) {
I->Ext[a].Ptr = ptr;
}
}
void ColorForgetExt(PyMOLGlobals * G, const char *name)
{
CColor *I = G->Color;
auto a = ColorFindExtByName(G, name);
if (a < 0)
return;
// currently leaks memory in I->Ext array
auto& ext = I->Ext[a];
ext.Ptr = nullptr;
// HaveOldSessionExtColors should only be true while we're loading a partial
// session, and ColorForgetExt probably means that we're replacing the ramp
// object with a another one, so we don't want to lose the name+index
// relationship.
if (ext.Name && !I->HaveOldSessionExtColors) {
I->Idx.erase(ext.Name);
ext.Name = nullptr;
}
}
PyObject *ColorExtAsPyList(PyMOLGlobals * G)
{
CColor *I = G->Color;
auto* result = PyList_New(I->Ext.size());
size_t a = 0;
for (const auto& ext : I->Ext) {
auto* list = PyList_New(2);
const char* name = ext.Name ? ext.Name : "";
PyList_SetItem(list, 0, PyString_FromString(name));
// obsolete since PyMOL 2.5, store for backwards compatibility
PyList_SetItem(list, 1, PyInt_FromLong(cColorGadgetRamp));
PyList_SetItem(result, a++, list);
}
assert(a == I->Ext.size());
return result;
}
/*========================================================================*/
PyObject *ColorAsPyList(PyMOLGlobals * G)
{
CColor *I = G->Color;
size_t n_custom = 0;
for (const auto& color : I->Color) {
if (color.Custom || color.LutColorFlag) {
n_custom++;
}
}
auto* result = PyList_New(n_custom);
size_t a = 0;
size_t c = 0;
for (const auto& color : I->Color) {
if (color.Custom || color.LutColorFlag) {
auto* list = PyList_New(7);
PyList_SetItem(list, 0, PyString_FromString(color.Name ? color.Name : ""));
PyList_SetItem(list, 1, PyInt_FromLong(a));
PyList_SetItem(list, 2, PConvFloatArrayToPyList(color.Color, 3));
PyList_SetItem(list, 3, PyInt_FromLong(color.Custom));
PyList_SetItem(list, 4, PyInt_FromLong(color.LutColorFlag));
PyList_SetItem(list, 5, PConvFloatArrayToPyList(color.LutColor, 3));
PyList_SetItem(list, 6, PyInt_FromLong(color.Fixed));
PyList_SetItem(result, c++, list);
}
++a;
}
assert(c == n_custom);
return result;
}
/*========================================================================*/
int ColorConvertOldSessionIndex(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
if(index > cColorExtCutoff) {
if(I->HaveOldSessionColors) {
for (int a = int(I->Color.size()) - 1; a >= 0; --a) {
if (index == I->Color[a].old_session_index) {
return a;
}
}
}
} else if(I->HaveOldSessionExtColors) {
for (int a = int(I->Ext.size()) - 1; a >= 0; --a) {
if (index == I->Ext[a].old_session_index) {
return cColorExtCutoff - a;
}
}
}
return index; /* failsafe */
}
#define return_error_if_fail(e) p_return_val_if_fail((e), false);
int ColorExtFromPyList(PyMOLGlobals * G, PyObject * list, int partial_restore)
{
CColor *I = G->Color;
size_t n_ext = 0;
assert(!I->HaveOldSessionExtColors);
if (list && PyList_Check(list)) {
n_ext = PyList_Size(list);
}
/* TO SUPPORT BACKWARDS COMPATIBILITY...
Always check ll when adding new PyList_GetItem's */
if (partial_restore) {
I->HaveOldSessionExtColors = I->Ext.size() > 0;
for (auto& ext : I->Ext) {
ext.old_session_index = 0;
}
} else {
I->Ext.clear();
}
for (int a = 0; a < n_ext; ++a) {
auto* rec = PyList_GetItem(list, a);
return_error_if_fail(rec != nullptr);
return_error_if_fail(PyList_Check(rec));
std::string name;
return_error_if_fail(PConvFromPyListItem(G, rec, 0, name));
char const* name_ptr =
reg_name(I, cColorExtCutoff - I->Ext.size(), name.c_str(), true);
int const a_new = cColorExtCutoff - I->Idx[name];
assert(a_new >= 0);
assert(a_new <= I->Ext.size());
assert(a_new == a || partial_restore);
if (a_new == I->Ext.size()) {
I->Ext.emplace_back();
} else {
assert(partial_restore);
}
auto& ext = I->Ext[a_new];
ext.Name = name_ptr;
ext.old_session_index = cColorExtCutoff - a;
CPythonVal_Free(rec);
}
return true;
}
/*========================================================================*/
int ColorFromPyList(PyMOLGlobals * G, PyObject * list, int partial_restore)
{
CColor* I = G->Color;
assert(!I->HaveOldSessionColors);
if (partial_restore) {
for (auto& color : I->Color) {
color.old_session_index = 0;
}
}
return_error_if_fail(list != nullptr );
return_error_if_fail(PyList_Check(list));
int const n_custom = PyList_Size(list);
for (int a = 0; a < n_custom; ++a) {
auto rec = PyList_GetItem(list, a);
return_error_if_fail(rec && PyList_Check(rec));
auto const ll = PyList_Size(rec);
/* TO SUPPORT BACKWARDS COMPATIBILITY...
Always check ll when adding new PyList_GetItem's */
int index = 0;
return_error_if_fail(PConvFromPyListItem(G, rec, 1, index));
std::string name;
return_error_if_fail(PConvFromPyListItem(G, rec, 0, name));
int const old_session_index = index;
if (partial_restore && I->Color.size() > index) {
// conflicts with an existing color
index = I->Color.size();
I->HaveOldSessionColors = true;
}
if (index >= I->Color.size()) {
assert(I->Color.size() == index);
I->Color.emplace_back(reg_name(I, index, name.c_str()));
}
auto& color = I->Color[index];
color.old_session_index = old_session_index;
assert(name == color.Name);
assert(index == I->Idx[name]);
return_error_if_fail(CPythonVal_PConvPyListToFloatArrayInPlace_From_List(
G, rec, 2, color.Color, 3));
if (PyList_Size(rec) >= 6) {
return_error_if_fail(PConvFromPyListItem(G, rec, 3, color.Custom));
return_error_if_fail(PConvFromPyListItem(G, rec, 4, color.LutColorFlag));
return_error_if_fail(CPythonVal_PConvPyListToFloatArrayInPlace_From_List(
G, rec, 5, color.LutColor, 3));
} else {
color.Custom = true;
}
if (ll > 6) {
PConvFromPyListItem(G, rec, 6, color.Fixed);
} else {
color.Fixed = false;
}
CPythonVal_Free(rec);
}
return true;
}
/*========================================================================*/
void ColorDef(PyMOLGlobals * G, const char *name, const float *v, int mode, int quiet)
{
CColor *I = G->Color;
int color = -1;
// Search for a perfect case-sensitive match
{
auto it = I->Idx.find(name);
if (it != I->Idx.end()) {
color = it->second;
}
}
if (color < 0) {
// Not found or ramp index -> do slow search
color = findByCaseInsensitiveName(G, I->Color, name);
if (color < 0) {
// Not found -> new entry
color = I->Color.size();
I->Color.emplace_back(reg_name(I, color, name));
assert(I->Idx[name] == color);
}
}
copy3f(v, I->Color[color].Color);
switch (mode) {
case 1:
I->Color[color].Fixed = true;
break;
default:
I->Color[color].Fixed = false;
break;
}
I->Color[color].Custom = true;
ColorUpdateFromLut(G, color);
if(!quiet) {
PRINTFB(G, FB_Executive, FB_Actions)
" Color: \"%s\" defined as [ %3.3f, %3.3f, %3.3f ].\n", name, v[0], v[1], v[2]
ENDFB(G);
}
PRINTFD(G, FB_Color)
" Color: and assigned number %d.\n", color ENDFD;
}
/*========================================================================*/
int ColorGetIndex(PyMOLGlobals * G, const char *name)
{
CColor *I = G->Color;
int i;
int is_numeric = true;
{
const char *c;
c = name;
while(*c) {
if((((*c) < '0') || ((*c) > '9')) && ((*c) != '-')) {
is_numeric = false;
break;
}
c++;
}
}
if(is_numeric) {
if(sscanf(name, "%d", &i)) {
if((i < I->Color.size()) && (i >= 0))
return (i);
else if(i == cColorNewAuto)
return (ColorGetNext(G));
else if(i == cColorCurAuto)
return (ColorGetCurrent(G));
else if(i == cColorAtomic)
return cColorAtomic;
else if(i == cColorObject)
return cColorObject;
else if(i == cColorFront)
return cColorFront;
else if(i == cColorBack)
return cColorBack;
else if(i == cColorDefault)
return cColorDefault;
if (i & cColor_TRGB_Bits)
return i;
}
}
if((name[0] == '0') && (name[1] == 'x')) { /* explicit hex RGB 0x000000 */
int tmp_color;
if(sscanf(name + 2, "%x", (unsigned int *) &tmp_color) == 1) {
tmp_color = (cColor_TRGB_Bits |
(tmp_color & 0x00FFFFFF) | ((tmp_color >> 2) & 0x3F000000));
return tmp_color;
}
}
// the following block used to allow prefix matches (before PyMOL 2.5)
if(WordMatch(G, name, "default", true) < 0)
return cColorDefault;
if(WordMatch(G, name, "auto", true) < 0)
return (ColorGetNext(G));
if(WordMatch(G, name, "current", true) < 0)
return (ColorGetCurrent(G));
if(WordMatch(G, name, "atomic", true) < 0)
return (cColorAtomic);
if(WordMatch(G, name, "object", true) < 0)
return (cColorObject);
if(WordMatch(G, name, "front", true) < 0)
return (cColorFront);
if(WordMatch(G, name, "back", true) < 0)
return (cColorBack);
// search for a perfect case-sensitive match (fast!)
{
auto it = I->Idx.find(name);
if (it != I->Idx.end()) {
return it->second;
}
}
// search for case-insensitive or partial match
// TODO does this even make sense? What's the use case? Should this be
// restricted to non-ambiguous matches? Note that the Python cmd.color()
// function does its own prefix lookup and rejects ambiguous matches.
int best = 0;
int color = findByCaseInsensitivePrefix(G, I->Color, name, best);
if (best != 0 || color < 0) {
int const ext_color = findByCaseInsensitivePrefix(G, I->Ext, name, best);
if (ext_color >= 0) {
color = cColorExtCutoff - ext_color;
}
}
return color;
}
/*========================================================================*/
const float *ColorGetNamed(PyMOLGlobals * G, const char *name)
{
return (ColorGet(G, ColorGetIndex(G, name)));
}
/*========================================================================*/
const char *ColorGetName(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
if((index >= 0) && (index < I->Color.size())) {
return I->Color[index].Name;
} else if((index & cColor_TRGB_Mask) == cColor_TRGB_Bits) {
index = (((index & 0xFFFFFF) | ((index << 2) & 0xFC000000) | /* convert 6 bits of trans into 8 */
((index >> 4) & 0x03000000)));
if(index & 0xFF000000) /* if transparent */
sprintf(I->RGBName, "0x%08x", index);
else /* else */
sprintf(I->RGBName, "0x%06x", index);
return I->RGBName;
} else if(index <= cColorExtCutoff) {
int a = cColorExtCutoff - index;
if (a < I->Ext.size()) {
return I->Ext[a].Name;
} else
return nullptr;
}
return (nullptr);
}
/*========================================================================*/
int ColorGetStatus(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
/* return 0 if color is invalid, -1 if hidden;
1 otherwise */
int result = 0;
if((index >= 0) && (index < I->Color.size())) {
auto* color_name = I->Color[index].Name;
if(color_name) {
const char* c = color_name;
result = 1;
while(*c) {
if(((*c) >= '0') && ((*c) <= '9')) {
result = -1;
break;
}
c++;
}
}
}
return (result);
}
/*========================================================================*/
int ColorGetNColor(PyMOLGlobals * G)
{
CColor *I = G->Color;
return (I->Color.size());
}
/*========================================================================*/
void ColorFree(PyMOLGlobals * G)
{
DeleteP(G->Color);
}
/*========================================================================*/
void ColorReset(PyMOLGlobals * G)
{
/* PyMOL core color names
1 1 1 white
.5 .5 .5 grey/gray
0 0 0 black
1 0 0 red
0 1 0 green
0 0 1 blue
1 1 0 yellow
1 0 1 magenta
0 1 1 cyan
1 1 .5 paleyellow .
1 .5 1 violet .
.5 1 1 aquamarine .
1 .5 .5 deepsalmon .
.5 1 .5 palegreen .
.5 .5 1 slate .
.75 .75 0 olive .
.75 0 .75 purple .
0 .75 .75 teal .
.6 .6 .1 deepolive .
.6 .1 .6 deeppurple .
.1 .6 .6 deepteal .
1 .5 0 orange .
1 0 .5 hotpink .
.5 1 0 chartreuse .
0 1 .5 limegreen .
0 .5 1 marine .
.5 0 1 purpleblue .
*/
CColor *I = G->Color;
I->Idx.clear();
I->Ext.clear();
auto& Color = I->Color;
Color.clear();
Color.reserve(5500);
char name[10];
int a;
int set1;
float f;
float spectrumS[13][3] = {
{1.0, 0.0, 1.0}, /* magenta - 0 */
{0.5, 0.0, 1.0},
{0.0, 0.0, 1.0}, /* blue - 166.66 */
{0.0, 0.5, 1.0},
{0.0, 1.0, 1.0}, /* cyan - 333.33 */
{0.0, 1.0, 0.5},
{0.0, 1.0, 0.0}, /* green - 500 */
{0.5, 1.0, 0.0},
{1.0, 1.0, 0.0}, /* yellow - 666.66 */
{1.0, 0.5, 0.0},
{1.0, 0.0, 0.0}, /* red - 833.33 */
{1.0, 0.0, 0.5},
{1.0, 0.0, 1.0}, /* magenta - 999 */
};
float spectrumR[13][3] = {
{1.0, 1.0, 0.0}, /* yellow - 0 */
{0.5, 1.0, 0.0}, /* chartreuse */
{0.0, 1.0, 0.0}, /* green - 166.66 */
{0.0, 1.0, 0.5}, /* limegreen */
{0.0, 1.0, 1.0}, /* cyan - 333.33 */
{0.0, 0.5, 1.0}, /* marine */
{0.0, 0.0, 1.0}, /* blue - 500 */
{0.5, 0.0, 1.0}, /* purpleblue */
{1.0, 0.0, 1.0}, /* magenta - 666.66 */
{1.0, 0.0, 0.5}, /* hotpink */
{1.0, 0.0, 0.0}, /* red - 833.33 */
{1.0, 0.5, 0.0}, /* orange */
{1.0, 1.0, 0.0}, /* yellow - 999 */
};
float spectrumC[][3] = {
{1.0, 1.0, 0.0}, /* yellow - 0 */
{0.0, 0.0, 1.0}, /* blue - 83.333 */
{1.0, 0.0, 0.0}, /* red - 167.67 */
{0.0, 1.0, 0.0}, /* green - 250.00 */
{1.0, 0.0, 1.0}, /* magenta - 333.33 */
{0.0, 1.0, 1.0}, /* cyan - 416.67 */
{1.0, 1.0, 0.0}, /* yellow - 500.00 */
{0.0, 1.0, 0.0}, /* green - 583.33 */
{0.0, 0.0, 1.0}, /* blue - 666.67 */
{1.0, 0.0, 1.0}, /* magenta - 750.00 */
{1.0, 1.0, 0.0}, /* yellow - 833.33 */
{1.0, 0.0, 0.0}, /* red - 916.67 */
{0.0, 1.0, 1.0}, /* cyan - 999 */
};
float spectrumW[][3] = {
{1.0, 1.0, 0.0}, /* yellow - 0 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 0.0, 1.0}, /* blue - 83.333 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 0.0, 0.0}, /* red - 166.67 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 1.0, 0.0}, /* green - 250.00 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 0.0, 1.0}, /* magenta - 333.33 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 1.0, 1.0}, /* cyan - 416.67 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 1.0, 0.0}, /* yellow - 500.00 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 1.0, 0.0}, /* green - 583.33 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 0.0, 1.0}, /* blue - 666.67 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 0.0, 1.0}, /* magenta - 750.00 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 1.0, 0.0}, /* yellow - 833.33 */
{1.0, 1.0, 1.0}, /* white */
{1.0, 0.0, 0.0}, /* red - 916.67 */
{1.0, 1.0, 1.0}, /* white */
{0.0, 1.0, 1.0}, /* cyan - 999 */
};
float spectrumO[29][3] = {
/* a rainbow with perceptive color balancing and extra blue/red at the ends */
{1.0, 0.0, 1.0}, /* violet */
{0.8F, 0.0, 1.0},
{0.5F, 0.0, 1.0}, /* blend */
{0.0, 0.0, 1.0}, /* blue */
{0.0, 0.0, 1.0}, /* blue */
{0.0, 0.2F, 1.0},
{0.0, 0.5F, 1.0}, /* blend */
{0.0, 0.8F, 1.0},
{0.0, 1.0, 1.0}, /* cyan */
{0.0, 1.0, 0.8F},
{0.0, 1.0, 0.5F}, /* blend */
{0.0, 1.0, 0.2F},
{0.0, 1.0, 0.0}, /* green */
{0.2F, 1.0, 0.0},
{0.5F, 1.0, 0.0}, /* blend */
{0.8F, 1.0, 0.0},
{1.0, 1.0, 0.0}, /* yellow */
{1.0, 0.9F, 0.0},
{1.0, 0.75F, 0.0}, /* blend */
{1.0, 0.6F, 0.0},
{1.0, 0.5F, 0.0}, /* orange */
{1.0, 0.4F, 0.0},
{1.0, 0.3F, 0.0}, /* blend */
{1.0, 0.2F, 0.0},
{1.0, 0.0, 0.0}, /* red */
{1.0, 0.0, 0.0}, /* red */
{1.0, 0.0, 0.5F}, /* blend */
{1.0, 0.0, 0.8F}, /* violet */
{1.0, 0.0, 1.0}, /* violet */
};
/* BLUE->VIOLET->RED r546 to r909 */
/* BLUE->CYAN->GREEN->YELLOW->RED s182 to s909 */
/* BLUE->WHITE->RED w00 to */
#define reg_named_color(name, R, G, B) \
{ \
Color.emplace_back(reg_name(I, Color.size(), name)); \
set3f(Color.back().Color, R, G, B); \
assert(I->Idx[name] == Color.size() - 1); \
}
reg_named_color("white", 1.F, 1.F, 1.F);
reg_named_color("black", 0.F, 0.F, 0.F);
reg_named_color("blue", 0.F, 0.F, 1.F);
reg_named_color("green", 0.F, 1.F, 0.F);
reg_named_color("red", 1.F, 0.F, 0.F);
reg_named_color("cyan", 0.F, 1.F, 1.F);
reg_named_color("yellow", 1.F, 1.F, 0.F);
reg_named_color("dash", 1.F, 1.F, 0.F);
reg_named_color("magenta", 1.F, 0.F, 1.F);
reg_named_color("salmon", 1.F, 0.6F, 0.6F);
reg_named_color("lime", 0.5F, 1.F, 0.5F);
reg_named_color("slate", 0.5F, 0.5F, 1.F);
reg_named_color("hotpink", 1.F, 0.F, 0.5F);
reg_named_color("orange", 1.F, 0.5F, 0.F);
reg_named_color("chartreuse", 0.5F, 1.F, 0.F); /* AKA puke green */
reg_named_color("limegreen", 0.F, 1.F, 0.5F);
reg_named_color("purpleblue", 0.5F, 0.F, 1.F); /* legacy name */
reg_named_color("marine", 0.F, 0.5F, 1.F);
reg_named_color("olive", 0.77F, 0.7F, 0.F);
reg_named_color("purple", 0.75F, 0.F, 0.75F);
reg_named_color("teal", 0.F, 0.75F, 0.75F);
reg_named_color("ruby", 0.6F, 0.2F, 0.2F);
reg_named_color("forest", 0.2F, 0.6F, 0.2F);
reg_named_color("deepblue", 0.25F, 0.25F, 0.65F); /* was "deep" */
reg_named_color("grey", 0.5F, 0.5F, 0.5F); /* english spelling */
reg_named_color("gray", 0.5F, 0.5F, 0.5F); /* american spelling */
reg_named_color("carbon", 0.2F, 1.F, 0.2F);
reg_named_color("nitrogen", 0.2F, 0.2F, 1.F);
reg_named_color("oxygen", 1.F, 0.3F, 0.3F);
reg_named_color("hydrogen", 0.9F, 0.9F, 0.9F);
reg_named_color("brightorange", 1.F, 0.7F, 0.2F);
reg_named_color("sulfur", 0.9F, 0.775F, 0.25F);
reg_named_color("tv_red", 1.F, 0.2F, 0.2F);
reg_named_color("tv_green", 0.2F, 1.F, 0.2F);
reg_named_color("tv_blue", 0.3F, 0.3F, 1.F);
reg_named_color("tv_yellow", 1.F, 1.F, 0.2F);
reg_named_color("yelloworange", 1.F, 0.87F, 0.37F);
reg_named_color("tv_orange", 1.F, 0.55F, 0.15F);
reg_named_color("br0", 0.1F, 0.1F, 1.F);
reg_named_color("br1", 0.2F, 0.1F, 0.9F);
reg_named_color("br2", 0.3F, 0.1F, 0.8F);
reg_named_color("br3", 0.4F, 0.1F, 0.7F);
reg_named_color("br4", 0.5F, 0.1F, 0.6F);
reg_named_color("br5", 0.6F, 0.1F, 0.5F);
reg_named_color("br6", 0.7F, 0.1F, 0.4F);
reg_named_color("br7", 0.8F, 0.1F, 0.3F);
reg_named_color("br8", 0.9F, 0.1F, 0.2F);
reg_named_color("br9", 1.F, 0.1F, 0.1F);
reg_named_color("pink", 1.F, 0.65F, 0.85F);
reg_named_color("firebrick", 0.698F, 0.13F, 0.13F);
reg_named_color("chocolate", 0.555F, 0.222F, 0.111F);
reg_named_color("brown", 0.65F, 0.32F, 0.17F);
reg_named_color("wheat", 0.99F, 0.82F, 0.65F);
reg_named_color("violet", 1.F, 0.5F, 1.F);
/* greybow */
strcpy(name, "grey00"); /* english spelling */
for(a = 0; a < 100; a = a + 1) {
name[5] = (a % 10) + '0';
name[4] = ((a % 100) / 10) + '0';
/* sprintf(color->Name,"grey%02d",a); */
reg_named_color(name, a / 99.F, a / 99.F, a / 99.F);
}
reg_named_color("lightmagenta", 1.F, 0.2F, 0.8F);
#define A_DIV 83.333333333F
/* full spectrum (s000-s999) */
strcpy(name, "s000");
for(a = 0; a < 1000; a = a + 1) {
set1 = (int) (a / A_DIV);
name[3] = (a % 10) + '0';
name[2] = ((a % 100) / 10) + '0';
name[1] = ((a % 1000) / 100) + '0';
/* sprintf(color->Name,"s%03d",a); */
f = 1.0F - (a - (set1 * A_DIV)) / A_DIV;
reg_named_color(name,
f * spectrumS[set1][0] + (1.F - f) * spectrumS[set1 + 1][0],
f * spectrumS[set1][1] + (1.F - f) * spectrumS[set1 + 1][1],
f * spectrumS[set1][2] + (1.F - f) * spectrumS[set1 + 1][2]);
}
/* offset & reversed full spectrum (r000-r999) */
strcpy(name, "r000");
for(a = 0; a < 1000; a = a + 1) {
set1 = (int) (a / A_DIV);
/* sprintf(color->Name,"r%03d",a); */
name[3] = (a % 10) + '0';
name[2] = ((a % 100) / 10) + '0';
name[1] = ((a % 1000) / 100) + '0';
f = 1.0F - (a - (set1 * A_DIV)) / A_DIV;
reg_named_color(name,
f * spectrumR[set1][0] + (1.F - f) * spectrumR[set1 + 1][0],
f * spectrumR[set1][1] + (1.F - f) * spectrumR[set1 + 1][1],
f * spectrumR[set1][2] + (1.F - f) * spectrumR[set1 + 1][2]);
}
/* complementary spectra (c000-c999) */
strcpy(name, "c000");
for(a = 0; a < 1000; a = a + 1) {
set1 = (int) (a / A_DIV);
/* sprintf(color->Name,"c%03d",a); */
name[3] = (a % 10) + '0';
name[2] = ((a % 100) / 10) + '0';
name[1] = ((a % 1000) / 100) + '0';
f = 1.0F - (a - (set1 * A_DIV)) / A_DIV;
reg_named_color(name,
f * spectrumC[set1][0] + (1.F - f) * spectrumC[set1 + 1][0],
f * spectrumC[set1][1] + (1.F - f) * spectrumC[set1 + 1][1],
f * spectrumC[set1][2] + (1.F - f) * spectrumC[set1 + 1][2]);
}
#define W_DIV 41.666666667F
/* complementary spectra separated by white (w000-w999) */
strcpy(name, "w000");
for(a = 0; a < 1000; a = a + 1) {
set1 = (int) (a / W_DIV);
/* sprintf(color->Name,"w%03d",a); */
name[3] = (a % 10) + '0';
name[2] = ((a % 100) / 10) + '0';
name[1] = ((a % 1000) / 100) + '0';
f = 1.0F - (a - (set1 * W_DIV)) / W_DIV;
reg_named_color(name,
f * spectrumW[set1][0] + (1.F - f) * spectrumW[set1 + 1][0],
f * spectrumW[set1][1] + (1.F - f) * spectrumW[set1 + 1][1],
f * spectrumW[set1][2] + (1.F - f) * spectrumW[set1 + 1][2]);
}
reg_named_color("density", 0.1F, 0.1F, 0.6F);
strcpy(name, "gray00"); /* american */
for(a = 0; a < 100; a = a + 1) {
name[5] = (a % 10) + '0';
name[4] = ((a % 100) / 10) + '0';
/* sprintf(color->Name,"gray%02d",a); */
reg_named_color(name, a / 99.F, a / 99.F, a / 99.F);
}
/* original full spectrum, with extra blue and red at the ends (o000-o999) */
#define B_DIV 35.7143F
strcpy(name, "o000");
for(a = 0; a < 1000; a = a + 1) {
set1 = (int) (a / B_DIV);
name[3] = (a % 10) + '0';
name[2] = ((a % 100) / 10) + '0';
name[1] = ((a % 1000) / 100) + '0';
/* sprintf(color->Name,"o%03d",a); */
f = 1.0F - (a - (set1 * B_DIV)) / B_DIV;
reg_named_color(name,
f * spectrumO[set1][0] + (1.F - f) * spectrumO[set1 + 1][0],
f * spectrumO[set1][1] + (1.F - f) * spectrumO[set1 + 1][1],
f * spectrumO[set1][2] + (1.F - f) * spectrumO[set1 + 1][2]);
}
reg_named_color("paleyellow", 1.F, 1.F, 0.5F);
reg_named_color("aquamarine", 0.5F, 1.F, 1.F);
reg_named_color("deepsalmon", 1.F, 0.5F, 0.5F);
reg_named_color("palegreen", 0.65F, 0.9F, 0.65F);
reg_named_color("deepolive", 0.6F, 0.6F, 0.1F);
reg_named_color("deeppurple", 0.6F, 0.1F, 0.6F);
reg_named_color("deepteal", 0.1F, 0.6F, 0.6F);
reg_named_color("lightblue", 0.75F, 0.75F, 1.F);
reg_named_color("lightorange", 1.F, 0.8F, 0.5F);
reg_named_color("palecyan", 0.8F, 1.F, 1.F);
reg_named_color("lightteal", 0.4F, 0.7F, 0.7F);
reg_named_color("splitpea", 0.52F, 0.75F, 0.F);
reg_named_color("raspberry", 0.7F, 0.3F, 0.4F);
reg_named_color("sand", 0.72F, 0.55F, 0.3F);
reg_named_color("smudge", 0.55F, 0.7F, 0.4F);
reg_named_color("violetpurple", 0.55F, 0.25F, 0.6F);
reg_named_color("dirtyviolet", 0.7F, 0.5F, 0.5F);
// was deepsalmon (duplicated name!)
reg_named_color("_deepsalmon", 1.F, 0.42F, 0.42F);
reg_named_color("lightpink", 1.F, 0.75F, 0.87F);
reg_named_color("greencyan", 0.25F, 1.F, 0.75F);
reg_named_color("limon", 0.75F, 1.F, 0.25F);
reg_named_color("skyblue", 0.2F, 0.5F, 0.8F);
reg_named_color("bluewhite", 0.85F, 0.85F, 1.F);
reg_named_color("warmpink", 0.85F, 0.2F, 0.5F);
reg_named_color("darksalmon", 0.73F, 0.55F, 0.52F);
reg_named_color("helium", 0.850980392F, 1.F, 1.F);
reg_named_color("lithium", 0.8F, 0.501960784F, 1.F);
reg_named_color("beryllium", 0.760784314F, 1.F, 0.F);
reg_named_color("boron", 1.F, 0.709803922F, 0.709803922F);
reg_named_color("fluorine", 0.701960784F, 1.F, 1.F);
reg_named_color("neon", 0.701960784F, 0.890196078F, 0.960784314F);
reg_named_color("sodium", 0.670588235F, 0.360784314F, 0.949019608F);
reg_named_color("magnesium", 0.541176471F, 1.F, 0.F);
reg_named_color("aluminum", 0.749019608F, 0.650980392F, 0.650980392F);
reg_named_color("silicon", 0.941176471F, 0.784313725F, 0.62745098F);
reg_named_color("phosphorus", 1.F, 0.501960784F, 0.F);
reg_named_color("chlorine", 0.121568627F, 0.941176471F, 0.121568627F);
reg_named_color("argon", 0.501960784F, 0.819607843F, 0.890196078F);
reg_named_color("potassium", 0.560784314F, 0.250980392F, 0.831372549F);
reg_named_color("calcium", 0.239215686F, 1.F, 0.F);
reg_named_color("scandium", 0.901960784F, 0.901960784F, 0.901960784F);
reg_named_color("titanium", 0.749019608F, 0.760784314F, 0.780392157F);
reg_named_color("vanadium", 0.650980392F, 0.650980392F, 0.670588235F);
reg_named_color("chromium", 0.541176471F, 0.6F, 0.780392157F);
reg_named_color("manganese", 0.611764706F, 0.478431373F, 0.780392157F);
reg_named_color("iron", 0.878431373F, 0.4F, 0.2F);
reg_named_color("cobalt", 0.941176471F, 0.564705882F, 0.62745098F);
reg_named_color("nickel", 0.31372549F, 0.815686275F, 0.31372549F);
reg_named_color("copper", 0.784313725F, 0.501960784F, 0.2F);
reg_named_color("zinc", 0.490196078F, 0.501960784F, 0.690196078F);
reg_named_color("gallium", 0.760784314F, 0.560784314F, 0.560784314F);
reg_named_color("germanium", 0.4F, 0.560784314F, 0.560784314F);
reg_named_color("arsenic", 0.741176471F, 0.501960784F, 0.890196078F);
reg_named_color("selenium", 1.F, 0.631372549F, 0.F);
reg_named_color("bromine", 0.650980392F, 0.160784314F, 0.160784314F);
reg_named_color("krypton", 0.360784314F, 0.721568627F, 0.819607843F);
reg_named_color("rubidium", 0.439215686F, 0.180392157F, 0.690196078F);
reg_named_color("strontium", 0.F, 1.F, 0.F);
reg_named_color("yttrium", 0.580392157F, 1.F, 1.F);
reg_named_color("zirconium", 0.580392157F, 0.878431373F, 0.878431373F);
reg_named_color("niobium", 0.450980392F, 0.760784314F, 0.788235294F);
reg_named_color("molybdenum", 0.329411765F, 0.709803922F, 0.709803922F);
reg_named_color("technetium", 0.231372549F, 0.619607843F, 0.619607843F);
reg_named_color("ruthenium", 0.141176471F, 0.560784314F, 0.560784314F);
reg_named_color("rhodium", 0.039215686F, 0.490196078F, 0.549019608F);
reg_named_color("palladium", 0.F, 0.411764706F, 0.521568627F);
reg_named_color("silver", 0.752941176F, 0.752941176F, 0.752941176F);
reg_named_color("cadmium", 1.F, 0.850980392F, 0.560784314F);
reg_named_color("indium", 0.650980392F, 0.458823529F, 0.450980392F);
reg_named_color("tin", 0.4F, 0.501960784F, 0.501960784F);
reg_named_color("antimony", 0.619607843F, 0.388235294F, 0.709803922F);
reg_named_color("tellurium", 0.831372549F, 0.478431373F, 0.F);
reg_named_color("iodine", 0.580392157F, 0.F, 0.580392157F);
reg_named_color("xenon", 0.258823529F, 0.619607843F, 0.690196078F);
reg_named_color("cesium", 0.341176471F, 0.090196078F, 0.560784314F);
reg_named_color("barium", 0.F, 0.788235294F, 0.F);
reg_named_color("lanthanum", 0.439215686F, 0.831372549F, 1.F);
reg_named_color("cerium", 1.F, 1.F, 0.780392157F);
reg_named_color("praseodymium", 0.850980392F, 1.F, 0.780392157F);
reg_named_color("neodymium", 0.780392157F, 1.F, 0.780392157F);
reg_named_color("promethium", 0.639215686F, 1.F, 0.780392157F);
reg_named_color("samarium", 0.560784314F, 1.F, 0.780392157F);
reg_named_color("europium", 0.380392157F, 1.F, 0.780392157F);
reg_named_color("gadolinium", 0.270588235F, 1.F, 0.780392157F);
reg_named_color("terbium", 0.188235294F, 1.F, 0.780392157F);
reg_named_color("dysprosium", 0.121568627F, 1.F, 0.780392157F);
reg_named_color("holmium", 0.F, 1.F, 0.611764706F);
reg_named_color("erbium", 0.F, 0.901960784F, 0.458823529F);
reg_named_color("thulium", 0.F, 0.831372549F, 0.321568627F);
reg_named_color("ytterbium", 0.F, 0.749019608F, 0.219607843F);
reg_named_color("lutetium", 0.F, 0.670588235F, 0.141176471F);
reg_named_color("hafnium", 0.301960784F, 0.760784314F, 1.F);
reg_named_color("tantalum", 0.301960784F, 0.650980392F, 1.F);
reg_named_color("tungsten", 0.129411765F, 0.580392157F, 0.839215686F);
reg_named_color("rhenium", 0.149019608F, 0.490196078F, 0.670588235F);
reg_named_color("osmium", 0.149019608F, 0.4F, 0.588235294F);
reg_named_color("iridium", 0.090196078F, 0.329411765F, 0.529411765F);
reg_named_color("platinum", 0.815686275F, 0.815686275F, 0.878431373F);
reg_named_color("gold", 1.F, 0.819607843F, 0.137254902F);
reg_named_color("mercury", 0.721568627F, 0.721568627F, 0.815686275F);
reg_named_color("thallium", 0.650980392F, 0.329411765F, 0.301960784F);
reg_named_color("lead", 0.341176471F, 0.349019608F, 0.380392157F);
reg_named_color("bismuth", 0.619607843F, 0.309803922F, 0.709803922F);
reg_named_color("polonium", 0.670588235F, 0.360784314F, 0.F);
reg_named_color("astatine", 0.458823529F, 0.309803922F, 0.270588235F);
reg_named_color("radon", 0.258823529F, 0.509803922F, 0.588235294F);
reg_named_color("francium", 0.258823529F, 0.F, 0.4F);
reg_named_color("radium", 0.F, 0.490196078F, 0.F);
reg_named_color("actinium", 0.439215686F, 0.670588235F, 0.980392157F);
reg_named_color("thorium", 0.F, 0.729411765F, 1.F);
reg_named_color("protactinium", 0.F, 0.631372549F, 1.F);
reg_named_color("uranium", 0.F, 0.560784314F, 1.F);
reg_named_color("neptunium", 0.F, 0.501960784F, 1.F);
reg_named_color("plutonium", 0.F, 0.419607843F, 1.F);
reg_named_color("americium", 0.329411765F, 0.360784314F, 0.949019608F);
reg_named_color("curium", 0.470588235F, 0.360784314F, 0.890196078F);
reg_named_color("berkelium", 0.541176471F, 0.309803922F, 0.890196078F);
reg_named_color("californium", 0.631372549F, 0.211764706F, 0.831372549F);
reg_named_color("einsteinium", 0.701960784F, 0.121568627F, 0.831372549F);
reg_named_color("fermium", 0.701960784F, 0.121568627F, 0.729411765F);
reg_named_color("mendelevium", 0.701960784F, 0.050980392F, 0.650980392F);
reg_named_color("nobelium", 0.741176471F, 0.050980392F, 0.529411765F);
reg_named_color("lawrencium", 0.780392157F, 0.F, 0.4F);
reg_named_color("rutherfordium", 0.8F, 0.F, 0.349019608F);
reg_named_color("dubnium", 0.819607843F, 0.F, 0.309803922F);
reg_named_color("seaborgium", 0.850980392F, 0.F, 0.270588235F);
reg_named_color("bohrium", 0.878431373F, 0.F, 0.219607843F);
reg_named_color("hassium", 0.901960784F, 0.F, 0.180392157F);
reg_named_color("meitnerium", 0.921568627F, 0.F, 0.149019608F);
reg_named_color("deuterium", 0.9F, 0.9F, 0.9F);
reg_named_color("lonepair", 0.5F, 0.5F, 0.5F);
reg_named_color("pseudoatom", 0.9F, 0.9F, 0.9F);
}
int ColorTableLoad(PyMOLGlobals * G, const char *fname, float gamma, int quiet)
{
CColor *I = G->Color;
int ok = true;
I->Gamma = gamma;
if(!fname[0]) {
ColorUpdateFromLut(G, -1);
} else {
int width = 512, height = 512;
if(!strcmp(fname, "rgb")) {
if(!I->ColorTable.empty()) {
I->ColorTable.clear();
PRINTFB(G, FB_Color, FB_Actions)
" Color: purged table; restoring RGB colors.\n" ENDFB(G);
}
ColorUpdateFromLut(G, -1);
} else if(!strcmp(fname, "greyscale")) {
int x, y;
unsigned int r = 0, g = 0, b = 0;
unsigned int *pixel, mask, *p;
unsigned int rc;
if(I->BigEndian)
mask = 0x000000FF;
else
mask = 0xFF000000;
I->ColorTable.resize(512 * 512);
p = I->ColorTable.data();
for(x = 0; x < width; x++)
for(y = 0; y < height; y++)
*(p++) = mask;
for(y = 0; y < height; y++)
for(x = 0; x < width; x++) {
rc = (r + g + b)/3;
pixel = I->ColorTable.data() + ((width) * y) + x;
if(I->BigEndian) {
*(pixel) = mask | (rc << 24) | (rc << 16) | (rc << 8);
} else {
*(pixel) = mask | (rc << 16) | (rc << 8) | rc;
}
b = b + 4;
if(!(0xFF & b)) {
b = 0;
g = g + 4;
if(!(0xFF & g)) {
g = 0;
r = r + 4;
}
}
}
if(!quiet) {
PRINTFB(G, FB_Color, FB_Actions)
" Color: defined table '%s'.\n", fname ENDFB(G);
}
ColorUpdateFromLut(G, -1);
ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
SceneChanged(G);
} else if(!strcmp(fname, "pymol")) {
int x, y;
unsigned int r = 0, g = 0, b = 0;
unsigned int *pixel, mask, *p;
unsigned int rc, bc, gc;
unsigned int gf, bf, rf;
float green_max = 0.75F;
float red_max = 0.95F;
float blue_max = 0.97F;
float min_factor = 0.15F;
red_max = SettingGetGlobal_f(G, cSetting_pymol_space_max_red);
green_max = SettingGetGlobal_f(G, cSetting_pymol_space_max_green);
blue_max = SettingGetGlobal_f(G, cSetting_pymol_space_max_blue);
min_factor = SettingGetGlobal_f(G, cSetting_pymol_space_min_factor);
if(I->BigEndian)
mask = 0x000000FF;
else
mask = 0xFF000000;
I->ColorTable.resize(512 * 512);
p = I->ColorTable.data();
for(x = 0; x < width; x++)
for(y = 0; y < height; y++)
*(p++) = mask;
for(y = 0; y < height; y++)
for(x = 0; x < width; x++) {
rc = r;
gc = g;
bc = b;
if((r >= g) && (r >= b)) {
if(rc > 255 * red_max) {
rc = (unsigned int) (red_max * 255);
bc = bc * rc / r;
gc = gc * rc / r;
}
} else if((g >= b) && (g >= r)) {
if(gc > 255 * green_max) {
gc = (unsigned int) (green_max * 255);
bc = bc * gc / g;
rc = rc * gc / g;
}
} else if((b >= g) && (b >= r)) {
if(bc > 255 * blue_max) {
bc = (unsigned int) (blue_max * 255);
gc = gc * bc / b;
rc = rc * bc / b;
}
}
rf = (int) (min_factor * rc + 0.49999F);
gf = (int) (min_factor * gc + 0.49999F);
bf = (int) (min_factor * bc + 0.49999F);
if(rc < gf)
rc = (int) gf;
if(bc < gf)
bc = (int) gf;
if(rc < bf)
rc = (int) bf;
if(gc < bf)
gc = (int) bf;
if(gc < rf)
gc = (int) rf;
if(bc < rf)
bc = (int) rf;
if(rc > 255)
rc = 255;
if(bc > 255)
bc = 255;
if(gc > 255)
gc = 255;
pixel = I->ColorTable.data() + ((width) * y) + x;
if(I->BigEndian) {
*(pixel) = mask | (rc << 24) | (gc << 16) | (bc << 8);
} else {
*(pixel) = mask | (bc << 16) | (gc << 8) | rc;
}
b = b + 4;
if(!(0xFF & b)) {
b = 0;
g = g + 4;
if(!(0xFF & g)) {
g = 0;
r = r + 4;
}
}
}
if(!quiet) {
PRINTFB(G, FB_Color, FB_Actions)
" Color: defined table '%s'.\n", fname ENDFB(G);
}
ColorUpdateFromLut(G, -1);
ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
SceneChanged(G);
} else {
if(strlen(fname)) {
auto image = MyPNGRead(fname);
if(image) {
std::tie(width, height) = image->getSize();
if((width == 512) && (height == 512)) {
auto imageSize = width * height;
I->ColorTable.resize(imageSize);
std::copy(image->pixels(), image->pixels() + imageSize, I->ColorTable.data());
if(!quiet) {
PRINTFB(G, FB_Color, FB_Actions)
" Color: loaded table '%s'.\n", fname ENDFB(G);
}
ColorUpdateFromLut(G, -1);
} else {
PRINTFB(G, FB_Color, FB_Errors)
" ColorTableLoad-Error: invalid dimensions w x h = %d x %d; should be 512 x 512.\n",
width, height ENDFB(G);
ok = false;
}
} else {
PRINTFB(G, FB_Color, FB_Errors)
" ColorTableLoad-Error: unable to load '%s'.\n", fname ENDFB(G);
ok = false;
}
} else {
PRINTFB(G, FB_Color, FB_Actions)
" Color: purged table; colors unchanged.\n" ENDFB(G);
I->ColorTable.clear();
}
}
}
if(ok) {
ExecutiveInvalidateRep(G, cKeywordAll, cRepAll, cRepInvColor);
SceneChanged(G);
}
return (ok);
}
static void lookup_color(CColor * I, const float *in, float *out, int big_endian)
{
const float _1 = 1.0F;
unsigned int *table = I->ColorTable.data();
if(table) {
unsigned int r, g, b, rr, gr, br;
unsigned int ra, ga, ba;
unsigned int rc[2][2][2], gc[2][2][2], bc[2][2][2];
unsigned int *entry;
int x, y, z;
float fr, fg, fb, frm1x, fgm1, fbm1, rct, gct, bct;
const float _2 = 2.0F, _0 = 0.0F, _05 = 0.5F, _04999 = 0.4999F;
const float inv255 = 1.0F / 255.0F;
r = ((int) (255 * in[0] + _05)) & 0xFF;
g = ((int) (255 * in[1] + _05)) & 0xFF;
b = ((int) (255 * in[2] + _05)) & 0xFF;
rr = r & 0x3;
gr = g & 0x3;
br = b & 0x3;
r = (r >> 2);
g = (g >> 2);
b = (b >> 2);
/* now for a crude little trilinear */
for(x = 0; x < 2; x++) {
ra = r + x;
if(ra > 63)
ra = 63;
for(y = 0; y < 2; y++) {
ga = g + y;
if(ga > 63)
ga = 63;
for(z = 0; z < 2; z++) {
ba = b + z;
if(ba > 63)
ba = 63;
entry = table + (ra << 12) + (ga << 6) + ba;
if(big_endian) {
rc[x][y][z] = 0xFF & ((*entry) >> 24);
gc[x][y][z] = 0xFF & ((*entry) >> 16);
bc[x][y][z] = 0xFF & ((*entry) >> 8);
} else {
rc[x][y][z] = 0xFF & ((*entry));
gc[x][y][z] = 0xFF & ((*entry) >> 8);
bc[x][y][z] = 0xFF & ((*entry) >> 16);
}
}
}
}
frm1x = rr / 4.0F;
fgm1 = gr / 4.0F;
fbm1 = br / 4.0F;
fr = 1.0F - frm1x;
fg = 1.0F - fgm1;
fb = 1.0F - fbm1;
rct = _04999 +
(fr * fg * fb * rc[0][0][0]) +
(frm1x * fg * fb * rc[1][0][0]) +
(fr * fgm1 * fb * rc[0][1][0]) +
(fr * fg * fbm1 * rc[0][0][1]) +
(frm1x * fgm1 * fb * rc[1][1][0]) +
(fr * fgm1 * fbm1 * rc[0][1][1]) +
(frm1x * fg * fbm1 * rc[1][0][1]) + (frm1x * fgm1 * fbm1 * rc[1][1][1]);
gct = _04999 +
(fr * fg * fb * gc[0][0][0]) +
(frm1x * fg * fb * gc[1][0][0]) +
(fr * fgm1 * fb * gc[0][1][0]) +
(fr * fg * fbm1 * gc[0][0][1]) +
(frm1x * fgm1 * fb * gc[1][1][0]) +
(fr * fgm1 * fbm1 * gc[0][1][1]) +
(frm1x * fg * fbm1 * gc[1][0][1]) + (frm1x * fgm1 * fbm1 * gc[1][1][1]);
bct = _04999 +
(fr * fg * fb * bc[0][0][0]) +
(frm1x * fg * fb * bc[1][0][0]) +
(fr * fgm1 * fb * bc[0][1][0]) +
(fr * fg * fbm1 * bc[0][0][1]) +
(frm1x * fgm1 * fb * bc[1][1][0]) +
(fr * fgm1 * fbm1 * bc[0][1][1]) +
(frm1x * fg * fbm1 * bc[1][0][1]) + (frm1x * fgm1 * fbm1 * bc[1][1][1]);
if(r >= 63)
rct += rr;
if(g >= 63)
gct += gr;
if(b >= 63)
bct += br;
if(rct <= _2)
rct = _0; /* make sure black is black */
if(gct <= _2)
gct = _0;
if(bct <= _2)
bct = _0;
out[0] = rct * inv255;
out[1] = gct * inv255;
out[2] = bct * inv255;
} else {
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
if((I->Gamma != 1.0F) && (I->Gamma > R_SMALL4)) {
float inv_gamma = 1.0F / I->Gamma;
float inp = (out[0] + out[1] + out[2]) * (1 / 3.0F);
if(inp >= R_SMALL4) {
float sig = (float) (pow(inp, inv_gamma)) / inp;
out[0] *= sig;
out[1] *= sig;
out[2] *= sig;
}
}
if(out[0] > _1)
out[0] = _1;
if(out[1] > _1)
out[1] = _1;
if(out[2] > _1)
out[2] = _1;
}
/*========================================================================*/
void ColorUpdateFromLut(PyMOLGlobals * G, int index)
{
int i;
int once = false;
CColor *I = G->Color;
float *color, *new_color;
I->LUTActive = (!I->ColorTable.empty() || (I->Gamma != 1.0F));
i = index;
if(index >= 0) {
once = true;
}
for(i = 0; i < I->Color.size(); i++) {
if(!once)
index = i;
if(index < I->Color.size()) {
if(!I->LUTActive) {
I->Color[index].LutColorFlag = false;
} else if(!I->Color[index].Fixed) {
color = I->Color[index].Color;
new_color = I->Color[index].LutColor;
lookup_color(I, color, new_color, I->BigEndian);
PRINTFD(G, FB_Color)
"%5.3f %5.3f %5.3f -> %5.3f %5.3f %5.3f\n",
color[0], color[1], color[2], new_color[0], new_color[1], new_color[2]
ENDFD;
I->Color[index].LutColorFlag = true;
}
}
if(once)
break;
}
}
/*========================================================================*/
int ColorLookupColor(PyMOLGlobals * G, float *color)
{
CColor *I = G->Color;
if(I->LUTActive) {
lookup_color(I, color, color, I->BigEndian);
return true;
} else {
return false;
}
}
/*========================================================================*/
int ColorInit(PyMOLGlobals * G)
{
CColor *I = nullptr;
if ((G->Color = new CColor())) {
I = G->Color;
unsigned int test;
unsigned char *testPtr;
test = 0xFF000000;
testPtr = (unsigned char *) &test;
I->BigEndian = (*testPtr) & 0x01;
ColorReset(G); /* will alloc I->Idx and I->Lex */
return 1;
} else {
return 0;
}
}
void ColorUpdateFront(PyMOLGlobals * G, const float *back)
{
CColor *I = G->Color;
copy3f(back, I->Back);
I->Front[0] = 1.0F - back[0];
I->Front[1] = 1.0F - back[1];
I->Front[2] = 1.0F - back[2];
if(diff3f(I->Front, back) < 0.5F)
zero3f(I->Front);
}
void ColorUpdateFrontFromSettings(PyMOLGlobals * G){
int bg_gradient = SettingGet_b(G, nullptr, nullptr, cSetting_bg_gradient);
const char * bg_image_filename = SettingGet_s(G, nullptr, nullptr, cSetting_bg_image_filename);
short bg_image = bg_image_filename && bg_image_filename[0];
if (!bg_gradient){
if (!bg_image && !OrthoBackgroundDataIsSet(*G->Ortho)){
const float *v = ColorGet(G, SettingGet_color(G, nullptr, nullptr, cSetting_bg_rgb));
ColorUpdateFront(G, v);
} else {
float v[] = { 0.f, 0.f, 0.f };
ColorUpdateFront(G, v);
}
} else {
float vv[3];
const float *v = ColorGet(G, SettingGet_color(G, nullptr, nullptr, cSetting_bg_rgb_bottom));
const float *vb = ColorGet(G, SettingGet_color(G, nullptr, nullptr, cSetting_bg_rgb_top));
average3f(v, vb, vv);
ColorUpdateFront(G, vv);
}
}
/*========================================================================*/
const float *ColorGetSpecial(PyMOLGlobals * G, int index)
{
if(index >= 0)
return ColorGet(G, index);
else {
CColor *I = G->Color;
I->RGBColor[0] = (float) index;
I->RGBColor[1] = -1.0F;
I->RGBColor[2] = -1.0F;
return I->RGBColor;
}
}
const float *ColorGet(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
const float *ptr;
if((index >= 0) && (index < I->Color.size())) {
if(I->Color[index].LutColorFlag && SettingGetGlobal_b(G, cSetting_clamp_colors))
ptr = I->Color[index].LutColor;
else
ptr = I->Color[index].Color;
return (ptr);
} else if((index & cColor_TRGB_Mask) == cColor_TRGB_Bits) { /* a 24-bit RGB color */
I->RGBColor[0] = ((index & 0x00FF0000) >> 16) / 255.0F;
I->RGBColor[1] = ((index & 0x0000FF00) >> 8) / 255.0F;
I->RGBColor[2] = ((index & 0x000000FF)) / 255.0F;
if(I->LUTActive)
lookup_color(I, I->RGBColor, I->RGBColor, I->BigEndian);
return I->RGBColor;
} else if(index == cColorFront) {
return I->Front;
} else if(index == cColorBack) {
return I->Back;
} else {
/* invalid color id, then simply return white */
return (I->Color[0].Color);
}
}
const float *ColorGetRaw(PyMOLGlobals * G, int index)
{
CColor *I = G->Color;
const float *ptr;
if((index >= 0) && (index < I->Color.size())) {
ptr = I->Color[index].Color;
return (ptr);
} else if((index & cColor_TRGB_Mask) == cColor_TRGB_Bits) { /* a 24-bit RGB color */
I->RGBColor[0] = ((index & 0x00FF0000) >> 16) / 255.0F;
I->RGBColor[1] = ((index & 0x0000FF00) >> 8) / 255.0F;
I->RGBColor[2] = ((index & 0x000000FF)) / 255.0F;
return I->RGBColor;
} else {
/* invalid color id, then simply return white */
return (I->Color[0].Color);
}
}
int ColorGetEncoded(PyMOLGlobals * G, int index, float *color)
{
CColor *I = G->Color;
float *ptr;
if((index >= 0) && (index < I->Color.size())) {
if(I->Color[index].LutColorFlag && SettingGetGlobal_b(G, cSetting_clamp_colors))
ptr = I->Color[index].LutColor;
else
ptr = I->Color[index].Color;
copy3f(ptr, color);
} else if((index & cColor_TRGB_Mask) == cColor_TRGB_Bits) { /* a 24-bit RGB color */
float rgb_color[3];
rgb_color[0] = ((index & 0x00FF0000) >> 16) / 255.0F;
rgb_color[1] = ((index & 0x0000FF00) >> 8) / 255.0F;
rgb_color[2] = ((index & 0x000000FF)) / 255.0F;
if(I->LUTActive)
lookup_color(I, rgb_color, rgb_color, I->BigEndian);
copy3f(rgb_color, color);
} else if(index <= cColorExtCutoff) {
color[0] = (float) index;
color[1] = 0.0F;
color[2] = 0.0F;
} else if(index == cColorFront) {
copy3f(I->Front, color);
} else if(index == cColorBack) {
copy3f(I->Back, color);
} else {
color[0] = 1.0F;
color[1] = 1.0F;
color[2] = 1.0F;
/* invalid color id, then simply return white */
return 0;
}
return 1;
}
int Color3fToInt(PyMOLGlobals * G, const float *rgb){
unsigned int rc, gc, bc;
rc = pymol_roundf(rgb[0] * 255.);
gc = pymol_roundf(rgb[1] * 255.);
bc = pymol_roundf(rgb[2] * 255.);
return ( ( cColor_TRGB_Bits & 0xFF000000) |
( ( rc << 16 ) & 0x00FF0000) |
( ( gc << 8 ) & 0x0000FF00) |
( ( bc & 0x000000FF ) ) );
}
void ColorRenameExt(
PyMOLGlobals* G, pymol::zstring_view oldName, pymol::zstring_view newName)
{
auto I = G->Color;
// Find color idx
auto oldNameIt = I->Idx.find(oldName.c_str());
if (oldNameIt == I->Idx.end()) {
return;
}
auto idx = oldNameIt->second;
reg_name(I, idx, newName.c_str(), true);
if (ExecutiveFindObject<ObjectGadgetRamp>(G, newName) == nullptr) {
return;
}
// Find corresponding color ext and provide it the new name
auto extIt = std::find_if(I->Ext.begin(), I->Ext.end(), [oldName](const ExtRec& rec) {
return oldName == rec.Name;
});
if (extIt == I->Ext.end()) {
return;
}
auto newNameit = I->Idx.find(newName.c_str());
if (newNameit == I->Idx.end()) {
return;
}
auto& ext = *extIt;
ext.Name = newNameit->first.c_str();
}