Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

py_color.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: py_color.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.38 $       $Date: 2020/12/24 05:34:15 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *  Python color control interface.
00019  ***************************************************************************/
00020 
00021 #include "py_commands.h"
00022 #include "VMDApp.h"
00023 
00024 static const char categories_doc[] =
00025 "Get available color categories\n\n"
00026 "Returns:\n"
00027 "    (list of str): Available color categories";
00028 static PyObject* py_categories(PyObject *self, PyObject *args) {
00029   PyObject *newlist = NULL;
00030   int i;
00031 
00032   VMDApp *app;
00033   if (!(app = get_vmdapp()))
00034     return NULL;
00035 
00036   int num = app->num_color_categories();
00037 
00038   if (!(newlist = PyList_New(num)))
00039     goto failure;
00040 
00041   for (i = 0; i < num; i++) {
00042     PyList_SET_ITEM(newlist, i, as_pystring(app->color_category(i)));
00043     if (PyErr_Occurred())
00044       goto failure;
00045   }
00046   return newlist;
00047 
00048 failure:
00049   PyErr_SetString(PyExc_RuntimeError, "Problem listing color categories");
00050   Py_XDECREF(newlist);
00051   return NULL;
00052 }
00053 
00054 
00055 static const char get_colormap_doc[] =
00056 "Get name/color pairs in a given colormap category\n\n"
00057 "Args:\n"
00058 "    name (str): Colormap to query\n\n"
00059 "Returns:\n"
00060 "   (dict str->str): Name/color pairs in colormap";
00061 static PyObject* py_get_colormap(PyObject *self, PyObject *args,
00062                                  PyObject *kwargs) {
00063   const char *kwnames[] = {"name", NULL};
00064   PyObject *newdict = NULL;
00065   char *name;
00066   int i;
00067 
00068   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:color.get_colormap",
00069                                    (char**) kwnames, &name))
00070     return NULL;
00071 
00072   VMDApp *app;
00073   if (!(app = get_vmdapp()))
00074     return NULL;
00075 
00076   int num_names = app->num_color_category_items(name);
00077   if (!num_names) {
00078     PyErr_Format(PyExc_ValueError, "Colormap '%s' does not exist", name);
00079     return NULL;
00080   }
00081 
00082   if (!(newdict = PyDict_New()))
00083     goto failure;
00084 
00085   // Populate colormap dictionary
00086   for (i = 0; i < num_names; i++) {
00087     const char *key = app->color_category_item(name, i);
00088     const char *value = app->color_mapping(name, key);
00089 
00090     // SetItemString does correctly steal a reference.
00091     if (PyDict_SetItemString(newdict, key, as_pystring(value)))
00092       goto failure;
00093   }
00094   return newdict;
00095 
00096 failure:
00097   PyErr_Format(PyExc_RuntimeError, "Problem getting colormap '%s'", name);
00098   Py_XDECREF(newdict);
00099   return NULL;
00100 }
00101 
00102 
00103 static const char set_colormap_doc[] =
00104 "Update name/color pairs in given color category\n\n"
00105 "Args:\n"
00106 "    name (str): Name of colormap to update\n"
00107 "    pairs (dict str->str): Colors to update and new values. Keys must come\n"
00108 "        from the keys listed by `get_colormap` for that color category. Not\n"
00109 "        all keys need to be listed. Values must be legal color names";
00110 static PyObject* py_set_colormap(PyObject *self, PyObject *args,
00111                                  PyObject *kwargs) {
00112   const char *kwnames[] = {"name", "pairs", NULL};
00113   PyObject *newdict, *keys, *vals;
00114   PyObject *result = NULL;
00115   char *name;
00116 
00117   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!:color.set_colormap",
00118                                    (char**) kwnames, &name, &PyDict_Type,
00119                                    &newdict))
00120     return NULL;
00121 
00122   VMDApp *app;
00123   if (!(app = get_vmdapp()))
00124     return NULL;
00125 
00126   keys = PyDict_Keys(newdict);
00127   vals = PyDict_Values(newdict);
00128 
00129   for (int i=0; i<PyList_Size(keys); i++) {
00130     const char *keyname = as_constcharptr(PyList_GetItem(keys, i));
00131     const char *valname = as_constcharptr(PyList_GetItem(vals, i));
00132 
00133     if (!keyname || !valname || PyErr_Occurred()) {
00134       PyErr_SetString(PyExc_ValueError, "set_colormap dictionary invalid");
00135       goto cleanup;
00136     }
00137 
00138     if (!app->color_change_name(name, keyname, valname)) {
00139       PyErr_SetString(PyExc_ValueError,
00140                       "Invalid color category or item specified");
00141       goto cleanup;
00142     }
00143   }
00144   result = Py_None;
00145 
00146   // Getting the keys and values from the dictionary makes a new reference.
00147   // We tell Python we're done with it so as to not leak memory.
00148   // This needs to happen even if there was a problem setting color, which is
00149   // why we don't return NULL from the error checking statements above.
00150 cleanup:
00151   Py_DECREF(keys);
00152   Py_DECREF(vals);
00153 
00154   Py_XINCREF(result);
00155   return result;
00156 }
00157 
00158 
00159 static const char get_colors_doc[] =
00160 "Get all legal color names and corresponding RGB values.\n\n"
00161 "Returns:\n"
00162 "    (dict str->3 tuple of float): Color name and RGB value. RGB values\n"
00163 "        should be in the range 0 to 1.";
00164 static PyObject* py_get_colors(PyObject *self, PyObject *args) {
00165   PyObject *newdict = NULL, *newtuple = NULL;
00166   const char *name;
00167   float col[3];
00168   int i, j;
00169 
00170   VMDApp *app;
00171   if (!(app = get_vmdapp()))
00172     return NULL;
00173 
00174   if (!(newdict = PyDict_New()))
00175     goto failure;
00176 
00177   for (i = 0; i < app->num_regular_colors(); i++) {
00178     name = app->color_name(i);
00179     if (!app->color_value(name, col, col+1, col+2))
00180       goto failure;
00181 
00182     if (!(newtuple = PyTuple_New(3)))
00183       goto failure;
00184 
00185     for (j = 0; j < 3; j++) {
00186       PyTuple_SET_ITEM(newtuple, j, PyFloat_FromDouble(col[j]));
00187       if (PyErr_Occurred())
00188         goto failure;
00189     }
00190 
00191     PyDict_SetItemString(newdict, name, newtuple);
00192 
00193     if (PyErr_Occurred())
00194       goto failure;
00195   }
00196   return newdict;
00197 
00198 failure:
00199   PyErr_SetString(PyExc_RuntimeError, "Problem getting color names");
00200   Py_XDECREF(newdict);
00201   Py_XDECREF(newtuple);
00202   return NULL;
00203 }
00204 
00205 
00206 static const char get_colorlist_doc[] =
00207 "Get list of all defined colors by RGB value\n\n"
00208 "Returns:\n"
00209 "    (list of 3-tuple): Currently defined RGB values";
00210 static PyObject* py_get_colorlist(PyObject *self, PyObject *args) {
00211   PyObject *newlist = NULL, *newtuple = NULL;
00212   int i, j, listlen;
00213   const char *name;
00214   float col[3];
00215 
00216   VMDApp *app;
00217   if (!(app = get_vmdapp()))
00218     return NULL;
00219 
00220   listlen = app->num_regular_colors();
00221   if (!(newlist = PyList_New(listlen)))
00222     goto failure;
00223 
00224   for (i = 0; i < listlen; i++) {
00225     name = app->color_name(i);
00226     if (!app->color_value(name, col, col+1, col+2))
00227       goto failure;
00228 
00229     if (!(newtuple = PyTuple_New(3)))
00230       goto failure;
00231 
00232     for (j = 0; j < 3; j++) {
00233       PyTuple_SET_ITEM(newtuple, j, PyFloat_FromDouble(col[j]));
00234       if (PyErr_Occurred())
00235         goto failure;
00236     }
00237 
00238     PyList_SET_ITEM(newlist, i, newtuple);
00239     if (PyErr_Occurred())
00240       goto failure;
00241   }
00242   return newlist;
00243 
00244 failure:
00245   PyErr_SetString(PyExc_RuntimeError, "Problem getting color list");
00246   Py_XDECREF(newlist);
00247   Py_XDECREF(newtuple);
00248   return NULL;
00249 }
00250 
00251 
00252 static const char set_colors_doc[] =
00253 "Change the RGB values for named colors.\n\n"
00254 "Args:\n"
00255 "    colors (dict str->3-tuple of floats): Name and RGB values for colors \n"
00256 "        to update. RGB values should be in the range 0 to 1.";
00257 static PyObject* py_set_colors(PyObject *self, PyObject *args, PyObject *kwargs) {
00258   PyObject *newdict, *newtuple, *keys, *vals;
00259   const char *kwnames[] = {"colors", NULL};
00260   PyObject *retval = NULL;
00261   const char *keyname;
00262   float rgb[3];
00263 
00264   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!:color.set_colors",
00265                                    (char**) kwnames, &PyDict_Type, &newdict))
00266     return NULL;
00267 
00268   VMDApp *app;
00269   if (!(app = get_vmdapp()))
00270     return NULL;
00271 
00272   keys = PyDict_Keys(newdict);
00273   vals = PyDict_Values(newdict);
00274 
00275   for (int i=0; i<PyList_Size(keys); i++) {
00276     // Get color name from input dictionary
00277     keyname = as_constcharptr(PyList_GetItem(keys, i));
00278     if (PyErr_Occurred())
00279       goto cleanup;
00280 
00281     // Check this color name actually exists
00282     if (app->color_index(keyname) < 0) {
00283       PyErr_Format(PyExc_ValueError, "Unknown color '%s'", keyname);
00284       goto cleanup;
00285     }
00286 
00287     // Unpack value tuples into 3 floats
00288     newtuple = PyList_GetItem(vals, i);
00289     if (!PyTuple_Check(newtuple) || PyTuple_Size(newtuple) != 3) {
00290       PyErr_SetString(PyExc_ValueError,
00291                       "color definition must be 3-tuple of floats");
00292       goto cleanup;
00293     }
00294 
00295     for (int j=0; j<3; j++) {
00296       rgb[j] = (float)PyFloat_AsDouble(PyTuple_GET_ITEM(newtuple, j));
00297 
00298       if (PyErr_Occurred()) {
00299         PyErr_SetString(PyExc_ValueError, "color definition must be floats");
00300         goto cleanup;
00301       }
00302     }
00303 
00304     // Finally actually change the color
00305     app->color_change_rgb(keyname, rgb[0], rgb[1], rgb[2]);
00306   }
00307   retval = Py_None;
00308 
00309   // Getting the keys and values from the dictionary makes a new reference.
00310   // We tell Python we're done with it so as to not leak memory.
00311   // This needs to happen even if there was a problem setting color, which is
00312   // why we don't return NULL from the error checking statements above.
00313 cleanup:
00314   Py_DECREF(keys);
00315   Py_DECREF(vals);
00316 
00317   Py_XINCREF(retval);
00318   return retval;
00319 }
00320 
00321 
00322 static const char set_colorid_doc[] =
00323 "Set RGB value of a color at a given index\n\n"
00324 "Args:\n"
00325 "    id (int): Color ID to change\n"
00326 "    rgb (3-tuple of floats): New RGB value for color. Values should be in\n"
00327 "        the range 0 to 1.";
00328 static PyObject* py_set_colorid(PyObject *self, PyObject *args,
00329                                 PyObject *kwargs) {
00330   const char *kwnames[] = {"id", "rgb", NULL};
00331   float rgb[3];
00332   int colorid;
00333 
00334   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i(fff):color.set_colorid",
00335                                    (char**) kwnames, &colorid, &rgb[0], &rgb[1],
00336                                    &rgb[2]))
00337     return NULL;
00338 
00339   VMDApp *app;
00340   if (!(app = get_vmdapp()))
00341     return NULL;
00342 
00343   if (colorid >= app->num_regular_colors() || colorid < 0) {
00344     PyErr_Format(PyExc_ValueError, "color index '%d' out of range", colorid);
00345     return NULL;
00346   }
00347 
00348   app->color_change_rgb(app->color_name(colorid), rgb[0], rgb[1], rgb[2]);
00349 
00350   Py_INCREF(Py_None);
00351   return Py_None;
00352 }
00353 
00354 
00355 static const char scale_method_doc[] =
00356 "Get the current colorscale method\n\n"
00357 "Returns:\n"
00358 "    (str) Current method name";
00359 static PyObject* py_scale_method(PyObject *self, PyObject *args) {
00360   const char *method;
00361 
00362   VMDApp *app;
00363   if (!(app = get_vmdapp()))
00364     return NULL;
00365 
00366   method = app->colorscale_method_name(app->colorscale_method_current());
00367 
00368   return as_pystring(method);
00369 }
00370 
00371 
00372 static const char scale_methods_doc[] =
00373 "Get list of available colorscale methods\n\n"
00374 "Returns:\n"
00375 "    (list of str) Available colorscale methods";
00376 static PyObject* py_scale_methods(PyObject *self, PyObject *args) {
00377   PyObject *newlist = NULL;
00378   int i;
00379 
00380   VMDApp *app;
00381   if (!(app = get_vmdapp()))
00382     return NULL;
00383 
00384   int num = app->num_colorscale_methods();
00385   if (!(newlist = PyList_New(num)))
00386     goto failure;
00387 
00388   for (i = 0; i < num; i++) {
00389     PyList_SET_ITEM(newlist, i, as_pystring(app->colorscale_method_name(i)));
00390     if (PyErr_Occurred())
00391       goto failure;
00392   }
00393 
00394   return newlist;
00395 
00396 failure:
00397   PyErr_SetString(PyExc_RuntimeError, "Problem listing colorscales");
00398   Py_XDECREF(newlist);
00399   return NULL;
00400 }
00401 
00402 
00403 static const char scale_midpoint_doc[] =
00404 "Get current colorscale midpoint value\n\n"
00405 "Returns:\n"
00406 "    (float) Current midpoint";
00407 static PyObject* py_scale_midpoint(PyObject *self, PyObject *args) {
00408   float mid, min, max;
00409   int rev, posterize;
00410 
00411   VMDApp *app;
00412   if (!(app = get_vmdapp()))
00413     return NULL;
00414 
00415   // obtain current state
00416   app->colorscale_params(&mid, &min, &max, &rev, &posterize);
00417   return PyFloat_FromDouble(mid);
00418 }
00419 
00420 
00421 static const char scale_min_doc[] =
00422 "Get current colorscale minimum value\n\n"
00423 "Returns:\n"
00424 "    (float): Current minimum";
00425 static PyObject* py_scale_min(PyObject *self, PyObject *args) {
00426   float mid, min, max;
00427   int rev, posterize;
00428 
00429   VMDApp *app;
00430   if (!(app = get_vmdapp()))
00431     return NULL;
00432 
00433   // obtain current state
00434   app->colorscale_params(&mid, &min, &max, &rev, &posterize);
00435   return PyFloat_FromDouble(min);
00436 }
00437 
00438 
00439 static const char scale_max_doc[] =
00440 "Get current colorscale max.\n\n"
00441 "Returns:\n"
00442 "    (float) Current maximum value";
00443 static PyObject* py_scale_max(PyObject *self, PyObject *args) {
00444   float mid, min, max;
00445   int rev, posterize;
00446 
00447   VMDApp *app;
00448   if (!(app = get_vmdapp()))
00449     return NULL;
00450 
00451   // obtain current state
00452   app->colorscale_params(&mid, &min, &max, &rev, &posterize);
00453   return PyFloat_FromDouble(max);
00454 }
00455 
00456 
00457 static const char scale_reverse_doc[] =
00458 "Get current colorscale reversal flag.\n\n"
00459 "Returns:\n"
00460 "    (int) Reversed color scale flag";
00461 static PyObject* py_scale_reverse(PyObject *self, PyObject *args) {
00462   float mid, min, max;
00463   int rev, posterize;
00464 
00465   VMDApp *app;
00466   if (!(app = get_vmdapp()))
00467     return NULL;
00468 
00469   // obtain current state
00470   app->colorscale_params(&mid, &min, &max, &rev, &posterize);
00471   return as_pyint(rev);
00472 }
00473 
00474 
00475 static const char scale_posterize_doc[] =
00476 "Get current colorscale posterization count.\n\n"
00477 "Returns:\n"
00478 "    (int) Color scale posterization count";
00479 static PyObject* py_scale_posterize(PyObject *self, PyObject *args) {
00480   float mid, min, max;
00481   int rev, posterize;
00482 
00483   VMDApp *app;
00484   if (!(app = get_vmdapp()))
00485     return NULL;
00486 
00487   // obtain current state
00488   app->colorscale_params(&mid, &min, &max, &rev, &posterize);
00489   return as_pyint(posterize);
00490 }
00491 
00492 
00493 static const char set_scale_doc[] =
00494 "Set colorscale parameters. One or more parameters may be set with each "
00495 "function invocation.\n\n"
00496 "Args:\n"
00497 "    method (str): Coloring method. Valid values are in scale_methods()\n"
00498 "    midpoint (float): Midpoint of color scale\n"
00499 "    min (float): Minimum value of color scale\n"
00500 "    max (float): Maximum value of color scale\n"
00501 "    reverse (int): Reverse color scale order\n";
00502 static PyObject* py_set_scale(PyObject *self, PyObject *args, PyObject *kwargs) {
00503   const char *kwnames[] = {"method", "midpoint", "min", "max", "reverse", "posterize", NULL};
00504   float midpoint = -1, min = -1, max = -1;
00505   int reverse = 0, posterize = 0;
00506   char *method = NULL;
00507 
00508   VMDApp *app;
00509   if (!(app = get_vmdapp()))
00510     return NULL;
00511 
00512   app->colorscale_params(&midpoint, &min, &max, &reverse, &posterize);
00513 
00514   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zfffii:color.set_scale",
00515                                    (char**) kwnames, &method, &midpoint, &min,
00516                                    &max, &reverse, &posterize))
00517     return NULL;
00518 
00519   if (method) {
00520     int ind = app->colorscale_method_index(method);
00521     if (ind < 0) {
00522       PyErr_SetString(PyExc_ValueError, "Invalid color scale method");
00523       return NULL;
00524     }
00525     app->colorscale_setmethod(ind);
00526   }
00527   app->colorscale_setparams(midpoint, min, max, reverse, posterize);
00528 
00529   Py_INCREF(Py_None);
00530   return Py_None;
00531 }
00532 
00533 
00534 static PyMethodDef ColorMethods[] = {
00535   {"categories", (PyCFunction)py_categories, METH_NOARGS, categories_doc},
00536   {"get_colormap", (PyCFunction)py_get_colormap, METH_VARARGS | METH_KEYWORDS, get_colormap_doc},
00537   {"set_colormap", (PyCFunction)py_set_colormap, METH_VARARGS | METH_KEYWORDS, set_colormap_doc},
00538   {"get_colors", (PyCFunction)py_get_colors, METH_NOARGS, get_colors_doc},
00539   {"get_colorlist", (PyCFunction)py_get_colorlist, METH_NOARGS, get_colorlist_doc},
00540   {"set_colors", (PyCFunction)py_set_colors, METH_VARARGS | METH_KEYWORDS, set_colors_doc},
00541   {"set_colorid", (PyCFunction)py_set_colorid, METH_VARARGS | METH_KEYWORDS, set_colorid_doc},
00542   {"scale_method", (PyCFunction)py_scale_method, METH_NOARGS, scale_method_doc},
00543   {"scale_methods", (PyCFunction)py_scale_methods, METH_NOARGS, scale_methods_doc},
00544   {"scale_midpoint", (PyCFunction)py_scale_midpoint, METH_NOARGS, scale_midpoint_doc},
00545   {"scale_min", (PyCFunction)py_scale_min, METH_NOARGS, scale_min_doc},
00546   {"scale_max", (PyCFunction)py_scale_max, METH_NOARGS, scale_max_doc},
00547   {"scale_reverse", (PyCFunction)py_scale_reverse, METH_NOARGS, scale_reverse_doc},
00548   {"scale_posterize", (PyCFunction)py_scale_posterize, METH_NOARGS, scale_posterize_doc},
00549   {"set_scale", (PyCFunction)py_set_scale, METH_VARARGS | METH_KEYWORDS, set_scale_doc},
00550   {NULL, NULL}
00551 };
00552 
00553 
00554 static const char color_moddoc[] =
00555 "Contains methods for working with colors, including changing color "
00556 "definitions, color maps, or edit the color scale. All RGB and color scale "
00557 "values should be in the range 0 to 1";
00558 
00559 
00560 #if PY_MAJOR_VERSION >= 3
00561 struct PyModuleDef colordef = {
00562   PyModuleDef_HEAD_INIT,
00563   "color",
00564   color_moddoc,
00565   -1,
00566   ColorMethods,
00567 };
00568 #endif
00569 
00570 
00571 PyObject* initcolor() {
00572 #if PY_MAJOR_VERSION >= 3
00573   PyObject *m = PyModule_Create(&colordef);
00574 #else
00575   PyObject *m = Py_InitModule3("color", ColorMethods, color_moddoc);
00576 #endif
00577 
00578   return m;
00579 }
00580 
00581 

Generated on Fri Apr 19 02:45:13 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002